Laravel 5 Repositories is used to abstract the data layer, making our application more flexible to maintain.
You want to understand a little more about the Repository pattern? Read this great article.
Add prettus/l5-repository
to the "require" section of your composer.json
file.
"prettus/l5-repository": "2.0.*"
Run composer update
to get the latest version of the package.
In your config/app.php
add 'Prettus\Repository\Providers\RepositoryServiceProvider'
to the end of the providers
array:
'providers' => array(
...,
'Illuminate\Workbench\WorkbenchServiceProvider',
'Prettus\Repository\Providers\RepositoryServiceProvider',
),
Publish Configuration
php artisan vendor:publish --provider="Prettus\Repository\Providers\RepositoryServiceProvider"
- all($columns = array('*'))
- paginate($limit = null, $columns = ['*'])
- find($id, $columns = ['*'])
- findByField($field, $value, $columns = ['*'])
- findWhere(array $where, $columns = ['*'])
- create(array $attributes)
- update(array $attributes, $id)
- delete($id)
- with(array $relations);
- hidden(array $fields);
- visible(array $fields);
- getFieldsSearchable();
- setPresenter($presenter);
- skipPresenter($status = true);
- pushCriteria(CriteriaInterface $criteria)
- getCriteria()
- getByCriteria(CriteriaInterface $criteria)
- skipCriteria($status = true)
- getFieldsSearchable()
- setCacheRepository(CacheRepository $repository)
- getCacheRepository()
- getCacheKey($method, $args = null)
- getCacheMinutes()
- skipCache($status = true)
- present($data);
- apply($model, RepositoryInterface $repository);
- transform();
Create your model normally, but it is important to define the attributes that can be filled from the input form data.
namespace App;
class Post extends Eloquent { // or Ardent, Or any other Model Class
protected $fillable = [
'title',
'author',
...
];
...
}
namespace App;
use Prettus\Repository\Eloquent\BaseRepository;
class PostRepository extends BaseRepository {
/**
* Specify Model class name
*
* @return string
*/
function model()
{
return "App\\Post";
}
}
Create your repositories easily through the generator.
You must first configure the storage location of the repository files. By default is the "app" folder and the namespace "App".
...
'generator'=>[
'basePath' =>app_path(),
'rootNamespace' =>'App\\'
]
You may want to save the root of your project folder out of the app and add another namespace for example
...
'generator'=>[
'basePath' => base_path('src/Lorem'),
'rootNamespace' => 'Lorem\\'
]
To generate a repository for your Post model, use the following command
php artisan repository:generate Post
To generate a repository for your Post model with Blog namespace, use the following command
php artisan repository:generate "Blog\Post"
Added fields that are fillable
php artisan repository:generate "Blog\Post" --fillable="title,content"
When running commado, you will be creating the "Entities" folder and "Repositories" inside the folder that you set as the default.
Done, done that just now you do bind its interface for your real repository
App::bind('{YOUR_NAMESPACE}Repositories\PostRepository', '{YOUR_NAMESPACE}Repositories\PostRepositoryEloquent');
And use
public function __construct({YOUR_NAMESPACE}Repositories\PostRepository $repository){
$this->repository = $repository;
}
namespace App\Http\Controllers;
use App\PostRepository;
class PostsController extends BaseController {
/**
* @var PostRepository
*/
protected $repository;
public function __construct(PostRepository $repository){
$this->repository = $repository;
}
....
}
Find all results in Repository
$posts = $this->repository->all();
Find all results in Repository with pagination
$posts = $this->repository->paginate($limit = null, $columns = ['*']);
Find by result by id
$post = $this->repository->find($id);
Hiding attributes of the model
$post = $this->repository->hidden(['country_id'])->find($id);
Showing only specific attributes of the model
$post = $this->repository->visible(['id', 'state_id'])->find($id);
Loading the Model relationships
$post = $this->repository->with(['state'])->find($id);
Find by result by field name
$posts = $this->repository->findByField('country_id','15');
Find by result by multiple fields
$posts = $this->repository->findWhere([
//Default Condition =
'state_id'=>'10',
'country_id'=>'15',
//Custom Condition
['columnName','>','10']
]);
Create new entry in Repository
$post = $this->repository->create( Input::all() );
Update entry in Repository
$post = $this->repository->update( Input::all(), $id );
Delete entry in Repository
$this->repository->delete($id)
Criteria is a way to change the repository of the query by applying specific conditions according to their need. You can add multiple Criteria in your repository
use Prettus\Repository\Contracts\RepositoryInterface;
use Prettus\Repository\Contracts\CriteriaInterface;
class MyCriteria implements CriteriaInterface {
public function apply($model, RepositoryInterface $repository)
{
$model = $model->where('user_id','=', Auth::user()->id );
return $model;
}
}
namespace App\Http\Controllers;
use App\PostRepository;
class PostsController extends BaseController {
/**
* @var PostRepository
*/
protected $repository;
public function __construct(PostRepository $repository){
$this->repository = $repository;
}
public function index()
{
$this->repository->pushCriteria(new MyCriteria());
$posts = $this->repository->all();
...
}
}
Getting results from Criteria
$posts = $this->repository->getByCriteria(new MyCriteria());
Setting the default Criteria in Repository
use Prettus\Repository\Eloquent\BaseRepository;
class PostRepository extends BaseRepository {
public function boot(){
$this->pushCriteria(new MyCriteria());
$this->pushCriteria(new AnotherCriteria());
...
}
function model(){
return "App\\Post";
}
}
Use skipCriteria
before any method in the repository
$posts = $this->repository->skipCriteria()->all();
RequestCriteria is a standard Criteria implementation. It enables filters to perform in the repository from parameters sent in the request.
You can perform a dynamic search, filter the data and customize the queries
To use the Criteria in your repository, you can add a new criteria in the boot method of your repository, or directly use in your controller, in order to filter out only a few requests
####Enabling in your Repository
use Prettus\Repository\Eloquent\BaseRepository;
use Prettus\Repository\Criteria\RequestCriteria;
class PostRepository extends BaseRepository {
/**
* @var array
*/
protected $fieldSearchable = [
'name',
'email'
];
public function boot(){
$this->pushCriteria(app('Prettus\Repository\Criteria\RequestCriteria'));
...
}
function model(){
return "App\\Post";
}
}
Remember, you need to define which fields from the model can be searchable.
In your repository set $fieldSearchable with the name of the fields to be searchable.
protected $fieldSearchable = [
'name',
'email'
];
You can set the type of condition which will be used to perform the query, the default condition is "="
protected $fieldSearchable = [
'name'=>'like',
'email', // Default Condition "="
'your_field'=>'condition'
];
####Enabling in your Controller
public function index()
{
$this->repository->pushCriteria(app('Prettus\Repository\Criteria\RequestCriteria'));
$posts = $this->repository->all();
...
}
Request all data without filter by request
http://prettus.local/users
[
{
"id": 1,
"name": "John Doe",
"email": "john@gmail.com",
"created_at": "-0001-11-30 00:00:00",
"updated_at": "-0001-11-30 00:00:00"
},
{
"id": 2,
"name": "Lorem Ipsum",
"email": "lorem@ipsum.com",
"created_at": "-0001-11-30 00:00:00",
"updated_at": "-0001-11-30 00:00:00"
},
{
"id": 3,
"name": "Laravel",
"email": "laravel@gmail.com",
"created_at": "-0001-11-30 00:00:00",
"updated_at": "-0001-11-30 00:00:00"
}
]
Conducting research in the repository
http://prettus.local/users?search=John%20Doe
or
http://prettus.local/users?search=John&searchFields=name:like
or
http://prettus.local/users?search=john@gmail.com&searchFields=email:=
or
http://prettus.local/users?search=name:John Doe;email:john@gmail.com
or
http://prettus.local/users?search=name:John;email:john@gmail.com&searchFields=name:like;email:=
[
{
"id": 1,
"name": "John Doe",
"email": "john@gmail.com",
"created_at": "-0001-11-30 00:00:00",
"updated_at": "-0001-11-30 00:00:00"
}
]
Filtering fields
http://prettus.local/users?filter=id;name
[
{
"id": 1,
"name": "John Doe"
},
{
"id": 2,
"name": "Lorem Ipsum"
},
{
"id": 3,
"name": "Laravel"
}
]
Sorting the results
http://prettus.local/users?filter=id;name&orderBy=id&sortedBy=desc
[
{
"id": 3,
"name": "Laravel"
},
{
"id": 2,
"name": "Lorem Ipsum"
},
{
"id": 1,
"name": "John Doe"
}
]
####Overwrite params name
You can change the name of the parameters in the configuration file config/repository.php
Add a layer of cache easily to your repository
Implements the interface CacheableInterface and use CacheableRepository Trait.
use Prettus\Repository\Eloquent\BaseRepository;
use Prettus\Repository\Contracts\CacheableInterface;
use Prettus\Repository\Traits\CacheableRepository;
class PostRepository extends BaseRepository implements CacheableInterface {
use CacheableRepository;
...
}
Done , done that your repository will be cached , and the repository cache is cleared whenever an item is created , modified or deleted .
You can change the cache settings in the file config/repository.php and also directly on your repository.
config/repository.php
'cache'=>[
//Enable or disable cache repositories
'enabled' => true,
//Lifetime of cache
'minutes' => 30,
//Repository Cache, implementation Illuminate\Contracts\Cache\Repository
'repository'=> 'cache',
//Sets clearing the cache
'clean' => [
//Enable, disable clearing the cache on changes
'enabled' => true,
'on' => [
//Enable, disable clearing the cache when you create an item
'create'=>true,
//Enable, disable clearing the cache when upgrading an item
'update'=>true,
//Enable, disable clearing the cache when you delete an item
'delete'=>true,
]
],
'params' => [
//Request parameter that will be used to bypass the cache repository
'skipCache'=>'skipCache'
],
'allowed'=>[
//Allow caching only for some methods
'only' =>null,
//Allow caching for all available methods, except
'except'=>null
],
],
It is possible to override these settings directly in the repository.
use Prettus\Repository\Eloquent\BaseRepository;
use Prettus\Repository\Contracts\CacheableInterface;
use Prettus\Repository\Traits\CacheableRepository;
class PostRepository extends BaseRepository implements CacheableInterface {
// Setting the lifetime of the cache to a repository specifically
protected $cacheMinutes = 90;
protected $cacheOnly = ['all', ...];
//or
protected $cacheExcept = ['find', ...];
use CacheableRepository;
...
}
The cacheable methods are : all, paginate, find, findByField, findWhere, getByCriteria
Easy validation with prettus/laravel-validator
In the example below, we define some rules for both creation and edition
use \Prettus\Validator\LaravelValidator;
class PostValidator extends LaravelValidator {
protected $rules = [
'title' => 'required',
'text' => 'min:3',
'author'=> 'required'
];
}
To define specific rules, proceed as shown below:
use \Prettus\Validator\Contracts\ValidatorInterface;
use \Prettus\Validator\LaravelValidator;
class PostValidator extends LaravelValidator {
protected $rules = [
ValidatorInterface::RULE_CREATE => [
'title' => 'required',
'text' => 'min:3',
'author'=> 'required'
],
ValidatorInterface::RULE_UPDATE => [
'title' => 'required'
]
];
}
use Prettus\Repository\Eloquent\BaseRepository;
use Prettus\Repository\Criteria\RequestCriteria;
class PostRepository extends BaseRepository {
/**
* Specify Model class name
*
* @return mixed
*/
function model(){
return "App\\Post";
}
/**
* Specify Validator class name
*
* @return mixed
*/
public function validator()
{
return "App\\PostValidator";
}
}
Alternatively , instead of using a class to define its validation rules, you can set your rules directly into the rules repository property , it will have the same effect Validation class
use Prettus\Repository\Eloquent\BaseRepository;
use Prettus\Repository\Criteria\RequestCriteria;
use Prettus\Validator\Contracts\ValidatorInterface;
class PostRepository extends BaseRepository {
/**
* Specify Validator Rules
* @var array
*/
protected $rules = [
ValidatorInterface::RULE_CREATE => [
'title' => 'required',
'text' => 'min:3',
'author'=> 'required'
],
ValidatorInterface::RULE_UPDATE => [
'title' => 'required'
]
];
/**
* Specify Model class name
*
* @return mixed
*/
function model(){
return "App\\Post";
}
}
Validation is ready. In case of failure will be released an exception. Prettus\Validator\Exceptions\ValidatorException
Presenter to wrap and render objects.
There are two ways to implement Presenter, the first is creating a TransformerAbstract and Set using your Presenter class as described in the Create a Transformer Class.
The second way is to make your model implement Transformable interface, and use the default Prenseter ModelFractarPresenter, ready, this will have the same effect.
use App\Post;
use League\Fractal\TransformerAbstract;
class PostTransformer extends TransformerAbstract
{
public function transform(Post $post)
{
return [
'id' => (int) $post->id,
'title' => $post->title,
'content' => $post->content
];
}
}
use Prettus\Repository\Presenter\FractalPresenter;
class PostPresenter extends FractalPresenter {
/**
* Prepare data to present
*
* @return \League\Fractal\TransformerAbstract
*/
public function getTransformer()
{
return new PostTransformer();
}
}
use Prettus\Repository\Eloquent\BaseRepository;
class PostRepository extends BaseRepository {
...
public function presenter()
{
return "App\\Presenter\\PostPresenter";
}
}
or enable in your controller with
$this->repository->setPresenter("App\\Presenter\\PostPresenter");
namespace App;
use Prettus\Repository\Contracts\Transformable;
class Post extends Eloquent implements Transformable {
...
/**
* @return array
*/
public function transform()
{
return [
'id' => (int) $this->id,
'title' => $this->title,
'content' => $this->content
];
}
}
Prettus\Repository\Presenter\ModelFractalPresenter
is a Presenter default for Model implementing Transformable
use Prettus\Repository\Eloquent\BaseRepository;
class PostRepository extends BaseRepository {
...
public function presenter()
{
return "Prettus\\Repository\\Presenter\\ModelFractalPresenter";
}
}
or enable in your controller with
$this->repository->setPresenter("Prettus\\Repository\\Presenter\\ModelFractalPresenter");
Use skipPresenter before any method in the repository
$posts = $this->repository->skipPresenter()->all();
or
$this->repository->skipPresenter();
$posts = $this->repository->all();