awema-pl / module-repository
Implementation of repository pattern for Laravel. The package allows out-of-the-box filtering of data based on parameters in the <b>request</b>, and also allows you to quickly integrate the list filters and custom criteria.
Fund package maintenance!
Patreon
Open Collective
Requires
- illuminate/support: ~5|~6|~7|~8|~9
README
Repository
Repository Pattern in Laravel. The package allows to filter by request out-of-the-box, as well as to integrate customized criteria and any kind of filters.
Table of Contents
Installation
Via Composer
$ composer require awema-pl/module-repository
The package will automatically register itself.
Configuration
First publish config:
php artisan vendor:publish --provider="AwemaPL\Repository\RepositoryServiceProvider" --tag="config"
// $repository->smartPaginate() related parameters 'smart_paginate' => [ // name of request parameter to take paginate by value from 'request_parameter' => 'limit', // default paginate by value 'default_limit' => 15, // max paginate by value 'max_limit' => 100, ]
Overview
Package allows you to filter data based on incoming request parameters:
https://example.com/news?title=Title&custom=value&orderBy=name_desc
It will automatically apply built-in constraints onto the query as well as any custom scopes and criteria you need:
protected $searchable = [ // where 'title' equals 'Title' 'title', ]; protected $scopes = [ // and custom parameter used in your scope 'custom' => MyScope::class, ];
class MyScope extends ScopeAbstract { public function scope($builder, $value, $scope) { return $builder->where($scope, $value)->orWhere(...); } }
Ordering by any field is available:
protected $scopes = [ // orderBy field 'orderBy' => OrderByScope::class, ];
Package can also apply any custom criteria:
return $this->news->withCriteria([ new MyCriteria([ 'category_id' => '1', 'name' => 'Name' ]) ... ])->get();
Usage
Create a Model
Create your model:
namespace App; use Illuminate\Database\Eloquent\Model; class News extends Model { ... }
Create a Repository
Extend it from AwemaPL\Repository\Eloquent\BaseRepository
and provide entity()
method to return full model class name:
namespace App; use AwemaPL\Repository\Eloquent\BaseRepository; class NewsRepository extends BaseRepository { public function entity() { return News::class; } }
Use built-in methods
use App\NewsRepository; class NewsController extends BaseController { protected $news; public function __construct(NewsRepository $news) { $this->news = $news; } .... }
Execute the query as a "select" statement or get all results:
$news = $this->news->get();
Execute the query and get the first result:
$news = $this->news->first();
Find a model by its primary key:
$news = $this->news->find(1);
Add basic where clauses and execute the query:
$news = $this->news->->findWhere([ // where id equals 1 'id' => '1', // other "where" operations ['news_category_id', '<', '3'], ... ]);
Paginate the given query:
$news = $this->news->paginate(15);
Paginate the given query into a simple paginator:
$news = $this->news->simplePaginate(15);
Paginate the given query by 'limit' request parameter:
$news = $this->news->smartPaginate();
Add an "order by" clause to the query:
$news = $this->news->orderBy('title', 'desc')->get();
Save a new model and return the instance:
$news = $this->news->create($request->all());
Update a record:
$this->news->update($request->all(), $id);
Delete a record by id:
$this->news->destroy($id);
Attach models to the parent:
$this->news->attach($parentId, $relationship, $idsToAttach);
Detach models from the relationship:
$this->news->detach($parentId, $relationship, $idsToDetach);
Find model or throw an exception if not found:
$this->news->findOrFail($id);
Execute the query and get the first result or throw an exception:
$this->news->firstOrFail();
Create a Criteria
Criteria are a way to build up specific query conditions.
use AwemaPL\Repository\Contracts\CriterionInterface; class MyCriteria implements CriterionInterface { protected $conditions; public function __construct(array $conditions) { $this->conditions = $conditions; } public function apply($entity) { foreach ($this->conditions as $field => $value) { $entity = $entity->where($field, '=', $value); } return $entity; } }
Multiple Criteria can be applied:
use App\NewsRepository; class NewsController extends BaseController { protected $news; public function __construct(NewsRepository $news) { $this->news = $news; } public function index() { return $this->news->withCriteria([ new MyCriteria([ 'category_id' => '1', 'name' => 'Name' ]), new WhereAdmin(), ... ])->get(); } }
Scope, Filter and Order
In your repository define which fields can be used to scope your queries by setting $searchable
property.
protected $searchable = [ // where 'title' equals parameter value 'title', // orWhere equals 'body' => 'or', // where like 'author' => 'like', // orWhere like 'email' => 'orLike', ];
Search by searchables:
public function index($request) { return $this->news->scope($request)->get(); }
https://example.com/news?title=Title&body=Text&author=&email=gmail
Also several serchables enabled by default:
protected $scopes = [ // orderBy field 'orderBy' => OrderByScope::class, // where created_at date is after 'begin' => WhereDateGreaterScope::class, // where created_at date is before 'end' => WhereDateLessScope::class, ];
$this->news->scope($request)->get();
Enable ordering for specific fields by adding $orderable
property to your model class:
public $orderable = ['email'];
https://example.com/news?orderBy=email_desc&begin=2019-01-24&end=2019-01-26
orderBy=email_desc
will order by email in descending order, orderBy=email
- in ascending
You can also build your own custom scopes. In your repository override scope()
method:
public function scope($request) { // apply build-in scopes parent::scope($request); // apply custom scopes $this->entity = (new NewsScopes($request))->scope($this->entity); return $this; }
Create your scopes
class and extend ScopesAbstract
use AwemaPL\Repository\Scopes\ScopesAbstract; class NewsScopes extends ScopesAbstract { protected $scopes = [ // here you can add field-scope mappings 'field' => MyScope::class, ]; }
Now you can build any scopes you need:
use AwemaPL\Repository\Scopes\ScopeAbstract; class MyScope extends ScopeAbstract { public function scope($builder, $value, $scope) { return $builder->where($scope, $value); } }
Artisan Commands
Package provides useful artisan command:
php artisan repository:generate Models/Order --scope=Search
It'll generate several classes for App\Models\Order
:
Main repository: App\Repositories\Orders\OrdersRepository
Main scopes class: App\Repositories\Orders\Scopes\OrdersScopes
Individual search scope class: App\Repositories\Orders\Scopes\SearchOrdersScope
Testing
The coverage of the package is .
You can run the tests with:
composer test
Contributing
Please see contributing.md for details and a todolist.