Yii2 關鍵概念

pardon110發表於2019-07-21

與laravel相較,Yii2 將配置(依賴關係定義)外化,用行為(更類似於python中的織入)類,彌補 Trait的一些不足,好處是可以動態擴充套件動作。至於事件處理則大同小異,有趣的是在命名上,yii2借用了jquery事件系統的那一套,on,off,trigger。當然也相同之處,比如應用都是建立在容器之上。相比其它的面向領域,面向介面程式設計,Yii2使用模組,來分層,分中心小應用,細化大架構。而getter/setter,及過濾器,java痕跡太明顯。

元件Component

  • Yii 應用的主要基石。是 yii\base\Component 類或其子類的例項
    • 主要由 屬性(Property),事件(Event),行為(Behavior)三個功能組成
    • 比常規的物件(Object)稍微重量級,要使用額外的記憶體和 CPU 時間來處理 事件 和 行為
    • 不需要使用事件和行為時,繼承 yii\base\Object ,支援屬性(Property)功能
  • 重寫 Component 或 Object
    • 永遠在重寫的構造方法結尾處呼叫一下父類的構造方法
    • 傳入 $config 作為構造器方法最後一個引數,由父構造方呼叫,在應用配置之前初始化
    • 若重寫了 BaseObject::init() 方法,確保在 init 方法的開頭處呼叫了父類的 init 方法
  • 元件例項化2種方式
    • 例項化元件類,new元件類, 元件普通引數 + 元件配置屬性引數
    • 透過 \Yii::createObject靜態方法,建立元件例項
      • 第一個陣列引數 class關聯元件類名,後續關聯元素依次為元件例項屬性及值
      • 第二陣列引數 元件的普通引數

Yii::createObject() 基於依賴注入容器實現

  • yii\base\BaseObject 類執行時的生命週期
    • 構造方法內的預初始化過程
    • 透過 $config 配置物件
    • 在 init() 方法內進行初始化後的收尾工作
    • 物件方法呼叫, 上述步驟皆在物件的構造方法內完成,即獲得例項就已經初始化,可供使用

