Yii权威指南

1.创建YII

生成项目
我们将使用 yiic (命令行工具)创建一个新的 Yii 应用。Gii(强大的基于web的代码生成器)为特定的任务完成自动代码生成。假定 YiiRoot 为 Yii 的安装目录,WebRoot 是服务器的文档根目录。 
% YiiRoot/framework/yiic webapp WebRoot/testdrive 
这样我们就在WebRoot目录下面建立了一个基本的webapp项目,并且项目名称是testdrive。
目录结构
testdrive/
   index.php                 Web 应用入口脚本文件
   index-test.php            功能测试使用的入口脚本文件
   assets/                   包含公开的资源文件
   css/                      包含 CSS 文件
   images/                   包含图片文件
   themes/                   包含应用主题
   protected/                包含受保护的应用文件
      yiic                   yiic 命令行脚本
      yiic.bat               Windows 下的 yiic 命令行脚本
      yiic.php               yiic 命令行 PHP 脚本
      commands/              包含自定义的 'yiic' 命令
         shell/              包含自定义的 'yiic shell' 命令
      components/            包含可重用的用户组件
         Controller.php      所有控制器类的基础类
         Identity.php        用来认证的 'Identity' 类
      config/                包含配置文件
         console.php         控制台应用配置
         main.php            Web 应用配置
         test.php            功能测试使用的配置
      controllers/           包含控制器的类文件
         SiteController.php  默认控制器的类文件
      data/                  包含示例数据库
         schema.mysql.sql    示例 MySQL 数据库
         schema.sqlite.sql   示例 SQLite 数据库
         testdrive.db        示例 SQLite 数据库文件
      extensions/            包含第三方扩展
      messages/              包含翻译过的消息
      models/                包含模型的类文件
         LoginForm.php       'login' 动作的表单模型
         ContactForm.php     'contact' 动作的表单模型
      runtime/               包含临时生成的文件
      tests/                 包含测试脚本
      views/                 包含控制器的视图和布局文件
         layouts/            包含布局视图文件
            main.php         所有视图的默认布局
            column1.php      使用单列页面使用的布局
            column2.php      使用双列的页面使用的布局
         site/               包含 'site' 控制器的视图文件
            pages/           包含 "静态" 页面
               about.php     "about" 页面的视图
            contact.php      'contact' 动作的视图
            error.php        'error' 动作的视图(显示外部错误)
            index.php        'index' 动作的视图
            login.php        'login' 动作的视图
         system/             包含系统视图文件
连接数据库
修改应用的配置文件 WebRoot/testdrive/protected/config/main.php 即可,如下所示:
//连接到Mysql
'db'=>array(
               'connectionString' => 'mysql:host=localhost;dbname=yiitest',              
               'emulatePrepare' => true,
               'username' => 'root',
               'password' => 'xiaozhe',
               'charset' => 'utf8',
          ),
//连接到Sqlite
 'db'=>array(
            'connectionString'=>'sqlite:protected/data/source.db',
        ),
注意: 要使用 Yii 的数据库功能,我们需要启用 PHP 的 PDO 扩展和相应的驱动扩展。
Gii生成Model
我们可以使用Gii来建立一系列的CURD操作,首先开启Gii。
编辑文件 WebRoot/testdrive/protected/main.php找到modules里面的gii并进行设置。
然后,访问 URL http://hostname/testdrive/index.php?r=gii。这里我们需要输入密码,它是在我们在上面的配置中指定的。
比如现在我们进去之后,想要生成一个User Model,假设我们现在的表是tbl_user,则操作如下:
在 Table Name 输入框中,输入 tbl_user。在 Model Class 输入框中,输入 User。然后点击 Preview 按钮。这里将展示将要生成的新文件。现在点击 Generate 按钮。一个名为 User.php 将生成到 protected/models 目录中。如我们稍后描述的, User 模型类允许我们以面向对象的方式来访问数据表 tbl_user 。
Gii生成CURD
在创建模型类之后,我们将生成执行 CRUD 操作的代码。我们选择 Gii 中的 Crud Generator。
在 Model Class 输入框中,输入 User。在 Controller ID 输入框中,输入 user (小写格式)。现在点击 Generate 按钮后的 Preview 按钮。CRUD 代码生成完成了。

2.YII规范

Yii典型的工作流

下图展示了一个 Yii 应用在处理用户请求时典型的工作流。
Yii权威指南_第1张图片
 

应用或前端控制器
应用是指请求处理中的执行上下文。它的主要任务是分析用户请求并将其分派到合适的控制器中以作进一步处理。 它同时作为服务中心,维护应用级别的配置。鉴于此,应用也叫做前端控制器。
应用由 入口脚本 创建为一个单例对象。这个应用单例对象可以在任何地方通过 Yii::app() 访问。
应用配置
我们通常将应用配置放在protected/config/main.php中,里面我们可以自定义一些配置如:
array(
   'name'=>'Yii Framework',
   'defaultController'=>'site',
   ...
)
然后我们继续查看入口文件,可以看到加载Application的方法是读取此配置文件:
$config=dirname(__FILE__).'/protected/config/main.php'; 
Yii::createWebApplication($config)->run();
应用组件
通过配置应用的 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),
            ),
        ),
    ),
)
如上所示,我们在 components 数组中添加了 cache 元素。cache 元素表明此组件的类是 CMemCache, 他的 servers 属性应依此初始化。

要访问一个应用组件,使用 Yii::app()->ComponentID ,其中的 ComponentID 是指组件的ID(例如 Yii::app()->cache)。
应用的组件可以通过在其配置中设置 enabled 为 false 禁用。当我们访问被禁用的组件时将返回 Null。
注:默认情况下,应用组件会按需创建。这意味着一个应用的组件如果没有在一个用户请求中被访问,它可能根本不被创建。 因此,如果一个应用配置了很多组件,其总体性能可能并不会下降。有的应用组件 (例如 CLogRouter) 可能需要在无论它们是否被访问的情况下都要被创建。 要实现这个,需将其ID列在应用的 preload 属性里。
核心应用组件
我们可以在protected/config/main.php里面看到默认的一些核心应用组件。
  • assetManager: CAssetManager - 管理私有资源文件的发布。

  • authManager: CAuthManager - 管理基于角色的访问控制 (RBAC).

  • cache: CCache - 提供数据缓存功能。注意,你必须指定实际的类(例如CMemCache, CDbCache)。 否则,当你访问此组件时将返回 NULL。

  • clientScript: CClientScript - 管理客户端脚本 (javascripts 和 CSS).

  • coreMessages: CPhpMessageSource - 提供 Yii 框架用到的核心信息的翻译。

  • db: CDbConnection - 提供数据库连接。注意,使用此组件你必须配置其 connectionString 属性。

  • errorHandler: CErrorHandler - 处理未捕获的 PHP 错误和异常。

  • format: CFormatter - 格式化数值显示。此功能从版本 1.1.0 起开始提供。

  • messages: CPhpMessageSource - 提供Yii应用中使用的信息翻译。

  • request: CHttpRequest - 提供关于用户请求的信息。

  • securityManager: CSecurityManager - 提供安全相关的服务,例如散列,加密。

  • session: CHttpSession - 提供session相关的功能。

  • statePersister: CStatePersister - 提供全局状态持久方法。

  • urlManager: CUrlManager - 提供 URL 解析和创建相关功能

  • user: CWebUser - 提供当前用户的识别信息。

  • themeManager: CThemeManager - 管理主题。

