イントロダクションIntroduction
Laravelの契約は、一連のインターフェイスで、フレームワークにより提供されているコアサービスを定義したものです。例えばQueue
契約は、ジョブをキューするために必要なメソッドを定義しており、一方、Mailer
契約はメール送信に必要なメソッドを定義しています。Laravel's Contracts are a set of interfaces that define the core services provided by the framework. For example, a Queue
contract defines the methods needed for queueing jobs, while the Mailer
contract defines the methods needed for sending e-mail.
それぞれの契約は、フレームワークにより提供されている実装と対応しています。例えば、LaravelはQueue
の実装は、多様なドライバーを使用しており、Mailer
の実装は、SwiftMailerにより動作しています。Each contract has a corresponding implementation provided by the framework. For example, Laravel provides a Queue
implementation with a variety of drivers, and a Mailer
implementation that is powered by SwiftMailer[http://swiftmailer.org/].
Laravelの全契約は、GitHubのリポジトリーで参照してもらえます。これは全契約を素早く参照できる方法であり、同時に他のパッケージ開発者にとっては、個別に独立したパッケージを利用する参考になるでしょう。All of the Laravel contracts live in their own GitHub repository[https://github.com/illuminate/contracts]. This provides a quick reference point for all available contracts, as well as a single, decoupled package that may be utilized by other package developers.
なぜ契約?Why Contracts?
契約については皆さん多くの質問をお持ちでしょう。なぜ、インターフェイスを使うんだ?インターフェイスを使えば、もっと複雑になるんじゃないか?You may have several questions regarding contracts. Why use interfaces at all? Isn't using interfaces more complicated?
インターフェイスを利用する理由を突き詰めれば、次の2つになります。緩い結合と、単純さです。Let's distill the reasons for using interfaces to the following headings: loose coupling and simplicity.
緩い結合Loose Coupling
最初にキャッシュの実装とがっちり結合したコードをレビューしてみましょう。次のコードをご覧ください。First, let's review some code that is tightly coupled to a cache implementation. Consider the following:
<?php namespace App\Orders;
class Repository {
/**
* キャッシュ
*/
protected $cache;
/**
* 新しいリポジトリーインスタンスの生成
*
* @param \Package\Cache\Memcached $cache
* @return void
*/
public function __construct(\SomePackage\Cache\Memcached $cache)
{
$this->cache = $cache;
}
/**
* IDにより注文を取得
*
* @param int $id
* @return Order
*/
public function find($id)
{
if ($this->cache->has($id))
{
//
}
}
}
このクラスにおけるコードは、使用されているキャッシュの実装にきつく結合しています。つまりパッケージベンダーの具象キャッシュクラスに依存しているために、結合が強くなっています。パッケージのAPIが変更されたら、同時にこのコードも変更しなくてはなりません。In this class, the code is tightly coupled to a given cache implementation. It is tightly coupled because we are depending on a concrete Cache class from a package vendor. If the API of that package changes our code must change as well.
キャッシュの裏で動作している技術(Memcached)を別のもの(Redis)へ置き換えたくなれば、リポジトリーを修正する必要があるというのは、起こり得ます。リポジトリーは誰がデータを提供しているかとか、どのように提供しているかという知識を沢山持っていてはいけません。Likewise, if we want to replace our underlying cache technology (Memcached) with another technology (Redis), we again will have to modify our repository. Our repository should not have so much knowledge regarding who is providing them data or how they are providing it.
このようなアプローチを取る代わりに、ベンダーと関連がないシンプルなインターフェイスへ依存するコードにより、向上させることができます。Instead of this approach, we can improve our code by depending on a simple, vendor agnostic interface:
<?php namespace App\Orders;
use Illuminate\Contracts\Cache\Repository as Cache;
class Repository {
/**
* 新しいリポジトリーインスタンスの生成
*
* @param Cache $cache
* @return void
*/
public function __construct(Cache $cache)
{
$this->cache = $cache;
}
}
これでコードは特定のベンダー、しかもLaravelにさえ依存しなくなりました。契約パッケージは実装も依存も含んでいないため、与えられた契約の異なった実装を簡単に記述できます。キャッシュを使用するコードを変更することなく、キャッシュ実装を置き換えることができるようになりました。Now the code is not coupled to any specific vendor, or even Laravel. Since the contracts package contains no implementation and no dependencies, you may easily write an alternative implementation of any given contract, allowing you to replace your cache implementation without modifying any of your cache consuming code.
単純さSimplicity
Laravelのサービスは全部、シンプルなインターフェイスの中で適切に定義されているので、サービスが提供する機能も簡単に定義できています。 契約はフレームワークの機能の簡単なドキュメントとして使えます。When all of Laravel's services are neatly defined within simple interfaces, it is very easy to determine the functionality offered by a given service. The contracts serve as succinct documentation to the framework's features.
それに加え、シンプルなインターフェイスに基づけば、あなたのコードは簡単に理解でき、メンテナンスできるようにもなります。大きくて複雑なクラスの中で、どのメソッドが使用可能かを探し求めるよりも、シンプルでクリーンなインターフェイスを参照できます。In addition, when you depend on simple interfaces, your code is easier to understand and maintain. Rather than tracking down which methods are available to you within a large, complicated class, you can refer to a simple, clean interface.
契約リファレンスContract Reference
Laravelのほぼ全ての契約と、Laravelファサードとの比較一覧です。This is a reference to most Laravel Contracts, as well as their Laravel "facade" counterparts:
契約Contract | Laravel 4.x ファサードLaravel 4.x Facade |
---|---|
Illuminate\Contracts\Auth\GuardIlluminate\Contracts\Auth\Guard[https://github.com/illuminate/contracts/blob/master/Auth/Guard.php] | AuthAuth |
Illuminate\Contracts\Auth\PasswordBrokerIlluminate\Contracts\Auth\PasswordBroker[https://github.com/illuminate/contracts/blob/master/Auth/PasswordBroker.php] | PasswordPassword |
Illuminate\Contracts\Cache\RepositoryIlluminate\Contracts\Cache\Repository[https://github.com/illuminate/contracts/blob/master/Cache/Repository.php] | CacheCache |
Illuminate\Contracts\Cache\FactoryIlluminate\Contracts\Cache\Factory[https://github.com/illuminate/contracts/blob/master/Cache/Factory.php] | Cache::driver()Cache::driver() |
Illuminate\Contracts\Config\RepositoryIlluminate\Contracts\Config\Repository[https://github.com/illuminate/contracts/blob/master/Config/Repository.php] | ConfigConfig |
Illuminate\Contracts\Container\ContainerIlluminate\Contracts\Container\Container[https://github.com/illuminate/contracts/blob/master/Container/Container.php] | AppApp |
Illuminate\Contracts\Cookie\FactoryIlluminate\Contracts\Cookie\Factory[https://github.com/illuminate/contracts/blob/master/Cookie/Factory.php] | CookieCookie |
Illuminate\Contracts\Cookie\QueueingFactoryIlluminate\Contracts\Cookie\QueueingFactory[https://github.com/illuminate/contracts/blob/master/Cookie/QueueingFactory.php] | Cookie::queue()Cookie::queue() |
Illuminate\Contracts\Encryption\EncrypterIlluminate\Contracts\Encryption\Encrypter[https://github.com/illuminate/contracts/blob/master/Encryption/Encrypter.php] | CryptCrypt |
Illuminate\Contracts\Events\DispatcherIlluminate\Contracts\Events\Dispatcher[https://github.com/illuminate/contracts/blob/master/Events/Dispatcher.php] | EventEvent |
Illuminate\Contracts\Filesystem\CloudIlluminate\Contracts\Filesystem\Cloud[https://github.com/illuminate/contracts/blob/master/Filesystem/Cloud.php] | |
Illuminate\Contracts\Filesystem\FactoryIlluminate\Contracts\Filesystem\Factory[https://github.com/illuminate/contracts/blob/master/Filesystem/Factory.php] | FileFile |
Illuminate\Contracts\Filesystem\FilesystemIlluminate\Contracts\Filesystem\Filesystem[https://github.com/illuminate/contracts/blob/master/Filesystem/Filesystem.php] | FileFile |
Illuminate\Contracts\Foundation\ApplicationIlluminate\Contracts\Foundation\Application[https://github.com/illuminate/contracts/blob/master/Foundation/Application.php] | AppApp |
Illuminate\Contracts\Hashing\HasherIlluminate\Contracts\Hashing\Hasher[https://github.com/illuminate/contracts/blob/master/Hashing/Hasher.php] | HashHash |
Illuminate\Contracts\Logging\LogIlluminate\Contracts\Logging\Log[https://github.com/illuminate/contracts/blob/master/Logging/Log.php] | LogLog |
Illuminate\Contracts\Mail\MailQueueIlluminate\Contracts\Mail\MailQueue[https://github.com/illuminate/contracts/blob/master/Mail/MailQueue.php] | Mail::queue()Mail::queue() |
Illuminate\Contracts\Mail\MailerIlluminate\Contracts\Mail\Mailer[https://github.com/illuminate/contracts/blob/master/Mail/Mailer.php] | MailMail |
Illuminate\Contracts\Queue\FactoryIlluminate\Contracts\Queue\Factory[https://github.com/illuminate/contracts/blob/master/Queue/Factory.php] | Queue::driver()Queue::driver() |
Illuminate\Contracts\Queue\QueueIlluminate\Contracts\Queue\Queue[https://github.com/illuminate/contracts/blob/master/Queue/Queue.php] | QueueQueue |
Illuminate\Contracts\Redis\DatabaseIlluminate\Contracts\Redis\Database[https://github.com/illuminate/contracts/blob/master/Redis/Database.php] | RedisRedis |
Illuminate\Contracts\Routing\RegistrarIlluminate\Contracts\Routing\Registrar[https://github.com/illuminate/contracts/blob/master/Routing/Registrar.php] | RouteRoute |
Illuminate\Contracts\Routing\ResponseFactoryIlluminate\Contracts\Routing\ResponseFactory[https://github.com/illuminate/contracts/blob/master/Routing/ResponseFactory.php] | ResponseResponse |
Illuminate\Contracts\Routing\UrlGeneratorIlluminate\Contracts\Routing\UrlGenerator[https://github.com/illuminate/contracts/blob/master/Routing/UrlGenerator.php] | URLURL |
Illuminate\Contracts\Support\ArrayableIlluminate\Contracts\Support\Arrayable[https://github.com/illuminate/contracts/blob/master/Support/Arrayable.php] | |
Illuminate\Contracts\Support\JsonableIlluminate\Contracts\Support\Jsonable[https://github.com/illuminate/contracts/blob/master/Support/Jsonable.php] | |
Illuminate\Contracts\Support\RenderableIlluminate\Contracts\Support\Renderable[https://github.com/illuminate/contracts/blob/master/Support/Renderable.php] | |
Illuminate\Contracts\Validation\FactoryIlluminate\Contracts\Validation\Factory[https://github.com/illuminate/contracts/blob/master/Validation/Factory.php] | Validator::make()Validator::make() |
Illuminate\Contracts\Validation\ValidatorIlluminate\Contracts\Validation\Validator[https://github.com/illuminate/contracts/blob/master/Validation/Validator.php] | |
Illuminate\Contracts\View\FactoryIlluminate\Contracts\View\Factory[https://github.com/illuminate/contracts/blob/master/View/Factory.php] | View::make()View::make() |
Illuminate\Contracts\View\ViewIlluminate\Contracts\View\View[https://github.com/illuminate/contracts/blob/master/View/View.php] |
契約使用法How To Use Contracts
では、契約の実装はどうやって入手するのでしょうか?とてもシンプルです。Laravelでは多くのタイプのクラスがサービスコンテナを利用して依存解決されています。コントローラーを始め、イベントリスナー、フィルター、キュージョブ、それにルートクロージャーもそうです。契約の実装を手に入れるには、依存を解決するクラスのコンストラクターで、「タイプヒント」を指定するだけです。例として、次のイベントハンドラーをご覧ください。So, how do you get an implementation of a contract? It's actually quite simple. Many types of classes in Laravel are resolved through the service container[/docs/master/container], including controllers, event listeners, filters, queue jobs, and even route Closures. So, to get an implementation of a contract, you can just "type-hint" the interface in the constructor of the class being resolved. For example, take a look at this event handler:
<?php namespace App\Handlers\Events;
use App\User;
use App\Events\NewUserRegistered;
use Illuminate\Contracts\Redis\Database;
class CacheUserInformation {
/**
* Redisデータベースの実装
*/
protected $redis;
/**
* 新しいイベントリスナーインスタンスの生成
*
* @param Database $redis
* @return void
*/
public function __construct(Database $redis)
{
$this->redis = $redis;
}
/**
* イベントの処理
*
* @param NewUserRegistered $event
* @return void
*/
public function handle(NewUserRegistered $event)
{
//
}
}
イベントリスナーの依存解決時に、サービスコンテナはクラスのコンストラクターで指定されているタイプヒントを読み取り、適切な値を注入します。サービスコンテナへ何かを登録する方法を学ぶには、ドキュメントを参照してください。When the event listener is resolved, the service container will read the type-hints on the constructor of the class, and inject the appropriate value. To learn more about registering things in the service container, check out the documentation[/docs/master/container].