longaodai/laravel-repository

A clean and simple repository pattern implementation for Laravel

v1.0.1 2025-09-15 15:06 UTC

README

Latest Version on Packagist Total Downloads License

A comprehensive Laravel package that implements the Repository and Service Layer design patterns, providing a clean and maintainable way to interact with your Eloquent models. This package promotes separation of concerns, making your code more testable, organized, and following SOLID principles.

Features

  • Repository Pattern Implementation - Clean abstraction layer between your controllers and models
  • Service Layer Pattern - Business logic separation from controllers
  • Artisan Commands - Generate repositories and services with simple commands
  • Interface-Based - Follows dependency inversion principle
  • Performance Optimized - Built with Laravel best practices
  • Rich Query Methods - Comprehensive set of query methods out of the box

Table of Contents

Installation

You can install the package via Composer:

composer require longaodai/laravel-repository

Configuration

Publish the configuration file:

php artisan vendor:publish --tag=laravel-repository

This will create a config/repository.php file where you can customize.

Quick Start

1. Generate Repository and Service

Generate a complete repository and service for your model:

php artisan make:repository User

Options:

  • --model=ModelName : Specify the model class name
  • --force : Overwrite existing files Result:
Repository Interface ........... App\Repositories\User\UserRepositoryInterface
Repository Implementation ...... App\Repositories\User\UserEloquentRepository
Service Interface .............. App\Services\User\UserServiceInterface  
Service Implementation ......... App\Services\User\UserService

2. Register Service Providers

Add the generated service providers to your bootstrap/providers.php (only one in the first time):

<?php
return [
    App\Providers\AppServiceProvider::class,
    // ... other providers
    
    // Add these lines
    App\Providers\RepositoryServiceProvider::class,
    App\Providers\InternalServiceProvider::class,
];

3. Usage

Generating Repositories

The package provides an Artisan command to generate repositories and services:

# Generate repository for existing model
php artisan make:repository User

# Generate repository and specify model
php artisan make:repository User --model=User

# Force overwrite existing files
php artisan make:repository User --force

This generates four files:

  1. UserRepositoryInterface - Repository contract
  2. UserEloquentRepository - Eloquent implementation
  3. UserServiceInterface - Service contract
  4. UserService - Service implementation

Use in Controllers

Inject and use the service in your controllers:

<?php

namespace App\Http\Controllers;

use App\Services\User\UserServiceInterface;
use Illuminate\Http\Request;

class UserController extends Controller
{
    protected $userService;

    public function __construct(UserServiceInterface $userService)
    {
        $this->userService = $userService;
    }

    /**
     * Display a paginated list of users
     */
    public function index(Request $request)
    {
        // Get paginated list with optional filters
        $users = $this->userService->getList([
            'name' => $request->get('name'),
        ]);

        return response()->json($users);
    }

    /**
     * Store a new user
     */
    public function store(Request $request)
    {
        $userData = collect([
            'name' => $request->name,
            'email' => $request->email,
            'password' => bcrypt($request->password),
        ]);

        $user = $this->userService->create($userData);

        return response()->json($user, 201);
    }

    /**
     * Display the specified user
     */
    public function show($id)
    {
        $user = $this->userService->find(['id' => $id]);

        if (!$user) {
            return response()->json(['message' => 'User not found'], 404);
        }

        return response()->json($user);
    }

    /**
     * Update the specified user
     */
    public function update(Request $request, $id)
    {
        $updateData = [
            'name' => $request->name,
            'email' => $request->email,
        ];

        $user = $this->userService->update($updateData, ['id' => $id]);

        return response()->json($user);
    }

    /**
     * Remove the specified user
     */
    public function destroy($id)
    {
        $deleted = $this->userService->destroy(['id' => $id]);

        return response()->json(['deleted' => $deleted]);
    }
}

Custom Repository Methods

Add custom methods to your repository:

<?php

namespace App\Repositories\User;

use App\Models\User;
use LongAoDai\LaravelRepository\Eloquent\BaseRepository;

class UserEloquentRepository extends BaseRepository implements UserRepositoryInterface
{
    public function model(): string
    {
        return User::class;
    }

    /**
     * Hook for filtering queries.
     *
     * @param RepositoryResponse $params
     * @return static
     */
    protected function filter(RepositoryResponse $params): static
    {
        if (!empty($params->get('id'))) {
            $this->method('where', 'id', $params->get('id'));
        }
        
        if (!empty($params->get('name'))) {
            $this->method('where', 'name', $params->get('name'));
        }
        
        if (!empty($params->get('status'))) {
            $this->method('where', 'status', $params->get('status'));
        }
        
        if (!empty($params->option('with_post'))) {
            $this->method('with', ['posts' => function ($query) {
                return $query->select('id', 'name');
            }]);
        }

        return parent::filter($params);
    }