控制器
控制器 是 CController 或其子类的实例。它在当用户请求时由应用创建。 当一个控制器运行时,它执行所请求的动作,动作通常会引入所必要的模型并渲染相应的视图。 动作 的最简形式,就是一个名字以 action 开头的控制器类方法。
我们可以在一个控制器里面设置一个默认的action方法,例如设置一个test为默认action.
class SiteController extends Controller
{
     ...
     public $defaultAction = 'test';
     ...
}
路由器大小写敏感
注意: 默认情况下,路由是大小写敏感的,从版本 1.0.1 开始,可以通过设置应用配置中的CUrlManager::caseSensitive 为 false 使路由对大小写不敏感。当在大小写不敏感模式中时, 要确保你遵循了相应的规则约定,即:包含控制器类文件的目录名小写,且 控制器映射 和 动作映射 中使用的键为小写。
控制器接收所有的请求
如果指定了 CWebApplication::catchAllRequest , 控制器将基于此属性创建, 而用户指定的控制器 ID 将被忽略。这通常用于将应用设置为维护状态并显示一个静态提示页面。
动作Action
如前文所述,动作可以被定义为一个以 action 单词作为前缀命名的方法。而更高级的方式是定义一个动作类并让控制器在收到请求时将其实例化。 这使得动作可以被复用,提高了可复用度。
要定义一个新动作类,可用如下代码:
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
动作Action参数绑定
从版本 1.1.4 开始,Yii 提供了对自动动作参数绑定的支持。 就是说,控制器动作可以定义命名的参数,参数的值将由 Yii 自动从 $_GET 填充。假设有2个参数,一个category和一个language。
public function actionCreate($category, $language='en')
    {
        // ... fun code starts here ...
    }
注意我们在动作方法 actionCreate 中添加了两个参数。 这些参数的名字必须和我们想要从 $_GET 中提取的名字一致。 当用户没有在请求中指定 $language 参数时,这个参数会使用默认值 en 。
过滤器
过滤器是一段代码,可被配置在控制器动作执行之前或之后执行。例如, 访问控制过滤器将被执行以确保在执行请求的动作之前用户已通过身份验证;性能过滤器可用于测量控制器执行所用的时间。
一个动作可以有多个过滤器。过滤器执行顺序为它们出现在过滤器列表中的顺序。过滤器可以阻止动作及后面其他过滤器的执行。
1.过滤器可以定义为一个控制器类的方法。方法名必须以 filter 开头。例如,现有的 filterAccessControl 方法定义了一个名为 accessControl 的过滤器。 过滤器方法必须为如下结构:
public function filterAccessControl($filterChain)
{
    // 调用 $filterChain->run() 以继续后续过滤器与动作的执行。
}
2.控制器可以说一个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 之外的动作。 如果过滤器配置中没有使用加减号,则此过滤器将被应用于所有动作。
模型Model
Yii 实现了两种类型的模型:表单模型和 Active Record。二者均继承于相同的基类 CModel。
表单模型是 CFormModel 的实例。表单模型用于保持从用户的输入获取的数据。 这些数据经常被获取,使用,然后丢弃。例如,在一个登录页面中, 我们可以使用表单模型用于表示由最终用户提供的用户名和密码信息。
Active Record (AR) 是一种用于通过面向对象的风格抽象化数据库访问的设计模式。 每个 AR 对象是一个 CActiveRecord 或其子类的实例。代表数据表中的一行。 行中的字段对应 AR 对象中的属性。
视图View
要渲染时,需通过传递视图的名称调用 CController::render()。下面将会渲染一个edit.php试图并传入了2个值:
$this->render('edit', array(
    'var1'=>$value1,
    'var2'=>$value2,
));
在以上的方式中, render() 方法将提取数组的第二个参数到变量里.其产生的结果是,在视图脚本里,我们可以直接访问变量 $var1 和 $var2.
布局
布局是一种用来修饰视图的特殊的视图文件.它通常包含了用户界面中通用的一部分视图.例如:布局可以包含header和footer的部分,然后把内容嵌入其间.
......header here......
<?php echo $content; ?>
......footer here......
其中的 $content 则储存了内容视图的渲染结果.
当使用 render() 时,布局被隐式应用.视图脚本 protected/views/layouts/main.php 是默认的布局文件.这可以通过改变 CWebApplication::layout 或者 CWebApplication::layout 进行自定义。要渲染一个不带布局的视图,则需调用 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 应用建立于组件之上。组件是 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').
组件行为
行为类必须实现  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();
模块
块是一个独立的软件单元,它包含  模型 视图 控制器  和其他支持的组件。 在许多方面上,模块看起来像一个  应用 。主要的区别就是模块不能单独部署,它必须存在于一个应用里。 用户可以像他们访问普通应用的控制器那样访问模块中的控制器。
模块在一些场景里很有用。对大型应用来说,我们可能需要把它划分为几个模块,每个模块可以单独维护和部署。一些通用的功能,例如用户管理, 评论管理,可以以模块的形式开发,这样他们就可以容易地在以后的项目中被复用。
创建模块
模块组织在一个目录中,目录的名字即模块的唯一  ID  。 模块目录的结构跟  应用基础目录  很相似。下面列出了一个  fourm  的模块的典型的目录结构:
forum/
   ForumModule.php            模块类文件
   components/                包含可复用的用户组件
      views/                  包含小物件的视图文件
   controllers/               包含控制器类文件
      DefaultController.php   默认的控制器类文件
   extensions/                包含第三方扩展
   models/                    包含模块类文件
   views/                     包含控制器视图和布局文件
      layouts/                包含布局文件
      default/                包含 DefaultController 的视图文件
         index.php            首页视图文件
模块必须由一个继承自 CWebModule 的模块类。类的名字通过表达式 ucfirst($id).'Module' 确定, 其中的 $id 代表模块的 ID (或者说模块的目录名字)。 模块类是存储模块代码间可共享信息的中心位置。例如,我们可以使用 CWebModule::params 存储模块参数,使用 CWebModule::components 分享模块级的 应用组件 .
使用模块
要使用模块,首先将模块目录放在  应用基础目录  的  modules  中。 然后在应用的  modules  属性中声明模块 ID 。例如,为了使用上面的  forum  模块, 我们可以使用如下  应用配置 :(main.php)
return array(
    ......
    'modules'=>array('forum',...),
    ......
);

模块也可以在配置时带有初始属性值。做法和配置 应用组件 很类似。 例如, forum 模块可以在其模块类中有一个名为 postPerPage 的属性,它可以在 应用配置(main.php)中配置如下:
return array(
    ......
    'modules'=>array(
        'forum'=>array(
            'postPerPage'=>20,
        ),
    ),
    ......
);
模块的实例可通过当前活动控制器的 module 属性访问。在模块实例中,我们可以访问在模块级中共享的信息。 例如,为访问上面的 postPerPage 信息,我们可使用如下表达式:
$postPerPage=Yii::app()->controller->module->postPerPage;
模块中的控制器动作可以通过  路由   moduleID/controllerID/actionID  访问。 例如,假设上面的  forum  模块有一个名为 PostController  的控制器,我们就可以通过  路由   forum/post/create  访问此控制器中的  create  动作。 此路由对应的 URL 即  http://www.example.com/index.php?r=forum/post/create .
路径别名与名字空间
Yii 中广泛的使用了路径别名。路径别名关联于一个目录或文件的路径。它以点号语法指定,类似于广泛使用的名字空间(namespace)格式:
通过使用  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 开始有效。

额外的,如果应用使用了 模块, (Yii) 也为每个模块ID定义了根别名,指向相应模块的跟目录。 此功能从版本 1.0.3 起有效。

通过使用 YiiBase::getPathOfAlias(), 别名可以被翻译为其相应的路径。 例如, system.web.CController 会被翻译为yii/framework/web/CController

使用别名导入Importing Classes
例如,如果我们想包含 CController 类的定义,我们可以调用如下代码
Yii::import('system.web.CController');
使用Class Map
Yii允许用户定义的类通过使Class Map机制来预先导入,这也是Yii内置类使用的方法。 预先引入机制可以在Yii应用的任何地方使用,无需显式地导入或者包含文件。这个特性对于一个建立在Yii基础上的框架或者类库来说很有用。
若要使用预导入功能,要在CWebApplication::run()执行前执行下面的代码:
Yii::$classMap=array(
    'ClassName1' => 'path/to/ClassName1.php',
    'ClassName2' => 'path/to/ClassName2.php',
    ......
);
导入目录
我们还可以使用如下语法导入整个目录,这样此目录下的类文件就会在需要时被自动包含。
Yii::import('system.web.*');
YII代码书写规则
变量名和函数名应该使它们的第一个单词全部小写,以使其区别于类名(例如:$basePath, runController(), LinkPager)。对私有类成员变量来说,我们推荐以下划线作为其名字前缀(例如: $_actionList)。
YII数据库书写规范
1.数据库表名和列名都使用小写命名。
2. 名字中的单词应使用下划线分割 (例如  product_order )。
3. 对于表名,你既可以使用单数也可以使用复数。但 不要 同时使用两者。为简单起见,我们推荐使用单数名字。
4. 表名可以使用一个通用前缀,例如  tbl_  。这样当应用所使用的表和另一个应用说使用的表共存于同一个数据库中时就特别有用。 这两个应用的表可以通过使用不同的表前缀很容易地区别开。


