27
19

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

LaravelAdvent Calendar 2016

Day 21

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

Last updated at Posted at 2016-12-21

表題のl5-repositoryとは

repositoryに統一されたインターフェースを持たせられそうなplugin
githubのスター数: 1191
対応laravel < 5.2 (リポジトリのコメントみる限り)

実行環境

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

27
19
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
27
19

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?