adobrovolsky97/laravel-repository-service-pattern
Installation:
composer require adobrovolsky97/laravel-repository-service-pattern
Publish the config (if needed):
php artisan vendor:publish --provider="Adobrovolsky97\LaravelRepositoryServicePattern\RepositoryServicePatternServiceProvider" --tag="config"
Define a Repository Interface:
Create a contract for your repository (e.g., app/Repositories/Contracts/UserRepositoryInterface.php):
namespace App\Repositories\Contracts;
interface UserRepositoryInterface {
public function find(int $id);
public function all();
public function create(array $data);
}
Implement the Repository:
Extend BaseRepository (e.g., app/Repositories/Eloquent/UserRepository.php):
namespace App\Repositories\Eloquent;
use App\Models\User;
use App\Repositories\Contracts\UserRepositoryInterface;
use Adobrovolsky97\LaravelRepositoryServicePattern\BaseRepository;
class UserRepository extends BaseRepository implements UserRepositoryInterface {
public function model() {
return User::class;
}
}
Bind the Repository in a Service Provider:
Register the binding in AppServiceProvider:
$this->app->bind(
UserRepositoryInterface::class,
UserRepository::class
);
First Use Case: Inject the repository into a controller or service:
use App\Repositories\Contracts\UserRepositoryInterface;
class UserController extends Controller {
protected $userRepository;
public function __construct(UserRepositoryInterface $userRepository) {
$this->userRepository = $userRepository;
}
public function index() {
$users = $this->userRepository->all();
return view('users.index', compact('users'));
}
}
CRUD Operations:
Leverage the base repository methods (find, findByField, create, update, delete) for standard operations:
$user = $this->userRepository->find(1);
$user = $this->userRepository->create(['name' => 'John']);
$this->userRepository->update(1, ['name' => 'Jane']);
Query Scopes: Define custom scopes in your model and use them via the repository:
// In User model:
public function scopeActive($query) {
return $query->where('active', true);
}
// In controller:
$activeUsers = $this->userRepository->scopeQuery('active')->all();
Service Layer Integration: Use repositories in services to encapsulate business logic:
class UserService {
protected $userRepository;
public function __construct(UserRepositoryInterface $userRepository) {
$this->userRepository = $userRepository;
}
public function register(array $data) {
$user = $this->userRepository->create($data);
// Additional logic (e.g., send welcome email)
return $user;
}
}
Transaction Management: Wrap repository operations in transactions:
DB::transaction(function () {
$this->userRepository->create($userData);
$this->orderRepository->create($orderData);
});
Event Dispatching: Trigger events after repository operations:
$this->userRepository->create($data, true); // `true` dispatches `created` event
Repository Factories: Dynamically resolve repositories based on conditions:
$repository = app()->make(\App\Repositories\Contracts\UserRepositoryInterface::class);
Caching: Cache repository results using Laravel's cache:
$this->userRepository->remember(60)->find(1); // Cache for 60 seconds
API Integration: Use repositories to abstract API calls (e.g., for third-party services):
class ThirdPartyUserRepository extends BaseRepository {
public function model() {
return ThirdPartyUser::class; // Mock model or API client
}
}
Soft Deletes: Enable soft deletes in the repository:
$this->userRepository->delete(1, true); // Soft delete
Pagination: Use built-in pagination:
$users = $this->userRepository->paginate(10);
Over-Abstraction:
User::where(...)->get()).N+1 Query Problem:
with() in repository methods or leverage Laravel's load():
$this->userRepository->with('posts')->find(1);
Repository Bloat:
Forgetting to Bind Interfaces:
Ignoring Base Repository Features:
find, all) when they exist in BaseRepository.BaseRepository and override only custom methods.Query Logging: Enable Laravel's query logging to inspect repository-generated queries:
DB::enableQueryLog();
$this->userRepository->all();
dd(DB::getQueryLog());
Repository Method Tracing:
Use dd() or dump() to trace repository method calls:
public function customMethod() {
dump('Custom method called'); // Debug entry point
// ...
}
Interface Mismatches:
Class 'App\Repositories\Eloquent\UserRepository' not found.Caching Defaults:
config/repository-service-pattern.php if needed:
'cache' => [
'enabled' => false,
],
Event Dispatching:
$this->userRepository->create($data, true); // Dispatches events
Model Binding:
model() method in your repository returns the fully qualified class name of your Eloquent model.Custom Base Repository:
BaseRepository to add shared methods:
class CustomBaseRepository extends BaseRepository {
public function findByEmail(string $email) {
return $this->model->where('email', $email)->first();
}
}
Repository Events:
repository.created) in EventServiceProvider:
protected $listen = [
'repository.created' => [
'App\Listeners\LogUserCreation',
],
];
Repository Middleware:
class UserRepository extends BaseRepository {
public function find(int $id) {
if (!auth()->can('view-user', $id)) {
abort(403);
}
return parent::find($id);
}
}
Dynamic Repository Resolution:
resolveRepository() helper to dynamically resolve repositories:
$repository = resolveRepository(UserRepositoryInterface::class);
Testing:
$mock = Mockery::mock(UserRepositoryInterface::class);
$mock->shouldReceive('find')->andReturn($user);
$this->app->instance(UserRepositoryInterface::class, $mock);
How can I help you explore Laravel packages today?