3.实用表单

创建表单模型
1. 如果用户输入被收集、使用然后丢弃,我们应该创建一个  表单模型
2.  如果用户的输入被收集后要保存到数据库,我们应使用一个  Active Record

定义表单模型
class LoginForm extends CFormModel
{
    public $username;
    public $password;
    public $rememberMe=false;
}
注:我们将这些成员变量称为 特性(attributes) 而不是 属性(properties),以区别于普通的属性(properties)。 特性(attribute)是一个主要用于存储来自用户输入或数据库数据的属性(propertiy)。


声明验证规则
如果我们想要验证用户的输入是否合法,可以定义一个rules()方法进行验证,rules()方法应该是一个返回数组return array()。例如:
 public function rules()
    {
        return array(
            array('username, password', 'required'),
            array('rememberMe', 'boolean'),
            array('password', 'authenticate'),
        );
    }
rules() 返回的每个规则必须是以下格式:
array('AttributeList', 'Validator', 'on'=>'ScenarioList', ...附加选项)
AttributeList(特性列表) 是需要通过此规则验证的特性列表字符串,每个特性名字由逗号分隔;
Validator(验证器) 指定要执行验证的种类;
on 参数是可选的,它指定此规则应被应用到的场景列表;
附加选项是一个名值对数组,用于初始化相应验证器的属性值;

针对Validator,我们有3种方法进行定义:
1) Validator 可以是模型类中一个方法的名字,并且验证方法格式必须如下结构:
public function ValidatorName($attribute,$params) { ... }
2)Validator可以说一个验证器类的名字, 当此规则被应用时, 一个验证器类的实例将被创建以执行实际验证。规则中的附加选项用于初始化实例的属性值。 验证器类必须继承自  CValidator
3) Validator  可以是一个预定义的验证器类的别名。在上面的例子中,  required  名字是 CRequiredValidator  的别名,它用于确保所验证的特性值不为空。


安全的特性赋值(Controllers进行处理)
当我们已经写好了一个LoginForm的时候,当用户提交表单我们就必须对用户提交的数据进行处理了,我们可以通过块赋值(massive assignment)方式实现:
$model=new LoginForm;$model->attributes=$_POST['LoginForm'];
最后的表达式被称作 块赋值(massive assignment) ,它将 $_POST['LoginForm'] 中的每一项复制到相应的模型特性中。

1.1中的安全特性
特性如果出现在相应场景的一个验证规则中,即被认为是安全的。 例如:
array('username, password', 'required', 'on'=>'login, register'), array('email', 'required', 'on'=>'register'),
如上所示, username 和 password 特性在 login 场景中是必填项。而 username, password 和 email 特性在 register 场景中是必填项。 于是,如果我们在 login 场景中执行块赋值,就只有 username 和 password 会被块赋值。 因为只有它们出现在 login 的验证规则中。 另一方面,如果场景是 register ,这三个特性就都可以被块赋值.
// 在登录场景中
$model=new User('login');
$model->attributes=$_POST['User'];
 
// 在注册场景中
$model=new User('register');
$model->attributes=$_POST['User'];

请记住,验证规则是用于检查用户输入的数据,而不是检查我们在代码中生成的数据(例如时间戳,自动产生的主键)。 因此,不要 为那些不接受最终用户输入的特性添加验证规则。

有时候我们想声明一个特性是安全的,比如用户输入的文章内容:
array('content', 'safe')

触发验证规则
对于表单模型,如果数据已经赋值了( $model->attributes=$_POST['User'];),则我们可以直接调用   CModel::validate()方法,例如$model->validate()将返回一个bool值。
对于AR模型,验证中我们调用其   CActiveRecord::save()  方法时自动触发。

提取验证错误
验证完成后,任何可能产生的错误将被存储在模型对象中。 我们可以通过调用  CModel::getErrors() CModel::getError()  提取这些错误信息。 这两个方法的不同点在于第一个方法将返回  所有  模型特性的错误信息,而第二个将只返回  第一个  错误信息。

