Yii2.0 對比 Yii1.1 的重大改進
這部分内容是專門為已經有Yii1.1基礎的讀者朋友寫的。将Yii2.0與Yii1.1的不同點着重寫出來,對比學起來會快得多。 而對于從未接觸過Yii的讀者朋友,這部分内容掃一掃就可以了,作為對過往曆史的一個了解就夠了。 如果有的内容你一時沒看明白,也不要緊,本書的正文部分會講清楚的。 另外,沒有Yii1.1的經驗,并不妨礙對Yii2.0的學習。
Yii官方有專門的文檔歸納總結1.1版本和2.0版本的不同。以下内容,主要來自于官方的文檔,我做了下精簡, 選擇比較重要的變化,并加入了一些個人的經驗。
PHP新特性
從對PHP新特性的使用上,兩者就存在很大不同。Yii2.0大量使用了PHP的新特性,這在Yii1.1中是沒有的。是以,Yii2.0對于PHP的版本要求更高,要求PHP5.4及以上。Yii2.0中使用到的PHP新特性,主要有:
- 命名空間(Namespace)
- 匿名函數
- 數組短文法形式: [1,2,3] 取代 array(1,2,3) 。這在多元數組、嵌套數組中,代碼更清晰、簡短。
- 在視圖檔案中使用PHP的 <?= 标簽,取代 echo 語句。
- 标準PHP庫(SPL) 類和接口,具體可以檢視 SPL Class and Interface
- 延遲靜态綁定, 具體可以檢視 Late Static Bindings
- PHP标準日期時間
- 特性(Traits)
- 使用PHP intl 擴充實作國際化支援, 具體可以檢視 PECL init 。
了解Yii2.0使用了PHP的新特性,可以避免開發時由于環境不當,特别是開發生産環境切換時,産生莫名其妙的錯誤。 同時,也是讓讀者朋友借機學習PHP新知識的意思。
命名空間(Namespace)
Yii2.0與Yii1.1之間最顯著的不同是對于PHP命名空間的使用。Yii1.1中沒有命名空間一說, 為避免Yii核心類與使用者自定義類的命名沖突,所有的Yii核心類的命名,均冠以 C 字首,以示差別。
而Yii2.0中所有核心類都使用了命名空間,是以, C 字首也就人老珠黃,退出曆史舞台了。
命名空間與實際路徑相關聯,比如 yii\base\Object 對應Yii目錄下的 base/Object.php 檔案。
基礎類
Yii1.1中使用了一個基礎類 CComponent ,提供了屬性支援等基本功能,是以幾乎所有的Yii核心類都派生自該類。 到了Yii2.0,将一家獨大的 CComponent進行了拆分。拆分成了 yii\base\Object 和 yii\base\Component 。 拆分的考慮主要是 CComponent 尾大不掉,有影響性能之嫌。 于是,Yii2.0中,把 yii\base\Object 定位于隻需要屬性支援,無需事件、行為。 而 yii\base\Component 則在前者的基礎上,加入對于事件和行為的支援。 這樣,開發者可以根據需要,選擇繼承自哪基礎類。
這一功能上的明确劃分,帶來了效率上的提升。在僅表示基礎資料結構,而非反映客觀事物的情況下, yii\base\Object 比較适用。
值得一提的是, yii\base\Object 與 yii\base\Component 兩者并不是同一層級的,前者是後者他爹。
事件(Event)
在Yii1.1中,通過一個 on 字首的方法來建立事件,比如 CActiveRecord 中的 onBeforeSave() 。 在Yii2.0中,可以任意定義事件的名稱,并自己觸發它:
| |
别名(Alias)
Yii2.0中改變了Yii1.1中别名的使用形式,并擴大了别名的範疇。 Yii1.1中,别名以 . 的形式使用:
而在Yii2.0中,别名以 @ 字首的方式使用:
另外,Yii2.0中,不僅有路徑别名,還有URL别名:
| |
别名與命名空間是緊密相關的,Yii建議為所有根命名空間都定義一個别名,比如上面提到的 yii\base\Object , 事實上是定義了 @yii 的别名,表示Yii在系統中的安裝路徑。 這樣一來,Yii就能根據命名空間找到實際的類檔案所在路徑,并自動加載。這一點上,Yii2.0與Yii1.1并沒有本質差別。
而如果沒有為根命名空間定義别名,則需要進行額外的配置。将命名空間與實際路徑的映射關系,告知Yii。
關于别名的更詳細内容請看 别名(Alias) 。
視圖(View)
Yii1.1中,MVC(model-view-controller)中的視圖一直是依賴于Controller的,并非是真正意義上獨立的View。 Yii2.0引入了 yii\web\View 類,使得View完全獨立。這也是一個相當重要變化。
首先,Yii2.0中,View作為Application的一個元件,可以在全局中代碼中進行通路。 是以,視圖渲染代碼不必再局限于Controller中或Widget中。
其次,Yii1.1中視圖檔案中的 $this 指的是目前控制器,而在 Yii2.0中,指的是視圖本身。 要在視圖中通路控制器,可以使用 $this->context 。這個 $this->context 是指誰調用了 yii\base\View::renderFile() 來渲染這個視圖。 一般是某個控制器,也可以是其他實作了 yii\base\ViewContextInterface接口的對象。
同時,Yii1.1中的 CClientScript 也被淘汰了,相關的前端資源及其依賴關系的管理,交由Assert Bundle yii\web\AssertBundle 來專職處理。 一個Assert Bundle代表一系列的前端資源,這些前端資源以目錄形式進行管理,這樣顯得更有序。 更為重要的是,Yii1.1中需要你格外注意資源在HTML中的順序,比如CSS檔案的順序(後面的會覆寫前面的), JavaScript檔案的順序(前後順序出錯會導緻有的庫未加載)等。 而在Yii2.0中,使用一個Assert Bundle可以定義依賴于另外的一個或多個Assert Bundle的關系, 這樣在向HTML頁面注冊這些CSS或者JavaScript時,Yii2.0會自動把所依賴的檔案先注冊上。
在視圖模版引擎方面,Yii2.0仍然使用PHP作為主要的模版語言。 同時官方提供了兩個擴充以支援目前兩大主流PHP模版引擎:Smarty和Twig,而對于Pardo引擎官方不再提供支援了。 當然,開發者可以通過設定 yii\web\View::$renderers 來使用其他模版。
另外,Yii1.1中,調用 $this->render('viewFile', ...) 是不需要使用 echo 指令的。 而Yii2.0中,記得 render() 隻是傳回視圖渲染結果,并不對直接顯示出來,需要自己調用 echo
如果有一天你發現怎麼Yii輸出了個空白頁給你,就要注意是不是忘記使用 echo 了。 還别說,這個錯誤很常見,特别是在對Ajax請求作出響應時,會更難發現這一錯誤。請你們程式設計時留意。
在視圖的主題(Theme)化方面,Yii2.0的運作機理采用了完全不同的方式。 在Yii2.0中,使用路徑映射的方式,将一個源視圖檔案路徑,映射成一個主題化後的視圖檔案路徑。 是以, ['/web/views' => '/web/themes/basic'] 定義了一個主題映射關系, 源視圖檔案 /web/views/site/index.php 主題化後将是 /web/themes/basic/site/index.php 。 是以, Yii1.1中的 CThemeManager 也被淘汰了。
模型(Model)
MVC中的M指的就是模型,Yii1.1中使用 CModel 來表示,而Yii2.0使用 yii\base\Model 來表示。
Yii1.1中, CFormModel 用來表示使用者的表單輸入,以差別于資料庫中的表。 這在Yii2.0中也被淘汰,Yii2.0傾向于使用繼承自 yii\base\Model 來表示送出的表單資料。
另外,Yii2.0為Model引入了 yii\base\Model::load() 和 yii\base\Model::loadMutiple() 兩個新的方法, 用于簡化将使用者輸入的表單資料指派給Model:
| |
另外一個重要變化就是Yii2.0中改變了Model應用于不同場景的邏輯。通過引入 yii\base\Model::scenarios() 來集中管理場景,使得一個Model所有适用的場景都比較清晰,一目了然。而Yii1.1是沒有一個統一管理場景的方法的。
由此帶來的一個很容易出現的問題就是,當你聲明一個Model處于某一場景時,可能由于拼寫錯誤, 不小心将場景的名稱寫錯了,那麼在Yii1.1中,這個錯誤的場景并沒有任何的提示。假設有以下情況:
| |
這裡針對UserForm的注冊和登陸兩個場景,設定了不同的驗證規則。接下來,你要在注冊場景中使用這個UserForm, 但你一不小心将 Registration 場景設定成了 SignUp , 說實在,我不是學英文出身的,這兩個單詞的意思在我眼裡是一樣一樣的。隻是Yii不會智能到把這兩個場景等同起來。 那麼Yii1.1将不會有任何的提示,并自動地使用第一個驗證規則,而使用者注冊時填寫的 email 和 password_repeat 字段就被抛棄了。這在實際程式設計中,是經常出現的一個低級錯誤。
從這裡可以看到,Yii1.1中對于場景,沒有一個集中統一的管理,也就是說一個Model可适用的場景, 是不确定的、任意的。通過 rules() 你很難一眼看出來一個Model可以适用于多少個場景,每個場景下都有哪些字段是有效的、需要驗證的。
而在Yii2.0中,由于引入了 yii\base\Model::scenarios() 新的方法, 将本Model所有适用的場景,及不同場景下的有效字段都進行了聲明, 這個邏輯就顯得清晰了。而且,如果使用了一個未聲明的場景,Yii2.0會有相應的提示, 這避免了上面這個低級錯誤的可能:
| |
這樣看來,是不是很清晰?這個User僅有兩種場景,每種場景的有效字段也一目了然。 而至于具體場景下每個字段的驗證規則,仍然由 yii\base\Model::rules() 來确定。 這也意味着, unsafe 驗證在Yii2.0中也沒有了立足之地,凡是 unsafe 的字段,就不在特定的場景中列出來。 或者為了更加明顯的表示某一字段在特定場景下是無效的,可以給這個字段加上 ! 字首。
在預設情況下, yii\base\Model::scenarios() 所有适用的場景和對應的字段由 yii\base\Model::rules() 的内容自動生成。也就是說,如果你的 rules()很完備、很清晰,那麼也是不需要重載這個 scenarios() 的。 這種情況下,Yii1.1和Yii2.0在這一點上的表現形式,是一樣的。但是,個人經驗看, 我更傾向于将 scenarios() 聲明清楚,而在 rules() 中,僅指定字段的驗證規則,而不涉及場景的内容。 這樣的邏輯更加清晰,便于其他團隊成員閱讀你的代碼,也便于後續的維護和開發。
控制器(Controller)
除了上面講到的控制器中要使用 echo 來顯示渲染視圖的輸出這點差別外, Yii1.1與Yii2.0的控制器還表現出更為明顯的差別,那就是動作過濾器(Action Filter) 的不同。
在Yii2.0中,動作過濾器以行為(behavior)的方式出現, 一般繼承自 yii\base\ActionFilter ,并注入到一個控制器中,以發生作用。比如,Yii1.1中很常見的:
| |
看着是不是有點像,但又确實不一樣?
Active Record
還記得麼?在Yii1.1中,資料庫查詢被分散成 CDbCommand , CDbCriteria 和 CDbCommandBuilder 。 所謂天下大勢分久必合,到了Yii2.0,采用 yii\db\Query 來表示資料庫查詢:
| |
最最最爽的是, yii\db\Query 可以在 Active Record中使用,而在Yii1.1中,要結合兩者,并不容易。
Active Record在Yii2.0中最大的變化一個是查詢的建構,另一個是關聯查詢的處理。
Yii1.1中的 CDbCriteria 在Yii2.0中被 yii\db\ActiveQuery 所取代, 這個把前輩拍死在沙灘上的家夥,繼承自 yii\db\Query ,是以可以進行類似上面代碼的查詢。 調用 yii\db\ActiveRecord::find() 就可以啟動查詢的建構了:
$customers = Customer::find()
->where(['status' => $active])
->orderBy('id')
->all();
這在Yii1.1中,是不容易實作的。特别是比較複雜的查詢關系。
在關聯查詢方面,Yii1.1是在一個統一的地方 relations() 定義關聯關系。 而Yii2.0改變了這一做法,定義一個關聯關系:
- 定義一個getter方法
- getter方法的方法名表示關聯關系的名稱,如 getOrders() 表示關系 orders
- getter方法中定義關聯的依據,通常是外鍵關系
- getter傳回一個 yii\db\ActiveQuery 對象
比如以下代碼就定義了 Customer 的 orders 關聯關系:
| |
這樣的話,可以通過 Customer 通路關聯的 Order
| |
對于關聯查詢,有積極的方式也有消極的方式。差別在于采用積極方式時,關聯的查詢會一并執行, 而消極方式時,僅在顯示調用關聯記錄時材會執行關聯的查詢。
在積極方式的實作上,Yii2.0與Yii1.1也存在不同。Yii1.1使用一個JOIN查詢, 來實作同時查詢主記錄及其關聯的記錄。 而Yii2.0棄用JOIN查詢的方式,而使用兩個順序的SQL語句, 第一個語句查詢主記錄,第二個語句根據第一個語句的傳回結果進行過濾。
同時,Yii2.0為Active Record引入了 asArray() 方法。在傳回大量記錄時,可以以數組形式儲存, 而不再以對象形式儲存,這樣可以節約大量的空間,提高效率。
另外一個變化是,在Yii1.1中,字段的預設值可以通過為類的public 成員變量賦初始值來指定。 而在Yii2.0中,這樣的方式是行不通的,必須通過重載 init() 成員函數的方式實作了。
Yii2.0還淘汰掉了原來的 CActiveRecordBehavior 類。在Yii2.0中,将行為與類進行綁定采用了統一的方式進行, 具體請參考 行為(Behavior) 的有關内容。
Yii2.0中,ActiveRecord得到極大的加強,在相關的章節中我們已經進行專門的講解。
這裡的内容主要是點一點Yii2.0之于Yii1.1的變化。大緻了解下就可以了, 主要還是要看正文專門針對每個知識點的深入講解。