<article><h1>契约 (Contracts)</h1><ul><li><a href="#introduction">简介</a><ul><li><a href="#contracts-vs-facades">契约 (Contracts) Vs. 门面 (Facades)</a></li></ul></li><li><a href="#when-to-use-contracts">何时使用契约</a><ul><li><a href="#loose-coupling">低耦合</a></li><li><a href="#simplicity">简单性</a></li></ul></li><li><a href="#how-to-use-contracts">如何使用 Contracts</a></li><li><a href="#contract-reference">Contract 参考</a></li></ul><p><a name="introduction"></a></p><h2><a href="#introduction">简介</a></h2><p>Laravel 的 契约(Contracts ) 是一系列框架用来定义核心服务的接口。例如,<code class=" language-php">Illuminate\<span class="token package">Contracts<span class="token punctuation">\</span>Queue<span class="token punctuation">\</span>Queue</span></code> 契约中定义了队列任务所需的方法,而 <code class=" language-php">Illuminate\<span class="token package">Contracts<span class="token punctuation">\</span>Mail<span class="token punctuation">\</span>Mailer</span></code> 契约中定义了发送电子邮件所需的方法。</p><p>框架对每个契约都提供了相应的实现。例如,Laravel 为队列提供了各种驱动的实现,为邮件提供了由 <a href="http://swiftmailer.org/">SwiftMailer</a> 驱动的实现。</p><p>所有 Laravel 契约都有相应的 <a href="https://github.com/illuminate/contracts">GitHub 库</a> ,这给所有可用的契约提供了一个快速参考指南,同时也可单独作为低耦合的扩展包给其他包开发者去使用。</p><p><a name="contracts-vs-facades"></a></p><h3>契约 (contracts) VS 门面 (facades)</h3><p><a href="/docs/5.4/facades">门面 (facades)</a> 和一些辅助函数提供了一种使用 Laravel 服务的简单方法,即不再需要通过类型约束和在服务容器之外解析契约。 在大多数情况下,每个门面都有一个等效契约。</p><p>不像门面,门面不需要你在类构造函数中约束什么,契约则是需要你明显地定义依赖关系。 一些开发人员喜欢契约这种方式明显地去定义它们的依赖关系,而另一些开发人员则更喜欢门面带来的便捷。</p><blockquote class="has-icon tip"><p><div class="flag"><span class="svg"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/" version="1.1" x="0px" y="0px" width="56.6px" height="87.5px" viewBox="0 0 56.6 87.5" enable-background="new 0 0 56.6 87.5" xml:space="preserve"><path fill="#FFFFFF" d="M28.7 64.5c-1.4 0-2.5-1.1-2.5-2.5v-5.7 -5V41c0-1.4 1.1-2.5 2.5-2.5s2.5 1.1 2.5 2.5v10.1 5 5.8C31.2 63.4 30.1 64.5 28.7 64.5zM26.4 0.1C11.9 1 0.3 13.1 0 27.7c-0.1 7.9 3 15.2 8.2 20.4 0.5 0.5 0.8 1 1 1.7l3.1 13.1c0.3 1.1 1.3 1.9 2.4 1.9 0.3 0 0.7-0.1 1.1-0.2 1.1-0.5 1.6-1.8 1.4-3l-2-8.4 -0.4-1.8c-0.7-2.9-2-5.7-4-8 -1-1.2-2-2.5-2.7-3.9C5.8 35.3 4.7 30.3 5.4 25 6.7 14.5 15.2 6.3 25.6 5.1c13.9-1.5 25.8 9.4 25.8 23 0 4.1-1.1 7.9-2.9 11.2 -0.8 1.4-1.7 2.7-2.7 3.9 -2 2.3-3.3 5-4 8L41.4 53l-2 8.4c-0.3 1.2 0.3 2.5 1.4 3 0.3 0.2 0.7 0.2 1.1 0.2 1.1 0 2.2-0.8 2.4-1.9l3.1-13.1c0.2-0.6 0.5-1.2 1-1.7 5-5.1 8.2-12.1 8.2-19.8C56.4 12 42.8-1 26.4 0.1zM43.7 69.6c0 0.5-0.1 0.9-0.3 1.3 -0.4 0.8-0.7 1.6-0.9 2.5 -0.7 3-2 8.6-2 8.6 -1.3 3.2-4.4 5.5-7.9 5.5h-4.1H28h-0.5 -3.6c-3.5 0-6.7-2.4-7.9-5.7l-0.1-0.4 -1.8-7.8c-0.4-1.1-0.8-2.1-1.2-3.1 -0.1-0.3-0.2-0.5-0.2-0.9 0.1-1.3 1.3-2.1 2.6-2.1H41C42.4 67.5 43.6 68.2 43.7 69.6zM37.7 72.5H26.9c-4.2 0-7.2 3.9-6.3 7.9 0.6 1.3 1.8 2.1 3.2 2.1h4.1 0.5 0.5 3.6c1.4 0 2.7-0.8 3.2-2.1L37.7 72.5z"></path></svg></span></div> 对于大多数应用程序来说不管是使用门面还是契约只要你喜欢都行。但是 ,如果你正在构建一个扩展包,为了方便测试建议考虑使用契约比较好。</p></blockquote><p><a name="when-to-use-contracts"></a></p><h2><a href="#when-to-use-contracts">何时使用 contracts</a></h2><p>综上所述,使用契约或者门面很大程度上归结于个人或者开发团队的喜好。不管是契约还是门面都可以创建出强大的、容易测试的 Laravel 应用程序。 如果你长期关注类的单一职责,你会注意到使用契约和门面其实没多少实际意义上的区别。</p><p>然而,你可能还是会有几个关于契约的问题。像是,为什么要使用接口?使用接口会不会变得更加复杂?下面让我们简单阐述一下使用接口的原因:低耦合和简单性。</p><p><a name="loose-coupling"></a></p><h3>低耦合</h3><p>首先,让我们回顾一些与缓存实现的高耦合代码。如下:</p><pre class=" language-php"><code class=" language-php"><span class="token delimiter"><?php</span>
<span class="token keyword">namespace</span> <span class="token package">App<span class="token punctuation">\</span>Orders</span><span class="token punctuation">;</span>
<span class="token keyword">class</span> <span class="token class-name">Repository</span>
<span class="token punctuation">{</span>
<span class="token comment" spellcheck="true">/**
* 缓存实例。
*/</span>
<span class="token keyword">protected</span> <span class="token variable">$cache</span><span class="token punctuation">;</span>
<span class="token comment" spellcheck="true">/**
* 创建一个仓库实例。
*
* @param \SomePackage\Cache\Memcached $cache
* @return void
*/</span>
<span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">__construct<span class="token punctuation">(</span></span>\<span class="token package">SomePackage<span class="token punctuation">\</span>Cache<span class="token punctuation">\</span>Memcached</span> <span class="token variable">$cache</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token this">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">cache</span> <span class="token operator">=</span> <span class="token variable">$cache</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment" spellcheck="true">/**
* 按照Id检索订单。
*
* @param int $id
* @return Order
*/</span>
<span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">find<span class="token punctuation">(</span></span><span class="token variable">$id</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token this">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">cache</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">has<span class="token punctuation">(</span></span><span class="token variable">$id</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment" spellcheck="true"> //
</span> <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre><p>在这个类中,程序跟给定缓存实现之间是高耦合的。因为我们依赖于一个扩展包的特定缓存类。一旦这个扩展包的 API 被更改了,那我们的代码也必须得跟着改变。</p><p>同样的,如果想要将底层的缓存技术(Memcached )替换成另一种技术来实现( Redis ),那又得再一次修改这个 <code class=" language-php">repository</code> 类。而 <code class=" language-php">repository</code> 类不应该知道这么多信息,比如关于谁提供了这些数据,或是他们又是如何提供的等等。</p><p><strong>比起上面的做法,我们可以使用一个简单、和扩展包无关的接口来改进代码:</strong></p><pre class=" language-php"><code class=" language-php"><span class="token delimiter"><?php</span>
<span class="token keyword">namespace</span> <span class="token package">App<span class="token punctuation">\</span>Orders</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation">\</span>Contracts<span class="token punctuation">\</span>Cache<span class="token punctuation">\</span>Repository</span> <span class="token keyword">as</span> Cache<span class="token punctuation">;</span>
<span class="token keyword">class</span> <span class="token class-name">Repository</span>
<span class="token punctuation">{</span>
<span class="token comment" spellcheck="true">/**
* 缓存实例。
*/</span>
<span class="token keyword">protected</span> <span class="token variable">$cache</span><span class="token punctuation">;</span>
<span class="token comment" spellcheck="true">/**
* 创建一个仓库实例。
*
* @param Cache $cache
* @return void
*/</span>
<span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">__construct<span class="token punctuation">(</span></span>Cache <span class="token variable">$cache</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token this">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">cache</span> <span class="token operator">=</span> <span class="token variable">$cache</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre><p>现在,更改之后的代码没有与任何扩展包耦合,甚至是 Laravel 。而契约扩展包不包含实现和依赖,你可以轻松地对任何契约包进行实现,比如不需要修改任何关于缓存的代码就可以替换缓存实现。</p><p><a name="simplicity"></a></p><h3>简单性</h3><p>当所有的 Laravel 服务都使用简洁的接口定义,就能够很容易决定一个服务需要提供的功能。 <strong>可以将契约视为说明框架特色的简洁文档。</strong></p><p>除此之外,当依赖的接口足够简洁时,代码的可读性和可维护性会大大提高。比起搜索一个大型复杂的类里有哪些可用的方法,不如检索一个简单、干净的接口来参考更妥当。</p><p><a name="how-to-use-contracts"></a></p><h2><a href="#how-to-use-contracts">如何使用 contracts</a></h2><p>那么,如何获取一个契约的实现呢?这其实很简单。</p><p>Laravel 中的许多类型的类都是通过 <a href="/docs/5.4/container">服务容器</a> 解析出来的。包括控制器、事件监听器、中间件、任务队列,甚至路由的闭包。所以说,要获得一个契约的实现,你只需要解析在类的构造函数中相应的类型约束即可。</p><p>例如,看看这个事件监听器:</p><pre class=" language-php"><code class=" language-php"><span class="token delimiter"><?php</span>
<span class="token keyword">namespace</span> <span class="token package">App<span class="token punctuation">\</span>Listeners</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">App<span class="token punctuation">\</span>User</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">App<span class="token punctuation">\</span>Events<span class="token punctuation">\</span>OrderWasPlaced</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation">\</span>Contracts<span class="token punctuation">\</span>Redis<span class="token punctuation">\</span>Database</span><span class="token punctuation">;</span>
<span class="token keyword">class</span> <span class="token class-name">CacheOrderInformation</span>
<span class="token punctuation">{</span>
<span class="token comment" spellcheck="true">/**
* Redis 数据库实现。
*/</span>
<span class="token keyword">protected</span> <span class="token variable">$redis</span><span class="token punctuation">;</span>
<span class="token comment" spellcheck="true">/**
* 创建事件处理器实例。
*
* @param Database $redis
* @return void
*/</span>
<span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">__construct<span class="token punctuation">(</span></span>Database <span class="token variable">$redis</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token this">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">redis</span> <span class="token operator">=</span> <span class="token variable">$redis</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment" spellcheck="true">/**
* 处理事件。
*
* @param OrderWasPlaced $event
* @return void
*/</span>
<span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">handle<span class="token punctuation">(</span></span>OrderWasPlaced <span class="token variable">$event</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token comment" spellcheck="true"> //
</span> <span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre><p>当事件监听器被解析时,服务容器会从构造函数里读取到类型约束,并注入对应的值。 想了解关于容器的注册绑定,可以查看 <a href="/docs/5.4/container">服务容器</a>。</p><p><a name="contract-reference"></a></p><h2><a href="#contract-reference">Contract 参考</a></h2><p>下面的表格提供了 Laravel 契约及其对应的门面的参考:</p><table><thead><tr><th>Contract</th><th>References Facade</th></tr></thead><tbody><tr><td><a href="https://github.com/illuminate/contracts/blob/5.4/Auth/Factory.php">Illuminate\Contracts\Auth\Factory</a></td><td>Auth</td></tr><tr><td><a href="https://github.com/illuminate/contracts/blob/5.4/Auth/PasswordBroker.php">Illuminate\Contracts\Auth\PasswordBroker</a></td><td>Password</td></tr><tr><td><a href="https://github.com/illuminate/contracts/blob/5.4/Bus/Dispatcher.php">Illuminate\Contracts\Bus\Dispatcher</a></td><td>Bus</td></tr><tr><td><a href="https://github.com/illuminate/contracts/blob/5.4/Broadcasting/Broadcaster.php">Illuminate\Contracts\Broadcasting\Broadcaster</a></td><td> </td></tr><tr><td><a href="https://github.com/illuminate/contracts/blob/5.4/Cache/Repository.php">Illuminate\Contracts\Cache\Repository</a></td><td>Cache</td></tr><tr><td><a href="https://github.com/illuminate/contracts/blob/5.4/Cache/Factory.php">Illuminate\Contracts\Cache\Factory</a></td><td>Cache::driver()</td></tr><tr><td><a href="https://github.com/illuminate/contracts/blob/5.4/Config/Repository.php">Illuminate\Contracts\Config\Repository</a></td><td>Config</td></tr><tr><td><a href="https://github.com/illuminate/contracts/blob/5.4/Container/Container.php">Illuminate\Contracts\Container\Container</a></td><td>App</td></tr><tr><td><a href="https://github.com/illuminate/contracts/blob/5.4/Cookie/Factory.php">Illuminate\Contracts\Cookie\Factory</a></td><td>Cookie</td></tr><tr><td><a href="https://github.com/illuminate/contracts/blob/5.4/Cookie/QueueingFactory.php">Illuminate\Contracts\Cookie\QueueingFactory</a></td><td>Cookie::queue()</td></tr><tr><td><a href="https://github.com/illuminate/contracts/blob/5.4/Encryption/Encrypter.php">Illuminate\Contracts\Encryption\Encrypter</a></td><td>Crypt</td></tr><tr><td><a href="https://github.com/illuminate/contracts/blob/5.4/Events/Dispatcher.php">Illuminate\Contracts\Events\Dispatcher</a></td><td>Event</td></tr><tr><td><a href="https://github.com/illuminate/contracts/blob/5.4/Filesystem/Cloud.php">Illuminate\Contracts\Filesystem\Cloud</a></td><td> </td></tr><tr><td><a href="https://github.com/illuminate/contracts/blob/5.4/Filesystem/Factory.php">Illuminate\Contracts\Filesystem\Factory</a></td><td>File</td></tr><tr><td><a href="https://github.com/illuminate/contracts/blob/5.4/Filesystem/Filesystem.php">Illuminate\Contracts\Filesystem\Filesystem</a></td><td>File</td></tr><tr><td><a href="https://github.com/illuminate/contracts/blob/5.4/Foundation/Application.php">Illuminate\Contracts\Foundation\Application</a></td><td>App</td></tr><tr><td><a href="https://github.com/illuminate/contracts/blob/5.4/Hashing/Hasher.php">Illuminate\Contracts\Hashing\Hasher</a></td><td>Hash</td></tr><tr><td><a href="https://github.com/illuminate/contracts/blob/5.4/Logging/Log.php">Illuminate\Contracts\Logging\Log</a></td><td>Log</td></tr><tr><td><a href="https://github.com/illuminate/contracts/blob/5.4/Mail/MailQueue.php">Illuminate\Contracts\Mail\MailQueue</a></td><td>Mail::queue()</td></tr><tr><td><a href="https://github.com/illuminate/contracts/blob/5.4/Mail/Mailer.php">Illuminate\Contracts\Mail\Mailer</a></td><td>Mail</td></tr><tr><td><a href="https://github.com/illuminate/contracts/blob/5.4/Queue/Factory.php">Illuminate\Contracts\Queue\Factory</a></td><td>Queue::driver()</td></tr><tr><td><a href="https://github.com/illuminate/contracts/blob/5.4/Queue/Queue.php">Illuminate\Contracts\Queue\Queue</a></td><td>Queue</td></tr><tr><td><a href="https://github.com/illuminate/contracts/blob/5.4/Redis/Database.php">Illuminate\Contracts\Redis\Database</a></td><td>Redis</td></tr><tr><td><a href="https://github.com/illuminate/contracts/blob/5.4/Routing/Registrar.php">Illuminate\Contracts\Routing\Registrar</a></td><td>Route</td></tr><tr><td><a href="https://github.com/illuminate/contracts/blob/5.4/Routing/ResponseFactory.php">Illuminate\Contracts\Routing\ResponseFactory</a></td><td>Response</td></tr><tr><td><a href="https://github.com/illuminate/contracts/blob/5.4/Routing/UrlGenerator.php">Illuminate\Contracts\Routing\UrlGenerator</a></td><td>URL</td></tr><tr><td><a href="https://github.com/illuminate/contracts/blob/5.4/Support/Arrayable.php">Illuminate\Contracts\Support\Arrayable</a></td><td> </td></tr><tr><td><a href="https://github.com/illuminate/contracts/blob/5.4/Support/Jsonable.php">Illuminate\Contracts\Support\Jsonable</a></td><td> </td></tr><tr><td><a href="https://github.com/illuminate/contracts/blob/5.4/Support/Renderable.php">Illuminate\Contracts\Support\Renderable</a></td><td> </td></tr><tr><td><a href="https://github.com/illuminate/contracts/blob/5.4/Validation/Factory.php">Illuminate\Contracts\Validation\Factory</a></td><td>Validator::make()</td></tr><tr><td><a href="https://github.com/illuminate/contracts/blob/5.4/Validation/Validator.php">Illuminate\Contracts\Validation\Validator</a></td><td> </td></tr><tr><td><a href="https://github.com/illuminate/contracts/blob/5.4/View/Factory.php">Illuminate\Contracts\View\Factory</a></td><td>View::make()</td></tr><tr><td><a href="https://github.com/illuminate/contracts/blob/5.4/View/View.php">Illuminate\Contracts\View\View</a></td><td> </td></tr></tbody></table><h2>译者署名</h2><table><thead><tr><th>用户名</th><th>头像</th><th>职能</th><th>签名</th></tr></thead><tbody><tr><td><a href="https://github.com/e421083458">@e421083458</a></td><td><img class="avatar-66 rm-style" src="https://dn-phphub.qbox.me/uploads/avatars/10802_1486368142.jpeg?imageView2/1/w/100/h/100"></td><td>翻译</td><td>Github求star,<a href="https://github.com/e421083458/">@e421083458</a> at Github</td></tr></tbody></table></article>
- 入门指南
- 安装
- 配置信息
- 文件夹结构
- 请求周期
- 开发环境部署
- Valet
- Homestead
- 核心概念
- 服务提供者
- Facades
- Contracts
- 服务容器
- HTTP 层
- 路由
- 中间件
- CSRF 保护
- 控制器
- 请求
- 响应
- 视图
- Session
- 表单验证
- 前端
- Blade 模板
- 本地化
- 前端指南
- 编辑资源 Mix
- 安全
- API 认证
- 用户认证
- 用户授权
- 加密解密
- 哈希
- 重置密码
- 数据库
- 快速入门
- 查询构造器
- 分页
- 数据库迁移
- Redis
- 数据填充
- Eloquent ORM
- Eloquent ORM快速入门
- 模型关联
- Eloquent 集合
- 修改器
- 序列化
- 综合话题
- Artisan 命令行
- 广播系统
- 缓存系统
- 集合
- 错误与日志
- 事件系统
- 文件存储
- 辅助函数
- 邮件发送
- 消息通知
- 扩展包开发
- 队列
- 任务调度