<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 命令行
- 廣播系統
- 緩存系統
- 集合
- 錯誤與日志
- 事件系統
- 文件存儲
- 輔助函數
- 郵件發送
- 消息通知
- 擴展包開發
- 隊列
- 任務調度