sajadsdi/laravel-repository
Laravel repository pattern package to structure data access in your apps. Provides base repository classes, common CRUD methods, and an easy way to keep queries and persistence logic out of controllers and services for cleaner, testable code.
Installation
composer require sajadsdi/laravel-repository
Publish the config (optional):
php artisan vendor:publish --provider="Sajadsdi\LaravelRepository\LaravelRepositoryServiceProvider"
Define a Repository
Create a repository class (e.g., UserRepository) extending BaseRepository:
namespace App\Repositories;
use App\Models\User;
use Sajadsdi\LaravelRepository\BaseRepository;
class UserRepository extends BaseRepository
{
public function __construct(User $model)
{
parent::__construct($model);
}
}
Register the Repository
Bind the repository in a service provider (e.g., AppServiceProvider):
$this->app->bind(
\App\Repositories\UserRepository::class,
\App\Repositories\UserRepository::class
);
First Use Case Inject and use the repository in a controller/service:
use App\Repositories\UserRepository;
class UserController extends Controller
{
protected $userRepository;
public function __construct(UserRepository $userRepository)
{
$this->userRepository = $userRepository;
}
public function index()
{
$users = $this->userRepository->all(); // Basic query
return view('users.index', compact('users'));
}
}
CRUD Operations Leverage built-in methods for common queries:
$user = $this->userRepository->find(1); // Find by ID
$users = $this->userRepository->paginate(10); // Pagination
$this->userRepository->create(['name' => 'John']); // Create
$this->userRepository->update(1, ['name' => 'Jane']); // Update
$this->userRepository->delete(1); // Delete
Custom Scopes Define reusable query scopes in the repository:
class UserRepository extends BaseRepository
{
public function scopeActive($query)
{
return $query->where('is_active', true);
}
public function getActiveUsers()
{
return $this->scopeActive()->get();
}
}
Relationship Handling
Use with() for eager loading:
$users = $this->userRepository->with('posts')->get();
Transactions Wrap operations in transactions:
$this->userRepository->transaction(function ($repo) {
$repo->create(['name' => 'Alice']);
$repo->create(['name' 'Bob']);
});
$this->mock(UserRepository::class)->shouldReceive('find')->andReturn($user);
Model Binding
Ensure the repository’s constructor matches the bound model exactly (e.g., User vs. App\Models\User). Use fully qualified names if needed.
Overriding Base Methods
Avoid redefining model() in child repositories unless extending functionality. Instead, use scopes or custom methods.
Performance with Scopes
Scopes are applied in the order they’re called. Chain them carefully to avoid unintended WHERE clauses:
// Bad: Applies both scopes to the same query
$this->scopeActive()->scopeAdmin()->get();
// Good: Explicitly chain
$this->scopeActive()->where('role', 'admin')->get();
Transaction Isolation
Nested transactions may fail silently. Use save() or create() within transactions instead of direct model persistence.
\DB::enableQueryLog();
$this->userRepository->all();
\DB::getQueryLog();
retrieved, created) via:
$this->userRepository->onRetrieved(function ($model) {
logger($model);
});
Custom Base Repository
Extend BaseRepository to add shared logic:
class CustomBaseRepository extends BaseRepository
{
public function softDelete($id)
{
return $this->find($id)->delete();
}
}
Dynamic Scopes
Use addDynamicScope() to enable dynamic filtering:
$this->userRepository->scopeByRole('admin')->get();
Event Dispatching Trigger events after operations:
$this->userRepository->afterCreate(function ($model) {
event(new UserCreated($model));
});
API Resources Pair repositories with API resources for consistent JSON responses:
return $this->userRepository->paginate(10)->resource();
How can I help you explore Laravel packages today?