在開發中擴展Yii是一個很常見的行為.例如,當你寫一個新的控制器時,你通過繼承 CController 類擴展了 Yii;當你編寫一個新的組件時,你正在繼承 CWidget 或者一個已存在的組件類.如果擴展代碼是由第三方開發者為了復用而設計的,我們則稱之為 extension(擴展)。
一個擴展通常是為了一個單一的目的服務的.在 Yii 中,他可以按照如下分類:
* 應用的部件
* 組件
* 控制器
* 動作
* 過濾器
* 控制臺命令
* 校驗器: 校驗器是一個繼承自 CValidator 類的部件。
* 輔助器: 輔助器是一個只具有靜態方法的類.它類似于使用類名作為命名空間的全局函數。
* 模塊: 模塊是一個有著若干個類文件和相應特長文件的包.一個模塊通常更高級,比一個單一的部件具備更先進的功能.
例如我們可以擁有一個具備整套用戶管理功能的模塊。
擴展也可以是不屬于上述分類中的任何一個的部件。事實上,Yii 是設計得很謹慎的,以至于幾乎它的每段代碼都可以被擴展和訂制以適用于特定需求。
一、使用擴展
使用擴展通常包含了以下三步:
- 從 Yii 的 擴展庫 下載擴展。
- 解壓到 應用程序的基目錄 的子目錄 extensions/xyz 下,這里的 xyz 是擴展的名稱。
- 導入, 配置和使用擴展。
每個擴展都有一個所有擴展中唯一的名稱標識。把一個擴展命名為 xyz ,我們也可以使用路徑別名定位到包含了 xyz 所有文件的基目錄。
不同的擴展有著不同的導入,配置,使用要求.以下是我們通常會用到擴展的場景,按照他們在 概述 中的描述分類。
1、應用的部件
使用 應用的部件, 首先我們需要添加一個新條目到 應用配置 的 components 屬性, 如下所示:
return array(
// 'preload'=>array('xyz',...),
'components'=>array(
'xyz'=>array(
'class'=>'application.extensions.xyz.XyzClass',
'property1'=>'value1',
'property2'=>'value2',
),
// 其他部件配置
),
);
然后,我們可以在任何地方通過使用 Yii::app()->xyz
來訪問部件.部件將會被 惰性創建(就是,僅當它第一次被訪問時創建.) , 除非我們把它配置到 preload
屬性里。
2、組件
組件 主要用在 視圖 里.假設組件類 XyzClass 屬于 xyz 擴展,我們可以如下在視圖中使用它:
// 組件不需要主體內容
<?php $this->widget('application.extensions.xyz.XyzClass', array(
'property1'=>'value1',
'property2'=>'value2')); ?>
// 組件可以包含主體內容
<?php $this->beginWidget('application.extensions.xyz.XyzClass', array(
'property1'=>'value1',
'property2'=>'value2')); ?>
...組件的主體內容...
<?php $this->endWidget(); ?>
3、動作
動作 被 控制器 用于響應指定的用戶請求.假設動作的類 XyzClass
屬于 xyz
擴展,我們可以在我們的控制器類里重寫 CController::actions
方法來使用它:
class TestController extends CController
{
public function actions()
{
return array(
'xyz'=>array(
'class'=>'application.extensions.xyz.XyzClass',
'property1'=>'value1',
'property2'=>'value2',
),
// 其他動作
);
}
}
然后,我們可以通過 路由 test/xyz 來訪問。
4、過濾器
過濾器 也被 控制器 使用。過濾器主要用于當其被 動作 掛起時預處理,提交處理用戶請求。假設過濾器的類 XyzClass 屬于 xyz 擴展,我們可以在我們的控制器類里重寫 CController::filters
方法來使用它:
class TestController extends CController
{
public function filters()
{
return array(
array(
'application.extensions.xyz.XyzClass',
'property1'=>'value1',
'property2'=>'value2',
),
// 其他過濾器
);
}
}
在上述代碼中,我們可以在數組的第一個元素離使用加號或者減號操作符來限定過濾器只在那些動作中生效。更多信息,請參照文檔的 CController
。
5、控制器
控制器 提供了一套可以被用戶請求的動作。我們需要在 應用配置 里設置 CWebApplication::controllerMap
屬性,才能在控制器里使用擴展:
return array(
'controllerMap'=>array(
'xyz'=>array(
'class'=>'application.extensions.xyz.XyzClass',
'property1'=>'value1',
'property2'=>'value2',
),
// 其他控制器
),
);
然后, 一個在控制里的 a 行為就可以通過 路由 xyz/a 來訪問了。
6、校驗器
校驗器主要用在 模型類 (繼承自 CFormModel 或者 CActiveRecord) 中.假設校驗器類 XyzClass 屬于 xyz 擴展,我們可以在我們的模型類中通過 CModel::rules
重寫 CModel::rules
來使用它:
class MyModel extends CActiveRecord // or CFormModel
{
public function rules()
{
return array(
array(
'attr1, attr2',
'application.extensions.xyz.XyzClass',
'property1'=>'value1',
'property2'=>'value2',
),
// 其他校驗規則
);
}
}
7、控制臺命令
控制臺命令擴展通常使用一個額外的命令來增強 yiic 的功能.假設命令控制臺 XyzClass 屬于 xyz 擴展,我們可以通過設定控制臺應用的配置來使用它:
return array(
'commandMap'=>array(
'xyz'=>array(
'class'=>'application.extensions.xyz.XyzClass',
'property1'=>'value1',
'property2'=>'value2',
),
// 其他命令
),
);
然后,我們就能使用配備了額外命令 xyz 的 yiic 工具了。
注意: 控制臺應用通常使用了一個不同于 Web 應用的配置文件.如果使用了 yiic webapp
命令創建了一個應用,這樣的話,控制臺應用的 protected/yiic 的配置文件就是 protected/config/console.php 了,而Web應用的配置文件 則是 protected/config/main.php。
8、模塊
模塊通常由多個類文件組成,且往往綜合上述擴展類型。因此,你應該按照和以下一致的指令來使用模塊。
9、通用部件
使用一個通用 部件, 我們首先需要通過使用
Yii::import('application.extensions.xyz.XyzClass');
來包含它的類文件。然后,我們既可以創建一個類的實例,配置它的屬性,也可以調用它的方法。我們還可以創建一個新的子類來擴展它。
二、創建擴展
由于擴展意味著是第三方開發者使用,需要一些額外的努力去創建它。以下是一些一般性的指導原則:
*擴展最好是自己自足。也就是說,其外部的依賴應是最少的。如果用戶的擴展需要安裝額外的軟件包,類或資源檔案,
這將是一個頭疼的問題。
*文件屬于同一個擴展的,應組織在同一目錄下,目錄名用擴展名稱。
*擴展里面的類應使用一些單詞字母前綴,以避免與其他擴展命名沖突。
*擴展應該提供詳細的安裝和API文檔。這將減少其他開發員使用擴展時花費的時間和精力。
*擴展應該用適當的許可。如果您想您的擴展能在開源和閉源項目中使用,你可以考慮使用許可證,
如BSD的,麻省理工學院等,但不是GPL的,因為它要求其衍生的代碼是開源的。
在下面,我們根據 overview
中所描述的分類,描述如何創建一個新的擴展。當您要創建一個主要用于在您自己項目的component
部件,這些描述也適用。
1、Application Component(應用部件)
一個application component
應實現接口IApplicationComponent
或繼承CApplicationComponent
。主要需要實現的方法是 IApplicationComponent::init
,部件在此執行一些初始化工作。此方法在部件創建和屬性值(在application configuration里指定的 )被賦值后調用。
默認情況下,一個應用程序部件創建和初始化,只有當它首次訪問期間要求處理。如果一個應用程序部件需要在應用程序實例被創建后創建,它應要求用戶在CApplication::preload
的屬性中列出他的編號。
2、Widget(小工具)
widget
應繼承CWidget
或其子類。 A widget should extend from CWidget or its child classes.
最簡單的方式建立一個新的小工具是繼承一個現成的小工具和重載它的方法或改變其默認的屬性值。例如,如果您想為CTabView使用更好的CSS樣式,您可以配置其CTabView::cssFile屬性,當使用的小工具時。您還可以繼承CTabView如下,讓您在使用小工具時,不再需要配置屬性。
class MyTabView extends CTabView
{
public function init()
{
if($this->cssFile===null)
{
$file=dirname(__FILE__).DIRECTORY_SEPARATOR.'tabview.css';
$this->cssFile=Yii::app()->getAssetManager()->publish($file);
}
parent::init();
}
}
在上面的,我們重載CWidget::init
方法和指定CTabView::cssFile
的 URL到我們的新的默認CSS樣式如果此屬性未設置時。我們把新的CSS樣式文件和MyTabView類文件放在相同的目錄下,以便他們能夠封裝成擴展。由于CSS樣式文件不是通過Web訪問,我們需要發布作為一項asset資源。
要從零開始創建一個新的小工具,我們主要是需要實現兩個方法:CWidget::init
和CWidget::run
。第一種方法是當我們在視圖中使用 $this->beginWidget
插入一個小工具時被調用,第二種方法在$this->endWidget
被調用時調用。如果我們想在這兩個方法調用之間捕捉和處理顯示的內容,我們可以開始output buffering
在CWidget::init
和在CWidget::run
中回收緩沖輸出作進一步處理。 If we want to capture and process the content displayed between these two method invocations, we can start output buffering in CWidget::init and retrieve the buffered output in CWidget::run for further processing.
在網頁中使用的小工具,小工具往往包括CSS,Javascript或其他資源文件。我們叫這些文件assets
,因為他們和小工具類在一起,而且通常Web用戶無法訪問。為了使這些檔案通過Web訪問,我們需要用CWebApplication::assetManager
發布他們,例如上述代碼段所示。此外,如果我們想包括CSS或JavaScript文件在當前的網頁,我們需要使用CClientScript
注冊 :
class MyWidget extends CWidget
{
protected function registerClientScript()
{
// ...publish CSS or JavaScript file here...
$cs=Yii::app()->clientScript;
$cs->registerCssFile($cssFile);
$cs->registerScriptFile($jsFile);
}
}
小工具也可能有自己的視圖文件。如果是這樣,創建一個目錄命名views在包括小工具類文件的目錄下,并把所有的視圖文件放里面。在小工具類中使用$this->render('ViewName')
來render渲染小工具視圖,類似于我們在控制器里做。
3、Action(動作)
action應繼承CAction或者其子類。action要實現的主要方法是IAction::run
。
4、Filter(過濾器)
filter應繼承CFilter 或者其子類。filter要實現的主要方法是CFilter::preFilter
和CFilter::postFilter
。前者是在action之前被執行,而后者是在之后。
class MyFilter extends CFilter
{
protected function preFilter($filterChain)
{
// logic being applied before the action is executed
return true; // false if the action should not be executed
}
protected function postFilter($filterChain)
{
// logic being applied after the action is executed
}
}
參數$filterChain的類型是CFilterChain,其包含當前被filter的action的相關信息。
5、Controller(控制器)
controller要作為擴展需繼承CExtController
,而不是 CController
。主要的原因是因為CController
認定控制器視圖文件位于application.views.ControllerID 下,而CExtController認定視圖文件在views目錄下,也是包含控制器類目錄的一個子目錄。因此,很容易重新分配控制器,因為它的視圖文件和控制類是在一起的。
6、Validator(驗證)
Validator
需繼承CValidator
和實現CValidator::validateAttribute
方法。
class MyValidator extends CValidator
{
protected function validateAttribute($model,$attribute)
{
$value=$model->$attribute;
if($value has error)
$model->addError($attribute,$errorMessage);
}
}
7、Console Command(控制臺命令)
console command 應繼承CConsoleCommand
和實現CConsoleCommand::run
方法。 或者,我們可以重載CConsoleCommand::getHelp
來提供一些更好的有關幫助命令。
class MyCommand extends CConsoleCommand
{
public function run($args)
{
// $args gives an array of the command-line arguments for this command
}
public function getHelp()
{
return 'Usage: how to use this command';
}
}
8、Module(模塊)
請參閱modules一節中關于就如何創建一個模塊。
一般準則制訂一個模塊,它應該是獨立的。模塊所使用的資源文件(如CSS , JavaScript ,圖片),應該和模塊一起分發。還有模塊應發布它們,以便可以Web訪問它們 。
9、Generic Component(通用組件)
開發一個通用組件擴展類似寫一個類。還有,該組件還應該自足,以便它可以很容易地被其他開發者使用。
三、使用第三方庫
Yii是精心設計的,使第三方庫可易于集成,進一步擴大Yii的功能。 當在一個項目中使用第三方庫,程序員往往遇到關于類命名和文件包含的問題。 因為所有Yii類以C字母開頭,這就減少可能會出現的類命名問題;而且因為Yii依賴SPL autoload執行類文件包含,如果他們使用相同的自動加載功能或PHP包含路徑包含類文件,它可以很好地結合。
下面我們用一個例子來說明如何在一個Yii application從Zend framework使用Zend_Search_Lucene部件。
首先,假設protected是application base directory,我們提取Zend Framework的發布文件到protected/vendors目錄 。 確認protected/vendors/Zend/Search/Lucene.php文件存在。
第二,在一個controller類文件的開始,加入以下行:
Yii::import('application.vendors.*');
require_once('Zend/Search/Lucene.php');
上述代碼包含類文件Lucene.php。因為我們使用的是相對路徑,我們需要改變PHP的包含路徑,以使文件可以正確定位。這是通過在require_once之前調用Yii::import
做到。
一旦上述設立準備就緒后,我們可以在controller action
里使用Lucene
類,類似如下:
$lucene=new Zend_Search_Lucene($pathOfIndex);
$hits=$lucene->find(strtolower($keyword));