验证逻辑(action)
首先实例化需要的模型,然后将数据填充( $model -> attributes = $_POST [ ' LoginForm ' ] ; ),填充完毕后,我们可以进行验证( $model -> validate ( ) )。当验证成功后,我们会重定向浏览器到指定页面如:(  $this -> redirect ( Yii :: app ( ) -> user -> returnUrl ) ;
注:在 login 动作中,我们使用 Yii::app()->user->returnUrl 获取之前需要身份验证的页面URL。 组件 Yii::app()->user 是一种 CWebUser (或其子类) ,它表示用户会话信息(例如 用户名,状态)。更多详情, 请参考 验证与授权.

创建表单(View)
Yii 提供了几个助手(helper)类简化视图编写。例如, 要创建一个文本输入域,我们可以调用 CHtml::textField(); 要创建一个下拉列表,则调用 CHtml::dropDownList()。
从版本 1.1.1 开始,提供了一个新的小物件 CActiveForm 以简化表单创建。 这个小物件可同时提供客户端及服务器端无缝的、一致的验证。使用 CActiveForm,:
<div class="form"> <?php $form=$this->beginWidget('CActiveForm'); ?> <?php echo $form->errorSummary($model); ?> <div class="row"> <?php echo $form->label($model,'username'); ?> <?php echo $form->textField($model,'username') ?> </div> <div class="row"> <?php echo $form->label($model,'password'); ?> <?php echo $form->passwordField($model,'password') ?> </div> <div class="row rememberMe"> <?php echo $form->checkBox($model,'rememberMe'); ?> <?php echo $form->label($model,'rememberMe'); ?> </div> <div class="row submit"> <?php echo CHtml::submitButton('Login'); ?> </div> <?php $this->endWidget(); ?> </div><!-- form -->

收集表格输入
有时我们想通过批量模式收集用户输入。也就是说, 用户可以为多个模型实例输入信息并将它们一次性提交。 我们将此称为  表格输入(tabular input)  ,因为这些输入项通常以 HTML 表格的形式呈现。


使用表单生成器
表单的输入框参数被组织为根据表单元素的分层结构。 在结构的顶层,是  CForm  对象。此对象的成员分为两大类:  CForm::buttons  和  CForm::elements 。前者包含 按钮元素(例如提交按钮,重设按钮),后者包含输入元素,静态文本和子表单。

创建一个简单的表单
public function actionLogin(){
    $model = new LoginForm; $form = new CForm('application.views.site.loginForm', $model); if($form->submitted('login') && $form->validate())
        $this->redirect(array('site/index')); else
        $this->render('login', array('form'=>$form)); }
在上面的代码中,我们使用由路径别名 application.views.site.loginForm  指定的参数创建了 CForm 对象。 CForm 对象和 LoginForm 模型关联。
路径别名 application.views.site.loginForm 实际指的是 PHP 文件 protected/views/site/loginForm.php。此文件应当返回一个 PHP 数组,这个数组代表了 CForm 所需的配置, 如下所示:
return array(
    'title'=>'Please provide your login credential', 'elements'=>array(
        'username'=>array(
            'type'=>'text', 'maxlength'=>32, ), 'password'=>array(
            'type'=>'password', 'maxlength'=>32, ), 'rememberMe'=>array(
            'type'=>'checkbox', )
    ), 'buttons'=>array(
        'login'=>array(
            'type'=>'submit', 'label'=>'Login', ), ), );
最后,我们编写 login 视图,可以简洁地如下所示,
<h1>Login</h1> <div class="form"> <?php echo $form; ?> </div>

指定输入的表单元素
'username'=>array(
    'type'=>'text', 'maxlength'=>32, ),
它说明模型属性被命名为 username,输入框的类型为 text,它的 maxlength 属性为 32。
任何  CFormInputElement  可写的属性都可以如上配置。例如,我们可以指定  hint  选项来显示提示信息,或者我们可以指定  items  选项若输入框是一个 list box,一个下拉列表,一个多选列表或一个单选按钮列表。 若选项的名字不是一个  CFormInputElement  属性,它将被认为是对应 HTML 输入元素的属性, 例如,因为上面的  maxlength 不是一个  CFormInputElement  属性,它被渲染作为 HTML 文本输入框的  maxlength  属性。

这里需要说明的一个地方是type类型是list的, 包括  dropdownlist ,  checkboxlist  和 radiolist 。这些类型需要设置对应输入元素的  items  属性。可以这样做:
'gender'=>array(
    'type'=>'dropdownlist', 'items'=>User::model()->getGenderOptions(), 'prompt'=>'Please select:', ),
class User extends CActiveRecord{
    public function getGenderOptions()
    {
        return array(
            0 => 'Male', 1 => 'Female', ); }}

指定静态文本
很多情况下,一个表单包含一些装饰性的 HTML 代码。 例如,一个水平线被用来分隔表单中不同的部分;一个图像出现在特定的位置来增强表单的视觉外观。 我们可以在  CForm::elements  集合中指定这些 HTML 代码作为静态文本。要这样做,我们只要指定一个静态文本字符串作为一个数组元素,在  CForm::elements  恰当的位置。例如
return array(
    'elements'=>array( ...... 'password'=>array(
            'type'=>'password', 'maxlength'=>32, ), '<hr />', 'rememberMe'=>array(
            'type'=>'checkbox', )
    ), ...... );

指定子表单
子表单被用来分离一个长的表单为几个逻辑部分。 例如,我们可以分离用户注册表单为两部分:登录信息和档案信息。
一个子表单也表示为一个 CForm  对象。要指定一个子表单,我们应当配置  CForm::elements  属性为一个类型是 form  的元素:
return array(
    'elements'=>array( ...... 'user'=>array(
            'type'=>'form', 'title'=>'Login Credential', 'elements'=>array(
                'username'=>array(
                    'type'=>'text', ), 'password'=>array(
                    'type'=>'password', ), 'email'=>array(
                    'type'=>'text', ), ), ), 'profile'=>array(
            'type'=>'form', ...... ), ...... ), ...... );

访问表单元素
访问表单元素和访问数组元素一样简单。 CForm::elements  属性返回一个  CFormElementCollection  对象, 它扩展自  CMap  并允许以类似于一个普通数组的方式来访问它的元素。例如,要访问登录表单中的元素  username ,我们可以使用下面的代码
$username = $form['username']; $email = $form['user']['email'];
比如我们可以直接在View里面echo $email,可以直接输出显示表单的HTML元素。

创建一个嵌套表单
我们假设用户的认证信息存储为一个 User 模型,而用户的档案信息被存储为一个 Profile 模型。这2个的是子表单。
我们首先创建 register action 如下:
public function actionRegister(){
    $form = new CForm('application.views.user.registerForm'); $form['user']->model = new User; $form['profile']->model = new Profile; if($form->submitted('register') && $form->validate())
    {
        $user = $form['user']->model; $profile = $form['profile']->model; if($user->save(false))
        {
            $profile->userID = $user->id; $profile->save(false); $this->redirect(array('site/index')); }
    }
 
    $this->render('register', array('form'=>$form)); }
子表单,我们编写表单配置文件 protected/views/user/registerForm.php:
return array(
    'elements'=>array(
        'user'=>array(
            'type'=>'form', 'title'=>'Login information', 'elements'=>array(
                'username'=>array(
                    'type'=>'text', ), 'password'=>array(
                    'type'=>'password', ), 'email'=>array(
                    'type'=>'text', )
            ), ), 'profile'=>array(
            'type'=>'form', 'title'=>'Profile information', 'elements'=>array(
                'firstName'=>array(
                    'type'=>'text', ), 'lastName'=>array(
                    'type'=>'text', ), ), ), ), 'buttons'=>array(
        'register'=>array(
            'type'=>'submit', 'label'=>'Register', ), ), );
最后,我们编写 register 视图脚本:
<h1>Register</h1> <div class="form"> <?php echo $form; ?> </div>

定制表单显示
使用表单生成器最主要的好处是逻辑 (表单配置被存储在一个单独的文件中) 和表现 ( CForm::render 方法) 的分离。 这样,我们可以实现定制表单显示,通过重写  CForm::render  或提供一个局部视图来渲染表单。两种方法都可以保持表单配置的完整性,并且可以容易地重用。
当重写  CForm::render  时, 你主要需要遍历  CForm::elements  和  CForm::buttons  并调用每个表单元素的 CFormElement::render  方法。例如我们解决上面的问题,使用自定义表单显示, 首先请确保已经存在了:
LoginForm.php(Model)这个是表单的模型
views/site/loginForm.php(View)这个是生成表单的配置文件,
SiteControllers里面有一个方法比如function actionFormCreate() 用来作为逻辑处理
function actionFormCreate()
     {
          $loginFormModel = new LoginForm;
          $form = new CForm('application.views.site.loginForm',$loginFormModel); //调用生产表单的配置文件和模型联系起来
          $this->render('FormCreate',array('form'=>$form)); //调用显示的View
     }

然后我们可以建立一个_form.php的文件来处理表单的自定义(view/site/_form.php),代码如下:
<?php
echo $form->renderBegin();

foreach($form->getElements() as $element)
{
    echo $element->render().'Test';
}

echo $form->renderEnd();
?>

最后一步当然是在我们的FormCreate这个试图里面显示这个表单啦:
<div class="form">
<?php $this->renderPartial('_form', array('form'=>$form)); ?>
</div>

4.使用数据库的DAO和AR

Yii数据访问对象(DAO)建立在PHP的数据对象(PDO)extension上,使得在一个单一的统一的接口可以访问不同的数据库管理系统(DBMS)。使用Yii的DAO开发的应用程序可以很容易地切换使用不同的数据库管理系统,而不需要修改数据访问代码。
Yii 的Active Record( AR ),实现了被广泛采用的对象关系映射(ORM)办法,进一步简化数据库编程。按照约定,一个类代表一个表,一个实例代表一行数据。Yii AR消除了大部分用于处理CRUD(创建,读取,更新和删除)数据操作的sql语句的重复任务。

建立数据库连接
要建立一个数据库连接,创建一个  CDbConnection  实例并将其激活。 连接到数据库需要一个数据源的名字(DSN)以指定连接信息。
$connection=new CDbConnection($dsn,$username,$password); $connection->active=true; ...... $connection->active=false; // 关闭连接
常用的几个DNS格式:
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,我们也可以将其作为一个 应用组件 使用。所以我们可以直接在main.php里面修改db的配置。
然后我们就可以通过 Yii::app()->db 访问数据库连接了。它已经被自动激活了,除非我们特意配置了CDbConnection::autoConnect 为 false。通过这种方式,这个单独的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() 方法以直接返回查询结果。

$dataReader=$command->query();   // 执行一个 SQL 查询
$rowCount=$command->execute();   // 执行无查询 SQL
$rows=$command->queryAll();      // 查询并返回结果中的所有行
$row=$command->queryRow();       // 查询并返回结果中的第一行
$column=$command->queryColumn(); // 查询并返回结果中的第一列
$value=$command->queryScalar();  // 查询并返回结果中第一行的第一个字段

获取查询结果
在  CDbCommand::query() 生成  CDbDataReader 实例之后,你可以通过重复调用  CDbDataReader::read() 获取结果中的行。你也可以在 PHP 的  foreach 语言结构中使用  CDbDataReader 一行行检索数据。
$dataReader=$command->query();
foreach($dataReader as $row) {
     print_r($row)
 }
//也 一次性提取所有行到一个数组
$rows=$dataReader->readAll();

使用事务
当一个应用要执行几条查询,每条查询要从数据库中读取并/或向数据库中写入信息时, 保证数据库没有留下几条查询而只执行了另外几条查询是非常重要的。 事务,在 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注入攻击
参数占位符可以是命名的 (表现为一个唯一的标记) 或未命名的 (表现为一个问号)。调用 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 变量绑定参数, 而后者使用一个值。对于那些内存中的大数据块参数,处于性能的考虑,应优先使用前者。

使用表前缀
要使用表前缀,配置  CDbConnection::tablePrefix 属性为所希望的表前缀。 然后,在 SQL 语句中使用 {{TableName}} 代表表的名字,其中的  TableName 是指不带前缀的表名。 例如,如果数据库含有一个名为 tbl_user 的表,而  tbl_ 被配置为表前缀,那我们就可以使用如下代码执行用户相关的查询。
$sql='SELECT * FROM {{user}}'; $users=$connection->createCommand($sql)->queryAll();

Active Record
Active Record (AR) 是一个流行的 对象-关系映射 (ORM) 技术。 每个 AR 类代表一个数据表(或视图),数据表(或视图)的列在 AR 类中体现为类的属性,一个 AR 实例则表示表中的一行。 常见的 CRUD 操作作为 AR 的方法实现。因此,我们可以以一种更加面向对象的方式访问数据。 例如,我们可以使用以下代码向 tbl_post 表中插入一个新行。
$post=new Post; $post->title='sample post'; $post->content='post body content'; $post->save();
注:我们在配置数据库(main.php)的时候, 由于 Active Record 依靠表的元数据(metadata)测定列的信息,读取元数据并解析需要时间。 如果你数据库的表结构很少改动,你应该通过配置 CDbConnection::schemaCachingDuration 属性的值为一个大于零的值开启表结构缓存。


由于 AR 类经常在多处被引用,我们可以导入包含 AR 类的整个目录,而不是一个个导入。 例如,如果我们所有的 AR 类文件都在 protected/models 目录中,我们可以配置应用如下:
return array(
  'import'=>array(
      'application.models.*', ), );

AR模型对应的类名与表名
默认情况下,AR 类的名字和数据表的名字相同。如果不同,请覆盖 tableName() 方法。 要使用 1.1.0 版本中引入的 表前缀功能 AR 类的 tableName() 方法可以通过如下方式覆盖:
public function tableName(){
    return '{{post}}'; }


创建记录
要向数据表中插入新行,我们要创建一个相应 AR 类的实例,设置其与表的列相关的属性,然后调用  save()  方法完成插入:
$post=new Post; $post->title='sample post'; $post->content='content for the sample post'; $post->create_time=time(); $post->save();
如果我们需要给某一个数据设置一个默认值,可以定义成此类的一个属性,例如:
class Post extends CActiveRecord{
    public $title='please enter a title'; ...... }
 
$post=new Post; echo $post->title; // 这儿将显示: please enter a title



查找记录
// 查找满足指定条件的结果中的第一行 $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);
请记住,静态方法 model() 是每个 AR 类所必须的。 此方法返回在对象上下文中的一个用于访问类级别方法(类似于静态类方法的东西)的 AR 实例。

调用 find 时,我们使用 $condition 和 $params 指定查询条件。此处 $condition 可以是 SQL 语句中的 WHERE 字符串,$params 则是一个参数数组,其中的值应绑定到 $condation 中的占位符。例如:
$post=Post::model()->find('postID=:postID', array(':postID'=>10));
一种替代  CDbCriteria  的方法是给  find  方法传递一个数组。 数组的键和值各自对应标准(criterion)的属性名和值,上面的例子可以重写为如下:
$post=Post::model()->find(array(
    'select'=>'title', 'condition'=>'postID=:postID', 'params'=>array(':postID'=>10), ));
当有多行数据匹配指定的查询条件时,我们可以通过下面的 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);
除了上面讲述的 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 在前面的段落中已有描述。如果以数组的形式,可以这样写:
Post::model()->updateAll(
      array('title' => 'update_name'),
      array(
          'condition'    => 'title=:title',
           'params'     =>  array(':title'=>'post1')
      )
);



