Contracts
When to use contracts
- Low coupling
- How to use the contract
- Introduction Laravel's contract is a set of interfaces provided by the framework that defines core services. For example, the
- Illuminate\Contracts\Queue\Queue contract defines the methods required for queue tasks, and the Illuminate\Contracts\Mail\Mailer contract defines the methods required for sending emails.
Each contract has an implementation provided by the corresponding framework. For example, Laravel provides multiple driver queue implementations and uses SwiftMailer to implement email contracts. All Laravel contracts are in
their respective GitHub repositoriesLaravel's Facades and helper functions provide an easy way to use Laravel services without type hints, and can also resolve contracts outside the service container. In most cases, each Facade has an equivalent contract. Unlike Facades (which do not require the constructor in your class to reference dependencies), contracts allow you to define explicit dependencies for your class. Some developers prefer dependencies to be clearly defined and so prefer to use contracts, while other developers enjoy the convenience brought by facades. {tip} In most applications, whether you prefer Facades or Contracts is fine. However, if you are building an extension package, you should strongly consider using contracts, as they make it easier to test within the context of the package. To sum up, whether to use contracts or Facades to a large extent Depends on your personal or team preference. Both Contracts and Facades can be used to build robust, well-tested Laravel applications. As long as you keep the responsibilities of your classes simple, you'll find that the actual difference between using contracts and Facades is very small. However, you may still have many questions about the contract. For example, why do you use interface? Isn't it more complicated to use interface? Let us distill the reasons in the following content ("low coupling" and "simplicity"). First, let’s look at some high-coupling code for cache implementation. Suppose there is the following code: In this class, the code is highly coupled with the given cache implementation. It is highly coupled because we rely on a specific cache class in an extension package. If the extension's API changes, our code will have to change as well. Similarly, if we want to replace the underlying caching technology (Memcached) with another caching technology (Redis), we have to modify our code base again. Our codebase shouldn't know too much about who provided the data or how the data was provided. We can improve our code by relying on a simple interface that has nothing to do with the extension package, instead of the previous implementation: The current code is not consistent with Any specific extension package is coupled, not even related to Laravel. Since the contract extension package does not contain any implementations and dependencies, you can easily code an alternative implementation for a given contract, allowing you to replace the cached implementation without modifying any cache code. When all of Laravel's services are neatly defined in simple interfaces, it's easy to determine the functionality provided by a given service. The contract serves as a concise document of the framework's functionality. Additionally, when you rely on simple interfaces, your code is easier to understand and maintain. Instead of tracking which methods are available in large, complex classes, you can refer to a simple, clean interface. So, how do you implement a contract? It's actually quite simple. Many types of classes in Laravel are resolved through the service container, including controllers, event listeners, middleware, queue jobs, and even routing closures, etc. Then, to obtain the implementation of a contract, you only need to type the "type hint" interface in the constructor of the class to be parsed. For example, take a look at this event listener: When the event listener is resolved, the service container will read the type hint on the constructor of the class and inject the appropriate value. To learn more about registering with a service container, check out the service container documentation. This table provides a quick reference of all Laravel contracts and their equivalent facades: Contracts Vs. Facades
When to use contracts
Low coupling
<?php
namespace App\Orders;
class Repository{
/**
* 缓存实例.
*/
protected $cache;
/**
* 创建一个新的仓库实例.
*
* @param \SomePackage\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))
{
//
}
}
}
<?php
namespace App\Orders;
use Illuminate\Contracts\Cache\Repository as Cache;
class Repository{
/**
* 缓存实例.
*/ protected $cache; /**
* 创建一个新的仓库实例.
*
* @param Cache $cache
* @return void
*/
public function __construct(Cache $cache)
{
$this->cache = $cache;
}
}
Simple
How to use a contract
<?php
namespace App\Listeners;
use App\User;use App\Events\OrderWasPlaced;
use Illuminate\Contracts\Redis\Factory;
class CacheOrderInformation{
/**
* Redis 工厂实现。
*/
protected $redis;
/**
* 创建一个新的事件处理器实例。
*
* @param Factory $redis
* @return void
*/
public function __construct(Factory $redis)
{
$this->redis = $redis;
}
/**
* 处理事件。
*
* @param OrderWasPlaced $event
* @return void
*/
public function handle(OrderWasPlaced $event)
{
//
}
}
Contract Reference