Installation:
composer require czim/laravel-repository
Publish the config (if needed):
php artisan vendor:publish --provider="Czim\Repository\RepositoryServiceProvider"
Define a Model:
// app/Models/User.php
class User extends Model
{
// Model definition
}
Create a Repository Interface:
// app/Repositories/Interfaces/UserRepositoryInterface.php
namespace App\Repositories\Interfaces;
use Czim\Repository\Interfaces\RepositoryInterface;
interface UserRepositoryInterface extends RepositoryInterface
{
// Custom methods here
}
Generate a Repository Class:
php artisan make:repository UserRepository --interface=UserRepositoryInterface
This creates:
// app/Repositories/UserRepository.php
class UserRepository extends \Czim\Repository\Repository
{
public function __construct(User $model)
{
$this->model = $new Model($model);
}
}
Register in Service Provider:
// app/Providers/AppServiceProvider.php
public function register()
{
$this->app->bind(
\App\Repositories\Interfaces\UserRepositoryInterface::class,
\App\Repositories\UserRepository::class
);
}
First Use Case:
// In a controller or service
$user = app(UserRepositoryInterface::class)->find(1);
vendor/czim/laravel-repository/src/Interfaces/RepositoryInterface.php
(Defines core methods like find(), all(), create(), etc.)vendor/czim/laravel-repository/src/Criteria/
(For filtering/scoping queries dynamically)vendor/czim/laravel-repository/src/Events/
(For post-retrieval model manipulation)// Find a single record
$user = $repo->find(1);
// Find all with eager loading
$users = $repo->all(['posts']);
// Create/update/delete
$repo->create(['name' => 'John']);
$repo->update(1, ['name' => 'Jane']);
$repo->delete(1);
// Apply a global scope (e.g., for "active" users)
$repo->pushCriteria(new ActiveUsersCriteria());
// Temporarily apply a scope
$activeUsers = $repo->scope(function ($query) {
return $query->where('active', true);
})->all();
// Remove a scope
$repo->popCriteria();
// Apply a transformer (e.g., for API responses)
$repo->pushTransformer(new UserTransformer());
// Temporarily transform models
$transformedUsers = $repo->transform(function ($model) {
$model->api_key = 'secret-' . $model->id;
return $model;
})->all();
// Mock the repository in tests
$mockRepo = Mockery::mock(UserRepositoryInterface::class);
$mockRepo->shouldReceive('find')->andReturn($user);
// Inject into tested class
$this->app->instance(UserRepositoryInterface::class, $mockRepo);
// In a controller/service
public function __construct(UserRepositoryInterface $repo) {
$this->repo = $repo;
}
// Eager load relationships
$users = $repo->with(['posts', 'roles'])->all();
// Access relationships
foreach ($users as $user) {
$user->posts->first();
}
DB::transaction(function () use ($repo) {
$repo->create(['name' => 'Alice']);
$repo->create(['name' => 'Bob']);
});
// Add to your repository class
public function findByEmail($email)
{
return $this->findWhere(['email' => $email])->first();
}
// Listen for model events
$repo->on('retrieved', function ($models) {
// Post-process models after retrieval
});
Criteria Order Matters:
pushCriteria(A) then pushCriteria(B) applies B first, then A.popCriteria() to remove the last applied scope.Model Binding:
find($id) or findWhere() explicitly.Transformer Conflicts:
Memory Leaks:
popCriteria() to clean up after operations.Mass Assignment:
$model->fill() or $model->update() carefully.Query Logging: Enable Laravel’s query logging to inspect generated queries:
DB::enableQueryLog();
$repo->all();
dd(DB::getQueryLog());
Criteria Debugging:
Temporarily override a Criteria’s apply() method to log conditions:
$repo->pushCriteria(new class extends Criteria {
public function apply($model, Builder $query)
{
\Log::debug('Criteria query:', [$query->toSql(), $query->getBindings()]);
return parent::apply($model, $query);
}
});
Transformer Debugging:
Add a dump() in the transformer’s transform() method to inspect model states:
$repo->pushTransformer(new class extends Transformer {
public function transform($model)
{
\Log::debug('Model before transform:', $model->toArray());
// ... transform logic ...
return $model;
}
});
Custom Criteria:
Extend \Czim\Repository\Criteria\Criteria to create reusable scopes:
class PublishedCriteria extends Criteria
{
public function apply($model, Builder $query)
{
return $query->where('published_at', '<=', now());
}
}
Custom Transformers:
Extend \Czim\Repository\Transformers\Transformer for model post-processing:
class UserTransformer extends Transformer
{
public function transform($model)
{
$model->full_name = "{$model->first_name} {$model->last_name}";
return $model;
}
}
Repository Events:
Listen for events like retrieved, created, or deleted:
$repo->on('created', function ($model) {
\Log::info("Model created: {$model->id}");
});
Repository Decorators: Decorate repositories to add cross-cutting concerns (e.g., logging, caching):
class LoggingRepositoryDecorator extends Repository
{
public function __construct(RepositoryInterface $repo)
{
$this->repo = $repo;
}
public function find($id)
{
\Log::debug("Finding model ID: {$id}");
return $this->repo->find($id);
}
}
Default Model Binding:
The config (config/repository.php) allows setting a default model for repositories:
'default_model' => \App\Models\User::class,
Criteria Auto-Pushing: Disable auto-pushing of global Criteria in the config:
'auto_push_criteria' => false,
Transformer Auto-Pushing: Control whether transformers are auto-applied:
'auto_push_transformer' => true,
Avoid N+1 Queries:
Always use with() for eager loading:
$repo->with(['posts', 'comments'])->all();
Batch Processing:
Use chunk() for large datasets:
$repo->chunk(100, function ($models) {
foreach ($models as $model) {
// Process batch
}
});
Criteria Caching: Cache repeated Criteria applications:
$repo->
How can I help you explore Laravel packages today?