リポジトリパターンに従ってLaravelで関係を管理する 質問する

リポジトリパターンに従ってLaravelで関係を管理する 質問する

T. Otwell の Laravel の優れたデザイン パターンに関する本を読んだ後、Laravel 4 でアプリを作成しているときに、アプリケーション上のすべてのテーブルにリポジトリを作成していることに気付きました。

最終的に次のテーブル構造になりました。

  • 生徒: ID、名前
  • コース: id、name、teacher_id
  • 教師: ID、名前
  • 課題: id、name、course_id
  • スコア(生徒と課題の間のピボットとして機能します):student_id、assignment_id、scores

これらすべてのテーブルに対して、検索、作成、更新、削除メソッドを備えたリポジトリ クラスがあります。各リポジトリには、データベースと対話する Eloquent モデルがあります。関係は、Laravel のドキュメントに従ってモデル内で定義されます。http://laravel.com/docs/eloquent#relationships

新しいコースを作成するときは、コース リポジトリの create メソッドを呼び出すだけです。そのコースには課題があるため、課題を作成するときに、コースの各学生のスコア テーブルにエントリも作成する必要があります。これは、課題リポジトリを通じて行います。つまり、課題リポジトリは、課題モデルと学生モデルの 2 つの Eloquent モデルと通信します。

私の質問は、このアプリのサイズが大きくなり、より多くの関係が導入される可能性があるため、リポジトリ内のさまざまな Eloquent モデルと通信するのが良い方法なのか、それとも代わりに他のリポジトリを使用して行うべきなのか (つまり、割り当てリポジトリから他のリポジトリを呼び出す)、それともすべて Eloquent モデル内で行うべきなのかということです。

また、課題と生徒の間のピボットとしてスコア テーブルを使用するのは良い方法でしょうか、それとも別の場所で行うべきでしょうか?

ベストアンサー1

私は Laravel 4 を使用した大規模なプロジェクトを終えようとしており、今皆さんが尋ねているすべての質問に答える必要がありました。Leanpub で入手可能な Laravel の本をすべて読み、Google 検索を大量に行った後、次の構造にたどり着きました。

  1. データテーブルごとに 1 つの Eloquent モデルクラス
  2. Eloquentモデルごとに1つのリポジトリクラス
  3. 複数のリポジトリ クラス間で通信できるサービス クラス。

では、映画データベースを構築するとします。少なくとも次の Eloquent モデル クラスが必要になります。

  • 映画
  • スタジオ
  • 監督
  • 俳優
  • レビュー

リポジトリ クラスは各 Eloquent モデル クラスをカプセル化し、データベースの CRUD 操作を担当します。リポジトリ クラスは次のようになります。

  • 映画リポジトリ
  • スタジオリポジトリ
  • ディレクターリポジトリ
  • アクターリポジトリ
  • レビューリポジトリ

各リポジトリ クラスは、次のインターフェースを実装する BaseRepository クラスを拡張します。

interface BaseRepositoryInterface
{
    public function errors();

    public function all(array $related = null);

    public function get($id, array $related = null);

    public function getWhere($column, $value, array $related = null);

    public function getRecent($limit, array $related = null);

    public function create(array $data);

    public function update(array $data);

    public function delete($id);

    public function deleteWhere($column, $value);
}

サービスクラスは複数のリポジトリを結合するために使用され、アプリケーションの実際の「ビジネスロジック」を含みます。コントローラのみ作成、更新、削除アクションのサービス クラスと通信します。

したがって、データベースに新しい Movie レコードを作成する場合、MovieController クラスには次のメソッドが含まれる可能性があります。

public function __construct(MovieRepositoryInterface $movieRepository, MovieServiceInterface $movieService)
{
    $this->movieRepository = $movieRepository;
    $this->movieService = $movieService;
}

public function postCreate()
{
    if( ! $this->movieService->create(Input::all()))
    {
        return Redirect::back()->withErrors($this->movieService->errors())->withInput();
    }

    // New movie was saved successfully. Do whatever you need to do here.
}

コントローラーにデータを POST する方法は自由に決められますが、postCreate() メソッドの Input::all() によって返されるデータは次のようになるとします。

$data = array(
    'movie' => array(
        'title'    => 'Iron Eagle',
        'year'     => '1986',
        'synopsis' => 'When Doug\'s father, an Air Force Pilot, is shot down by MiGs belonging to a radical Middle Eastern state, no one seems able to get him out. Doug finds Chappy, an Air Force Colonel who is intrigued by the idea of sending in two fighters piloted by himself and Doug to rescue Doug\'s father after bombing the MiG base.'
    ),
    'actors' => array(
        0 => 'Louis Gossett Jr.',
        1 => 'Jason Gedrick',
        2 => 'Larry B. Scott'
    ),
    'director' => 'Sidney J. Furie',
    'studio' => 'TriStar Pictures'
)

MovieRepository はデータベースに俳優、監督、またはスタジオのレコードを作成する方法を知らないはずなので、次のような MovieService クラスを使用します。

public function __construct(MovieRepositoryInterface $movieRepository, ActorRepositoryInterface $actorRepository, DirectorRepositoryInterface $directorRepository, StudioRepositoryInterface $studioRepository)
{
    $this->movieRepository = $movieRepository;
    $this->actorRepository = $actorRepository;
    $this->directorRepository = $directorRepository;
    $this->studioRepository = $studioRepository;
}

public function create(array $input)
{
    $movieData    = $input['movie'];
    $actorsData   = $input['actors'];
    $directorData = $input['director'];
    $studioData   = $input['studio'];

    // In a more complete example you would probably want to implement database transactions and perform input validation using the Laravel Validator class here.

    // Create the new movie record
    $movie = $this->movieRepository->create($movieData);

    // Create the new actor records and associate them with the movie record
    foreach($actors as $actor)
    {
        $actorModel = $this->actorRepository->create($actor);
        $movie->actors()->save($actorModel);
    }

    // Create the director record and associate it with the movie record
    $director = $this->directorRepository->create($directorData);
    $director->movies()->associate($movie);

    // Create the studio record and associate it with the movie record
    $studio = $this->studioRepository->create($studioData);
    $studio->movies()->associate($movie);

    // Assume everything worked. In the real world you'll need to implement checks.
    return true;
}

それで、私たちに残されたものは、関心の適切な分離です。リポジトリは、データベースに挿入したり取得したりするEloquentモデルのみを認識します。コントローラーはリポジトリを気にせず、ユーザーから収集したデータを適切なサービスに渡すだけです。サービスは気にしません。どうやって受信したデータはデータベースに保存され、コントローラーから提供された関連データが適切なリポジトリに渡されるだけです。

おすすめ記事