Laravel のコントラクトは、ユーザー紹介の章で見たユーザー ガード コントラクト IllumninateContractsAuthGuard など、フレームワークによって提供されるコア サービスを定義する一連のインターフェイスです。ユーザー プロバイダー コントラクト IlluminateContractsAuthUserProvider
上記のコントラクトのソース コード ファイルを通じて、Laravel によって提供されるコントラクトがコア モジュール用に定義されたインターフェイスのセットであることがわかります。 。 Laravel は、各コントラクトに対応する実装クラスを提供します。次の表に、上記の 3 つのコントラクトに対して Laravel が提供する実装クラスを示します。
Contract |
Laravel カーネルによって提供される実装クラス |
IllumninateContractsAuthGuard | IlluminateAuthSessionGuard |
IlluminateContractsAuthUserProvider |
IlluminateAuthEloquentUserProvider |
##IlluminateContractsAuthAuthenticatable | IlluminateFoundationAuthAuthenticatable(User Model の親クラス) |
つまり、独自の開発プロジェクトで、Laravel が提供するユーザー認証システムではニーズを満たせない場合は、ニーズに応じてガードとユーザープロバイダーの実装クラスを定義できます。たとえば、私が以前に取り組んだプロジェクトは次のとおりです。ユーザー認証は従業員管理システムの API に依存していたため、ガードとユーザープロバイダーコントラクトの実装クラスを自分で作成し、Laravel がカスタマイズされた Guard と UserProvider を通じてユーザー認証を完了できるようにしました。ユーザー認証をカスタマイズする方法については、ユーザー認証を紹介する章で紹介しています。この記事を参照してください。
つまり、Laravel がすべてのコア機能のコントラクト インターフェイスを定義する目的は、開発者が独自のプロジェクトのニーズに応じて独自の実装クラスを定義できるようにすることと、これらのインターフェイスの利用者 (コントローラー、カーネルは AuthManager などを提供します。インターフェイスによって提供されるメソッドがどのように実装されるかを気にする必要はありません。インターフェイス メソッドがどのような機能を提供できるかだけを考慮し、それらの機能を使用することができます。必要に応じてインターフェースを変更する必要はありません。消費者側で変更を加える必要はありません。
コントラクトの定義と使用
上で述べたものはすべて、Laravel カーネルによって提供されるコントラクトです。大規模なプロジェクトを開発する場合、プロジェクト内でコントラクトを自分で定義することもできます。何もないところからコントラクトや実装クラスを追加するのは、組み込みのコントローラー層とモデル層だけで十分だと感じるかもしれません。簡単な例から始めて、次のコードの何が問題なのかを考えてみましょう。
class OrderController extends Controller
{
public function getUserOrders()
{
$orders= Order::where('user_id', '=', \Auth::user()->id)->get();
return View::make('order.index', compact('orders'));
}
}
ログイン後にコピー
このコードは非常に単純ですが、このコードをテストしたい場合は、間違いなく実際のデータベースと通信することになります。言い換えれば、ORM とこのコントローラーは密接に結合されています。 Eloquent ORM を使用して実際のデータベースに接続しない限り、このコードを実行またはテストする方法はありません。このコードは、「関心事の分離」というソフトウェア設計原則にも違反しています。簡単に言えば、このコントローラーは知りすぎています。コントローラーはデータがどこから来たのかを知る必要はなく、データにアクセスする方法だけを知る必要があります。コントローラーはデータが MySQL からどこから来たのかを知る必要はなく、データが現在利用可能であることを知っていればよいだけです。
関心の分離 すべてのクラスは単一の責任を持つべきであり、その責任はクラスによって完全にカプセル化される必要があります。
すべてのクラスは単一の責任を持つ必要があります。そして責任のすべてはこのクラスによってカプセル化される必要があります
次にインターフェースを定義し、インターフェースを実装します
interface OrderRepositoryInterface
{
public function userOrders(User $user);
}
class OrderRepository implements OrderRepositoryInterface
{
public function userOrders(User $user)
{
Order::where('user_id', '=', $user->id)->get();
}
}
ログイン後にコピー
インターフェースの実装をLaravelのサービスコンテナにバインドします
App::singleton('OrderRepositoryInterface', 'OrderRespository');
ログイン後にコピー
次に、このインターフェイスの実装をコントローラーに挿入します
class UserController extends Controller
{
public function __construct(OrderRepositoryInterface $orderRepository)
{
$this->orders = $orderRespository;
}
public function getUserOrders()
{
$orders = $this->orders->userOrders();
return View::make('order.index', compact('orders'));
}
}
ログイン後にコピー
これで、コントローラーはデータ層から完全に独立しました。ここでのデータは、MySQL、MongoDB、または Redis から取得される可能性があります。私たちのコントローラーは違いを知りませんし、知る必要もありません。こうすることで、データ層から独立して Web 層をテストでき、将来的にはストレージ実装を簡単に切り替えることができます。
インターフェイスとチーム開発
チームが大規模なアプリケーションを開発している場合、部分によって開発速度は異なります。たとえば、ある開発者はデータ層を開発し、別の開発者はコントローラー層を開発します。コントローラーを作成した開発者はコントローラーをテストしたいと考えていますが、データ層の開発が遅いため、同時にテストすることができません。 2 人の開発者が最初にインターフェイスの形式で合意に達すると、バックグラウンドで開発されるさまざまなクラスがこの合意に従います。合意が確立されると、合意がまだ実装されていない場合でも、開発者はこのインターフェイスの「偽の」実装を作成できます
class DummyOrderRepository implements OrderRepositoryInterface
{
public function userOrders(User $user)
{
return collect(['Order 1', 'Order 2', 'Order 3']);
}
}
ログイン後にコピー
偽の実装を作成したら、IoC コンテナにバインドできます
App::singleton('OrderRepositoryInterface', 'DummyOrderRepository');
ログイン後にコピー
これにより、アプリケーションのビューに偽のデータが設定される可能性があります。次に、バックエンド開発者が RedisOrderRepository などの実際の実装コードの作成を完了したら、次に、IoC コンテナ切り替えインターフェイスの実装を使用すると、アプリケーションは実際の実装に簡単に切り替えることができ、アプリケーション全体が Redis から読み取られたデータを使用するようになります。
インターフェイスとテスト
インターフェイスの合意を確立した後は、テスト中に模擬することがより容易になります
public function testIndexActionBindsUsersFromRepository()
{
// Arrange...
$repository = Mockery::mock('OrderRepositoryInterface');
$repository->shouldReceive('userOrders')->once()->andReturn(['order1', 'order2]);
App::instance('OrderRepositoryInterface', $repository);
// Act...
$response = $this->action('GET', 'OrderController@getUserOrders');
// Assert...
$this->assertResponseOk();
$this->assertViewHas('order', ['order1', 'order2']);
}
ログイン後にコピー
概要
インターフェイスは、プログラミング段階で非常に役立ちます。設計段階では、機能を完成させるためにどのインターフェイスを開発する必要があるかをチームと話し合い、各インターフェイスに実装する特定のメソッドを設計します。メソッドの入力パラメータや戻り値など、誰もがインターフェイス規約に従って独自のモジュールを開発できます。まだ実装されていないインターフェイスに遭遇した場合、最初にそのインターフェイスの偽の実装を定義できます。実際の実装開発が完了するまで待ってから切り替えます。これにより、ソフトウェア プログラム構造における上位層が下位層に与える影響が軽減されるだけでなく、各部分の開発の進行がソフトウェア プログラムの完了に過度に依存することもなくなります。他の部分。