删除记录
如果一个 AR 实例被一行数据填充,我们也可以删除此行数据。
$post=Post::model()->findByPk(10);      // 假设有一个帖子,其 ID 为 10
$post->delete();           // 从数据表中删除此行

使用下面的类级别代码,可以无需首先加载行就可以删除它
// 删除符合指定条件的行 Post::model()->deleteAll($condition,$params); // 删除符合指定条件和主键的行 Post::model()->deleteByPk($pk,$condition,$params);


数据验证
当调用  save()  时, AR 会自动执行数据验证。 验证是基于在 AR 类的  rules()  方法中指定的规则进行的。

如果有很多列,我们可以看到一个用于这种复制的很长的列表。 这可以通过使用如下所示的 attributes 属性简化操作。
$post->attributes=$_POST['Post'];// 假设 $_POST['Post'] 是一个以列名索引列值为值的数组
$post->save();



自定义方法

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(); }


5.使用数据库的表连接


我们已经了解了怎样使用 Active Record (AR) 从单个数据表中获取数据。 在本节中,我们讲解怎样使用 AR 连接多个相关数据表并取回关联(join)后的数据集。


 我们使用如下所示的实体-关系(ER)图中的数据结构演示此节中的例子
Yii权威指南_第2张图片


 从数据库的角度来说,表 A 和 B 之间有三种关系:一对多(one-to-many,例如  tbl_user  和  tbl_post ),一对一( one-to-one 例如  tbl_user 和  tbl_profile )和 多对多(many-to-many 例如  tbl_category  和 tbl_post )。

在 AR 中,有四种关系:

  • BELONGS_TO(属于): 如果表 A 和 B 之间的关系是一对多,则 表 B 属于 表 A (例如 Post 属于 User);

  • HAS_MANY(有多个): 如果表 A 和 B 之间的关系是一对多,则 A 有多个 B (例如 User 有多个 Post);

  • HAS_ONE(有一个): 这是 HAS_MANY 的一个特例,A 最多有一个 B (例如 User 最多有一个 Profile);

  • MANY_MANY: 这个对应于数据库中的 多对多 关系。 由于多数 DBMS 不直接支持 多对多 关系,因此需要有一个关联表将 多对多 关系分割为 一对多 关系。 在我们的示例数据结构中,tbl_post_category 就是用于此目的的。在 AR 术语中,我们可以解释 MANY_MANY 为 BELONGS_TO 和 HAS_MANY 的组合。 例如,Post 属于多个(belongs to many) Category ,Category 有多个(has many) Post.


AR 中定义关系需要覆盖  CActiveRecord  中的  relations()  方法。此方法返回一个关系配置数组。每个数组元素通过如下格式表示一个单一的关系。
'VarName'=>array('RelationType', 'ClassName', 'ForeignKey', ...additional options)
其中  VarName  是关系的名字; RelationType  指定关系类型,可以是一下四个常量之一: self::BELONGS_TO , self::HAS_ONE self::HAS_MANY  and  self::MANY_MANY ClassName  是所关联的 AR 类的名字; ForeignKey  指定关系中使用的外键(一个或多个)。额外的选项可以在每个关系的最后指定(稍后详述)。


以下代码演示了怎样定义  User  和  Post  类的关系:
class Post extends CActiveRecord{ ...... public function relations()
    {
        return array(
            'author'=>array(self::BELONGS_TO, 'User', 'author_id'), 'categories'=>array(self::MANY_MANY, 'Category', 'tbl_post_category(post_id, category_id)'), ); }}
 
class User extends CActiveRecord{ ...... public function relations()
    {
        return array(
            'posts'=>array(self::HAS_MANY, 'Post', 'author_id'), 'profile'=>array(self::HAS_ONE, 'Profile', 'owner_id'), ); }}
注:外键可能是复合的,包含两个或更多个列。 这种情况下,我们应该将这些外键名字链接,中间用空格或逗号分割。对于 MANY_MANY 关系类型, 关联表的名字必须也必须在外键中指定。例如, Post 中的 categories 关系由外键 tbl_post_category(post_id, category_id) 指定。

AR 类中的关系定义为每个关系向类中隐式添加了一个属性。在一个关联查询执行后,相应的属性将将被以关联的 AR 实例填充。 例如,如果  $author  代表一个  User  AR 实例, 我们可以使用  $author->posts  访问其关联的 Post  实例。


执行关联查询
执行关联查询,我们可以读取一个AR实例的属性,如果第一次读取就初始化AR实例 并使用当前 AR 实例的主键过滤 ,否则就将关联的AR实例的保存在属性中。这就是懒惰式加载(也称为迟加载)。例如:
// 获取 ID 为 10 的帖子      $post=Post::model()->findByPk(10);
// 获取帖子的作者(author): 此处将执行一个关联查询     $author=$post->author;

