Laravelアプリケーションでリポジトリパターンを使う方法
Time to read:
リポジトリは、ドメインとデータマッピングレイヤー間の抽象化レイヤーとして定義できます。これにより、コレクション的なインターフェイスを通じてドメインオブジェクトにアクセスして両者の仲介手段を提供します。
LaravelやSymfonyなど、最新のPHPフレームワークは、オブジェクト関係マッピング(ORM)を介してデータベースを操作します。SymfonyのデフォルトORMはDoctrineで、LaravelはEloquentを使用します。
ともに、データベース操作のために異なるアプローチがとられます。Eloquentの場合、データベーステーブルごとにモデルが生成され、操作の基盤が形成されます。一方、Doctrineはリポジトリパターンを使用します。各エンティティには対応するリポジトリがあり、データベースを操作するヘルパー関数が格納されています。Laravelは標準でこの機能は提供していませんが、Laravelプロジェクトでリポジトリパターンを使用することは可能です。
リポジトリパターンを使う主なメリットは、依存性逆転の原則(具体化でなく抽象化コード)を使用できる点です。これにより、例えば後でEloquentがサポートしないデータソースに変更した場合などに、変更の影響を受けにくいコードを作成できます。
データベース関連のロジックを一箇所で管理することにより、コードの構成を整理したり、重複を避けることにもつながります。このようなメリットは小規模のプロジェクトではすぐに実感できませんが、長期間の維持が必要な大規模プロジェクトでより顕著に感じることができます。
本稿では、Laravelアプリケーションにリポジトリパターンを実装する方法を紹介します。会社が顧客から受けた注文を管理するAPIを作成します。
必要条件
プロジェクトの準備
新規Laravelプロジェクトを作成し、次のようにcdコマンドでディレクトリを変更します。
データベースの設定
このチュートリアルでは、データベースにMySQLを使用します。.envファイルのデータベース関連のパラメーターを次のように変更します。
最後に、任意のデータベース管理アプリケーションを使用し、新しいデータベースorder_apiを作成します。
データベースの初期データの作成
注文管理アプリケーションを作成するために、次のコマンドを実行してモデルを作成します。
-a引数によりArtisanにOrderモデルのマイグレーションファイル、シーダー、ファクトリー、コントローラーを作成することを指示します。
上記のコマンドにより5つの新しいファイルが作成されます。
- コントローラー: app/Http/Controllers/OrderController.php
- データベースファクトリー: database/factories/OrderFactory.php
- マイグレーションファイル: database/migrations/YYYY_MM_DD_HHMMSS_create_orders_table.php
- モデル: app/Models/Order.php
- シーダーファイル: database/seeders/OrderSeeder.php
database/migrations/YYYY_MM_DD_HHMMSS_create_orders_table.phpのup関数を次のとおり変更します。
マイグレーションファイルに従い、orderテーブルには次の列が含まれます。
- ID。テーブルのプライマリーキーとなります。
- 注文の詳細。
- 発注した顧客の名前。
- 注文処理が完了か未了かを知らせるステータス。
- 注文の作成日時と更新日時。
timestamps関数により提供されるcreated_atとupdated_atが使用されます。
次にOrderFactoryを更新し、データベースに入力するダミーの注文を作成します。database/factories/OrderFactory.phpのdefinition関数を次のとおり変更します。
次に、database/seeders/OrderSeeder.phpを開き、run関数を次のとおり変更します。
これにより、OrderFactoryがデータベースに50件の注文を作成します。
src/database/seeders/DatabaseSeeder.phpのrun関数に以下を追加します。
これにより、Artisanのdb:seedコマンドを実行したときにQuoteSeederが実行されます。
最後に、 マイグレーションを実行し、以下のコマンドでデータベースにデータを格納します。
ordersテーブルを開くと、新しく追加された注文が含まれています。
リポジトリの作成
Orderモデルのリポジトリを作成する前に、リポジトリによる宣言が必要なすべてのメソッドを指定するインターフェースを定義します。直接リポジトリクラスを使う代わりに、コントローラー(と今後作成する注文コンポーネント)はこのインターフェースを使用して操作を行います。
これにより、今後変更の必要がある場合にコントローラーは影響を受けず、柔軟なコードを作成できます。例えば、注文管理をサードパーティアプリケーションにアウトソースする場合、OrderRepositoryInterfaceの署名に準拠する新しいモデルを作成してバインディング宣言を置き換えれば、コントローラーのコードを一行も変更することなく、コントローラーが正しく機能します。
appディレクトリにInterfacesディレクトリを新規作成します。続いて、InterfacesにOrderRepositoryInterface.phpファイルを新規作成して次のコードを追加します。
次に、appディレクトリにRepositoriesディレクトリを新規作成します。RepositoriesディレクトリにOrderRepository.phpファイルを新規作成して次のコードを追加します。
インターフェースが持つ柔軟性とは別に、このようにクエリーをカプセル化することにより、アプリケーション全体でクエリーの重複を避けられるメリットも得られます。
今後、getAllOrders()関数で受注処理が未了の注文のみを取得することがあれば、変更は一カ所だけでよいため、Order::all()が宣言されたすべての箇所を探す必要はなく、見落とすリスクが避けられます。
コントローラーの作成
リポジトリができたところで、コントローラーにコードを追加します。app/Http/Controllers/OrderController.phpを開き、コードを次のように変更します。
このコードにより、コンストラクタからOrderRepositoryInterfaceインスタンスが注入され、それぞれのコントローラーメソッドで関連オブジェクトのメソッドが使用されます。
まず、index()メソッドがorderRepositoryに定義されたgetAllOrders()メソッドを呼び出し、注文のリストを取得してJSON形式のレスポンスを返します。
次にstore()メソッドがorderRepositoryからcreateOrder()メソッドを呼び出し、新しい注文を作成します。配列として作成する注文詳細が取得され、成功するとレスポンスが返されます。
コントローラーのshow()メソッドは、APIルートから一意の注文Idを取得し、パラメーターとしてgetOrderById()に渡します。これにより、データベースからIDの一致する注文の詳細が取得され、JSON形式のレスポンスが返されます。
続いて、作成済みの注文の詳細を更新するために、リポジトリからupdateOrder()メソッドが呼び出されます。この呼び出しでは、一意の注文IDと更新が必要な注文の詳細の2つのパラメーターを引数に取ります。
最後に、destroy()メソッドがAPIルートから一意の注文IDを取得し、リポジトリからdeleteOrder()メソッドを呼び出して注文を削除します。
APIルートの追加
コントローラーに定義された各メソッドを個々のAPIルートにマップするために、routes/api.phpに次のコードを追加します。
インターフェイスと実装のバインド
最後に、OrderRepositoryをLaravelサービスコンテナのOrderRepositoryInterfaceにバインドする必要があります。これは、サービスプロバイダーを使用して行います。次のコマンドを使用して作成します。
app/Providers/RepositoryServiceProvider.phpを開き、register関数を次のように変更します。
最後に、config/app.phpのproviders配列に以下のように新しいサービスプロバイダーを追加します。
アプリケーションのテスト
次のコマンドを使用してアプリケーションを実行します。
アプリケーションはデフォルトでhttp://127.0.0.1:8000/を使用します。PostmanかcURLを使用し、新しく作成したAPIにリクエストを実行します。
次のコマンドを実行し、cURLを使用して/api/ordersエンドポイントをテストします。
次のようなJSONの出力が表示されます。以下の例は、見やすいように一部を省略しています。
Laravelアプリケーションのリポジトリパターンの使い方を紹介しました
この記事では、リポジトリパターンについて、Laravelアプリケーションでの使い方を紹介しました。大規模プロジェクトにおけるいくつかのメリットとして、具体化した実装でなく疎結合されたコードによる抽象化があることも見ていただきました。
一つ最後に注意を記しておきます。小規模のプロジェクトでは、このアプローチは多くの作業が必要で、複数の場所で繰り返される定型コード(ボイラープレートコード)が発生し、すぐにメリットが得られないように感じます。このため、このアプローチをとる前に、プロジェクトの規模を適切に考慮する必要があります。
チュートリアルで使用したすべてのコードベースはGitHubで入手できます。ぜひご活用ください。コーディングを楽しみましょう!
Oluyemi氏は、電気通信工学のバックグラウンドを持つ技術愛好家です。ユーザーが直面する日々の問題を解決することに強い関心を持ち、プログラミングの道に進んで以来、Webとモバイルの両方のソフトウェア開発で問題解決能力を磨いてきました。
Oluyemi氏は、知識の共有に情熱を注ぐフルスタックのソフトウェアエンジニアであり、いくつかのブログで多数の技術記事とコンテンツをインターネットに公開しています。技術にも精通しており、趣味は新しいプログラミング言語とフレームワークを試すことです。
- Twitter: https://twitter.com/yemiwebby
- GitHub: https://github.com/yemiwebby
- Webサイト: https://yemiwebby.com.ng/