--- tags: laravel --- # 合約 Contracts  ## 簡介 Laravel 的 Contracts 契約是一組介面(interfaces),它們由框架提供並定義了核心服務。例如,Illuste\Contracts\Queue\Queue 契約定義了隊獵作業所需具備的方法,而 Illuste\Contracts\Mail\Mailer 契約則定義了發送郵件所需具備的方法 > 補充說明: > > 可以把契約想作成你要做某件事情所需具備的條件或執行的動作,所以任何需要做這件事情的實作都必須告訴程式你想怎麼完成這些動作 針對每個契約 Laravel 框架都有為其提供的相應實作。例如 Laravel 提供了一個包含各種驅動的隊列實作,還有一個由 SwiftMailer 提供支持的郵件程式實作以簡化你的郵件發送作業等等 所有的 Laravel 契約都存在於 它們各自的 GitHub 倉庫。這為所有可用的契約提供了一個快速的參考點,以及在建構與 Laravel 服務交互的套件時可以使用的單個解耦包(後一句我也還沒有全明白,知道的歡迎告訴我) ### Contracts 與 Facades Laravel 的 Facades 和幫助函式提供了一種利用 Laravel 服務的簡易做法,無需型別提示並可以從服務容器中解析契約。 在大多數情況下,每個 Facade 都有一個對應的契約(幫助函式也是同樣道理) 和 Facades 不同(它不要求類別的建構子中導入它們),契約允許你為類別定義顯式依賴關係。 一些資深開發者更喜歡以這種顯式方式去定義其依賴關係,所以更喜歡使用契約,而多數開發者則享受 Facade 帶來的便利。總地來說,大部分應用使用 Facades 來進行開發是不會有問題的 >技巧: > >無論您更喜歡 Facades 還是 Contracts,大多數應用都運行得不錯。但是,如果要開發套件的話,則強烈考慮使用 Contracts,因為它們以顯式方式去定義的關係更容易測試 ## 何時要使用合約 Contracts 到底要使用 Contracts 或 Facades ,這個決定將取決於個人喜好又或者是開發團隊的喜好 Contracts 和 Facades 並非有你沒我的關係,你應用的某部分可能使用的是 Facades 而其他部分可能會依賴的是 Contracts。只要聚焦在類別的職責應該越單純越好,你會發現使用 Contracts 還是 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),就不得不再次修改我們的程式。為何我們的程式碼一定要有這些關於誰在為它們提供數據以及它們如何提供數據的內容? 當然可以不要,我們可以通過依賴一個簡單的、與套件無關的介面來改進我們的程式碼,來看修改版本: ``` <?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 的所有服務都被整齊地定義在簡單的介面中時,很容易確定服務供應者提供的功能。可把合約當作是框架功能的速讀文檔 此外,當你依賴簡單的介面時,程式碼更容易理解和維護。你可以引用一個簡單、乾淨的介面,而不需要去追龐大又複雜的類別中有哪些方法可用 >多數應用能夠使用 Facades 而不須擔心開發中會出問題。但假如你開發的套件有整合多個PHP 框架,你應該使用 illuminate/contracts 套件來定義你和 Laravel 服務的交互,而不需要在你套件的 composer.json 檔案內去引入 Laravel 的具體實作(後面這句恐怕要到大神級別才會用到) ## 該如何使用合約 Contracts 那麼,該如何實作合約呢?它其實很簡單 Laravel 中的許多類別都是通過服務容器去解析的,包括控制器、事件偵聽器、中介層、隊列任務,甚至路由 Closures。因此,要實作合約,您只需在被解析的類別的建構子中去作「型別提示」哪個介面 例如,看看這個事件偵聽器: ``` <?php namespace App\Listeners; use App\Events\OrderWasPlaced; use App\Models\User; use Illuminate\Contracts\Redis\Factory; class CacheOrderInformation { /** * Redis 工廠實作. * * @var \Illuminate\Contracts\Redis\Factory */ protected $redis; /** * 建立一個新的事件處理實例. * * @param \Illuminate\Contracts\Redis\Factory $redis * @return void */ public function __construct(Factory $redis) { $this->redis = $redis; } /** * 處理事件. * * @param \App\Events\OrderWasPlaced $event * @return void */ public function handle(OrderWasPlaced $event) { // } } ``` 當事件偵聽器被解析時,服務容器將讀取類別的建構子上的型別提示,並注入適當的依賴。 要了解更多有關在服務容器中註冊內容的信息,請查看[服務容器](/tBv0iPqSR1ms0NRsr8-HuQ) ## 合約參考 下表提供了所有 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\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()|
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up