[toc]
Laravel 5 Repositories (Laravel仓库)
仓库地址:[email protected]:andersao/l5-repository.git
Repositories
是数据抽象层,它使得我们的应用维护更加灵活。
目录
安装
Composer
执行以下命令获取最新安装包:
composer require prettus/l5-repository
Laravel
>= laravel5.5
ServiceProvider 会自动发现
Other
在 config/app.php
中 providers
数组末尾添加 Prettus\Repository\Providers\RepositoryServiceProvider::class
:
'providers' => [
...
Prettus\Repository\Providers\RepositoryServiceProvider::class,
],
如果你的框架是 Lumen,请注册该服务:
$app->register(Prettus\Repository\Providers\LumenRepositoryServiceProvider::class);
发布配置文件:
php artisan vendor:publish --provider "Prettus\Repository\Providers\RepositoryServiceProvider"
方法
PrettusRepositoryContractsRepositoryInterface
- all($columns = array('*'))
- first($columns = array('*'))
- paginate($limit = null, $columns = ['*'])
- find($id, $columns = ['*'])
- findByField($field, $value, $columns = ['*'])
- findWhere(array $where, $columns = ['*'])
- findWhereIn($field, array $where, $columns = [*])
- findWhereNotIn($field, array $where, $columns = [*])
- findWhereBetween($field, array $where, $columns = [*])
- create(array $attributes)
- update(array $attributes, $id)
- updateOrCreate(array $attributes, array $values = [])
- delete($id)
- deleteWhere(array $where)
- orderBy($column, $direction = 'asc');
- with(array $relations);
- has(string $relation);
- whereHas(string $relation, closure $closure);
- hidden(array $fields);
- visible(array $fields);
- scopeQuery(Closure $scope);
- getFieldsSearchable();
- setPresenter($presenter);
- skipPresenter($status = true);
PrettusRepositoryContractsRepositoryCriteriaInterface
- pushCriteria($criteria)
- popCriteria($criteria)
- getCriteria()
- getByCriteria(CriteriaInterface $criteria)
- skipCriteria($status = true)
- getFieldsSearchable()
PrettusRepositoryContractsCacheableInterface
- setCacheRepository(CacheRepository $repository)
- getCacheRepository()
- getCacheKey($method, $args = null)
- getCacheMinutes()
- skipCache($status = true)
PrettusRepositoryContractsPresenterInterface
- present($data);
PrettusRepositoryContractsPresentable
- setPresenter(PresenterInterface $presenter);
- presenter();
PrettusRepositoryContractsCriteriaInterface
- apply($model, RepositoryInterface $repository);
PrettusRepositoryContractsTransformable
- transform();
用法
创建 Model
和普通添加Model
的方法一样,创建一个Model
,但是,一定要定义一个可被表单数据批量赋值的属性$fillable
:
namespace App;
class Post extends Eloquent { // or Ardent, Or any other Model Class
protected $fillable = [
'title',
'author',
...
];
...
}
创建 Repository
namespace App;
use Prettus\Repository\Eloquent\BaseRepository;
class PostRepository extends BaseRepository {
/**
* Specify Model class name
*
* @return string
*/
function model()
{
return "App\\Post";
}
}
生成器
通过生成器可以轻松生成你的 Repository
配置
首先,你必须配置你的Repository
文件的存储位置。默认存放位置是app
文件夹下,命名空间是App
。请注意,paths
数组里的各项配置值实际上既是命名空间,又是文件路径。不过,别担心,正斜杠和反斜杠都是在生成过程中自动处理的。
...
'generator'=>[
'basePath'=>app()->path(),
'rootNamespace'=>'App\\',
'paths'=>[
'models' => 'Entities',
'repositories' => 'Repositories',
'interfaces' => 'Repositories',
'transformers' => 'Transformers',
'presenters' => 'Presenters',
'validators' => 'Validators',
'controllers' => 'Http/Controllers',
'provider' => 'RepositoryServiceProvider',
'criteria' => 'Criteria',
]
]
可能你想将你的项目保存在app
文件夹外边,并且添加其他命名空间,比如:
...
'generator'=>[
'basePath' => base_path('src/Lorem'),
'rootNamespace' => 'Lorem\\'
]
另外,您可能希望自定义生成的类最终保存的位置。你可以通过编辑paths
来实现。比如:
'generator'=>[
'basePath'=>app()->path(),
'rootNamespace'=>'App\\',
'paths'=>[
'models'=>'Models',
'repositories'=>'Repositories\\Eloquent',
'interfaces'=>'Contracts\\Repositories',
'transformers'=>'Transformers',
'presenters'=>'Presenters'
'validators' => 'Validators',
'controllers' => 'Http/Controllers',
'provider' => 'RepositoryServiceProvider',
'criteria' => 'Criteria',
]
]
命令
运行以下命令,你可以生成任何你想要的Model
:
php artisan make:entity Post
此命令可以生成 Controller
,Validator
,Model
,Repository
,Presenter
和Transformer
类。
它还会生成一个新的服务提供者用来绑定Repository Interface
及其相应的实现Eloquent Repository
。
将其添加到AppServiceProvider@register
方法即可加载它:
$this->app->register(RepositoryServiceProvider::class);
你还可以为它配置repository
命令选项,因为这个命令仅仅只是一个包装器而已。
为你的Post model
生成一个repository
层,使用如下命令:
php artisan make:repository Post
使用Blog命名空间,为Post model
生成一个repository
层,使用如下命令:
php artisan make:repository "Blog\Post"
为其添加可填充字段:
php artisan make:repository "Blog\Post" --fillable="title,content"
如果想要通过命令直接添加验证规则,需要添加--rules
选项并且同时创建迁移:
php artisan make:entity Cat --fillable="title:string,content:text" --rules="title=>required|min:2, content=>sometimes|min:10"
这个命令将会创建一个初始的包含基础的CRUD
的RESTfull
控制器,因此只需要添加下面一行代码到routes.php
即可:
Route::resource('cats', CatsController::class);
此命令将会依据你的默认设置自动创建一个包含Repositories
的Entities
文件夹:
以上设置完成以后,你仍需要为其接口绑定真实的repository
实现类,比如在Repositories Service Provider中。
App::bind('{YOUR_NAMESPACE}Repositories\PostRepository', '{YOUR_NAMESPACE}Repositories\PostRepositoryEloquent');
并且使用:
public function __construct({YOUR_NAMESPACE}Repositories\PostRepository $repository){
$this->repository = $repository;
}
另外,也可以用artisan
命令来绑定:
php artisan make:bindings Cats
使用方法
namespace App\Http\Controllers;
use App\PostRepository;
class PostsController extends BaseController {
/**
* @var PostRepository
*/
protected $repository;
public function __construct(PostRepository $repository){
$this->repository = $repository;
}
....
}
通过Repository
获取所有结果:
$posts = $this->repository->all();
通过Repository
获取分页结果:
$posts = $this->repository->paginate($limit = null, $columns = ['*']);
通过id
获取结果:
$post = $this->repository->find($id);
隐藏Model
的属性:
$post = $this->repository->hidden(['country_id'])->find($id);
显示Model
指定属性:
$post = $this->repository->visible(['id', 'state_id'])->find($id);
加载Model
关联关系:
$post = $this->repository->with(['state'])->find($id);
根据字段名称获取结果:
$posts = $this->repository->findByField('country_id','15');
根据多个字段获取结果:
$posts = $this->repository->findWhere([
//Default Condition =
'state_id'=>'10',
'country_id'=>'15',
//Custom Condition
['columnName','>','10']
]);
根据某一字段的多个值获取结果:
$posts = $this->repository->findWhereIn('id', [1,2,3,4,5]);
Find by result by excluding multiple values in one field
获取不包含某一字段的指定值的结果:
$posts = $this->repository->findWhereNotIn('id', [6,7,8,9,10]);
通过自定义scope获取结果:
$posts = $this->repository->scopeQuery(function($query){
return $query->orderBy('sort_order','asc');
})->all();
在Repository
中创建数据:
$post = $this->repository->create( Input::all() );
在Repository
中更新数据:
$post = $this->repository->update( Input::all(), $id );
在Repository
中删除数据:
$this->repository->delete($id)
在Repository
中通过多字段删除数据:
$this->repository->deleteWhere([
//Default Condition =
'state_id'=>'10',
'country_id'=>'15',
])
创建Criteria(条件)
使用命令
php artisan make:criteria My
Criteria
是一种根据需要,运用特定条件改变repository
层查询的一种方式。你可以在repository
层添加多个criteria
:
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;
}
}
在控制器中使用Criteria
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 MyCriteria1());
$this->repository->pushCriteria(MyCriteria2::class);
$posts = $this->repository->all();
...
}
}
通过Criteria
获取结果:
$posts = $this->repository->getByCriteria(new MyCriteria());
在Repository
中设置默认Criteria
:
use Prettus\Repository\Eloquent\BaseRepository;
class PostRepository extends BaseRepository {
public function boot(){
$this->pushCriteria(new MyCriteria());
// or
$this->pushCriteria(AnotherCriteria::class);
...
}
function model(){
return "App\\Post";
}
}
跳过在 repository
中设置的criteria
:
在任何其他方法链前使用skipCriteria
:
$posts = $this->repository->skipCriteria()->all();
移除criteria
使用popCriteria
移除criteria
:
$this->repository->popCriteria(new Criteria1());
// or
$this->repository->popCriteria(Criteria1::class);
使用RequestCriteria
RequestCriteria
是一个标准的Criteria
的实现类。它允许过滤器根据请求中发送的参数在Repository
中执行。
你可以用它来执行一个动态搜索,过滤数据,并且还可以自定义一个查询。要在Repository
中使用Criteria
,你可以在Repository
的boot
方法中添加一个新的criteria
;如果仅仅想要过滤一个新的请求,你可以直接在控制器中使用;
在 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";
}
}
记住,你需要定义哪些Model
字段可被搜索;
在你的repository
里通过设置$fieldSearchable
可被搜索的字段名或字段的关联关系;
protected $fieldSearchable = [
'name',
'email',
'product.name'
];
你可以设置执行查询时的条件类型,默认条件是 "="
protected $fieldSearchable = [
'name'=>'like',
'email', // Default Condition "="
'your_field'=>'condition'
];
在Controller
中使用
public function index()
{
$this->repository->pushCriteria(app('Prettus\Repository\Criteria\RequestCriteria'));
$posts = $this->repository->all();
...
}
Criteria
举例
通过request
请求所有数据:
http://prettus.local/users
[
{
"id": 1,
"name": "John Doe",
"email": "[email protected]",
"created_at": "-0001-11-30 00:00:00",
"updated_at": "-0001-11-30 00:00:00"
},
{
"id": 2,
"name": "Lorem Ipsum",
"email": "[email protected]",
"created_at": "-0001-11-30 00:00:00",
"updated_at": "-0001-11-30 00:00:00"
},
{
"id": 3,
"name": "Laravel",
"email": "[email protected]",
"created_at": "-0001-11-30 00:00:00",
"updated_at": "-0001-11-30 00:00:00"
}
]
在repository
中探究:
http://prettus.local/users?search=John%20Doe
或者
http://prettus.local/users?search=John&searchFields=name:like
或者
http://prettus.local/[email protected]&searchFields=email:=
或者
http://prettus.local/users?search=name:John Doe;email:[email protected]
or
http://prettus.local/users?search=name:John;email:[email protected]&searchFields=name:like;email:=
[
{
"id": 1,
"name": "John Doe",
"email": "[email protected]",
"created_at": "-0001-11-30 00:00:00",
"updated_at": "-0001-11-30 00:00:00"
}
]
默认情况下,RequestCriteria
通过or
比较操作符对每个查询参数进行查询:
http://prettus.local/users?search=age:17;email:[email protected]
以上例子将会执行以下查询:
SELECT * FROM users WHERE age = 17 OR email = '[email protected]';
如果想要使用and
进行查询,可以传递searchJoin
参数,如下所示:
http://prettus.local/users?search=age:17;email:[email protected]&searchJoin=and
过滤字段:
http://prettus.local/users?filter=id;name
[
{
"id": 1,
"name": "John Doe"
},
{
"id": 2,
"name": "Lorem Ipsum"
},
{
"id": 3,
"name": "Laravel"
}
]
结果排序:
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"
}
]
通过关联表进行排序:
http://prettus.local/users?orderBy=posts|title&sortedBy=desc
查询操作将会按照如下sql执行:
...
INNER JOIN posts ON users.post_id = posts.id
...
ORDER BY title
...
http://prettus.local/users?orderBy=posts:custom_id|posts.title&sortedBy=desc
查询操作将会按照如下sql执行:
...
INNER JOIN posts ON users.custom_id = posts.id
...
ORDER BY posts.title
...
添加关联关系:
http://prettus.local/users?with=groups
修改参数名
你可以在配置文件config/repository.php
中修改参数名
缓存
在repository
中轻松添加缓存层
缓存用法
继承CacheableInterface
接口,并使用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;
...
}
好了,这样你的repository
就可以缓存了,并且,当你创建、修改、删除一个item
时,repository
缓存将会被清除。
缓存配置
你可以在config/repository.php
中修改缓存设置,也可以直接在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
],
],
可以在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;
...
}
可缓存的方法有 : all
, paginate
, find
, findByField
, findWhere
, getByCriteria
验证器
composer require prettus/laravel-validator
轻松通过 prettus/laravel-validator
进行验证
使用验证类
创建验证器
在下面的例子中,我们定义一些创建和编辑的规则:
use \Prettus\Validator\LaravelValidator;
class PostValidator extends LaravelValidator {
protected $rules = [
'title' => 'required',
'text' => 'min:3',
'author'=> 'required'
];
}
按如下所示,定义指定规则:
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'
]
];
}
在Repository
中使用验证器
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";
}
}
在Repository
中定义规则
另外,除了使用class来定义验证规则之外,还可以直接在repository
属性中直接设置,这和在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";
}
}
验证器现在就已经准备好了。如果验证失败,将抛出该类型的异常:Prettus\Validator\Exceptions\ValidatorException
呈现器(Presenters)
Presenters
函数是对象的包装器和呈现器.
Fractal 呈现器
安装 Fractal.
composer require league/fractal
实现Presenter
有两个方法:
第一种方式:
创建一个TransformerAbstract
,并如创建Transformer
类中所描述的那样,使用Presenter
类设置它;
第二种方式:
让你的model
实现Transformable
接口,并使用默认的Presenter ModelFractarPresenter
,它们具有同样的效果;
Transformer
类
使用命令创建Transformer
php artisan make:transformer Post
这将会生成下面的类
创建Transformer
类
use League\Fractal\TransformerAbstract;
class PostTransformer extends TransformerAbstract
{
public function transform(\Post $post)
{
return [
'id' => (int) $post->id,
'title' => $post->title,
'content' => $post->content
];
}
}
使用命令创建Presenter
呈现器
php artisan make:presenter Post
如果你还没有创建Transformer
转换器,这个命令将会提示你也创建一个Transformer
创建Presenter
use Prettus\Repository\Presenter\FractalPresenter;
class PostPresenter extends FractalPresenter {
/**
* Prepare data to present
*
* @return \League\Fractal\TransformerAbstract
*/
public function getTransformer()
{
return new PostTransformer();
}
}
在Repository
中使用Presenter
:
use Prettus\Repository\Eloquent\BaseRepository;
class PostRepository extends BaseRepository {
...
public function presenter()
{
return "App\\Presenter\\PostPresenter";
}
}
或者通过如下所示在Controller
中使用Presenter
:
$this->repository->setPresenter("App\\Presenter\\PostPresenter");
在Model
后使用 Presenter
如果你记录了一个presenter
,并且有时候使用了skipPresenter()
方法,或者只是不想让presenter
自动改变结果。你可以在你的model
中实现Presentable
接口,这样,你可以在任何时候显示你的model
。见下文:
在你的model
里,实现接口Prettus\Repository\Contracts\Presentable
和 Prettus\Repository\Traits\PresentableTrait
namespace App;
use Prettus\Repository\Contracts\Presentable;
use Prettus\Repository\Traits\PresentableTrait;
class Post extends Eloquent implements Presentable {
use PresentableTrait;
protected $fillable = [
'title',
'author',
...
];
...
}
在那里,你可以单独提交你的model
,看一个例子:
$repository = app('App\PostRepository');
$repository->setPresenter("Prettus\\Repository\\Presenter\\ModelFractalPresenter");
//Getting the result transformed by the presenter directly in the search
$post = $repository->find(1);
print_r( $post ); //It produces an output as array
...
//Skip presenter and bringing the original result of the Model
$post = $repository->skipPresenter()->find(1);
print_r( $post ); //It produces an output as a Model object
print_r( $post->presenter() ); //It produces an output as array
如果,你想根据需要直接在model
中使用presenter
,你可以在你的repository
中将$skipPresenter
属性设置为true
,这样,每一次请求将会跳过presenter
use Prettus\Repository\Eloquent\BaseRepository;
class PostRepository extends BaseRepository {
/**
* @var bool
*/
protected $skipPresenter = true;
public function presenter()
{
return "App\\Presenter\\PostPresenter";
}
}
Model
类
实现接口
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
];
}
}
在 Repository
中使用
Prettus\Repository\Presenter\ModelFractalPresenter
是Models
的默认Presenter
呈现器,它实现了Transformable
接口
use Prettus\Repository\Eloquent\BaseRepository;
class PostRepository extends BaseRepository {
...
public function presenter()
{
return "Prettus\\Repository\\Presenter\\ModelFractalPresenter";
}
}
或者在controller
中使用:
$this->repository->setPresenter("Prettus\\Repository\\Presenter\\ModelFractalPresenter");
在repository
中跳过已定义的Presenter
在任何一个方法链前,使用skipPresenter
方法
$posts = $this->repository->skipPresenter()->all();
或者
$this->repository->skipPresenter();
$posts = $this->repository->all();