laravel5
l5-repository

l5-repository を使うのをやめた話

TLDR;

Laravel にはリポジトリパターンを適用しやすい l5-repository というライブラリがある。
https://github.com/andersao/l5-repository
しばらくこれをつかっていたけど、実は無駄なことをしていたんじゃないかと思い、使わないでリポジトリパターン的なことができるよという紹介というか雑なメモ。
現時点でのバージョンは laravel 5.6

リポジトリパターンを l5-repository 無しでやる

私はリポジトリパターンは DI によってテストをしやすくするために適用していた。
まずは l5-repository だとどのように書くか大まかに書く。

l5-repository での流れ

  • make:repository でマイグレーション、インターフェースと具象クラス、エンティティを作成する
  • リポジトリサービスプロバイダーに登録する
  • リポジトリを利用した実装をする
  • テストを書く

コードは簡単にこんな感じ

HogeRepository.php
interface HogeRepository extends RepositoryInterface
{
    //
}
HogeRepositoryEloquent.php
class HogeRepositoryEloquent extends BaseRepository implements HogeRepository
{
    /**
     * Specify Model class name
     *
     * @return string
     */
    public function model()
    {
        return Hoge::class;
    }



    /**
     * Boot up the repository, pushing criteria
     */
    public function boot()
    {
        $this->pushCriteria(app(RequestCriteria::class));
    }
}
Hoge.php
class Hoge extends Model implements Transformable
{
    use TransformableTrait;

    protected $guarded = [
        'id',
    ];
}
RepositoryServiceProvider.php
    public function register()
    {
        $repositories = [
            // ...
            \App\Repositories\HogeRepository::class, // 追加
            // ....
        ];

        foreach ($repositories as $repository) {
            $this->app->bind($repository, $repository . 'Eloquent');
        }
    }
HogeController.php
protected $hogeRepository;

public function __construct(HogeRepository $hogeRepository)
{
    $this->hogeRepository = $hogeRepository;
}

public function index(Request $request)
{
    return $this->hogeRepository->paginate();
}
HogeControllerTest.php
public function testHogeIndex()
{
    $hoge = factory(Hoge::class)->make();
    $mock = Mockery::mock(HogeRepository::class);
    $this->app->instance(HogeRepository::class, $mock);
    $mock->shouldReceive('paginate')->andReturn($hoge);

    $this->call('GET', '/hoge');
    $this->assertResponseOk();
    // レスポンスのテスト
}

こんな感じ。

ただ、こういう運用していて interface の旨味はなくて煩雑になった。プロバイダとか登録するの面倒だし。
これ、素の laravel でもどうやら似たことできる。すでに Eloquent がファサードなのでモックできるし。

素の laravel での書き方

Hoge.php
class Hoge extends Model
{
    protected $guarded = [
        'id',
    ];
}
HogeController.php
protected $hogeRepository;

public function __construct(Hoge $hogeRepository)
{
    $this->hogeRepository = $hogeRepository;
}

public function index(Request $request)
{
    return $this->hogeRepository->paginate();
}
HogeControllerTest.php
public function testHogeIndex()
{
    $hoge = factory(Hoge::class)->make();
    Hoge::shouldReceive('paginate')->andReturn($hoge);

    $this->call('GET', '/hoge');
    $this->assertResponseOk();
    // レスポンスのテスト
}

インターフェースつくらないからプロバイダの登録いらない。エンティティだけで十分な気がした。
ポイントはファサードをそのまま利用せず、コントローラーではコンストラクタでインスタンスを要求している点。

RequestCriteria について

あとひとつ有用な機能の RequestCriteria については別のライブラリ入れて解決した。

l5-repository では searchableField を指定すると、下記のようなリクエストを解析して検索クエリを発行してくれる
http://prettus.local/users?search=name:John;email:john@gmail.com&searchFields=name:like;email:=
これは name like 'John' and email = 'john@gmail.com' という検索クエリを追加する。

自分は pimpable というライブラリを入れた。
https://github.com/jedrzej/pimpable
同じようにリクエストのパラメタから素のEloquentに対して検索クエリを追加してくれる

検索可能なフィールドを指定してあげて、検索したいときに pimp を呼ぶだけ

Hoge.php
class Hoge extends Model
{
    use PimpableTrait;

    protected $guarded = [
        'id',
    ];

    public $searchable = ['name', 'email'];
}

HogeController.php
Hoge::pimp()->paginate();

http://localhost/hoge?name=%John%&email=john@gmail.com
このリクエストで上記と同じ結果になる