Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

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

smith-30
engineer elmをやりたいと思っている
elm-jp
主に日本で活動する Elm 利用者のコミュニティです。
https://elm-lang.jp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away