YII讀書筆記

akbarken發表於2014-09-11

=========================================================================================================================
                                                                                                                   元件架構
=========================================================================================================================


==========================================================================================================================
                                                                                                                                  基礎知識
==========================================================================================================================

模型-檢視-控制器 (MVC)

Yii 使用了 Web 開發中廣泛採用的模型-檢視-控制器(MVC)設計模式。 MVC的目標是將業務邏輯從使用者介面的考慮中分離,這樣開發者就可以更容易地改變每一部分而不會影響其他。 在 MVC中,模型代表資訊(資料)和業務規則檢視包含了使用者介面元素,例如文字,表單等; 控制器管理模型和檢視中的通訊

一個典型的工作流

下圖展示了一個 Yii 應用在處理使用者請求時典型的工作流



  1. 使用者發出了訪問 URL http://www.example.com/index.php?r=post/show&id=1 的請求, Web 伺服器通過執行入口指令碼 index.php 處理此請求。
  2. 入口指令碼建立了一個 應用 例項並執行。
  3. 應用從一個叫做 request 的 應用元件 中獲得了使用者請求的詳細資訊。
  4. 應用在一個名叫 urlManager 的應用元件的幫助下,決定請求的 控制器 和 動作 。在這個例子中,控制器是 post,它代表 PostController 類; 動作是 show ,其實際含義由控制器決定。
  5. 應用建立了一個所請求控制器的例項以進一步處理使用者請求。控制器決定了動作 show 指向控制器類中的一個名為actionShow 的方法。然後它建立並持行了與動作關聯的過濾器(例如訪問控制,基準測試)。 如果過濾器允許,動作將被執行。
  6. 動作從資料庫中讀取一個 ID 為 1 的 Post 模型
  7. 動作通過 Post 模型渲染一個名為 show 的 檢視
  8. 檢視讀取並顯示 Post 模型的屬性。
  9. 檢視執行一些 小物件
  10. 檢視的渲染結果被插入一個 佈局
  11. 動作完成檢視渲染並將其呈現給使用者。

入口指令碼

入口指令碼是處理使用者的初始引導PHP指令碼。它是唯一一個終端使用者可直接請求執行的PHP指令碼

// 在生產環境中請刪除此行defined('YII_DEBUG') or define('YII_DEBUG',true);
// 包含Yii引導檔案require_once('path/to/yii/framework/yii.php');
// 建立一個應用例項並執行$configFile='path/to/config/file.php';
Yii::createWebApplication($configFile)->run();
指令碼首先包含了 Yii 框架的引導檔案 yii.php。然後他按指定的配置建立了一個Web 應用例項並執行。

除錯模式

在包含 yii.php 檔案之前定義此常量YII_DEBUG 為true

應用

指請求處理中的執行上下文。
1.主要任務是分析使用者請求並將其分派到合適的控制器中以作進一步處理。
 2.同時作為服務中心,維護應用級別的配置。鑑於此,應用也叫做前端控制器

應用配置

1.預設情況下,應用是一個 CWebApplication 的例項。要自定義它,我們通常需要提供一個配置檔案 (或陣列) 以建立應用例項時初始化其屬性值
2.自定義應用的另一種方式是繼承 CWebApplication

配置是一個鍵值對陣列。每個鍵和應用例項屬性一一對應,每個值即相應屬性的初始值。 例如,如下的配置設定了CWebApplication應用的name  defaultController 屬性的值。

array(
    'name'=>'Yii Framework',
    'defaultController'=>'site',
)
通常在一個單獨的PHP 指令碼(e.g.protected/config/main.php)中儲存這些配置
要應用此配置,我們將配置檔案的名字作為引數傳遞給應用的構造器
$app=Yii::createWebApplication($webApplication,$configFile);

應用基礎目錄

指包含了所有安全敏感的PHP指令碼和資料的根目錄
認狀態下,它是一個位於同入口指令碼同級目錄的protected 的子目錄
應用基礎目錄下的內容應該保護起來防止網站訪客直接訪問

可以通過在基礎目錄中放置一個.htaccess 檔案很簡單的實現。 .htaccess 內容如下:

deny from all

應用元件

應用管理了一系列應用元件,每個元件實現一特定功能。 例如,應用通過 CUrlManager 和 CHttpRequest 的幫助解析來自使用者的請求。

通過配置config/main.php的 components 屬性, 我們可以自定義應用中用到的任何元件類及其屬性值。例如,我們可以配置應用的CMemCache 元件, 這樣它就可以使用多個 memcache 伺服器實現快取:

