Understanding Laravel Contracts: Benefits, Usage, and Comparison with Facades
Laravel contracts are framework-provided interfaces that define core services such as queues and mail, offering low-coupling and clear dependencies; the article explains their purpose, compares them with facades, demonstrates refactoring for decoupling with code examples, and shows how to inject contracts via the service container.
Contract Introduction
Laravel contracts are a set of interfaces provided by the framework that define core services. For example, Illuminate\Contracts\Queue\Queue defines the methods required for queue jobs, and Illuminate\Contracts\Mail\Mailer defines the methods required for sending mail.
Each contract has a corresponding implementation supplied by the framework; for instance, Laravel provides multiple queue driver implementations and a SwiftMailer‑driven mail implementation.
All Laravel contracts are available in their respective GitHub repositories, offering a quick reference for available contracts and decoupled packages for extension developers.
Contracts vs. Facades
Laravel facades and helper functions provide a convenient way to use services without type‑hinting, and they can resolve contracts outside the service container. In most cases, each facade has an equivalent contract.
Unlike facades, which do not require constructor injection, contracts let you explicitly define dependencies in your classes. Some developers prefer the explicitness of contracts, while others enjoy the convenience of facades.
Tip: In most applications either approach works, but when building packages you should strongly consider using contracts because they are easier to test.
When to Use Contracts
Ultimately, choosing contracts or facades depends on personal or team preference; both can be used to build robust, well‑tested Laravel applications, and the practical difference is minimal when responsibilities are kept single‑purpose.
Nevertheless, questions often arise: why use interfaces at all? Aren’t they more complex? The following sections on “Low Coupling” and “Simplicity” explain the reasons.
Low Coupling
Consider a tightly coupled cache implementation:
<?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)) {
//
}
}
}This class depends directly on a specific cache package; if the package API changes, the code must be modified. Replacing Memcached with Redis would also require changes.
By depending on a simple, package‑agnostic interface, the code can be decoupled:
<?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;
}
}The refactored version no longer ties to any specific package or even Laravel, making it easy to provide alternative implementations without altering the cache‑using code.
Simplicity
When all services are defined by concise interfaces, it becomes straightforward to understand the capabilities each service provides. Contracts act as a clean documentation of framework functionality.
Relying on simple interfaces also makes your code easier to read and maintain, as you can refer to a tidy contract rather than navigating large, complex classes.
How to Use Contracts
Implementing contracts is simple. Laravel resolves many classes via the service container, including controllers, event listeners, middleware, queue jobs, and route closures. By type‑hinting a contract in a class constructor, the container injects the appropriate implementation.
Example event listener using 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)
{
//
}
}When the listener is resolved, the container reads the type hint on the constructor and injects the correct Redis factory instance.
Laravel Tech Community
Specializing in Laravel development, we continuously publish fresh content and grow alongside the elegant, stable Laravel framework.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.