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

  • 11
    いいね
  • 0
    コメント

表題の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を注入したサービスクラスを別に作るべきなのかとも思っています。
みなさんどうしてるんでしょうか。笑

この投稿は Laravel Advent Calendar 201621日目の記事です。