maarsson / laravel-repository
Complete repository pattern for Laravel 8
Installs: 79
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 1
Forks: 0
Open Issues: 0
Type:package
Requires
- php: ^8.1
- laravel/framework: ^9.0|^10.0
README
This package adds and extendable repository pattern to your Laravel project.
Though using repository pattern over Eloquent models may be an outworn idea, you can make your Laravel project code clean with a bunch of built-in functions of this package.
Installation
- Add package to your Laravel project:
composer require maarsson/laravel-repository
- Publish config file
php artisan vendor:publish --tag=repository-config
Usage
-
Create repository to your existing Eloquent model:
php artisan make:repository 'YourModel'
. -
Add your model name to the
config/repository.php
:'models' => [ 'YourModel', ],
-
Use dependency injection in your code:
use App\Interfaces\YourModelRepositoryInterface; class TestController extends Controller { private $repository; public function __construct(YourModelRepositoryInterface $repository) { $this->repository = $repository; } }
-
Optionally you can add custom methods to the repository:
class YourModelRepository extends AbstractEloquentRepository implements YourModelRepositoryInterface { public function doSomeConverting() { // your code here } }
and also to the interface class:
interface YourModelRepositoryInterface { public function doSomeConverting(); }
Methods
Retrieving entities
Retrieving all entities from the database
$collection = $this->repository->all();
Retrieving entity by ID
$entity = $this->repository->find(3);
Retrieving entities by a specific column
$collection = $this->repository->findBy('title', 'Music');
Retrieving the first or last entity (by its timestamp)
$entity = $this->repository->first(); $entity = $this->repository->last();
You can also specify which columns to be fetched
$collection = $this->repository->all('id', 'title'); $collection = $this->repository->find(3, 'id', 'title'); $entity = $this->repository->first('id', 'title'); $entity = $this->repository->last('id', 'title');
Retrieving the number of entities
$count = $this->repository->count();
Creating an entity
$entity = $this->repository->create([ 'title' => 'Music', 'price' => 12.9, ]);
Updating an entity
Updating an entity by ID
$entity = $this->repository->update( 3, // the id of the entity to be updated [ 'title' => 'Music', 'price' => 12.9, ] );
Deleting an entity
Deleting an entity by ID
$this->repository->delete(3);
Mass-deleting entities by where closure
$this->repository->deleteWhere('value', '<', 100);
Paginating
Laravels paginating can be applied on queries, even combinated with other queries.
// simple pagination $this->repository ->paginate($per_page = 15); // pagination on ordered result $this->repository ->orderBy('name') ->paginate($per_page = 15);
Getters: custom filtering and sorting
Entities can be easily filtered or sorted using getter classes. Filter keys and sorter methods in the request without matching function in the getter class will be ignored.
-
Create getter class to your existing repository:
php artisan make:getter 'YourModel'
. -
Add the
Maarsson\Repository\Traits\GetterableTrait
trait to the model repository:namespace App\Repositories; use Maarsson\Repository\Traits\GetterableTrait; class YourModelRepository extends AbstractEloquentRepository implements CustomerRepositoryInterface { use GetterableTrait; }
-
Add the required filtering method(s) to the created
YourModelGetter
class. Filter method names must be camel cased and must end withFilter
:protected function nameFilter(string|null $searchString): \Illuminate\Database\Eloquent\Builder { return $this->builder->where('name', 'LIKE', '%' . $searchString . '%'); }
-
Get the filtered collection using the
filter[]
parameter in the query// HTTP GET //localhost/yourmodel?filter[name]=foo public function index(\App\Filters\YourModelGetter $getter) { return $this->repository ->filter($getter) ->get(); }
-
Add the required sorting method(s) to the created
YourModelGetter
class. Sorter method names must be camel cased and must end withSorter
:protected function relatedModelDateSorter(): \Illuminate\Database\Eloquent\Builder { return RelatedModel::select('date') ->whereColumn('this_model_column', 'related_table.column'); }
-
Get the sorted collection using the
sort_by
parameter in the query// HTTP GET //localhost/yourmodel?sort_by=related_model_date public function index(\App\Filters\YourModelGetter $getter) { return $this->repository ->order($getter) ->get(); }
Simplified filtering, sorting and paginating
Get the filtered, sorted and paginated result by the helper methods.
// HTTP GET //localhost/yourmodel?filter[name]=foo&page=5&per_page=20&sort_by=related_model_date&sort_order=desc public function index(\App\Filters\YourModelGetter $getter) { return $this->repository ->filter($getter) ->order() ->paginate(); }
The following request parameters are considered:
filter[]
default:null
page
default:1
per_page
default:20
sort_by
default:'id'
sort_order
default:'asc'
Attribute (and relation) filter
Using the attribute filter trait you can control the model attributes to be fetched, including the relations (and its attributes).
-
Add the
Maarsson\Repository\Traits\EloquentAttributeFilterTrait
trait to the Eloquent model:namespace App\Models; use Maarsson\Repository\Traits\EloquentAttributeFilterTrait; class YourModel extends Model { use EloquentAttributeFilterTrait; }
-
Get full control of the appended attributes and relations using a simple dot-notated array passed to the
withAttributes()
method. In this example you can also see how to combine this with the pagination.// HTTP GET //localhost/yourmodel?filter[name]=foo&page=5&per_page=20&sort_by=related_model_date&sort_order=desc public function index(\App\Filters\YourModelGetter $getter) { return $this->repository ->filter($getter) ->order() ->paginate(); ->through( fn ($item) => $item->withAttributes([ 'id', 'name', // a model property 'finalPrice', // even a model accessor 'users' // a relation (with all of its attributes) 'users.posts:id,title', // a relations relation (with limited attributes) ]) ); }
Using Eloquent Builder methods
Certain builder methods are available directly. Note using of the getter method at the end of the query.
Sophisticated where queries:
$this->repository ->where('value', '<', 100) ->get(); $this->repository ->where('value', '>', 50) ->orWhere('value', '<', 100) ->get();
Ordering result:
$this->repository->orderBy('title', 'desc')->get();
Working with soft-deleted entities:
$this->repository->withTrashed()->get(); $this->repository->onlyTrashed()->get();
Working with relations:
$this->repository->with('authors')->get();
Reaching native Eloquent builder:
$this->repository->builder() ->whereIn('id', [1,2,3]); ->limit(5); ->offset(10); ->toSql();
Events
Events fired automatically in certain cases:
Create events
- Before an entity is beeing created, the
\App\Events\YourModelIsCreatingEvent::class
is fired:- where the
$event->attributes
property contains the creating data array.
- where the
- After an entity was created, the
\App\Events\YourModelWasCreatedEvent::class
is fired:- where the
$event->model
property contains the created entity, - and the
$event->attributes
property contains the creating data array.
- where the
Update events
- Before an entity is beeing updated, the
\App\Events\YourModelIsUpdatingEvent::class
is fired:- where the
$event->model
property contains the original entity, - and the
$event->attributes
property contains the updating data array.
- where the
- After an entity was updated, the
\App\Events\YourModelWasUpdatedEvent::class
is fired:- where the
$event->model
property contains the updated entity, - and the
$event->attributes
property contains the updating data array.
- where the
Delete events
- Before an entity is beeing deleted, the
\App\Events\YourModelIsDeletingEvent::class
is fired:- where the
$event->model
property contains the original entity.
- where the
- After an entity was deleted, the
\App\Events\YourModelWasDeletedEvent::class
is fired:- where the
$event->model
property contains the already deleted entity.
- where the
Listeners
Event listeners automatically sets up for the events with the same naming conventions:
YourModelIsCreatingListener
listens theYourModelIsCreatingEvent
YourModelWasCreatedListener
listens theYourModelWasCreatedEvent
...and so on.
If a listeners handle()
method of a before-event returns false
it will prohibit the given action.
License
This package is open-sourced software licensed under the MIT license.