屬性Property

  • Yii 引入 yii\base\Object 的基類, 支援基於類內的 getter 和 setter(讀取器和設定器)方法來定義屬性
  • getter 和 setter 定義屬性規則和限制
    • 這類屬性的名字是不區分大小寫,源於PHP 方法名是不區分大小寫
    • 若此類屬性名和類成員變數相同,以後者為準
    • 該類屬性不支援可見性(訪問限制)
    • 這類屬性的 getter 和 setter 方法只能定義為非靜態的
    • 對不確定有無魔術方法(getter 或 setter的屬性正常呼叫 property_exists() 將不會生效
      • 若真有此需求,應用 canGetProperty() 或 canSetProperty()

事件Events

  • 事件可以將自定義程式碼“注入”到現有程式碼中的特定執行點

    • 附加自定義程式碼到某個事件,當這個事件被觸發時,這些程式碼就會自動執行
  • 事件處理器 Event Handlers

    • 事件處理器是一個PHP 回撥函式,也可以是一個可呼叫物件
    • 字串形式指定的 PHP 全域性函式 ,如 'trim'
    • 物件名和方法名陣列形式指定的物件方法,[$object, $method]
    • 類名和方法名陣列形式指定的靜態類方法,如 [$class, $method]
    • 匿名函式,如 function ($event) { ... }
  • 事件物件$event

    • event name:事件名
    • event sender:呼叫 trigger() 方法的物件
    • custom data 附加事件處理器時傳入的資料,預設為空
  • 附加事件處理器

    • 呼叫元件類的on方法,諸如 \yii\base\Component::on()
    • public void on ( $name, $handler, $data = null, $append = true )
  • 事件處理器順序(Event Handler Order)

    • 當事件被觸發,已附加的處理器將按附加次序依次呼叫
    • 若需要停止同一事件的後續處理器的呼叫,可設定 event 引數的 yii\base\Event::event引數的‘yii\base\Event::handled屬性為真
    • 第四個引數 $append 為假時,可在處理器佇列最前面插入新處理器
  • 觸發事件(Triggering Events)

    • 事件透過呼叫 yii\base\Component::trigger() 方法觸發
      • public void trigger ( $name, yii\base\Event $event = null )
    • 推薦使用類常量來表示事件名, 事件物件必須是 yii\base\Event類或其子類的例項
  • 移除事件處理器(Detaching Event Handlers)

    • public boolean off ( $name, $handler = null )
  • 類級別的事件處理器

    • 應用場景 想要一個類的所有例項都響應一個被觸發的事件
    • 呼叫靜態方法 yii\base\Event::on() 在類級別附加處理器
      • 在事件處理器內,透過 $event->sender 獲取觸發事件的物件
      • 當物件觸發事件時,它首先呼叫例項級別的處理器,然後才會呼叫類級別處理器
    • 靜態方法yii\base\Event::trigger()來觸發一個類級別事件,移除用off
    • 移除簽名 public static boolean off ( $class, $name, $handler = null )
  • 介面事件

    • 呼叫 Event::on() 並將介面類名作為第一個引數
    • 可在實現介面事件類中觸發這個事件,但不能讓所有實現這個介面的類都觸發事件
  • 全域性事件

    • 需要一個全域性可訪問的單例,如應用例項
    • 事件觸發者不呼叫其自身的 trigger() 方法,而是呼叫單例的 trigger() 方法來觸發全域性事件
    • 優點 是當附加處理器到一個物件要觸發的事件時, 不需要產生該物件
  • 萬用字元事件Wildcard Events

    • foo.event.* ,萬用字元模式支援例項 或類級別的事件

行為

  • 行為是 yii\base\Behavior 或其子類的例項,也稱為 mixins,類似於原生的Trait

    • 作用 無須改變類繼承關係即可增強一個已有的 元件 類功能
    • 當行為附加到元件後,它將“注入”它的方法和屬性到元件
    • 行為透過元件能響應被觸發的事件,從而自定義或調整元件正常執行的程式碼
  • 處理事件

    • 讓行為響應對應元件的事件觸發, 應覆寫 yii\base\Behavior::events() 方法
      • 行為的 events() 方法返回事件列表和相應的處理器,指定事件處理器格式如下
        • 指向行為類的方法名的字串
        • 物件或類名和方法名的陣列,如 [$object, 'methodName'];
        • 匿名方法
  • 附加行為

    • 靜態附加行為
      • 覆寫行為要附加的元件類的 behaviors() 方法即可
      • behaviors() 方法返回行為配置列表,每個行為配置可以是行為類名也可以是配置陣列
      • 過指定行為配置陣列相應的鍵可以給行為關聯一個名稱,這種行為稱為命名行為,反之匿名行為或命名行為
    • 動態附加行為
      • 在對應元件裡呼叫 yii\base\Component::attachBehavior() 方法
      • 或 yii\base\Component::attachBehaviors() 方法一次附加多個行為
        • public void attachBehaviors (array $behaviors )
      • 透過配置附加行為
        • ['as myBehavior2' => MyBehavior::className()]
  • 使用行為

    • 必須先將為附加到 component 類或其子類元件,然後可透過訪問元件訪問行為的公共成員變數
    • 若兩個行為都定義了一樣的屬性或方法,並且它們都附加到同一個元件,先附加者有優先權
    • 附加行為到元件時的命名行為,可以使用這個名稱來訪問行為物件$component->getBehavior('myBehavior');
    • 獲取附加到這個元件的所有行為 getBehaviors()
  • 移除行為

    • 可以呼叫 yii\base\Component::detachBehavior() 方法用行為相關聯的名字實現
  • Yii2內建行為類

    • yii\behaviors\TimestampBehavior 在 Active Record 儲存時自動更新它的時間戳屬性
    • yii\behaviors\BlameableBehavior 使用當前使用者 ID 自動填充指定的屬性
    • yii\behaviors\SluggableBehavior 自動填充指定的屬性,其值可以在 URL 中用作 slug
    • yii\behaviors\AttributeBehavior 在發生特定事件時自動為 ActiveRecord 物件的一個或多個屬性 指定一個指定的值
    • yii2tech\ar\softdelete\SoftDeleteBehavior 提供軟刪除和軟恢復 ActiveRecord 的 方法
    • yii2tech\ar\position\PositionBehavior 允許透過提供重新排序方法來 管理整數字段中的記錄順序
  • 行為 VS Traits

    • 都將自己的屬性和方法“注入”到主類中,二者類似互補類而非替代類
    • 行為類優點
      • 行為類像普通類支援繼承
      • 行為無須修改元件類就可動態附加到元件或移除
      • 行為是可配置的,而 traits 則不可行
      • 行為可以透過響應事件來定製元件的程式碼執行
    • traits 的原因
      • Traits 比行為更有效,因為行為是既需要時間又需要記憶體的物件
    • 名稱衝突解決方案
      • 當附屬於同一元件的不同行為之間可能存在名稱衝突時, 透過優先考慮附加到該元件的行為
      • 不同 traits 引起的名稱衝突需要透過 重新命名受影響的屬性或方法進行手動解決

配置 Configurations

  • 概述

    • 在建立新物件和初始化已存在物件時使用配置
    • 配置通常包含被建立物件的類名和一組將要賦值給物件 屬性的初始值
    • 亦可包含一組將被附加到物件事件上的控制程式碼,和一組將被附加到物件上的行為
  • 使用

    • Yii::createObject() 方法接受一個配置陣列並根據陣列中指定的類名建立物件
    • 對於已存在的物件,可以使用 Yii::configure() 方法根據配置去初始化其屬性
      • Yii::configure($object, $config)
      • 注若配置一個已存在的物件,那麼配置陣列中不應該包含指定類名的 class 元素
  • 配置的格式

    • class 元素指定了將要建立的物件的完全限定類名
    • propertyName 元素指定了物件屬性的初始值,鍵名是屬性名,值是該屬性對應的初始值
      • 只有公共成員變數以及透過 getter/setter 定義的 屬性可以被配置
    • on eventName 元素指定了附加到物件事件上的控制程式碼,陣列的鍵名由 on 字首加事件名組成
    • as behaviorName 元素指定了附加到物件的行為,值表示建立行為的配置資訊
  • 應用的配置

    • application 類擁有很多可配置的屬性和事件
    • components 屬性可以接收配置陣列並透過應用註冊為元件
    • yii2.0.11+ 系統配置支援使用 container 屬性來配置依賴注入容器
  • 小部件的配置

    • yii\base\Widget::widget() 和 yii\base\Widget::begin()方法都可以用來建立小部件
    • 透過用配置來自定義其屬性,注意 給出類名的情況下,配置陣列不需要再包含class鍵
  • 預設配置

    • Yii::createObject() 方法基於依賴注入容器實現
    • 使用 Yii::creatObject() 建立物件時,可以附加一系列預設配置到指定類的任何例項
    • 預設配置可以在入口指令碼 中呼叫 Yii::$container->set() 來定義

別名

  • 設定與解析
    • 使用 Yii::setAlias() 來給檔案路徑或 URL 定義別名
    • 呼叫 Yii::getAlias() 命令來解析根別名到對應的檔案路徑或 URL
  • 應用提供了一個名為 aliases 的可寫屬性, 可在應用配置中設定它
  • 使用別名 Yii內路徑屬性接受別名
  • Yii2 預定義別名
  • 擴充套件的別名
    • 一個透過 Composer 安裝的 擴充套件 都自動新增了一個別名,定義於引導啟動階段

類的自動載入

  • Yii自動載入器
    • 每個類都必須置於名稱空間之下
    • 每個類都必須儲存為單獨檔案
    • 要將自定義名稱空間新增到自動載入器,需要使用 Yii::setAlias() 為名稱空間的根目錄定義別名
  • 類對映表
    • 類對映表功能,建立一個從類的名字到類檔案路徑的對映
    • 當自動載入器載入一個檔案時,他首先檢查對映表裡有沒有該類
    • 可以用 Yii::$classMap 方法向對映表中新增類
  • 其他自動載入器
    • 在其他自動載入器安裝成功之後, 再包含 Yii.php (yii的自動載入器)
    • 目的使 Yii 成為第一個響應任何類自動載入請求的自動載入器
  • Yii 自動載入器支援自動載入擴充套件的類,需要在 composer.json 檔案里正確地定義 autoload 部分

服務定位器(Service Locator)

  • 定義
    • 提供各種應用所需的服務(或元件)的物件
    • 在服務定位器中, 每個元件都只有一個單獨的例項,並透過ID 唯一地標識
    • 在 Yii 中,服務定位器是 yii\di\ServiceLocator 或其子類的一個例項
  • 應用場景
    • 最常用的服務定位器是application(應用)物件,可以透過 \Yii::$app 訪問
    • 每個模組物件本身也是一個服務定位器, 模板可視為一個子應用
  • 使用服務定位器
    • 註冊相關元件
      • 透過 yii\di\ServiceLocator::set() 方法進行相關元件註冊。
      • public void set ( $id, $definition )
      • $definition 可以是類名,配置陣列,php可呼叫物件,或者本身就是一個物件例項
    • 允許透過元件 ID 像訪問一個屬性值那樣訪問一個元件
      • 服務定位器會返回同一個元件的單例
      • yii\di\ServiceLocator::has() 檢查某元件 ID 是否被註冊
      • yii\di\ServiceLocator::get()
  • 遍歷樹(Tree traversal)
    • 模組允許任意巢狀; Yii 應用程式本質上是一個模組樹
  • 模組中元件的配置決不會與來自父模組中元件的配置合併

依賴注入容器

  • 依賴注入容器是一個物件,知道怎樣初始化並配置物件及其依賴的所有物件
  • Yii 透過 yii\di\Container 類提供 DI 容器特性
    • 構造方法注入
      • 容器會嘗試獲取它所依賴的類或介面的例項,然後透過構造器將其注入新的物件
    • 方法注入
      • 可以提供僅由類的單個方法需要的依賴關係
    • Setter 和屬性注入
      • Setter 和屬性注入是透過配置提供,該配置會提供給容器用於透過相應的 Setter 或屬性注入依賴
    • PHP 回撥注入(PHP Callable Injection)
      • 容器將使用已註冊的 PHP 回撥來構建類的新例項

yii\di\Container::get() 方法將其第三個引數作為配置陣列應用於正在建立的物件 如果該類實現 yii\base\Configurable 介面(例如 yii\base\BaseObject),則配置陣列將作為最後一個引數傳遞給類建構函式

  • 註冊依賴關係
    • 用 yii\di\Container::set() 註冊依賴關係
    • 註冊會用到一個依賴關係名稱和一個依賴關係的定義, 鍵值對可遞迴,方便容器管理例項,類似laravel的別名系統
    • 依賴關係名稱可為類名,介面名或一個別名,依賴關係的定義可以是類名,配置陣列,一個PHP回撥
    • 透過 set() 註冊的依賴關係,在每次使用時都會產生一個新例項
    • 使用 yii\di\Container::setSingleton() 註冊一個單例的依賴關係
  • 解析依賴關係
    • 依賴關係解析是遞迴式進行
    • 註冊依賴關係後,容器會自動解析依賴關係, 將依賴例項化並注入新建立的物件
    • 依賴關係名,可以是透過 set() 或 setSingleton() 註冊的,也可以是一個類構造器引數列表和一個 configuration 用於配置新建立的物件
  • 使用依賴注入
    • 在應用程式的入口指令碼中引入 Yii.php 檔案時, Yii 就建立了一個 DI 容器
    • 該 DI 容器可以透過 Yii::$container 訪問
    • 當呼叫 Yii::createObject() 時,此方法實際上會呼叫這個容器的 get() 方法建立新物件
    • 在部件呼叫中給出的屬性將始終覆蓋DI容器中的定義,若報錯無法被例項化,需要告知容器如何處理依賴關係
  • 高階實用性
    • 一次配置多個定義,將配置陣列傳遞給 setDefinitions() 或 setSingletons() 方法
    • 配置陣列格式
      • key:類名稱,介面名稱或別名
      • value:與 class 關聯的定義,class關聯的定義,‘identifies 引數值將傳遞給set()方法
      • 可以選擇將依賴項的建構函式引數作為第三個引數
      • Instance::of('tempFileStorage') 符號,Container 將隱含地提供一個用 tempFileStorage 名稱註冊的依賴項
        • 應用場景 內部配置依賴項
    • 透過 set() 註冊的依賴項將在每次需要時例項化
  • 依賴關係註冊使用
    • 應用開發在入口註冊依賴關係
    • 擴充套件開發,則在擴充套件引導類中註冊依賴關係
  • 小結
    • Yii 在依賴住入(DI)容器之上實現了它的服務定位器
    • 當一個服務定位器嘗試建立一個新的物件例項時,它會把呼叫轉發到 DI 容器
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章