array(
    'components'=>array(
        'cache'=>array(
            'class'=>'CMemCache',
            'servers'=>array(
                array('host'=>'server1', 'port'=>11211, 'weight'=>60),
                array('host'=>'server2', 'port'=>11211, 'weight'=>40),
            ),
        ),
    ),
)
訪問一個應用元件
使用 Yii::app()->ComponentID ,其中的 ComponentID 是指元件的ID(例如Yii::app()->db

核心應用元件

Yii 預定義了一系列核心應用元件,提供常見 Web 應用中所用的功能

應用的生命週期


當處理使用者請求時,應用將經歷如下宣告週期:

  1. 通過 CApplication::preinit() 預初始化應用;

  2. 設定類的自動裝載器和錯誤處理      ;

  3. 註冊核心類元件;

  4. 載入應用配置;  (main.php 對應2,3,4步驟)

  5. 通過 CApplication::init() 初始化應用:

    • 註冊應用行為;
    • 載入靜態應用元件;
  6. 觸發 onBeginRequest 事件;

  7. 處理使用者請求:  (對應acitonCotroller)

    • 解析使用者請求;
    • 建立控制器;
    • 執行控制器;
  8. 觸發 onEndRequest 事件。

控制器


1.控制器 是 CController 或其子類的例項。它在當使用者請求時由應用建立。
2.當一個控制器執行時,它執行所請求的動作,動作通常會引入所必要的模型並渲染相應的檢視。
 3.動作 的最簡形式,就是一個名字以 action 開頭的控制器類方法。
4.當使用者的請求未指定要執行的動作時,預設動作將被執行,預設的動作名為index。它可以通過設定 CController::defaultAction 修改。

路由

控制器和動作以 ID 識別。控制器 ID 是一種 'path/to/xyz' 的格式,對應相應的控制器類檔案protected/controllers/path/to/XyzController.php, 其中的標誌 xyz 應被替換為實際的名字 (例如 post對應 protected/controllers/PostController.php). 動作 ID 是除去 action 字首的動作方法名。例如,如果一個控制器類含有一個名為 actionEdit 的方法,則相應的動作 ID 為 edit

使用者以路由的形式請求特定的控制器和動作。路由是由控制器 ID 和動作 ID 連線起來的,兩者以斜線分割。 例如,路由post/edit 代表 PostController 及其 edit 動作。預設情況下,URL http://hostname/index.php?r=post/edit 即請求此控制器和動作
從 1.0.3 版本開始,應用可以含有 模組(Module). 模組中,控制器動作的路由格式為moduleID/controllerID/actionID

控制器例項化

如果在 CWebApplication::controllerMap 中找到了 ID, 相應的控制器配置將被用於建立控制器例項
如果 ID 為 'path/to/xyz'的格式,控制器類的名字將判斷為 XyzController, 相應的類檔案則為protected/controllers/path/to/XyzController.php

例如, 控制器 ID admin/user 將被解析為控制器類 UserController,類檔案是 protected/controllers/admin/UserController.php。 如果類檔案不存在,將觸發一個 404 CHttpException 異常

動作

1.被定義為一個以 action 單詞作為字首命名的方法。
2.更高階的方式是定義一個動作類並讓控制器在收到請求時將其例項化。 這使得動作可以被複用,提高了可複用度。

定義一個新動作類,可用如下程式碼:

class UpdateAction extends CAction{
    public function run()
    {
        // place the action logic here
    }}

為了讓控制器注意到這個動作,我們要用如下方式覆蓋控制器類的actions() 方法:

class PostController extends CController{
    public function actions()
    {
        return array(
            'edit'=>'application.controllers.post.UpdateAction',
        );
    }}
我們使用了路徑別名 application.controllers.post.UpdateAction 指定動作類檔案為protected/controllers/post/UpdateAction.php.

通過編寫基於類的動作,我們可以將應用組織為模組的風格。例如, 如下目錄結構可用於組織控制器相關程式碼:

protected/
    controllers/
        PostController.php
        UserController.php
        post/
            CreateAction.php
            ReadAction.php
            UpdateAction.php
        user/
            CreateAction.php
            ListAction.php
            ProfileAction.php
            UpdateAction.php

動作引數繫結

從版本 1.1.4 開始,Yii 提供了對自動動作引數繫結的支援。 就是說,控制器動作可以定義命名的引數,引數的值將由 Yii 自動從$_GET 填充。

1.1.4之前,使用動作引數功能:從 $_GET 中提取引數時:

class PostController extends CController{
    public function actionCreate()
    {
        if(isset($_GET['category']))
            $category=(int)$_GET['category'];
        else
            throw new CHttpException(404,'invalid request');
 
        if(isset($_GET['language']))
            $language=$_GET['language'];
        else
            $language='en';
 
        // ... fun code starts here ...
    }}

 1.1.4開始,使用動作引數功能:

class PostController extends CController{
    public function actionCreate($category, $language='en')
    {
        $category=(int)$category;
 
        // ... fun code starts here ...
    }}
class PostController extends CController{
    public function actionCreate(array $categories)   //引數強制轉換為array型別
    {
        // Yii will make sure $categories be an array
    }}

過濾器

1.過濾器是一段程式碼,可被配置在控制器動作執行之前或之後執行。

例如, 訪問控制過濾器將被執行以確保在執行請求的動作之前使用者已通過身份驗證;
               效能過濾器可用於測量控制器執行所用的時間。
2.一個動作可以有多個過濾器。過濾器執行順序為它們出現在過濾器列表中的順序。
過濾器可以阻止動作及後面其他過濾器的執行

過濾器可以定義為一個控制器類的方法。方法名必須以 filter 開頭。例如,現有的 filterAccessControl 方法定義了一個名為 accessControl 的過濾器。 過濾器方法必須為如下結構:

public function filterAccessControl($filterChain){
    // 呼叫 $filterChain->run() 以繼續後續過濾器與動作的執行。}
其中的 $filterChain (過濾器鏈)是一個 CFilterChain 的例項,代表與所請求動作相關的過濾器列表。
在過濾器方法中, 可以呼叫 $filterChain->run() 以繼續執行後續過濾器和動作。

過濾器也可以是一個 CFilter 或其子類的例項。如下程式碼定義了一個新的過濾器類:

class PerformanceFilter extends CFilter{
    protected function preFilter($filterChain)
    {
        // 動作被執行之前應用的邏輯
        return true; // 如果動作不應被執行,此處返回 false
    }
 
    protected function postFilter($filterChain)
    {
        // 動作執行之後應用的邏輯
    }}

要對動作應用過濾器,我們需要覆蓋 CController::filters() 方法。此方法應返回一個過濾器配置陣列。例如:

class PostController extends CController{
    ......
    public function filters()
    {
        return array(
            'postOnly + edit, create'),
            array(
                'application.filters.PerformanceFilter - edit, create',
                'unit'=>'second',
            ),
        );
    } }

上述程式碼指定了兩個過濾器: postOnly 和 PerformanceFilter。 postOnly 過濾器是基於方法的(相應的過濾器方法已在 CController 中定義); 而 performanceFilter 過濾器是基於物件的。路徑別名application.filters.PerformanceFilter 指定過濾器類檔案是protected/filters/PerformanceFilter。我們使用一個陣列配置 PerformanceFilter ,這樣它就可被用於初始化過濾器物件的屬性值。此處 PerformanceFilter 的 unit 屬性值將被初始為 second

使用加減號,我們可指定哪些動作應該或不應該應用過濾器。上述程式碼中, postOnly 應只被應用於 edit 和 create 動作,而 PerformanceFilter 應被應用於 除了 edit 和 create 之外的動作。 如果過濾器配置中沒有使用加減號,則此過濾器將被應用於所有動作。

模型

1.模型是 CModel 或其子類的例項。模型用於保持資料以及與其相關的業務邏輯
2.模型是單獨的資料物件。它可以是資料表中的一行,或者一個使用者輸入的表單。
  資料物件的每個欄位對應模型中的一個屬性。每個屬性有一個標籤(label), 並且可以通過一系列規則進行驗證。
3.Yii 實現了兩種型別的模型:表單模型和 Active Record。二者均繼承於相同的基類 CModel
  1>表單模型是 CFormModel 的例項。表單模型用於保持從使用者的輸入獲取的資料。 這些資料經常被獲取,使用,然後丟棄。例如,在一個登入頁面中, 我們可以使用表單模型用於表示由終端使用者提供的使用者名稱和密碼資訊。更多詳情,請參考 使用表單

   2>Active Record (AR) 是一種用於通過物件導向的風格抽象化資料庫訪問的設計模式。 每個 AR 物件是一個 CActiveRecord 或其子類的例項。 資料物件的每個欄位代表資料表中的一行。 行中的欄位對應 AR 物件中的屬性。更多關於 AR 的細節請閱讀 Active Record.

檢視

檢視是一個包含了主要的使用者互動元素的PHP指令碼.建議這些語句不要去改變資料模型,且最好能夠保持其單純性(單純作為檢視)
實現邏輯和介面分離,大段的邏輯應該被放置於控制器或模型中,而不是檢視中

檢視有一個名字,當渲染(render)時,名字會被用於識別檢視指令碼檔案。檢視的名稱與其檢視指令碼名稱是一樣的.例如:檢視edit 的名稱出自一個名為 edit.php 的指令碼檔案.要渲染時如,需通過傳遞檢視的名稱呼叫 CController::render()。這個方法將在 protected/views/ControllerID 目錄下尋找對應的檢視檔案.如controller路徑為:\protected\controllers\contract\applyController.php  對應 檢視目錄:protected\views\contract\apply\
在檢視指令碼內部,我們可以通過 $this 來訪問當前控制器例項

用以下 推送 的方式傳遞資料到檢視裡:

$this->render('edit', array(
    'varName1'=>$value1,
    'varName2'=>$value2,
));
在以上的方式中, render() 方法將提取陣列的第二個引數到變數裡.其產生的結果是,在檢視指令碼里,我們可以直接訪問變數 $varName1和 $varName2.

佈局

1.佈局是一種用來修飾檢視的特殊的檢視檔案.它通常包含了使用者介面中通用的一部分檢視.例如:佈局可以包含header和footer的部分,然後把內容嵌入其間.
其中的 $content 則儲存了內容檢視的渲染結果.
2.當使用 render() 時,佈局被隱式應用.檢視指令碼 protected/views/layouts/main.php 是預設的佈局檔案.這可以通過改變 CWebApplication::layout 或者 CWebApplication::layout 進行自定義。
3.渲染一個不帶佈局的檢視,則需呼叫 renderPartial()

小物件

小物件是 CWidget 或其子類的例項.它是一個主要用於表現資料的元件.小物件通常內嵌於一個檢視來產生一些複雜而獨立的使用者介面.例如,一個日曆小物件可用於渲染一個複雜的日曆介面.
小物件使使用者介面更加可複用

按如下檢視指令碼來使用一個小物件:

<?php $this->beginWidget('path.to.WidgetClass'); ?>
...可能會由小物件獲取的內容主體...
<?php $this->endWidget(); ?>

或者

<?php $this->widget('path.to.WidgetClass'); ?>

後者用於不需要任何 body 內容的元件.

小物件可通過配置來定製它的表現.這是通過呼叫 CBaseController::beginWidget 或 CBaseController::widget 設定其初始化屬性值來完成的.例如,當使用 CMaskedTextField 小物件時,我們想指定被使用的 mask (可理解為一種輸出格式,譯者注).我們通過傳遞一個攜帶這些屬性初始化值的陣列來實現.這裡的陣列的鍵是屬性的名稱,而陣列的值則是小物件屬性所對應的值.正如以下所示 :

<?php$this->widget('CMaskedTextField',array(
    'mask'=>'99/99/9999'));
?>

繼承 CWidget 並覆蓋其init() 和 run() 方法,可以定義一個新的小物件:

class MyWidget extends CWidget{
    public function init()
    {
        // 此方法會被 CController::beginWidget() 呼叫
    }
 
    public function run()
    {
        // 此方法會被 CController::endWidget() 呼叫
    }}

小物件可以像一個控制器一樣擁有它自己的檢視.預設情況下,小物件的檢視檔案位於包含了小物件類檔案目錄的 views 子目錄之下.這些檢視可以通過呼叫 CWidget::render() 渲染,這一點和控制器很相似.唯一不同的是,小物件的檢視沒有佈局檔案支援。另外,小物件檢視中的$this指向小物件例項而不是控制器例項。

系統檢視

系統檢視的渲染通常用於展示 Yii 的錯誤和日誌資訊.例如,當使用者請求來自一個不存在的控制器或動作時,Yii 會丟擲一個異常來解釋這個錯誤. 這時,Yii 就會使用一個特殊的系統檢視來顯示此錯誤
在 framework/views 下, Yii 提供了一系列預設的系統檢視. 他們可以通過在 protected/views/system 下建立同名檢視檔案進行自定義

元件

Yii 應用建立於元件之上。
元件是 CComponent 或其子類的例項。
使用元件主要涉及訪問它的屬性以及觸發或處理它的時間
基類 CComponent 指定了如何定義屬性和事件

元件屬性

元件的屬性就像物件的公共成員變數。它是可讀寫的。例如:

$width=$component->textWidth;     // 獲取 textWidth 屬性$component->enableCaching=true;   // 設定 enableCaching 屬性

定義一個元件屬性: 只需在元件類中定義一個公共成員變數即可。更靈活的方式是定義其 getter 和 setter 方法,例如:

public function getTextWidth(){
    return $this->_textWidth;
}
 
public function setTextWidth($value){
    $this->_textWidth=$value;
}
上述程式碼定義了一個可寫的屬性名為 textWidth (名字是大小寫不敏感的)。 當讀取屬性時,getTextWidth() 就會被呼叫,其返回值則成為屬性值;相似的, 當寫入屬性時,setTextWidth() 被呼叫。如果 setter 方法沒有定義,則屬性將是隻讀的, 如果對其寫入則會丟擲一個異常。使用 getter 和 setter 方法定義一個屬性有一個好處:即當讀取或寫入屬性時, 可以執行額外的邏輯(例如,執行驗證,觸發事件)。

元件事件

元件事件是一些特殊的屬性,它們使用一些稱作 事件控制程式碼 (event handlers)的方法作為其值。 分配一個方法到一個事件將會引起方法在事件被喚起處自動被呼叫。因此, 一個元件的行為可能會被一種在部件開發過程中不可預見的方式修改。

元件事件以 on 開頭的命名方式定義。和屬性通過 getter/setter 方法來定義的命名方式一樣, 事件的名稱是大小寫不敏感的。以下程式碼定義了一個 onClicked 事件:

public function onClicked($event){
    $this->raiseEvent('onClicked', $event);
}

這裡作為事件引數的 $event 是 CEvent 或其子類的例項。

我們可以附加一個方法到此 event,如下所示:

$component->onClicked=$callback;

這裡的 $callback 指向了一個有效的 PHP 回撥。它可以是一個全域性函式也可以是類中的一個方法。 如果是後者,它必須以一個陣列的方式提供: array($object,'methodName').

事件控制程式碼的結構如下:

function methodName($event){
    ......
}

這裡的 $event 即描述事件的引數(它來源於 raiseEvent() 呼叫)。$event 引數是 CEvent 或其子類的例項。 至少,它包含了關於誰觸發了此事件的資訊。

從版本 1.0.10 開始,事件控制程式碼也可以是一個PHP 5.3以後支援的匿名函式。例如,

$component->onClicked=function($event) {
    ......
}

如果我們現在呼叫 onClicked()onClicked 事件將被觸發(在 onClicked() 中), 附屬的事件控制程式碼將被自動呼叫。

一個事件可以繫結多個控制程式碼。當事件觸發時, 這些控制程式碼將被按照它們繫結到事件時的順序依次執行。如果控制程式碼決定組織後續控制程式碼被執行,它可以設定 $event->handled 為 true

元件行為


從版本 1.0.2 開始,元件已新增了對 mixin 的支援,並可以繫結一個或多個行為。 
行為 是一個物件,其方法可以被它繫結的部件通過收集功能的方式來實現 繼承(inherited), 而不是專有化繼承(即普通的類繼承).
一個部件可以以'多重繼承'的方式實現多個行為的繫結.

行為類必須實現 IBehavior 介面。 大多數行為可以繼承自 CBehavior 
如果一個行為需要繫結到一個 模型, 它也可以從專為模型實現繫結特性的 CModelBehavior 或 CActiveRecordBehavior 繼承。

要使用一個行為,它必須首先通過呼叫此行為的 attach() 方法繫結到一個元件。然後我們就可以通過元件呼叫此行為方法:

// $name 在元件中實現了對行為的唯一識別$component->attachBehavior($name,$behavior);
// test() 是行為中的方法。$component->test();

已繫結的行為可以像一個元件中的普通屬性一樣訪問。 例如,如果一個名為 tree 的行為繫結到了一個元件,我們就可以通過如下程式碼獲得指向此行為的引用

$behavior=$component->tree;
// 等於下行程式碼:// $behavior=$component->asa('tree');

行為是可以被臨時禁止的,此時它的方法開就會在元件中失效.例如:

$component->disableBehavior($name);
// 下面的程式碼將丟擲一個異常$component->test();
$component->enableBehavior($name);
// 現在就可以使用了$component->test();

兩個同名行為繫結到同一個元件下是有可能的.在這種情況下,先繫結的行為則擁有優先權.

當和 events, 一起使用時,行為會更加強大. 當行為被繫結到元件時,行為裡的一些方法就可以繫結到元件的一些事件上了. 這樣一來,行為就有機觀察或者改變元件的常規執行流程.

自版本 1.1.0 開始,一個行為的屬性也可以通過繫結到的元件來訪問。 這些屬性包含公共成員變數以及通過 getters 和/或 setters 方式設定的屬性。 例如, 若一個行為有一個 xyz 的屬性,此行為被繫結到元件 $a,然後我們可以使用表示式 $a->xyz 訪問此行為的屬性。

模組

模組是一個獨立的軟體單元,它包含 模型檢視控制器 和其他支援的元件。 
模組在一些場景裡很有用。對大型應用來說,我們可能需要把它劃分為幾個模組,每個模組可以單獨維護和部署。一些通用的功能,例如使用者管理, 評論管理,可以以模組的形式開發,這樣他們就可以容易地在以後的專案中被複用。

建立模組

模組組織在一個目錄中,目錄的名字即模組的唯一 ID

模組的典型的目錄結構:

forum/
   ForumModule.php            模組類檔案
   components/                包含可複用的使用者元件
      views/                  包含小物件的檢視檔案
   controllers/               包含控制器類檔案
      DefaultController.php   預設的控制器類檔案
   extensions/                包含第三方擴充套件
   models/                    包含模組類檔案
   views/                     包含控制器檢視和佈局檔案
      layouts/                包含佈局檔案
      default/                包含 DefaultController 的檢視檔案
         index.php            首頁檢視檔案
.............

路徑別名與名字空間

Yii 中廣泛的使用了路徑別名。路徑別名關聯於一個目錄或檔案的路徑。它以點號語法指定
通過使用 YiiBase::getPathOfAlias(), 別名可以被翻譯為其相應的路徑。 例如, system.web.CController 會被翻譯為yii/framework/web/CController
通過呼叫 YiiBase::setPathOfAlias(),我們可以定義新的根路徑別名。

Root Alias

為方便起見,Yii 預定義了以下幾個根別名:
  • system: 表示 Yii 框架目錄;
  • zii: 表示 Zii 庫 目錄;
  • application: 表示應用的 基礎目錄
  • webroot: 表示 入口指令碼 檔案所在的目錄。此別名從版本 1.0.3 開始有效
  • ext: 表示包含了所有第三方 擴充套件 的目錄。此別名從版本 1.0.8 開始有效
通過使用 YiiBase::getPathOfAlias(), 別名可以被翻譯為其相應的路徑。 例如, system.web.CController 會被翻譯為yii/framework/web/CController

Importing Classes

使用別名可以很方便的匯入類的定義。 例如,如果我們想包含 CController 類的定義,我們可以呼叫如下程式碼

Yii::import('system.web.CController');
import 方法跟 include 和 require 不同,它更加高效。 匯入(import)的類定義並不會真正被包含進來,直到它第一次被引用。 多次匯入同樣的名字空間也會比 include_once 和 require_once 快得多

使用Class Map

使用Class Map


從1.1.5版本開始,Yii允許使用者定義的類通過使Class Map機制來預先匯入,這也是Yii內建類使用的方法。 預先引入機制可以在Yii應用的任何地方使用,無需顯式地匯入或者包含檔案。

若要使用預匯入功能,要在CWebApplication::run()執行前執行下面的程式碼:

Yii::$classMap=array(
    'ClassName1' => 'path/to/ClassName1.php',
    'ClassName2' => 'path/to/ClassName2.php',
    ......
);

匯入目錄

使用如下語法匯入整個目錄,這樣此目錄下的類檔案就會在需要時被自動包含。

Yii::import('system.web.*');

Namespace

不要將路徑別名和名字空間混淆了,名字空間是指對一些類名的一個邏輯組合,這樣它們就可以相互區分開,即使有相同的名字

使用名稱空間的類

若要自動匯入使用名稱空間的類,名稱空間的格式必須和路徑別名相似。
比如說,類application\components\GoogleMap 所對應的路徑必須和別名:
application.components.GoogleMap一致。

開發規範

1.Yii 偏愛規範勝於配置。遵循規範可使你能夠建立成熟的Yii應用而不需要編寫、維護複雜的配置。 
2.Yii 仍然可以在幾乎所有的方面通過配置實現自定義

Yii 程式設計中推薦的開發規範。 為簡單起見,我們假設 WebRoot 是 Yii 應用安裝的目錄。

URL

預設情況下,Yii 識別如下格式的 URL:

http://hostname/index.php?r=ControllerID/ActionID
1.r GET 變數意為 路由(route) ,它可以被Yii解析為 控制器和動作。 
2.如果 ActionID 被省略,控制器將使用預設的動作(在CController::defaultAction中定義); 
3.如果 ControllerID 也被省略(或者 r 變數不存在),應用將使用預設的控制器 (在CWebApplication::defaultController中定義)。
4.通過 CUrlManager 的幫助,可以建立更加可識別,更加 SEO 友好的 URL,
   例如http://hostname/ControllerID/ActionID.html

程式碼

1.命名變數、函式和類時使用 駝峰風格,即每個單詞的首字母大寫並連在一起,中間無空格。 變數名和函式名應該使它們的第一個單詞全部小寫,以使其區別於類名(例如:$basePathrunController()LinkPager)。對私有類成員變數來說,我們推薦以下劃線作為其名字字首(例如: $_actionList

2. 一個針對控制器名字的特殊規則是它們必須以單詞 Controller 結尾。那麼控制器 ID 即類名的首字母小寫並去掉單詞Controller。 例如,PageController 類的 ID 就是 page     

配置

配置是一個鍵值對陣列。每個鍵代表了所配置的物件中的屬性名,每個值則為相應屬性的初始值。 例如,array('name'=>'My application', 'basePath'=>'./protected') 初始化了 name 和 basePath 屬性為它們相應的陣列值

檔案

命名和使用檔案的規範取決於它們的型別。

類檔案應以它們包含的公有類命名。例如, CController 類位於 CController.php 檔案中
檢視檔案應以檢視的名字命名。例如, index 檢視位於 index.php 檔案中。 檢視檔案是一個PHP指令碼檔案,它包含了用於呈現內容的 HTML和PHP程式碼
配置檔案可以任意命名。 配置檔案是一個PHP指令碼,它的主要目的是返回一個體現配置的關聯陣列

目錄

Yii 假定了一系列預設的目錄用於不同的場合。如果需要,每個目錄都可以自定義

  • WebRoot/protected這是 應用基礎目錄, 是放置所有安全敏感的PHP指令碼和資料檔案的地方。Yii 有一個預設的application 別名指向此目錄。 此目錄及目錄中的檔案應該保護起來防止Web使用者訪問。它可以通過CWebApplication::basePath 自定義。

  • WebRoot/protected/runtime: 此目錄放置應用在執行時產生的私有臨時檔案。 此目錄必須對 Web 伺服器程式可寫。它可以通過 CApplication::runtimePath自定義。

  • WebRoot/protected/extensions: 此目錄放置所有第三方擴充套件。 它可以通過 CApplication::extensionPath 自定義。

  • WebRoot/protected/modules: 此目錄放置所有的應用 模組,每個模組使用一個子目錄。

  • WebRoot/protected/controllers: 此目錄放置所有控制器類檔案。 它可以通過CWebApplication::controllerPath 自定義。

  • WebRoot/protected/views: 此目錄放置所有試圖檔案, 包含控制器檢視,佈局檢視和系統檢視。 它可以通過CWebApplication::viewPath 自定義。

  • WebRoot/protected/views/ControllerID: 此目錄放置單個控制器類中使用的所有檢視檔案。 此處的ControllerID 是指控制器的 ID 。它可以通過 CController::viewPath 自定義。

  • WebRoot/protected/views/layouts: 此目錄放置所有佈局檢視檔案。它可以通過CWebApplication::layoutPath 自定義。

  • WebRoot/protected/views/system: 此目錄放置所有系統檢視檔案。 系統檢視檔案是用於顯示異常和錯誤的模板。它可以通過 CWebApplication::systemViewPath 自定義。

  • WebRoot/assets: 此目錄放置公共資原始檔。 資原始檔是可以被髮布的,可由Web使用者訪問的私有檔案。此目錄必須對 Web 伺服器程式可寫。 它可以通過 CAssetManager::basePath 自定義

  • WebRoot/themes: 此目錄放置應用使用的不同的主題。每個子目錄即一個主題,主題的名字即目錄的名字。 它可以通過 CThemeManager::basePath 自定義。

資料庫

多數Web 應用是由資料庫驅動的。 推薦在對錶和列命名時使用如下命名規範。

  • 資料庫表名和列名都使用小寫命名

  • 名字中的單詞應使用下劃線分割 (例如 product_order)。

  • 對於表名,推薦使用單數名字

  • 表名可以使用一個通用字首,例如 tbl_ 。這樣當應用所使用的表和另一個應用說使用的表共存於同一個資料庫中時就特別有用。 這兩個應用的表可以通過使用不同的表字首很容易地區別開。

開發流程

  1. 建立目錄結構骨架。

  2. 配置此 應用。通過修改應用配置檔案實現的。 此步驟可能也需要編寫一些應用元件(例如使用者元件)。

  3. 為每個型別的資料(表)建立一個 模型 類Gii 工具可以用於快速為每個資料表建立 active record 類

    每個型別的使用者請求 建立一個 控制器 類。 具體如何對使用者請求歸類要看實際需求。
    總體來說,如果一個模型類需要被使用者訪問,就應該有一個相應的控制器類。 Gii 工具也可以自動實現這一步驟。
  4. 實現 動作 和他們相應的 檢視。 這是真正所需要做的工作。

  5. 在控制器類中配置必要的動作 過濾器

  6. 如果需要主題功能,建立 主題 。

  7. 如果需要 國際化(I18N) ,建立翻譯資訊。

  8. 對可快取的資料點和檢視點應用適當的 快取 技術。

  9. 最終 調整 與部署。

上述的每個步驟中,可能需要建立並執行測試用例。


==========================================================================================================================
                                                                                                                                  使用表單
==========================================================================================================================

通過HTML表單收集使用者資料是Web程式開發的主要工作之一。除了表單設計外, 開發者還需要將現存的或預設的資料填充到表單,驗證使用者輸入, 對無效的輸入顯示適當的錯誤資訊,儲存輸入到永續性儲存器。通過其 MVC 結構極大地簡化了此工作流程。

處理表單時,通常需要以下步驟:

  1. 建立用於表現所要收集資料欄位的模型類
  2. 建立一個控制器動作,響應表單提交。
  3. 檢視指令碼中建立與控制器動作相關的表單。

建立模型

在編寫表單所需的 HTML 程式碼之前,先確定來自終端使用者輸入的資料的型別,以及這些資料應符合什麼樣的規則。
模型類可用於記錄這些資訊,儲存使用者輸入和驗證這些輸入的中心位置

根據使用者所輸入資料的方式建立不同型別的模型。
1. 如果使用者輸入被收集、使用然後丟棄,我們應該建立一個 表單模型
2.如果使用者的輸入被收集後要儲存到資料庫,我們應使用一個 Active Record 。 兩種型別的模型共享同樣的基類 CModel ,它定義了表單所需的通用介面

定義模型類

建立了一個 LoginForm 模型類用於在一個登入頁中收集使用者的輸入。 由於登錄資訊只被用於驗證使用者,並不需要儲存,因此我們將 LoginForm 建立為一個 表單模型。

class LoginForm extends CFormModel{
    public $username;
    public $password;
    public $rememberMe=false;
}

LoginForm 中定義了三個屬性: $username$password 和 $rememberMe。用於儲存使用者輸入的使用者名稱和密碼,還有使用者是否想記住他的登入的選項。 由於 $rememberMe 有一個預設的值 false,相應的選項在初始化顯示在登入表單中時將是未勾選狀態。

宣告驗證規則

使用者提交了他的輸入,模型被填充,需要在使用前確保使用者的輸入是有效的。 通過將使用者的輸入和一系列規則執行驗證實現的。
我們在 rules() 方法中指定這些驗證規則, 此方法應返回一個規則配置陣列。
class LoginForm extends CFormModel{
    public $username;
    public $password;
    public $rememberMe=false;
 
    private $_identity;
 
    public function rules()
    {
        return array(
            array('username, password', 'required'),
            array('rememberMe', 'boolean'),
            array('password', 'authenticate'),
        );
    }
 
    public function authenticate($attribute,$params)
    {
        $this->_identity=new UserIdentity($this->username,$this->password);
        if(!$this->_identity->authenticate())
            $this->addError('password','錯誤的使用者名稱或密碼。');
    }}

上述程式碼指定:username 和 password 為必填項, password 應被驗證(authenticated),rememberMe 應該是一個布林值。

rules() 返回的每個規則必須是以下格式:

array('AttributeList', 'Validator', 'on'=>'ScenarioList', ...附加選項)

其中 AttributeList(特性列表) 是需要通過此規則驗證的特性列表字串,每個特性名字由逗號分隔;Validator(驗證器) 指定要執行驗證的種類;on 引數是可選的,它指定此規則應被應用到的場景列表; 附加選項是一個名值對陣列,用於初始化相應驗證器的屬性值。

有三種方式可在驗證規則中指定 Validator 。第一, Validator 可以是模型類中一個方法的名字,就像上面示例中的authenticate 。驗證方法必須是下面的結構:

/**
 * @param string 所要驗證的特性的名字
 * @param array 驗證規則中指定的選項
 */public function ValidatorName($attribute,$params) { ... }

第二,Validator 可以是一個驗證器類的名字,當此規則被應用時, 一個驗證器類的例項將被建立以執行實際驗證。規則中的附加選項用於初始化例項的屬性值。 驗證器類必須繼承自 CValidator

第三,Validator 可以是一個預定義的驗證器類的別名。在上面的例子中, required 名字是 CRequiredValidator 的別名,它用於確保所驗證的特性值不為空。 下面是預定義的驗證器別名的完整列表:



==========================================================================================================================
                                                                                                                                 使用資料庫
==========================================================================================================================
Yii資料訪問物件(DAO)建立在PHP的資料物件(PDO)extension上
使得在一個單一的統一的介面可以訪問不同的資料庫管理系統(DBMS)
Yii 的Active Record( AR ),實現了被廣泛採用的物件關係對映(ORM)辦法,簡化資料庫程式設計。按約定,一個類代表一個表,一個例項代表一行資料
Yii AR消除了大部分用於處理CRUD(建立,讀取,更新和刪除)資料操作的sql語句的重複任務

1.資料訪問物件 (DAO)

資料訪問物件(DAO) 對訪問儲存在不同資料庫管理系統(DBMS)中的資料提供了一個通用的API。 因此,在將底層 DBMS 更換為另一個時,無需修改使用了 DAO 訪問資料的程式碼。
Yii DAO 基於 PHP Data Objects (PDO) 構建。

Yii DAO 主要包含如下四個類:

建立資料庫連線

1.建立一個資料庫連線,建立一個 CDbConnection 例項並將其啟用。 連線到資料庫需要一個資料來源的名字(DSN)以指定連線資訊。
2.使用者名稱和密碼也可能會用到。 當連線到資料庫的過程中發生錯誤時 (例如,錯誤的 DSN 或無效的使用者名稱/密碼),將會丟擲一個異常

3.DSN 的格式取決於所使用的 PDO 資料庫驅動。總體來說, DSN 要含有 PDO 驅動的名字,跟上一個冒號,再跟上驅動特定的連線語法。下面是一個常用DSN格式的列表。

  • SQLite: sqlite:/path/to/dbfile
  • MySQL: mysql:host=localhost;dbname=testdb
  • PostgreSQL: pgsql:host=localhost;port=5432;dbname=testdb
  • SQL Server: mssql:host=localhost;dbname=testdb
  • Oracle: oci:dbname=//localhost:1521/testdb

由於 CDbConnection 繼承自 CApplicationComponent,我們也可以將其作為一個 應用元件 使用。要這樣做的話, 請在 應用配置 中配置一個 db (或其他名字)應用元件如下:

array(
    ......
    'components'=>array(
        ......
        'db'=>array(
            'class'=>'CDbConnection',
            'connectionString'=>'mysql:host=localhost;dbname=testdb',
            'username'=>'root',
            'password'=>'password',
            'emulatePrepare'=>true,  // needed by some MySQL installations
        ),
    ),
)
可以通過 Yii::app()->db 訪問資料庫連線了。它已經被自動啟用了
通過這種方式,這個單獨的DB連線就可以在我們程式碼中的很多地方共享

執行 SQL 語句

資料庫連線建立後,SQL 語句就可以通過使用 CDbCommand 執行了。你可以通過使用指定的SQL語句作為引數呼叫CDbConnection::createCommand() 建立一個 CDbCommand 例項。

$connection=Yii::app()->db;   // 假設你已經建立了一個 "db" 連線// 如果沒有,你可能需要顯式建立一個連線:// $connection=new CDbConnection($dsn,$username,$password);$command=$connection->createCommand($sql);
// 如果需要,此 SQL 語句可通過如下方式修改:// $command->text=$newSQL;

一條 SQL 語句會通過 CDbCommand 以如下兩種方式被執行:

  • execute(): 執行一個無查詢 (non-query)SQL語句, 例如 INSERTUPDATE 和 DELETE 。如果成功,將返回此執行所影響的行數

  • query(): 執行一條會返回若干行資料的 SQL 語句,例如 SELECT。 如果成功,它將返回一個 CDbDataReader 例項,通過此例項可以遍歷資料的結果行。為簡便起見, (Yii)還實現了一系列 queryXXX() 方法以直接返回查詢結果。

$rowCount=$command->execute();   // 執行無查詢 SQL$dataReader=$command->query();   // 執行一個 SQL 查詢$rows=$command->queryAll();      // 查詢並返回結果中的所有行$row=$command->queryRow();       // 查詢並返回結果中的第一行$column=$command->queryColumn(); // 查詢並返回結果中的第一列$value=$command->queryScalar();  // 查詢並返回結果中第一行的第一個欄位

獲取查詢結果

在 CDbCommand::query() 生成 CDbDataReader 例項之後,可以通過重複呼叫 CDbDataReader::read() 獲取結果中的行。你也可以在 PHP 的 foreach 語言結構中使用 CDbDataReader 一行行檢索資料。

$dataReader=$command->query();
// 重複呼叫 read() 直到它返回 falsewhile(($row=$dataReader->read())!==false) { ... }// 使用 foreach 遍歷資料中的每一行foreach($dataReader as $row) { ... }// 一次性提取所有行到一個陣列$rows=$dataReader->readAll();
注意: 不同於 query(), 所有的 queryXXX() 方法會直接返回資料。 例如, queryRow() 會返回代表查詢結果第一行的一個陣列。

使用事務

當一個應用要執行幾條查詢,(每條查詢要從資料庫中讀取/或向資料庫中寫入資訊時) 保證所有sql語句都執行很重要。 事務,在 Yii 中表現為 CDbTransaction 例項,可能會在下面的情況中啟動:

  • 開始事務.
  • 一個個執行查詢。任何對資料庫的更新對外界不可見。
  • 提交事務。如果事務成功,更新變為可見。
  • 如果查詢中的一個失敗,整個事務回滾。

上述工作流可以通過如下程式碼實現:

$transaction=$connection->beginTransaction();
try{
    $connection->createCommand($sql1)->execute();
    $connection->createCommand($sql2)->execute();
    //.... other SQL executions
    $transaction->commit(); //提交事務}catch(Exception $e){
    $transaction->rollBack();//回滾事務}

繫結引數(即是變數)

要避免 SQL 注入攻擊 並提高重複執行的 SQL 語句的效率, 你可以 "準備(prepare)"一條含有可選引數佔位符的 SQL 語句,在引數繫結時,這些佔位符將被替換為實際的引數。

引數佔位符可以是命名的 (表現為一個唯一的標記) 或未命名的 (表現為一個問號)。呼叫 CDbCommand::bindParam() 或CDbCommand::bindValue() 以使用實際引數替換這些佔位符。 這些引數不需要使用引號引起來:底層的資料庫驅動會為你搞定這個。 引數繫結必須在 SQL 語句執行之前完成。


// 一條帶有兩個佔位符 ":username" 和 ":email"的 SQL
$sql="INSERT INTO tbl_user (username, email) VALUES(:username,:email)";
                                         $command=$connection->createCommand($sql);
// 用實際的使用者名稱替換佔位符 ":username"      $command->bindParam(":username",$username,PDO::PARAM_STR);
// 用實際的 Email 替換佔位符 ":email"       $command->bindParam(":email",$email,PDO::PARAM_STR);
                                          $command->execute();
// 使用新的引數集插入另一行                  $command->bindParam(":username",$username2,PDO::PARAM_STR);
$command->bindParam(":email",$email2,PDO::PARAM_STR);
$command->execute();

方法 bindParam() 和 bindValue() 非常相似。唯一的區別就是前者使用一個 PHP 變數繫結引數, 而後者使用一個值。對於那些記憶體中的大資料塊引數,處於效能的考慮,應優先使用前者。

繫結列

當獲取查詢結果時,你也可以使用 PHP 變數繫結列。 這樣在每次獲取查詢結果中的一行時就會自動使用最新的值填充。

$sql="SELECT username, email FROM tbl_user";
$dataReader=$connection->createCommand($sql)->query();
// 使用 $username 變數繫結第一列 (username)   $dataReader->bindColumn(1,$username);// 使用 $email 變數繫結第二列 (email) $dataReader->bindColumn(2,$email);
while($dataReader->read()!==false){
    // $username 和 $email 含有當前行中的 username 和 email }

使用表字首(區分作用)

從版本 1.1.0 起, Yii 提供了整合了對使用表字首的支援。 表字首是指在當前連線的資料庫中的資料表的名字前面新增的一個字串。 它常用於共享的伺服器環境,這種環境中多個應用可能會共享同一個資料庫,要使用不同的表字首以相互區分。 例如,一個應用可以使用 tbl_ 作為表字首而另一個可以使用 yii_

要使用表字首,配置 CDbConnection::tablePrefix 屬性為所希望的表字首。 然後,在 SQL 語句中使用 {{TableName}} 代表表的名字,其中的 TableName 是指不帶字首的表名。 例如,如果資料庫含有一個名為 tbl_user 的表,而 tbl_ 被配置為表字首,那我們就可以使用如下程式碼執行使用者相關的查詢:

$sql='SELECT * FROM {{user}}';  //對應表tbl_user $users=$connection->createCommand($sql)->queryAll();


2.Active Record

1.Active Record (AR) 是一個流行的 物件-關係對映 (ORM) 技術
2.每個 AR 類代表一個資料表(或檢視),資料表(或檢視)的列在 AR 類中體現為類的屬性,一個 AR 例項則表示表中的一行。 
3.常見的 CRUD 操作作為 AR 的方法實現更加物件導向的方式訪問資料。

簡單起見,我們使用下面的資料表作為此節中的例子。注意,如果你使用 MySQL 資料庫,你應該將下面的 SQL 中的 AUTOINCREMENT 替換為 AUTO_INCREMENT

CREATE TABLE tbl_post (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    title VARCHAR(128) NOT NULL,
    content TEXT NOT NULL,
    create_time INTEGER NOT NULL);
如果你想使用一個不是 db 的應用元件,或者如果你想使用 AR 處理多個資料庫,你應該覆蓋CActiveRecord::getDbConnection()。 CActiveRecord 類是所有 AR 類的基類。

定義 AR 類

要訪問一個資料表,我們首先需要通過整合 CActiveRecord 定義一個 AR 類。 每個 AR 類代表一個單獨的資料表,一個 AR 例項則代表那個表中的一行。 如下例子演示了代表 tbl_post 表的 AR 類的最簡程式碼:

class Post extends CActiveRecord{
    public static function model($className=__CLASS__)
    {
        return parent::model($className);
    }
 //認情況下,AR 類的名字和資料表的名字相同。如果不同,請覆蓋 tableName() 方法。 model() 方法為每個 AR 類宣告為如此(稍後解釋)
    public function tableName()
    {
        return 'tbl_post';
    }}
 例如,我們可以使用以下程式碼向 tbl_post 表中插入一個新行
$post=new Post;
//從未在 Post 類中顯式定義屬性 title,但通過上述程式碼訪問。 因為 titletbl_post 表中的一個列,CActiveRecord 通過PHP的 __get() 魔術方法使其成為一個可訪問的屬性$post->title='sample post';  //如果我們嘗試以同樣的方式訪問一個不存在的列,將會丟擲一個異常$post->content='post body content';
$post->save();

AR 依靠表中良好定義的主鍵。如果一個表沒有主鍵,則必須在相應的 AR 類中通過如下方式覆蓋 primaryKey() 方法指定哪一列或哪幾列作為主鍵。

public function primaryKey(){
    return 'id';
    // 對於複合主鍵,要返回一個類似如下的陣列
    // return array('pk1', 'pk2');}

建立記錄

要向資料表中插入新行,我們要建立一個相應 AR 類的例項,設定其與表的列相關的屬性,然後呼叫 save() 方法完成插入:

$post=new Post;
$post->title='sample post';
$post->content='content for the sample post';
$post->create_time=time();
$post->save();

如果表的主鍵是自增的,在插入完成後,AR 例項將包含一個更新的主鍵。在上面的例子中, id 屬性將反映出新插入帖子的主鍵值,即使我們從未顯式地改變它。

如果一個列在表結構中使用了靜態預設值(例如一個字串,一個數字)定義。則 AR 例項中相應的屬性將在此例項建立時自動含有此預設值。改變此預設值的一個方式就是在 AR 類中顯示定義此屬性:

class Post extends CActiveRecord{
    public $title='please enter a title';
    ......
}
 
$post=new Post;
echo $post->title;  // 這兒將顯示: please enter a title

從版本 1.0.2 起,記錄在儲存(插入或更新)到資料庫之前,其屬性可以賦值為 CDbExpression 型別。 例如,為儲存一個由 MySQL 的 NOW() 函式返回的時間戳,我們可以使用如下程式碼:

$post=new Post;
$post->create_time=new CDbExpression('NOW()');
// $post->create_time='NOW()'; 不會起作用,因為// 'NOW()' 將會被作為一個字串處理。$post->save();

提示: 由於 AR 允許我們無需寫一大堆 SQL 語句就能執行資料庫操作, 我們經常會想知道 AR 在背後到底執行了什麼 SQL 語句。這可以通過開啟 Yii 的 日誌功能 實現。例如,我們在應用配置中開啟了 CWebLogRoute ,我們將會在每個網頁的最後看到執行過的 SQL 語句。 從 1.0.5 版本起,我們可以在應用配置中設定CDbConnection::enableParamLogging 為 true ,這樣繫結在 SQL 語句中的引數值也會被記錄。

讀取記錄

條件的結果中的第一行$post=Post::model()->find($condition,$params);

// 查詢具有指定主鍵值的那一行                      $post=Post::model()->findByPk($postID,$condition,$params);
// 查詢具有指定屬性值的行                          $post=Post::model()->findByAttributes($attributes,$condition,$params);
// 通過指定的 SQL 語句查詢結果中的第一行            $post=Post::model()->findBySql($sql,$params);

如上所示,通過 Post::model() 呼叫 find 方法。 靜態方法 model() 是每個 AR 類所必須的。 此方法返回在物件上下文中的一個用於訪問類級別方法(類似於靜態類方法的東西)的 AR 例項。

如果 find 方法找到了一個滿足查詢條件的行,它將返回一個 Post 例項,例項的屬性含有資料錶行中相應列的值。 然後就可以像讀取普通物件的屬性那樣讀取載入的值,例如 echo $post->title;

如果使用給定的查詢條件在資料庫中沒有找到任何東西, find 方法將返回 null 。

呼叫 find 時,我們使用 $condition 和 $params 指定查詢條件。此處 $condition 可以是 SQL 語句中的 WHERE 字串,$params 則是一個引數陣列,其中的值應繫結到 $condation 中的佔位符。例如:

// 查詢 postID=10 的那一行          $post=Post::model()->find('postID=:postID', array(':postID'=>10));

注意: 在上面的例子中,我們可能需要在特定的 DBMS 中將 postID 列的引用進行轉義。 例如,如果我們使用 PostgreSQL,我們必須將此表示式寫為 "postID"=:postID,因為 PostgreSQL 在預設情況下對列名大小寫不敏感。

我們也可以使用 $condition 指定更復雜的查詢條件。 不使用字串,我們可以讓 $condition 成為一個 CDbCriteria 的例項,它允許我們指定不限於 WHERE 的條件。 例如:

$criteria=new CDbCriteria;
$criteria->select='title';  // 只選擇 'title' 列$criteria->condition='postID=:postID';
$criteria->params=array(':postID'=>10);
$post=Post::model()->find($criteria); // $params 不需要了

注意,當使用 CDbCriteria 作為查詢條件時,$params 引數不再需要了,因為它可以在 CDbCriteria 中指定,就像上面那樣。

一種替代 CDbCriteria 的方法是給 find 方法傳遞一個陣列。 陣列的鍵和值各自對應標準(criterion)的屬性名和值,上面的例子可以重寫為如下:

$post=Post::model()->find(array(
    'select'=>'title',
    'condition'=>'postID=:postID',
    'params'=>array(':postID'=>10),
));

資訊: 當一個查詢條件是關於按指定的值匹配幾個列時,我們可以使用 findByAttributes()。我們使 $attributes引數是一個以列名做索引的值的陣列。在一些框架中,此任務可以通過呼叫類似 findByNameAndTitle 的方法實現。雖然此方法看起來很誘人, 但它常常引起混淆,衝突和比如列名大小寫敏感的問題。

當有多行資料匹配指定的查詢條件時,我們可以通過下面的 findAll 方法將他們全部帶回。 每個都有其各自的 find 方法,就像我們已經講過的那樣。

// 查詢滿足指定條件的所有行$posts=Post::model()->findAll($condition,$params);
// 查詢帶有指定主鍵的所有行$posts=Post::model()->findAllByPk($postIDs,$condition,$params);
// 查詢帶有指定屬性值的所有行$posts=Post::model()->findAllByAttributes($attributes,$condition,$params);
// 通過指定的SQL語句查詢所有行$posts=Post::model()->findAllBySql($sql,$params);

如果沒有任何東西符合查詢條件,findAll 將返回一個空陣列。這跟 find 不同,find 會在沒有找到什麼東西時返回 null。

除了上面講述的 find 和 findAll 方法,為了方便,(Yii)還提供瞭如下方法:

// 獲取滿足指定條件的行數$n=Post::model()->count($condition,$params);
// 通過指定的 SQL 獲取結果行數$n=Post::model()->countBySql($sql,$params);
// 檢查是否至少有一行復合指定的條件$exists=Post::model()->exists($condition,$params);

更新記錄

在 AR 例項填充了列的值之後,我們可以改變它們並把它們存回資料表。

$post=Post::model()->findByPk(10);
$post->title='new post title';
$post->save(); // 將更改儲存到資料庫

正如我們可以看到的,我們使用同樣的 save() 方法執行插入和更新操作。 如果一個 AR 例項是使用 new 操作符建立的,呼叫save() 將會向資料表中插入一行新資料; 如果 AR 例項是某個 find 或 findAll 方法的結果,呼叫 save() 將更新表中現有的行。 實際上,我們是使用 CActiveRecord::isNewRecord 說明一個 AR 例項是不是新的。

直接更新資料表中的一行或多行而不首先載入也是可行的。 AR 提供瞭如下方便的類級別方法實現此目的:

// 更新符合指定條件的行Post::model()->updateAll($attributes,$condition,$params);
// 更新符合指定條件和主鍵的行Post::model()->updateByPk($pk,$attributes,$condition,$params);
// 更新滿足指定條件的行的計數列Post::model()->updateCounters($counters,$condition,$params);

在上面的程式碼中, $attributes 是一個含有以 列名作索引的列值的陣列; $counters 是一個由列名索引的可增加的值的陣列;$condition 和 $params 在前面的段落中已有描述。

刪除記錄

如果一個 AR 例項被一行資料填充,我們也可以刪除此行資料。

$post=Post::model()->findByPk(10); // 假設有一個帖子,其 ID 為 10$post->delete(); // 從資料表中刪除此行

注意,刪除之後, AR 例項仍然不變,但資料表中相應的行已經沒了。

使用下面的類級別程式碼,可以無需首先載入行就可以刪除它。

// 刪除符合指定條件的行Post::model()->deleteAll($condition,$params);
// 刪除符合指定條件和主鍵的行Post::model()->deleteByPk($pk,$condition,$params);

資料驗證

當插入或更新一行時,我們常常需要檢查列的值是否符合相應的規則。 如果列的值是由終端使用者提供的,這一點就更加重要。總體來說,我們永遠不能相信任何來自客戶端的資料。

當呼叫 save() 時, AR 會自動執行資料驗證。 驗證是基於在 AR 類的 rules() 方法中指定的規則進行的。 關於驗證規則的更多詳情,請參考 宣告驗證規則 一節。 下面是儲存記錄時所需的典型的工作流。

if($post->save()){
    // 資料有效且成功插入/更新}else{
    // 資料無效,呼叫  getErrors() 提取錯誤資訊}

當要插入或更新的資料由終端使用者在一個 HTML 表單中提交時,我們需要將其賦給相應的 AR 屬性。 我們可以通過類似如下的方式實現:

$post->title=$_POST['title'];
$post->content=$_POST['content'];
$post->save();

如果有很多列,我們可以看到一個用於這種複製的很長的列表。 這可以通過使用如下所示的 attributes 屬性簡化操作。 更多資訊可以在 安全的特性賦值 一節和 建立動作 一節找到。

// 假設 $_POST['Post'] 是一個以列名索引列值為值的陣列$post->attributes=$_POST['Post'];
$post->save();

對比記錄

類似於表記錄,AR 例項由其主鍵值來識別。 因此,要對比兩個 AR 例項,假設它們屬於相同的 AR 類, 我們只需要對比它們的主鍵值。 然而,一個更簡單的方式是呼叫 CActiveRecord::equals()

資訊: 不同於 AR 在其他框架的執行, Yii 在其 AR 中支援多個主鍵. 一個複合主鍵由兩個或更多欄位構成。相應地, 主鍵值在 Yii 中表現為一個陣列. primaryKey 屬性給出了一個 AR 例項的主鍵值。

自定義

CActiveRecord 提供了幾個佔位符方法,它們可以在子類中被覆蓋以自定義其工作流。

  • beforeValidate 和
  • beforeSave 和 afterSave: 這兩個將在儲存 AR 例項之前和之後被呼叫。

  • beforeDelete 和 afterDelete: 這兩個將在一個 AR 例項被刪除之前和之後被呼叫。

  • afterConstruct: 這個將在每個使用 new 操作符建立 AR 例項後被呼叫。

  • beforeFind: 這個將在一個 AR 查詢器被用於執行查詢(例如 find()findAll())之前被呼叫。 1.0.9 版本開始可用。

  • afterFind: 這個將在每個 AR 例項作為一個查詢結果建立時被呼叫。

使用 AR 處理事務

每個 AR 例項都含有一個屬性名叫 dbConnection ,是一個 CDbConnection 的例項,這樣我們可以在需要時配合 AR 使用由 Yii DAO 提供的 事務 功能:

$model=Post::model();
$transaction=$model->dbConnection->beginTransaction();
try{
    // 查詢和儲存是可能由另一個請求干預的兩個步驟
    // 這樣我們使用一個事務以確保其一致性和完整性
    $post=$model->findByPk(10);
    $post->title='new post title';
    $post->save();
    $transaction->commit();
}catch(Exception $e){
    $transaction->rollBack();
}

命名範圍

Note: 對命名範圍的支援從版本 1.0.5 開始。 命名範圍的最初想法來源於 Ruby on Rails.

命名範圍(named scope) 表示一個 命名的(named) 查詢規則,它可以和其他命名範圍聯合使用並應用於 Active Record 查詢。

命名範圍主要是在 CActiveRecord::scopes() 方法中以名字-規則對的方式宣告。 如下程式碼在 Post 模型類中宣告瞭兩個命名範圍, published 和 recently

class Post extends CActiveRecord{
    ......
    public function scopes()
    {
        return array(
            'published'=>array(
                'condition'=>'status=1',
            ),
            'recently'=>array(
                'order'=>'create_time DESC',
                'limit'=>5,
            ),
        );
    }}

每個命名範圍宣告為一個可用於初始化 CDbCriteria 例項的陣列。 例如,recently 命名範圍指定 order 屬性為create_time DESC , limit 屬性為 5。他們翻譯為查詢規則後就會返回最近的5篇帖子。

命名範圍多用作 find 方法呼叫的修改器。 幾個命名範圍可以鏈到一起形成一個更有約束性的查詢結果集。例如, 要找到最近釋出的帖子, 我們可以使用如下程式碼:

$posts=Post::model()->published()->recently()->findAll();

總體來說,命名範圍必須出現在一個 find 方法呼叫的左邊。 它們中的每一個都提供一個查詢規則,並聯合到其他規則, 包括傳遞給 find 方法呼叫的那一個。 最終結果就像給一個查詢新增了一系列過濾器。

從版本 1.0.6 開始,命名範圍也可用於 update 和 delete 方法。 例如,如下程式碼將刪除所有最近釋出的帖子:

Post::model()->published()->recently()->delete();

注意: 命名範圍只能用於類級別方法。也就是說,此方法必須使用 ClassName::model() 呼叫。

引數化的命名範圍

命名範圍可以引數化。例如, 我們想自定義 recently 命名範圍中指定的帖子數量,要實現此目的,不是在CActiveRecord::scopes 方法中宣告命名範圍, 而是需要定義一個名字和此命名範圍的名字相同的方法:

public function recently($limit=5){
    $this->getDbCriteria()->mergeWith(array(
        'order'=>'create_time DESC',
        'limit'=>$limit,
    ));
    return $this;
}

然後,我們就可以使用如下語句獲取3條最近釋出的帖子。

$posts=Post::model()->published()->recently(3)->findAll();

上面的程式碼中,如果我們沒有提供引數 3,我們將預設獲取 5 條最近釋出的帖子。

預設的命名範圍

模型類可以有一個預設命名範圍,它將應用於所有 (包括相關的那些) 關於此模型的查詢。例如,一個支援多種語言的網站可能只想顯示當前使用者所指定的語言的內容。 因為可能會有很多關於此網站內容的查詢, 我們可以定義一個預設的命名範圍以解決此問題。 為實現此目的,我們覆蓋 CActiveRecord::defaultScope 方法如下:

class Content extends CActiveRecord{
    public function defaultScope()
    {
        return array(
            'condition'=>"language='".Yii::app()->language."'",
        );
    }}

現在,如果下面的方法被呼叫,將會自動使用上面定義的查詢規則:

$contents=Content::model()->findAll();

注意,預設的命名範圍只會應用於 SELECT 查詢。INSERTUPDATE 和 DELETE 查詢將被忽略。














==========================================================================================================================
                                                                                                                                          快取
==========================================================================================================================





==========================================================================================================================
                                                                                                                                        擴充套件 Yii
==========================================================================================================================




==========================================================================================================================
                                                                                                                                        測試
==========================================================================================================================



==========================================================================================================================
                                                                                                                                      專題
==========================================================================================================================

驗證和授權

 驗證是指核查一個人是否真的是他自己所聲稱的那個人。這通常需要一個使用者名稱和密碼, 但也包括任何其他可以表明身份的方式,例如一個智慧卡,指紋等等。
 授權則是找出已通過驗證的使用者是否允許操作特定的資源。 這一般是通過查詢此使用者是否屬於一個有權訪問該資源的角色來判斷的。

Yii 有一個內建的驗證/授權(auth)框架,用起來很方便,還能對其進行自定義,使其符合特殊的需求

auth 框架的核心是一個預定義的 使用者(user)應用元件 它是一個實現了 IWebUser 介面的物件。 此使用者元件代表當前使用者的永續性認證資訊
可以通過Yii::app()->user在任何地方訪問
使用此使用者元件,我們可以通過 CWebUser::isGuest 檢查檢查一個使用者是否登陸
可以 登入(login) 或 登出(logout) 一個使用者
可以通過CWebUser::checkAccess檢查此使用者是否可以執行特定的操作
可以獲取此使用者的唯一標識(unique identifier)及其他永續性身份資訊

定義身份類

為了驗證一個使用者,我們定義一個有驗證邏輯的身份類。這個身份類實現IUserIdentity 介面

不同的類可能實現不同的驗證方式(例如:OpenID,LDAP)。最好是繼承 CUserIdentity,此類是居於使用者名稱和密碼的驗證方式。定義身份類的主要工作是實現IUserIdentity::authenticate方法。在使用者會話中根據需要,身份類可能需要定義別的身份資訊

使用Active Record來驗證提供的使用者名稱、密碼和資料庫的使用者表是否吻合。
通過重寫getId函式來返回驗證過程中獲得的_id變數(預設的實現則是返回使用者名稱)。
在驗證過程中,我們還藉助CBaseUserIdentity::setState函式把獲得的title資訊存成一個狀態













登入和登出

使用身份類和使用者部件,我們方便的實現登入和登出。

// 使用提供的使用者名稱和密碼登入使用者$identity=new UserIdentity($username,$password);
if($identity->authenticate())
    Yii::app()->user->login($identity);
else
    echo $identity->errorMessage;
......
// 登出當前使用者Yii::app()->user->logout();









基於Cookie 的登入







授權處理結果




訪問控制過濾器




概覽(Overview)







配置授權管理器





定義授權等級體系













使用業務規則










許可權檢查





使用預設角色





========================================================================================================================
                                                                                                                        關鍵word
========================================================================================================================
Yii::app()
Yii::app()  返回在index.php中建立的CWebApplication例項

主要負責一些全域性性的功能模組
比如Yii::app()->getUser()返回的是CWebUser例項(用於表達當前使用者的驗證資訊)
在index.php使用的配置檔案,其實質就是對Yii::app()進行屬性的初始化
關於CComponent的說明,這是Yii的基石
要訪問一個應用元件:
使用 Yii::app()->ComponentID ,其中的 ComponentID 是指元件的ID(例如Yii::app()->cache)。

========================================================================================================================
                                                                                                                      專題總結
========================================================================================================================


事務機制:
  1. $trans = Yii::app()->db->beginTransaction();  
  2. try {  
  3.     $manufacturer = new Manufacturer();   
  4.     $manufacturer->name = $name;  
  5.     $manufacturer->email = $email;  
  6.     $manufacturer->save();  
  7.     $trans->commit();  
  8. } catch (Exception $e) {  
  9.     $trans->rollback();  
  10.     $this->response(array('status' => 1, 'msg' => $e->getMessage()));     
  11. }  


$transaction=$connection->beginTransaction();
try{
    $connection->createCommand($sql1)->execute();
    $connection->createCommand($sql2)->execute();
    //.... other SQL executions
    $transaction->commit();
}catch(Exception $e) // 如果有一條查詢失敗,則會丟擲異常{
    $transaction->rollBack();
}
Yii思想關鍵字:
1.做專案
2.yii原始碼
3.UML圖 -->理解框架
4.MVC  ,元件,事件



雜談:
IAuthManager->createAuthItem
\
CApplicationComponent
\
 CAuthManager->createRole  call createAuthItem
 \
 CDbAuthManager  ->implents createAuthItem