懒惰式加载用起来很方便,但在某些情况下并不高效。如果我们想获取  N  个帖子的作者,使用这种懒惰式加载将会导致执行  N  个关联查询。 这种情况下,我们应该改为使用  渴求式加载(eager loading) 方式。
渴求式加载方式会在获取主 AR 实例的同时获取关联的 AR 实例。 这是通过在使用 AR 中的  find  或  findAll  方法时配合使用 with 方法完成的。例如:
$posts=Post::model()->with('author')->findAll();

上述代码将返回一个 Post 实例的数组。与懒惰式加载方式不同,在我们访问每个 Post 实例中的 author 属性之前,它就已经被关联的 User 实例填充了。 渴求式加载通过 一个 关联查询返回所有帖子及其作者,而不是对每个帖子执行一次关联查询。

我们可以在 with() 方法中指定多个关系名字,渴求式加载将一次性全部取回他们。例如,如下代码会将帖子连同其作者和分类一并取回。

$posts=Post::model()->with('author','categories')->findAll();

从版本 1.1.0 开始,渴求式加载也可以通过指定  CDbCriteria::with  的属性执行,就像下面这样:
$criteria=new CDbCriteria; $criteria->with=array(
    'author.profile', 'author.posts', 'categories', ); $posts=Post::model()->findAll($criteria);

或者