    /**
     * Hook for masking data before update.
     *
     * @param RepositoryResponse $params
     * @return static
     */
    protected function mask(RepositoryResponse $params): static
    {
        if (!empty($params->option('id'))) {
            $this->method('where', 'id', $params->option('id'));
        }

        return parent::mask($params);
    }
}

Service Layer Business Logic

Implement complex business logic in services:

<?php

namespace App\Services\User;

use App\Repositories\User\UserRepositoryInterface;
use App\Notifications\WelcomeNotification;
use Illuminate\Support\Facades\DB;

class UserService implements UserServiceInterface
{
    protected $userRepository;

    public function __construct(UserRepositoryInterface $userRepository)
    {
        $this->userRepository = $userRepository;
    }

    /**
     * Create a new user with welcome notification
     */
    public function createUserWithWelcome($userData)
    {
        return DB::transaction(function () use ($userData) {
            // Create user
            $user = $this->userRepository->create($userData);
            // Send welcome notification
            $user->notify(new WelcomeNotification());
            // Log user creation
            logger('New user created', ['user_id' => $user->id]);
            
            return $user;
        });
    }

    /**
     * Get users with specific business logic
     */
    public function getActiveUsersWithPosts($data, $options)
    {
        return $this->userRepository->all(
            [
                'status' => $data['status'],
            ], [
                'limit' => 10,
                'with_post' => true,
            ]
        );
    }
}

Available Methods

The base repository provides these methods out of the box:

Method Description Example
all() Get all records $service->all()
getList($params) Get paginated list with filters $service->getList(['per_page' => 10])
find($conditions) Find by id $service->find(['id' => 1])
first($conditions) Get first record by conditions $service->first(['status' => 'active'])
create($data) Create new record $service->create(['name' => 'John'])
update($data, $conditions) Update records $service->update(['name' => 'Jane'], ['id' => 1])
updateOrCreate($conditions, $data) Update or create record $service->updateOrCreate(['email' => 'test@example.com'], ['name' => 'Test'])
destroy($conditions) Delete records $service->destroy(['id' => 1])

Advanced Query Examples

// Get all users
$users = $userService->all();

// Get paginated list with search
$users = $userService->getList([
    'per_page' => 20,
    'search' => 'john',
    'status' => 'active'
]);

// Find specific user
$user = $userService->find(['id' => 1]);

// Get first user with conditions
$activeUser = $userService->first(['status' => 'active']);
$user = $userService->first(['email' => 'user@example.com']);

// Create new user
$newUser = $userService->create(collect([
    'name' => 'John Doe',
    'email' => 'john@example.com',
    'password' => bcrypt('password123'),
    'status' => 'active'
]));

// Update user
$updatedUser = $userService->update([
    'name' => 'John Smith',
    'updated_at' => now()
], ['id' => 1]);

// Update or create user
$user = $userService->updateOrCreate(
    collect(['email' => 'jane@example.com']), // conditions
    collect(['name' => 'Jane Doe', 'status' => 'active']) // data to update/create
);

// Delete user
$deleted = $userService->destroy(['id' => 1]);

// Delete multiple users
$deleted = $userService->destroy(['status' => 'inactive']);

Best Practices

1. Keep Controllers Thin

Move business logic to services, not controllers:

// ❌ Bad - Logic in controller
public function store(Request $request)
{
    $user = User::create([
        'name' => $request->name,
        'email' => $request->email,
        'password' => bcrypt($request->password),
    ]);
    
    $user->notify(new WelcomeNotification());
    
    return response()->json($user);
}

// ✅ Good - Logic in service
public function store(Request $request)
{
    $user = $this->userService->createUserWithWelcome($request->validated());
    
    return response()->json($user);
}

2. Use Type Hinting

Always use interface type hinting:

// ✅ Good
public function __construct(UserServiceInterface $userService)
{
    $this->userService = $userService;
}

// ❌ Bad
public function __construct(UserService $userService)
{
    $this->userService = $userService;
}

3. Handle Exceptions Properly

Implement proper exception handling:

public function findUser($id)
{
    try {
        $user = $this->userService->find(['id' => $id]);
        
        if (!$user) {
            throw new RepositoryFailureHandlingException('User not found');
        }
        
        return $user;
    } catch (Exception $e) {
        logger()->error('Error finding user', ['id' => $id, 'error' => $e->getMessage()]);
        throw $e;
    }
}

Requirements

  • PHP >= 8.0
  • Laravel >= 9.0

Security

If you discover any security-related issues, please email vochilong.work@gmail.com instead of using the issue tracker. LongAoDai

License

The MIT License (MIT). Please see License File for more information.

Support

If you find this package helpful, please consider:

  • Starring the repository
  • Reporting any bugs you encounter
  • Suggesting new features
  • Improving documentation

For questions and support, please use the GitHub Discussions or create an issue.