Edited at
LaravelDay 21

l5-repositoryでrepositoryに統一性を与えられるか

More than 1 year has passed since last update.


表題のl5-repositoryとは

repositoryに統一されたインターフェースを持たせられそうなplugin

githubのスター数: 1191

対応laravel < 5.2 (リポジトリのコメントみる限り)

https://github.com/andersao/l5-repository


実行環境


  • laravel5.1.*

  • php7.1.0


なぜl5-repositoryを使ってみようと思ったか

チーム開発においてデータベースへのアクセスを

repositoryに任せるのはいいがメソッドの書き方が属人的に

なるので統一された命名規則を使いたいと思った。

repositoryに実装されているのは本当にそのモデルに

依存しているものだけにしてあとは強制的に継承先のものを使う。


使ってみる


準備

インストール

$ composer require prettus/l5-repository

サービスプロバイダに登録

'providers' => [

...
Prettus\Repository\Providers\RepositoryServiceProvider::class,
],

l5-repositoryのconfigファイルを生成

$ php artisan vendor:publish

Copied File [/vendor/prettus/l5-repository/src/resources/config/repository.php] To [/config/repository.php]


Repositroyを作る

<?php

namespace App\Repositories;

use Prettus\Repository\Eloquent\BaseRepository;
use App\Models\User;

class UserRepository extends BaseRepository
{
/**
* このリポジトリーで使うモデルのパスを返す
* BaseRepository内で$this->app->make($this->model())
* として渡される
*
* @return string
*/

function model()
{
return User::class;
}
}

model()は継承しているBaseRepositoryのabstractで

定義されているので実装必須。

ちなみに上記インストールにて/config/repository.phpを作成している場合

下記コマンドにてrepositoryの雛形は作ってくれる

php artisan make:repository [Model]


BaseRepositoryに実装されているもの

userRepositoryを使っているとします

//全データを取得

$this->userRepository->all();

//idで検索
$this->userRepository->find($id);

/**
* リレーション先のモデルを指定して取得
* Userモデルにpost()でリレーションが定義されている前提
*/

$this->userRepository->with(['post'])->find($id);

//フィールド名を指定してデータを取得
$this->userRepository->findByField('name', 'root');

//複数条件を指定してデータを取得
$this->userRepository->findWhere([
//Default Condition =
'name' => 'root',
//Custom Condition
['created_at', '>', '2016-01-01 00:00:00']
]);

//1つのフィールドに対し、複数の値から検索
$this->userRepository->findWhereIn('id', [1, 2, 3, 4]);

// 上記の逆も用意されている
$this->userRepository->findWhereNotIn('id', [6, 7, 8, 9]);

/**
* 条件をクロージャでも渡せる
*
* 各メソッドにて$this->applyScope()が呼ばれていて
* そこで指定した条件は追加される
*/

$this->userRepository->scopeQuery(function($query){
return $query->orderBy('updated_at', 'asc');
})->all();

/**
* 一件取得
* ただ、これは引数に条件は渡せないので、scopeQueryや
* 後述のCriteriaを使う前提での実装
*/

$this->userRepository->first($columns);

/**
* create, update, deleteも実装されている(ラッパーみたいな感じ)
*/

$this->userRepository->create(Input::all());
$this->userRepository->update(Input::all(), $id);
$this->userRepos


Criteria

必要に応じてCriteriaで定義した条件をRepositoryに適用させ

クエリを変えることができるようになる

Criteria作成

php artisan make:criteria User

今回はUserRepositoryに関するCriteriaを作るので

Userを指定しました。命名規則はないと思います。

デフォルトでは、app/Criteria/にUserCriteriaが作られました。

中身はこんな感じ


UserCriteria.php

<?php

namespace App\Criteria;

use Prettus\Repository\Contracts\CriteriaInterface;
use Prettus\Repository\Contracts\RepositoryInterface;

/**
* Class UserCriteria
* @package namespace App\Criteria;
*/

class UserCriteria implements CriteriaInterface
{
/**
* Apply criteria in query repository
*
* @param $model
* @param RepositoryInterface $repository
*
* @return mixed
*/

public function apply($model, RepositoryInterface $repository)
{
return $model;
}
}


使用例

emailがgmailのユーザーをとってくる


UserCriteria.php

/**

* Class UserCriteria
* @package namespace App\Criteria;
*/

class UserCriteria implements CriteriaInterface
{
/**
* Apply criteria in query repository
*
* @param $model
* @param RepositoryInterface $repository
*
* @return mixed
*/

public function apply($model, RepositoryInterface $repository)
{
$model = $model->where('email', 'like', '%@gmail%');
return $model;
}
}

実行側

//Criteria適用

$this->userRepository->pushCriteria(UserCriteria::class);
dd($this->userRepository->all());

またはこんな書き方

$this->userRepository->getByCriteria(new UserCriteria());

Repository内にデフォで使用する場合は設定しておける

<?php

namespace App\Repositories;

use Prettus\Repository\Eloquent\BaseRepository;
use App\Models\User;
use App\Criteria\UserCriteria;

class UserRepository extends BaseRepository
{
/**
*
*/

/**
* defaultで使用するCriteriaがある場合は設定できる
*/

public function boot(){
$this->pushCriteria(UserCriteria::class);
}

}

いやいや、デフォで設定してあるけどこの時はデフォの条件使いたくない。。

スキップしましょう

$this->repository->skipCriteria()->all();

削除もできるみたいですが用途は如何に。。

$this->repository->popCriteria(Criteria1::class);


まとめ

チームにrepositoryを使う上での制約をもたらすには十分なのではないでしょうか

簡単なクエリでrepositoryが変に膨らむこともないだろうし

こちらで共通の処理を行う抽象的なメソッドを再発明しなくて

済むのもいいですね。

次回のプロジェクトではこいつ使ってみたいと思います。

RequestCriteriaやPresenters(オブジェクト出力のラッパー)

Validatorなども便利なものがあるようですが今回は割愛


だってFormRequest今のところ便利だし。。笑


Model周りの疑問

laravelはディレクトリ構成に自由が効くのでCriteriaなど

どう運用していってるのか既にプラクティスがある方いれば

是非教えていただきたいです。

自分的にはrepositoryでsaveなど任せるのは

違和感あるのでそこはmodelにやらせてました。

ただ、modelを注入したサービスクラスを別に作るべきなのかとも思っています。

みなさんどうしてるんでしょうか。笑