$posts=Post::model()->findAll(array(
    'with'=>array(
        'author.profile', 'author.posts', 'categories', ));
上述示例将取回所有帖子及其作者和所属分类。它还同时取回每个作者的简介(author.profile)和帖子(author.posts)



关系型查询选项
我们提到在关系声明时可以指定附加的选项。这些 名-值 对形式的选项用于自定义关系型查询:
  • select: 关联的 AR 类中要选择(select)的列的列表。 默认为 '*',即选择所有列。此选项中的列名应该是已经消除歧义的。

  • condition: 即 WHERE 条件。默认为空。此选项中的列名应该是已经消除歧义的。

  • params: 要绑定到所生成的 SQL 语句的参数。应该以 名-值 对数组的形式赋值。此选项从 1.0.3 版起有效。

  • on: 即 ON 语句。此处指定的条件将会通过 AND 操作符附加到 join 条件中。此选项中的列名应该是已经消除歧义的。 此选项不会应用到 MANY_MANY 关系中。此选项从 1.0.2 版起有效。

  • order: 即 ORDER BY 语句。默认为空。 此选项中的列名应该是已经消除歧义的。

  • with: a list of child related objects that should be loaded together with this object. Be aware that using this option inappropriately may form an infinite relation loop.

  • joinType: type of join for this relationship. It defaults to LEFT OUTER JOIN.

  • alias: the alias for the table associated with this relationship. This option has been available since version 1.0.1. It defaults to null, meaning the table alias is the same as the relation name.

  • together: whether the table associated with this relationship should be forced to join together with the primary table and other tables. This option is only meaningful for HAS_MANY and MANY_MANYrelations. If this option is set false, the table associated with the HAS_MANY or MANY_MANY relation will be joined with the primary table in a separate SQL query, which may improve the overall query performance since less duplicated data is returned. If this option is set true, the associated table will always be joined with the primary table in a single SQL query, even if the primary table is paginated. If this option is not set, the associated table will be joined with the primary table in a single SQL query only when the primary table is not paginated. For more details, see the section "Relational Query Performance". This option has been available since version 1.0.3.

  • join: the extra JOIN clause. It defaults to empty. This option has been available since version 1.1.3.

  • group: the GROUP BY clause. It defaults to empty. Column names referenced in this option should be disambiguated.

  • having: the HAVING clause. It defaults to empty. Column names referenced in this option should be disambiguated. Note: option has been available since version 1.0.1.

  • index: the name of the column whose values should be used as keys of the array that stores related objects. Without setting this option, an related object array would use zero-based integer index. This option can only be set for HAS_MANY and MANY_MANY relations. This option has been available since version 1.0.7.

In addition, the following options are available for certain relationships during lazy loading:

  • limit: limit of the rows to be selected. This option does NOT apply to BELONGS_TO relation.

  • offset: offset of the rows to be selected. This option does NOT apply to BELONGS_TO relation.

例如:
class User extends CActiveRecord{
    public function relations()
    {
        return array(
            'posts'=>array(self::HAS_MANY, 'Post', 'author_id', 'order'=>'posts.create_time DESC', ), 'profile'=>array(self::HAS_ONE, 'Profile', 'owner_id'), ); }}
如果我们访问$author->posts,则我们可以得到用户的posts按照时间顺序排序。


关系表相同列名的处理方法
当一个列名出现在2个或多个表的时候,我们就需要区别开来。最好的办法是在列名前加前缀。
你的主表的前缀名默认是t,而你的关系表默认前缀是表名。现在架设2个表Post和Comment有同一个列名是create_time,则我们可以如下使用:
$posts=Post::model()->with('comments')->findAll(array(
    'order'=>'t.create_time, comments.create_time'));

动态关系执行选项
在1.0.5的版本中,动态选项可以在懒加载中使用,例如我们使用User模型得到了数据,并且希望得到他发布的文章status=1的列表,可以使用动态加载,例如:
$user=User::model()->findByPk(1); $posts=$user->posts(array('condition'=>'status=1'));


关系的执行性能问题
假设我们现在需要得到发布的文章数据和留言数据,而一篇文章有10条留言数据。所以当我们执行一个大的复杂的Joining SQL语句时,我们会得到许多的数据并且每个留言对应他相应的文章,这样就造成了许多的冗余数据。架设我们执行2条SQL语句先得到所有的文章,在通过文章得到留言,虽然没有数据的冗余,但增加了数据库的开销。
现在Yii提供一个together执行选项给我们,默认Yii使用的是第一种方法,生成一个大的SQL语句来。但是我们现在可以将together=>false来决定选择第二种方法,使用方法:
public function relations(){
    return array(
        'comments' => array(self::HAS_MANY, 'Comment', 'post_id', 'together'=>false), ); } 
当然我们也可以动态的使用:
$posts  =  Post :: model ( ) -> with ( array ( ' comments ' => array ( ' together ' => false ) ) ) -> findAll ( ) ;

执行统计的Query
Yii提供一个统计的方法来检索相关对象,例如检索每篇文章的留言数量,产品的平均等级等。 Statistical query只能够被执行在HAS_MANY或者MANY_MANY的对象中。
执行一个统计Query和执行一个关联Query非常相似。首先我们需要在relations()里面定义 statistical query,例如:
class Post extends CActiveRecord{
    public function relations()
    {
        return array(
            'commentCount'=>array(self::STAT, 'Comment', 'post_id'), 'categoryCount'=>array(self::STAT, 'Category', 'post_category(post_id, category_id)'), ); }} 
在这里我们定义了2个statistical query commentCount  calculates the number of comments belonging to a post, and  categoryCount  calculates the number of categories that a post belongs to. 
然后我们可以通过$post->commentCount来取回得到的数据,这是一种懒惰式的执行方法,如果我们想直接获得数据,就可以使用渴求式的执行方法,例如:
$posts = Post :: model ( ) -> with ( ' commentCount ' ,  ' categoryCount ' ) -> findAll ( ) ;
上面的SQL语句会直接返回所有的counts数据。

6.缓存

在 Yii 中使用缓存主要包括配置并访问一个应用组件。 下面的应用配置设定了一个使用两个 memcache 缓存服务器的缓存组件。
array( ...... 'components'=>array( ...... 'cache'=>array(
            'class'=>'system.caching.CMemCache', 'servers'=>array(
                array('host'=>'server1', 'port'=>11211, 'weight'=>60), array('host'=>'server2', 'port'=>11211, 'weight'=>40), ), ), ), ); 
当应用运行时,缓存组件可通过  Yii::app()->cache  访问。

Yii 提供了不同的缓存组件,可以将缓存数据存储到不同的媒介中。例如, CMemCache 组件封装了 PHP 的 memcache 扩展并使用内存作为缓存存储媒介。 CApcCache 组件封装了 PHP APC 扩展; 而 CDbCache 组件会将缓存的数据存入数据库。下面是一个可用缓存组件的列表:

  • CMemCache: 使用 PHP memcache 扩展.

  • CApcCache: 使用 PHP APC 扩展.

  • CXCache: 使用 PHP XCache 扩展。注意,这个是从 1.0.1 版本开始支持的。

  • CEAcceleratorCache: 使用 PHP EAccelerator 扩展.

  • CDbCache: 使用一个数据表存储缓存数据。默认情况下,它将创建并使用在 runtime 目录下的一个 SQLite3 数据库。 你也可以通过设置其 connectionID 属性指定一个给它使用的数据库。

  • CZendDataCache: 使用 Zend Data Cache 作为后台缓存媒介。注意,这个是从 1.0.4 版本开始支持的。

  • CFileCache: 使用文件存储缓存数据。这个特别适合用于存储大块数据(例如页面)。注意,这个是从 1.0.6 版本开始支持的。

  • CDummyCache: 目前 dummy 缓存并不实现缓存功能。此组件的目的是用于简化那些需要检查缓存可用性的代码。 例如,在开发阶段或者服务器尚未支持实际的缓存功能,我们可以使用此缓存组件。当启用了实际的缓存支持后,我们可以切换到使用相应的缓存组件。 在这两种情况中,我们可以使用同样的代码Yii::app()->cache->get($key) 获取数据片段而不需要担心 Yii::app()->cache 可能会是null。此组件从 1.0.5 版开始支持。 


 
数据缓存
数据缓存即存储一些 PHP 变量到缓存中,以后再从缓存中取出来。出于此目的,缓存组件的基类  CCache  提供了两个最常用的方法:  set()  和  get()

设置缓存
// 值$value 在缓存中最多保留30秒Yii::app()->cache->set($id, $value, 30); 
读取缓存
$value = Yii :: app ( ) -> cache -> get ( $id ) ;
如果不存在,则会返回false。

删除缓存
要从缓存中清除一个缓存值,调用  delete() ; 要清楚缓存中的所有数据,调用  flush() 。 当调用  flush()  时一定要小心,因为它会同时清除其他应用中的缓存。

注:由于 CCache 实现了 ArrayAccess,缓存组件也可以像一个数组一样使用。下面是几个例子:
$cache=Yii::app()->cache;
$cache['var1']=$value1;  // 相当于: $cache->set('var1',$value1);
$value2=$cache['var2'];  // 相当于: $value2=$cache->get('var2');


缓存依赖
除了过期设置,缓存数据也可能会因为依赖条件发生变化而失效。例如,如果我们缓存了某些文件的内容,而这些文件发生了改变,我们就应该让缓存的数据失效, 并从文件中读取最新内容而不是从缓存中读取。
我们将一个依赖关系表现为一个  CCacheDependency  或其子类的实例。 当调用  set()  时,我们连同要缓存的数据将其一同传入。
// 此值将在30秒后失效// 也可能因依赖的文件发生了变化而更快失效Yii::app()->cache->set($id, $value, 30, new CFileCacheDependency('FileName'));

现在如果我们通过调用 get()  从缓存中获取  $value  ,依赖关系将被检查,如果发生改变,我们将会得到一个 false 值,表示数据需要被重新生成。

如下是可用的缓存依赖的简要说明:

  • CFileCacheDependency: 如果文件的最后修改时间发生改变,则依赖改变。

  • CDirectoryCacheDependency: 如果目录和其子目录中的文件发生改变,则依赖改变。

  • CDbCacheDependency: 如果指定 SQL 语句的查询结果发生改变,则依赖改变。

  • CGlobalStateCacheDependency: 如果指定的全局状态发生改变,则依赖改变。全局状态是应用中的一个跨请求,跨会话的变量。它是通过 CApplication::setGlobalState() 定义的。

  • CChainedCacheDependency: 如果链中的任何依赖发生改变,则此依赖改变。

  • CExpressionDependency: 如果指定的 PHP 表达式的结果发生改变,则依赖改变。此类从版本 1.0.4 起可用。 



片段缓存(Fragment Caching)
片段缓存指缓存网页某片段,不管什么缓存,我们首先还是需要在components配置里面配置好缓存类型才能使用缓存。例如,如果一个页面在表中显示每年的销售摘要,我们可以存储此表在缓存中,减少每次请求需要重新产生的时间。
要使用片段缓存,在控制器视图脚本中调用 CController::beginCache()  和 CController::endCache()  。这两种方法开始和结束包括的页面内容将被缓存。类似 data caching  ,我们需要一个编号,识别被缓存的片段。
...别的HTML内容... <?php if($this->beginCache($id)) { ?> ...被缓存的内容... <?php $this->endCache(); } ?> ...别的HTML内容...
在上面的,如果 beginCache()  返回false,缓存的内容将此地方自动插入; 否则,在 if 语句内的内容将被执行并在 endCache() 触发时缓存。

片段缓存的选项(cache options)
当调用 beginCache() ,可以提供一个数组由缓存选项组成的作为第二个参数,以自定义片段缓存。事实上为了方便, beginCache()  和 endCache() 方法是  COutputCache  widget的包装。因此 COutputCache 的所有属性都可以在缓存选项中初始化。

有效期(Duration)

也许是最常见的选项是duration,指定了内容在缓存中多久有效。和CCache::set()过期参数有点类似。下面的代码缓存内容片段最多一小时:

...其他HTML内容... <?php if($this->beginCache($id, array('duration'=>3600))) { ?> ...被缓存的内容... <?php $this->endCache(); } ?> ...其他HTML内容...

如果我们不设定期限,它将默认为60 ,这意味着60秒后缓存内容将无效。

依赖(Dependency)

像data caching ,内容片段被缓存也可以有依赖。例如,文章的内容被显示取决于文章是否被修改。

要指定一个依赖,我们建立了dependency选项,可以是一个实现ICacheDependency的对象或可用于生成依赖对象的配置数组。下面的代码指定片段内容取决于lastModified 列的值是否变化:

...其他HTML内容... <?php if($this->beginCache($id, array('dependency'=>array(
        'class'=>'system.caching.dependencies.CDbCacheDependency', 'sql'=>'SELECT MAX(lastModified) FROM Post')))) { ?> ...被缓存的内容... <?php $this->endCache(); } ?> ...其他HTML内容...

变化(Variation)
缓存的内容可根据一些参数变化。例如,每个人的档案都不一样。缓存的档案内容将根据每个人ID变化。这意味着,当调用 beginCache() 时将用不同的ID。

COutputCache内置了这一特征,程序员不需要编写根据ID变动内容的模式。以下是摘要。

  • varyByRoute: 设置此选项为true ,缓存的内容将根据route变化。因此,每个控制器和行动的组合将有一个单独的缓存内容。

  • varyBySession: 设置此选项为true ,缓存的内容将根据session ID变化。因此,每个用户会话可能会看到由缓存提供的不同内容。

  • varyByParam: 设置此选项的数组里的名字,缓存的内容将根据GET参数的值变动。例如,如果一个页面显示文章的内容根据id的GET参数,我们可以指定varyByParam为array('id'),以使我们能够缓存每篇文章内容。如果没有这样的变化,我们只能能够缓存某一文章。

  • varyByExpression: by setting this option to a PHP expression, we can make the cached content to be variated according to the result of this PHP expression. This option has been available since version 1.0.4.


请求类型(Request Types)

有时候,我们希望片段缓存只对某些类型的请求启用。例如,对于某张网页上显示表单,我们只想要缓存initially requested表单(通过GET请求)。任何随后显示(通过POST请求)的表单将不被缓存,因为表单可能包含用户输入。要做到这一点,我们可以指定requestTypes 选项:

...其他HTML内容... <?php if($this->beginCache($id, array('requestTypes'=>array('GET')))) { ?> ...被缓存的内容... <?php $this->endCache(); } ?> ...其他HTML内容...

嵌套片段缓存

片段缓存可以嵌套。就是说一个缓存片段附在一个更大的片段缓存里。例如,意见缓存在内部片段缓存,而且它们一起在外部缓存中在文章内容里缓存。

...其他HTML内容... <?php if($this->beginCache($id1)) { ?> ...外部被缓存内容... <?php if($this->beginCache($id2)) { ?> ...内部被缓存内容... <?php $this->endCache(); } ?> ...外部被缓存内容... <?php $this->endCache(); } ?> ...其他HTML内容...

嵌套缓存可以设定不同的缓存选项。例如, 在上面的例子中内部缓存和外部缓存可以设置时间长短不同的持续值。当数据存储在外部缓存无效,内部缓存仍然可以提供有效的内部片段。 然而,反之就不行了。如果外部缓存包含有效的数据, 它会永远保持缓存副本,即使内容中的内部缓存已经过期。


页面缓存(Page Cache)
页面缓存可以被看作是  片段缓存 一个特殊情况 。 由于网页内容是往往通过应用布局来生成,如果我们只是简单的在布局中调用 beginCache()  和 endCache() ,将无法正常工作。 这是因为布局在 CController::render() 方法里的加载是在页面内容产生之后。

如果想要缓存整个页面,我们应该跳过产生网页内容的动作执行。我们可以使用COutputCache作为动作 过滤器来完成这一任务。下面的代码演示如何配置缓存过滤器:

public function filters(){
    return array(
        array(
            'COutputCache', 'duration'=>100, 'varyByParam'=>array('id'), ), ); }

上述过滤器配置会使过滤器适用于控制器中的所有行动。 我们可能会限制它在一个或几个行动通过使用插件操作器。 更多的细节中可以看过滤器。




动态内容(Dynamic Content)

当使用fragment caching或page caching,我们常常遇到的这样的情况 整个部分的输出除了个别地方都是静态的。例如,帮助页可能会显示静态的帮助 信息,而用户名称显示的是当前用户的。

解决这个问题,我们可以根据用户名匹配缓存内容,但是这将是我们宝贵空间一个巨大的浪费,因为缓存除了用户名其他大部分内容是相同的。我们还可以把网页切成几个片段并分别缓存,但这种情况会使页面和代码变得非常复杂。更好的方法是使用由 CController 提供的动态内容dynamic content功能 。

动态内容是指片段输出即使是在片段缓存包括的内容中也不会被缓存。即使是包括的内容是从缓存中取出,为了使动态内容在所有时间是动态的,每次都得重新生成。出于这个原因,我们要求 动态内容通过一些方法或函数生成。

调用CController::renderDynamic()在你想的地方插入动态内容。

...别的HTML内容... <?php if($this->beginCache($id)) { ?> ...被缓存的片段内容... <?php $this->renderDynamic($callback); ?> ...被缓存的片段内容... <?php $this->endCache(); } ?> ...别的HTML内容...

在上面的, $callback指的是有效的PHP回调。它可以是指向当前控制器类的方法或者全局函数的字符串名。它也可以是一个数组名指向一个类的方法。其他任何的参数,将传递到renderDynamic()方法中。回调将返回动态内容而不是仅仅显示它。

我是这样使用$callback的,在Controllers里面Action传一个值给$callback,例如:
public function actionIndex()
 {                  
    $this->render('index',array('callback'=>'andy'));
 }

这样就会回调andy这个function,我们只需要在函数里面返回动态数据即可:
function andy()
{
     return 'test';
}

7.扩展YII


适用扩展通常包含了以下三步:

  1. 从 Yii 的 扩展库 下载扩展.
  2. 解压到 应用程序的基目录 的子目录 extensions/xyz 下,这里的 xyz 是扩展的名称.
  3. 导入, 配置和使用扩展.
每个扩展都有一个所有扩展中唯一的名称标识.把一个扩展命名为  xyz ,我们也可以使用路径别名定位到包含了 xyz 所有文件的基目录.

Zii Extensions
在我们介绍第三方扩展到用法前,我们应该知道Zii扩展库。它是Yii团队开发的一个扩展并默认从1.1.0版本开始包含。Zii库现在作为Google Project托管着。
在使用Zii扩展到时候,我们必须要参阅相关类了解路径别名的使用方法zii.path.to.ClassName。在这里Yii定义zii是一个Zii库的根目录。例如,如果我们使用   CGridView,我们必须使用如下代码在试图中使用这个扩展:
$this->widget('zii.widgets.grid.CGridView', array(
    'dataProvider'=>$dataProvider, ));


应用的部件

使用 应用的部件, 首先我们需要添加一个新条目到 应用配置 的 components 属性, 如下所示:

return array(
    // 'preload'=>array('xyz',...),
    'components'=>array(
        'xyz'=>array(
            'class'=>'application.extensions.xyz.XyzClass', 'property1'=>'value1', 'property2'=>'value2', ), // 其他部件配置
    ), );

然后,我们可以在任何地方通过使用  Yii::app()->xyz 来访问部件.部件将会被  惰性创建(就是,仅当它第一次被访问时创建.) , 除非我们把它配置到  preload 属性里.


行为(Behavior)
Behavior可以用在各种组件中,使用这个组件有2个步骤:1 一个hehavior要附在一个目标组件上;2 调用一个Behavior是通过它的目标组件。例如:
// $name uniquely identifies the behavior in the component
$component->attachBehavior($name,$behavior);
// test() is a method of $behavior
$component->test();

更多的时候,调用一个Behavior是通过配置而不是通过attachBehavior方法。例如:在一个应用组件上attach一个Behavior,我们可以这么配置:
return array(
    'components'=>array(
        'db'=>array(
            'class'=>'CDbConnection', 'behaviors'=>array(
                'xyz'=>array(
                    'class'=>'ext.xyz.XyzBehavior', 'property1'=>'value1', 'property2'=>'value2', ), ), ), //....
    ), );
上面的配置attach xyz Behavior到db应用组件。我们可以这样做是因为 CApplicationComponent定义了一个属性behaviors。所以我们可以设置这个属性,在加载的时候,这个组件attach相应的Behavior。
For  CController CFormModel  and  CActiveRecord  classes which usually need to be extended, attaching behaviors can be done by overriding their  behaviors()  method. The classes will automatically attach any behaviors declared in this method during initialization. For example,
public function behaviors(){
    return array(
        'xyz'=>array(
            'class'=>'ext.xyz.XyzBehavior', 'property1'=>'value1', 'property2'=>'value2', ), ); }


组件(Components)
组件  主要用在  视图  里.假设组件类  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(); ?>


动作(Action)

动作 被 控制器 用于响应指定的用户请求.假设动作的类 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 来访问.



过滤器(Filter)

过滤器 也被 控制器 使用.过滤器主要用于当其被 动作 挂起时预处理,提交处理用户请求.假设过滤器的类XyzClass 属于 xyz 扩展,我们可以在我们的控制器类里重写 CController::filters 方法来使用它:

class TestController extends CController{
    public function filters()
    {
        return array(
            array(
                'application.extensions.xyz.XyzClass', 'property1'=>'value1', 'property2'=>'value2', ), // 其他过滤器
        ); }}

在上述代码中,我们可以在数组的第一个元素里使用加号或者减号操作符来限定过滤器只在那些动作中生效.更多信息,请参照文档的 CController.


控制器

控制器 提供了一套可以被用户请求的动作.我们需要在 应用配置 里设置 CWebApplication::controllerMap 属性,才能在控制器里使用扩展:

return array(
    'controllerMap'=>array(
        'xyz'=>array(
            'class'=>'application.extensions.xyz.XyzClass', 'property1'=>'value1', 'property2'=>'value2', ), // 其他控制器
    ), );

然后, 一个在控制里的  a 行为就可以通过  路由  xyz/a 来访问了.



检验器

校验器主要用在 模型类(继承自 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', ), // 其他校验规则
        ); }}


控制台命令

控制台命令扩展通常使用一个额外的命令来增强 yiic 的功能.假设命令控制台 XyzClass 属于 xyz 扩展,我们可以通过设定控制台应用的配置来使用它:

return array(
    'commandMap'=>array(
        'xyz'=>array(
            'class'=>'application.extensions.xyz.XyzClass', 'property1'=>'value1', 'property2'=>'value2', ), // 其他命令
    ), );

然后,我们就能使用配备了额外命令 xyz 的 yiic 工具了.


通用部件

使用一个通用 部件, 我们首先需要通过使用

Yii::import('application.extensions.xyz.XyzClass');

来包含它的类文件.然后,我们既可以创建一个类的实例,配置它的属性,也可以调用它的方法.我们还可以创建一个新的子类来扩展它.





Using 3rd-Party Libraries(使用第三方库)

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));



你可能感兴趣的:(Yii权威指南)