Laravel 的契约 (Contracts)

优质
小牛编辑
157浏览
2023-12-01

简介

Laravel 的契约是一组定义框架提供的核心服务的接口。例如,Illuminate\Contracts\Queue\Queue 契约定义了队列任务所需的方法,而 Illuminate\Contracts\Mail\Mailer 契约定义了发送电子邮件所需的方法。

框架对每个契约都提供了相应的实现。例如,Laravel 提供了具有各种驱动的队列实现和由 SwiftMailer 提供支持的邮件驱动实现。

所有的 Laravel 契约都有 他们自己的 GitHub 库。这为所有可用的契约提供了一个快速参考指南,同时也可单独作为低耦合的扩展包给其他包开发者使用。

契约 VS Facades

Laravel Facades 和辅助函数提供了一种使用 Laravel 服务的简单方法,即不需要通过类型提示并从服务容器中解析契约。在大多数情况下,每个 Facades 都有一个等效的契约。

不像 Facades,不需要你在类的构造函数中类型提示,契约则需要你在类中明显地定义依赖项。一些开发者倾向于以契约这种方式明确地定义它们的依赖项,而其它开发者则更喜欢 Facades 带来的便捷。

{tip} 对于大多数应用程序来说不管是使用 facades 还是契约都可以。但是,如果你正在构建一个扩展包,为了方便测试,你应该强烈考虑契约。

何时使用契约

综上所述,使用契约或是 Facades 很大程度上归结于个人或者开发团队的喜好。不管是契约还是 Facades 都可以创建出健壮的、易测试的 Laravel 应用程序。如果你长期关注类的单一职责,你会注意到使用契约还是 Facades 其实没多少实际意义上的区别。

然而,你可能还是会有几个关于契约的问题。例如,为什么要使用接口?不使用接口会比较复杂吗?下面让我们谈下使用接口的原因:低耦合和简单性。

低耦合

首先,让我们来看一些高耦合缓存实现的代码。如下:

<?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))    {
            //
        }
    }
}

在这个类中,程序跟给定的缓存实现高耦合。因为我们依赖于一个扩展包的特定缓存类。一旦这个扩展包的 API 被更改了,我们的代码就必须跟着改变。

同样的,如果我们想要将底层的的缓存技术( Memcached )替换为另一种缓存技术( Redis ),那又得再次修改这个 Repository 类。而 Repository 类不应该了解太多关于谁提供了这些数据或是如何提供的等等。

比起上面的做法,我们可以使用一个简单的、与扩展包无关的接口来改进我们的代码:

<?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;
    }
}

现在,更改之后的代码没有与任何扩展包甚至是 Laravel 耦合。而契约扩展包不包含任何实现和依赖项,你可以轻松地写任何给定契约的替代实现,来实现不修改任何关于缓存消耗的代码就可以替换缓存实现。

简单性

当所有 Laravel 的服务都使用简洁的接口定义,就很容易判断给定服务提供的功能。 可以将契约视为说明框架功能的简洁文档。

除此之外,当依赖的接口足够简洁时,代码的可读性和可维护性会大大提高。比起搜索一个大型复杂的类中有哪些可用的方法,不如检索一个简单、 干净的接口来参考更妥当。

如何使用契约

那么,如何获得一个契约的实现呢?这其实很简单。

Laravel 中的许多类型的类都是通过 服务容器 解析出来的,包括控制器、事件监听器、中间件、任务队列,甚至路由闭包。所以说,要获得一个契约的实现,你只需要被解析的类的构造函数中添加「类型提示」即可。

例如,看看这个事件监听器:

<?php

namespace App\Listeners;

use App\User;
use App\Events\OrderWasPlaced;
use Illuminate\Contracts\Redis\Database;

class CacheOrderInformation
{
    /**
     * Redis 数据库实现。
     */
    protected $redis;

    /**
     * 创建事件处理器实例。
     *
     * @param  Database  $redis
     * @return void
     */
    public function __construct(Database $redis)
    {
        $this->redis = $redis;
    }

    /**
     * 处理事件。
     *
     * @param  OrderWasPlaced  $event
     * @return void
     */
    public function handle(OrderWasPlaced $event)
    {
        //
    }
}

当事件监听器被解析时,服务容器会读取类的构造函数上的类型提示,并注入对应的值。想了解更多关于服务容器的注册,请查看 这个文档。

契约参考

下表提供了所有 Laravel 契约及其对应的 Facades:

