從 Yii 1.1升級到 Yii2

從 Yii 1.1 升級

2.0 版框架是完全重寫的,在 1.1 和 2.0 兩個版本之間存在相當多差異。因此從 1.1 版升級並不像小版本間的跨越那麼簡單,通過本指南你將會了解兩個版本間主要的不同之處。

如果你之前沒有用過 Yii 1.1,可以跳過本章,直接從"[入門篇](start-installation.md)"開始讀起。

請注意,Yii 2.0 引入了很多本章並沒有涉及到的新功能。強烈建議你通讀整部權威指南來了解所有新特性。這樣有可能會發現一些以前你要自己開發的功能,而現在已經被包含在覈心代碼中了。

安裝

Yii 2.0 完全擁抱 [Composer](https://getcomposer.org/),它是PHP中的一個依賴管理工具。核心框架以及擴展的安裝都通過 Composer 來處理。想要了解更多如何安裝 Yii 2.0 請參閱本指南的 [安裝 Yii](start-installation.md) 章節。如果你想創建新擴展,或者把你已有的 Yii 1.1 的擴展改寫成兼容 2.0 的版本,你可以參考 [創建擴展](extend-creating-extensions.md) 章節。

PHP 需求

Yii 2.0 需要 PHP 5.4 或更高版本,該版本相對於 Yii 1.1 所需求的 PHP 5.2 而言有巨大的改進。因此在語言層面上有很多的值得注意的不同之處。下面是 PHP 層的主要變化彙總:

- [命名空間](http://php.net/manual/zh/language.namespaces.php)

- [匿名函數](http://php.net/manual/zh/functions.anonymous.php)

- 數組短語法 `[...元素...]` 用於取代 `array(...元素...)`

- 視圖文件中的短格式 echo 標籤 `<?=`,自 PHP 5.4 起總會被識別並且合法,無論 short_open_tag 的設置是什麼,可以安全使用。

- [SPL 類和接口](http://php.net/manual/zh/book.spl.php)

- [延遲靜態綁定](http://php.net/manual/zh/language.oop5.late-static-bindings.php)

- [日期和時間](http://php.net/manual/zh/book.datetime.php)

- [Traits](http://php.net/manual/zh/language.oop5.traits.php)

- [intl](http://php.net/manual/zh/book.intl.php) Yii 2.0 使用 PHP 擴展 `intl` 來支持國際化的相關功能。

命名空間

Yii 2.0 裏最明顯的改動就數命名空間的使用了。幾乎每一個核心類都引入了命名空間,比如 `yii\web\Request`。1.1 版用於類名前的字母 「C」 已經不再使用。當前的命名規範與目錄結構相吻合。例如,`yii\web\Request` 就表明對應的類文件是 Yii 框架文件夾下的 `web/Request.php` 文件。

(有了 Yii 的類自動加載器,你可以直接使用全部核心類而不需要顯式包含具體文件。)  

組件(Component)與對象(Object)

Yii 2.0 把 1.1 裏的 `CComponent` 類拆分成了兩個類:[[yii\base\Object]] 和 [[yii\base\Component]]。[[yii\base\Object|Object]] 類是一個輕量級的基類,你可以通過 getters 和 setters 來定義 [object 的屬性](concept-properties.md)。[[yii\base\Component|Component]] 類繼承自 [[yii\base\Object|Object]],同時進一步支持 [事件](concept-events.md) 和 [行爲](concept-behaviors.md)。

如果你不需要用到事件或行爲,應該考慮使用 [[yii\base\Object|Object]] 類作爲基類。這通常是表示基本數據結構的類。

對象的配置

[[yii\base\Object|Object]] 類引入了一種統一對象配置的方法。所有 [[yii\base\Object|Object]] 的子類都應該用以下方法聲明它的構造方法(如果需要的話),以正確配置它自身:

class MyClass extends \yii\base\Object
{
public function __construct($param1, $param2, $config = [])
{
// ... 配置生效前的初始化過程

    parent::\_\_construct($config);
}

public function init()
{
    parent::init();

    // ...配置生效後的初始化過程
}

}

在上面的例子裏,構造方法的最後一個參數必須輸入一個配置數組,包含一系列用於在方法結尾初始化相關屬性的鍵值對。你可以重寫 [[yii\base\Object::init()|init()]] 方法來執行一些需要在配置生效後進行的初始化工作。

你可以通過遵循以下約定俗成的編碼習慣,來使用配置數組創建並配置新的對象:

$object = Yii::createObject([
'class' => 'MyClass',
'property1' => 'abc',
'property2' => 'cde',
], [$param1, $param2]);

更多有關配置的細節可以在[配置](concept-configurations.md)章節找到。

事件(Event)

在 Yii 1 中,通常通過定義 `on` 開頭的方法(例如 `onBeforeSave`)來創建事件。而在 Yii 2 中,你可以使用任意的事件名了。同時通過調用 [[yii\base\Component::trigger()|trigger()]] 方法來觸發相關事件:

$event = new \yii\base\Event;
$component->trigger($eventName, $event);

要給事件附加一個事件句柄(Event Handler 或者叫事件處理器),需要使用 [[yii\base\Component::on()|on()]] 方法:

$component->on($eventName, $handler);
// 要解除相關句柄,使用 off 方法:
// $component->off($eventName, $handler);

事件功能還有更多增強之處。要了解它們,請查看[事件](concept-events.md)章節。

路徑別名(Path Alias)

Yii 2.0 將路徑別名的應用擴大至文件/目錄路徑和 URL。Yii 2.0 中路徑別名必須以 `@` 符號開頭,以區別於普通文件目錄路徑或 URL。例如 `@yii` 就是指向 Yii 安裝目錄的別名。絕大多數 Yii 核心代碼都支持別名。例如 [[yii\caching\FileCache::cachePath]] 就同時支持路徑別名或普通的目錄地址。

路徑別名也和類的命名空間密切相關。建議給每一個根命名空間定義一個路徑別名,從而無須額外配置,便可啓動 Yii 的類自動加載機制。例如,因爲有 `@yii` 指向 Yii 安裝目錄,那類似 `yii\web\Request` 的類就能被 Yii 自動加載。同理,若你用了一個第三方的類庫,如 Zend Framework,你只需定義一個名爲 `@Zend` 的路徑別名指向該框架的安裝目錄。之後 Yii 就可以自動加載任意 Zend Framework 中的類了。

更多路徑別名信息請參閱[路徑別名](concept-aliases.md)章節。

視圖(View)

Yii 2 中視圖最明顯的改動是視圖內的特殊變量 `$this` 不再指向當前控制器或小部件,而是指向**視圖**對象,它是 2.0 中引入的全新概念。**視圖**對象爲 [[yii\web\View]] 的實例,他代表了 MVC 模式中的視圖部分。如果你想要在視圖中訪問一個控制器或小部件,可以使用 `$this->context`。

要在其他視圖裏渲染一個局部視圖,使用 `$this->render()`,而不是 `$this->renderPartial()`。`render()` 現在只返回渲染結果,而不是直接顯示它,所以現在你必須顯式地把它 **echo** 出來。像這樣:

echo $this->render('_item', ['item' => $item]);

除了使用 PHP 作爲主要的模板語言,Yii 2.0 也裝備了兩種流行模板引擎的官方支持:Smarty 和 Twig。過去的 Prado 模板引擎不再被支持。要使用這些模板引擎,你需要配置 `view` 應用組件,給它設置 [[yii\base\View::$renderers|View::$renderers]] 屬性。具體請參閱[模板引擎](tutorial-template-engines.md)章節。

模型(Model)

Yii 2.0 使用 [[yii\base\Model]] 作爲模型基類,類似於 1.1 的 `CModel` 。`CFormModel` 被完全棄用了,現在要創建表單模型類,可以通過繼承 [[yii\base\Model]] 類來實現。

Yii 2.0 引進了名爲 [[yii\base\Model::scenarios()|scenarios()]] 的新方法來聲明支持的場景,並指明在哪個場景下某屬性必須經過驗證,可否被視爲安全值等等。如:

public function scenarios()
{
return [
'backend' => ['email', 'role'],
'frontend' => ['email', '!role'],
];
}

上面的代碼聲明瞭兩個場景:`backend` 和 `frontend` 。對於 `backend` 場景,`email` 和 `role` 屬性值都是安全的,且能進行批量賦值。對於 `frontend` 場景,`email` 能批量賦值而 `role` 不能。 `email` 和 `role` 都必須通過規則驗證。

[[yii\base\Model::rules()|rules()]] 方法仍用於聲明驗證規則。注意,由於引入了 [[yii\base\Model::scenarios()|scenarios()]],現在已經沒有 `unsafe` 驗證器了。

大多數情況下,如果 [[yii\base\Model::rules()|rules()]] 方法內已經完整地指定場景了,那就不必覆寫 [[yii\base\Model::scenarios()|scenarios()]],也不必聲明 `unsafe` 屬性值。

要了解更多有關模型的細節,請參考[模型](structure-models.md)章節。

控制器(Controller)

Yii 2.0 使用 [[yii\web\Controller]] 作爲控制器的基類,類似於 1.1 的 `CWebController`。使用 [[yii\base\Action]] 作爲操作類的基類。

這些變化最明顯的影響是,當你在寫控制器操作的代碼時,應該返回(return)要渲染的內容而不是輸出(echo)它:

public function actionView($id)
{
$model = \app\models\Post::findOne($id);
if ($model) {
return $this->render('view', ['model' => $model]);
} else {
throw new \yii\web\NotFoundHttpException;
}
}

請查看 [控制器(Controller)](structure-controllers.md) 章節瞭解有關控制器的更多細節。

小部件(Widget)

Yii 2.0 使用 [[yii\base\Widget]] 作爲小部件基類,類似於 1.1 的 `CWidget`。

爲了讓框架獲得更好的 IDE 支持,Yii 2.0 引進了一個調用小部件的新語法。包含 [[yii\base\Widget::begin()|begin()]],[[yii\base\Widget::end()|end()]] 和 [[yii\base\Widget::widget()|widget()]] 三個靜態方法,用法如下:

use yii\widgets\Menu;
use yii\widgets\ActiveForm;

// 注意必須 **"echo"** 結果以顯示內容
echo Menu::widget(['items' => $items]);

// 傳遞一個用於初始化對象屬性的數組
$form = ActiveForm::begin([
'options' => ['class' => 'form-horizontal'],
'fieldConfig' => ['inputOptions' => ['class' => 'input-xlarge']],
]);
... 表單輸入欄都在這裏 ...
ActiveForm::end();

更多細節請參閱[小部件](structure-widgets.md)章節。

主題(Theme)

2.0 主題的運作方式跟以往完全不同了。它們現在基於**路徑映射機制**,該機制會把一個源視圖文件的路徑映射到一個主題視圖文件路徑。舉例來說,如果路徑映射爲 `['/web/views' => '/web/themes/basic']`,那麼 `/web/views/site/index.php` 視圖經過主題修飾的版本就會是 `/web/themes/basic/site/index.php`。也因此讓主題現在可以應用在任何視圖文件之上,甚至是渲染控制器上下文環境之外的視圖文件或小部件。

同樣,`CThemeManager` 組件已經被移除了。取而代之的 `theme` 成爲了 `view` 應用組件的一個可配置屬性。

更多細節請參考[主題](output-theming.md)章節。

控制檯應用(Console Application)

控制檯應用現在如普通的 Web 應用程序一樣,由控制器組成,控制檯的控制器繼承自 [[yii\console\Controller]],類似於 1.1 的 `CConsoleCommand`。

運行控制檯命令使用 `yii `,其中 `` 代表控制器的路由(如 `sitemap/index`)。額外的匿名參數傳遞到對應的控制器操作方法,而有名的參數根據 [[yii\console\Controller::options()]] 的聲明來解析。

Yii 2.0 支持基於代碼註釋自動生成相的關命令行幫助(help)信息。

更多細節請參閱[控制檯命令](tutorial-console.md)章節。

國際化(I18N)

Yii 2.0 移除了原來內置的日期格式器和數字格式器,爲了支持 [PECL intl PHP module](http://pecl.php.net/package/intl)(PHP 的國際化擴展)的使用。

消息翻譯現在由 `i18n` 應用組件執行。該組件管理一系列消息源,允許使用基於消息類別的不同消息源。

更多細節請參閱[國際化(Internationalization)](tutorial-i18n.md)章節。

操作過濾器(Action Filters)

操作的過濾現在通過行爲(behavior)來實現。要定義一個新的,自定義的過濾器,請繼承 [[yii\base\ActionFilter]] 類。要使用一個過濾器,需要把過濾器類作爲一個 `behavior` 綁定到控制器上。例如,要使用 [[yii\filters\AccessControl]] 過濾器,你需要在控制器內添加如下代碼:

public function behaviors()
{
return [
'access' => [
'class' => 'yii\filters\AccessControl',
'rules' => [
['allow' => true, 'actions' => ['admin'], 'roles' => ['@']],
],
],
];
}

更多細節請參考[過濾器](structure-filters.md)章節。

前端資源(Assets)

Yii 2.0 引入了一個新的概念,稱爲**資源包**(Asset Bundle),以代替 1.1 的腳本包概念。

一個資源包是一個目錄下的資源文件集合(如 JavaScript 文件、CSS 文件、圖片文件等)。每一個資源包被表示爲一個類,該類繼承自 [[yii\web\AssetBundle]]。用 [[yii\web\AssetBundle::register()]] 方法註冊一個資源包後,就使它的資源可被 Web 訪問了,註冊了資源包的頁面會自動包含和引用資源包內指定的 JS 和 CSS 文件。

更多細節請參閱 [前端資源管理(Asset)](structure-assets.md) 章節。

助手類(Helpers)

Yii 2.0 很多常用的靜態助手類,包括:

* [[yii\helpers\Html]]

* [[yii\helpers\ArrayHelper]]

* [[yii\helpers\StringHelper]]

* [[yii\helpers\FileHelper]]

* [[yii\helpers\Json]]

請參考[助手一覽](helper-overview.md) 章節來了解更多。

表單

Yii 2.0 引進了**表單欄(field)**的概念,用來創建一個基於 [[yii\widgets\ActiveForm]]的表單。一個表單欄是一個由標籤、輸入框、錯誤消息(可能還有提示文字)組成的容器,被表示爲 [[yii\widgets\ActiveField|ActiveField]] 對象。使用表單欄建立表單的過程比以前更整潔利落:

passwordInput() ?>

請參考[創建表單](input-forms.md)章節來了解更多細節。

查詢生成器(Query Builder)

Yii 1.1 中,查詢語句的生成分散在多個類中,包括 `CDbCommand`,`CDbCriteria` 以及 `CDbCommandBuilder`。Yii 2.0 以 [[yii\db\Query|Query]] 對象的形式表示一個數據庫查詢,這個對象使用 [[yii\db\QueryBuilder|QueryBuilder]] 在幕後生成 SQL 語句。例如:

$query = new \yii\db\Query();
$query->select('id, name')
->from('user')
->limit(10);

$command = $query->createCommand();
$sql = $command->sql;
$rows = $command->queryAll();

最重要的是,這些查詢生成方法還可以和[活動記錄](db-active-record.md)配合使用。

請參考[查詢生成器(Query Builder)](db-query-builder.md)章節瞭解更多內容。

活動記錄(Active Record)

Yii 2.0 的[活動記錄](db-active-record.md)改動了很多。兩個最顯而易見的改動分別涉及查詢語句的生成(query building)和關聯查詢的處理(relational query handling)。

1.1 中的 `CDbCriteria` 類在 Yii 2 中被 [[yii\db\ActiveQuery]] 所替代。這個類是繼承自 [[yii\db\Query]],因此也繼承了所有查詢生成方法。開始拼裝一個查詢可以調用 [[yii\db\ActiveRecord::find()]] 方法進行:

// 檢索所有 *活動的* 客戶和訂單,並以 ID 排序:
$customers = Customer::find()
->where(['status' => $active])
->orderBy('id')
->all();

要聲明一個關聯關係,只需簡單地定義一個 getter 方法來返回一個 [[yii\db\ActiveQuery|ActiveQuery]] 對象。getter 方法定義的屬性名(譯者注:即 getOrders() 中的 orders)表示關聯關係名。如,以下代碼聲明瞭一個名爲 `orders` 的關係(1.1 中必須在 `relations()` 方法內聲明關係):

class Customer extends \yii\db\ActiveRecord
{
public function getOrders()
{
return $this->hasMany('Order', ['customer_id' => 'id']);
}
}

現在你就可以通過調用 `$customer->orders` 來訪問關聯表中某用戶的訂單了。你還可以用以下代碼進行一場指定條件的實時關聯查詢:

$orders = $customer->getOrders()->andWhere('status=1')->all();

當貪婪加載一段關聯關係時,Yii 2.0 和 1.1 的運作機理並不相同。具體來說,在 1.1 中使用一條 JOIN 語句同時查詢主表和關聯表記錄。在 Yii 2.0 中會使用兩個沒有 JOIN 的 SQL 語句:第一條語句取回主表記錄,第二條通過主表記錄經主鍵篩選後查詢關聯表記錄。

當生成返回大量記錄的查詢時,可以鏈式書寫 [[yii\db\ActiveQuery::asArray()|asArray()]] 方法,這樣會以數組的形式返回查詢結果,而不必返回

[[yii\db\ActiveRecord|ActiveRecord]] 對象,這能顯著降低因大量記錄讀取所消耗的 CPU 時間和內存。如:

$customers = Customer::find()->asArray()->all();

另一個改變是你不能再通過公共數據定屬性(Attribute)的默認值了。如果你需要這麼做的話,可以在你的記錄類的 `init` 方法中設置它們。

public function init()
{
parent::init();
$this->status = self::STATUS_NEW;
}

曾幾何時,在 1.1 中重寫一個活動記錄類的構造方法(Constructor)會導致一些問題。它們不會在 2.0 中出現了。需要注意的是,如果你需要在構造方法中添加一些參數,恐怕必須重寫 [[yii\db\ActiveRecord::instantiate()]] 方法。

活動記錄方面還有很多其他的變化與改進,請參考[活動記錄](db-active-record.md)章節以瞭解更多細節。

用戶及身份驗證接口(IdentityInterface)

1.1 中的 `CWebUser` 類現在被 [[yii\web\User]] 所取代,隨之 `CUserIdentity` 類也不在了。與之相對的,爲達到相同目的,你可以實現 [[yii\web\IdentityInterface]] 接口,它使用起來更直觀。在高級應用模版裏提供了一個這樣的一個例子。

要了解更多細節請參考[認證(Authentication)](security-authentication.md),[授權(Authorization)](security-authorization.md)以及[高級應用模版](tutorial-advanced-app.md) 這三個章節。

URL 管理

Yii 2.0 的 URL 管理跟 1.1 中很像。一個主要的改進是現在的 URL 管理支持**可選參數**了。比如,如果你在 2.0 中定義了一個下面這樣的規則,那麼它可以同時匹配 `post/popular` 和 `post/1/popular` 兩種 URL。而在 1.1 中爲達成相同效果,必須要使用兩條規則。

[
'pattern' => 'post/

/

', 'route' => 'post/index', 'defaults' => ['page' => 1], ]

請參考[URL 解析和生成](runtime-url-handling.md) 章節,以瞭解更多細節。.

同時使用 Yii 1.1 和 2.x

如果你有一些遺留的 Yii 1.1 代碼,需要跟 Yii 2.0 一起使用,可以參考 [1.1 和 2.0 共用](extend-using-v1-v2.md)章節。