契约参考 Facade
Illuminate\Contracts\Auth\Access\Authorizable
Illuminate\Contracts\Auth\Access\Gate`Gate`
Illuminate\Contracts\Auth\Authenticatable
Illuminate\Contracts\Auth\CanResetPassword
Illuminate\Contracts\Auth\Factory`Auth`
Illuminate\Contracts\Auth\Guard`Auth::guard()`
Illuminate\Contracts\Auth\PasswordBroker`Password::broker()`
Illuminate\Contracts\Auth\PasswordBrokerFactory`Password`
Illuminate\Contracts\Auth\StatefulGuard
Illuminate\Contracts\Auth\SupportsBasicAuth
Illuminate\Contracts\Auth\UserProvider
Illuminate\Contracts\Bus\Dispatcher`Bus`
Illuminate\Contracts\Bus\QueueingDispatcher`Bus::dispatchToQueue()`
Illuminate\Contracts\Broadcasting\Factory`Broadcast`
Illuminate\Contracts\Broadcasting\Broadcaster`Broadcast::connection()`
Illuminate\Contracts\Broadcasting\ShouldBroadcast
Illuminate\Contracts\Broadcasting\ShouldBroadcastNow
Illuminate\Contracts\Cache\Factory`Cache`
Illuminate\Contracts\Cache\Lock
Illuminate\Contracts\Cache\LockProvider
Illuminate\Contracts\Cache\Repository`Cache::driver()`
Illuminate\Contracts\Cache\Store
Illuminate\Contracts\Config\Repository`Config`
Illuminate\Contracts\Console\Application
Illuminate\Contracts\Console\Kernel`Artisan`
Illuminate\Contracts\Container\Container`App`
Illuminate\Contracts\Cookie\Factory`Cookie`
Illuminate\Contracts\Cookie\QueueingFactory`Cookie::queue()`
Illuminate\Contracts\Database\ModelIdentifier
Illuminate\Contracts\Debug\ExceptionHandler
Illuminate\Contracts\Encryption\Encrypter`Crypt`
Illuminate\Contracts\Events\Dispatcher`Event`
Illuminate\Contracts\Filesystem\Cloud`Storage::cloud()`
Illuminate\Contracts\Filesystem\Factory`Storage`
Illuminate\Contracts\Filesystem\Filesystem`Storage::disk()`
Illuminate\Contracts\Foundation\Application`App`
Illuminate\Contracts\Hashing\Hasher`Hash`
Illuminate\Contracts\Http\Kernel
Illuminate\Contracts\Logging\Log`Log`
Illuminate\Contracts\Mail\MailQueue`Mail::queue()`
Illuminate\Contracts\Mail\Mailable
Illuminate\Contracts\Mail\Mailer`Mail`
Illuminate\Contracts\Notifications\Dispatcher`Notification`
Illuminate\Contracts\Notifications\Factory`Notification`
Illuminate\Contracts\Pagination\LengthAwarePaginator
Illuminate\Contracts\Pagination\Paginator
Illuminate\Contracts\Pipeline\Hub
Illuminate\Contracts\Pipeline\Pipeline
Illuminate\Contracts\Queue\EntityResolver
Illuminate\Contracts\Queue\Factory`Queue`
Illuminate\Contracts\Queue\Job
Illuminate\Contracts\Queue\Monitor`Queue`
Illuminate\Contracts\Queue\Queue`Queue::connection()`
Illuminate\Contracts\Queue\QueueableCollection
Illuminate\Contracts\Queue\QueueableEntity
Illuminate\Contracts\Queue\ShouldQueue
Illuminate\Contracts\Redis\Factory`Redis`
Illuminate\Contracts\Routing\BindingRegistrar`Route`
Illuminate\Contracts\Routing\Registrar`Route`
Illuminate\Contracts\Routing\ResponseFactory`Response`
Illuminate\Contracts\Routing\UrlGenerator`URL`
Illuminate\Contracts\Routing\UrlRoutable
Illuminate\Contracts\Session\Session`Session::driver()`
Illuminate\Contracts\Support\Arrayable
Illuminate\Contracts\Support\Htmlable
Illuminate\Contracts\Support\Jsonable
Illuminate\Contracts\Support\MessageBag
Illuminate\Contracts\Support\MessageProvider
Illuminate\Contracts\Support\Renderable
Illuminate\Contracts\Support\Responsable
Illuminate\Contracts\Translation\Loader
Illuminate\Contracts\Translation\Translator`Lang`
Illuminate\Contracts\Validation\Factory`Validator`
Illuminate\Contracts\Validation\ImplicitRule
Illuminate\Contracts\Validation\Rule
Illuminate\Contracts\Validation\ValidatesWhenResolved
Illuminate\Contracts\Validation\Validator`Validator::make()`
Illuminate\Contracts\View\Engine
Illuminate\Contracts\View\Factory`View`
Illuminate\Contracts\View\View`View::make()`