应用app
配置文件protected/config/main.php
类型 | 相关类 |
CWebApplication | CApplication |
CConsoleApplication |
控制器Controller
存放路径protected/controllers/ControllerID.php
相关类Controller、CController、CBaseController
其它对象 | 类型 | 生效方式 | 相关类 |
action | 函数actionXxx() | 存在即生效 |
|
extends CAction:改写run() | 改写actions()函数以指定action | CAction | |
filter | 函数filterXxx() | 改写filters()函数以指定filter |
|
extends CFilter: 改写preFilter($filterChain)、postFilter($filterChain) | CFilter |
模型Model
存放路径protected\models\ModelID.php
其它对象 |
CFormModel |
CActiveRecord |
视图view
存放路径protected/views/ControllerID/ViewID.php
其它对象 |
|
| 相关类 |
布局 | 布局被隐式应用.视图脚本 protected/views/layouts/main.php |
|
|
可以通过改变 CWebApplication::layout进行自定义。 |
|
| |
要渲染一个不带布局的视图,则需调用 renderPartial() 。 |
|
| |
小物件 | <?php $this->beginWidget('path.to.WidgetClass'); ?> 或者<?php $this->widget('path.to.WidgetClass'); ?> | 继承 CWidget 并覆盖其init() 和 run() 方法,可以定义一个新的小物件:class MyWidget extends CWidget | CWidget |
系统view | 在 framework/views 下, Yii 提供了一系列默认的系统视图. 他们可以通过在 protected/views/system 下创建同名视图文件进行自定义. |
|
|
组件Component
| Yii 应用建立于组件之上。组件是 CComponent 或其子类的实例 |
声明形式 | return array( ...... 'components'=>array( 'user'=>array( // enable cookie-based authentication 'allowAutoLogin'=>true, ), ), ...... ); |
组件属性 | 公共成员变量即可。更灵活的方式是定义其 getter 和 setter 方法 |
组件事件(特殊的属性) | 组件事件以 on 开头的命名方式定义 如:public function onClicked($event) 1.其中$event是事件的参数,2.$event是CEvent或其子类的实例。 |
一个事件可以绑定多个句柄。当事件触发时, 这些句柄将被按照它们绑定到事件时的顺序依次执行。如果句柄决定组织后续句柄被执行,它可以设置 $event->handled 为 true。 | |
组件行为 | 行为类必须实现 IBehavior 接口。大多数行为可以继承自CBehavior。如果一个行为需要绑定到一个模型, 它也可以从专为模型实现绑定特性的 CModelBehavior 或 CActiveRecordBehavior 继承。
要使用一个行为,它必须首先通过调用此行为的 attach() 方法绑定到一个组件。然后我们就可以通过组件调用此行为方法: // $name 在组件中实现了对行为的唯一识别 $component->attachBehavior($name,$behavior); // test() 是行为中的方法。 $component->test(); |
模块Module
存放路径protected/modules
访问形式r=moduleID/controllerID/actionID
可以使用Gii中的模块创建器创建新模块的基本骨架。
特征 | 1.模块是一个独立的软件单元,它包含 模型, 视图, 控制器 和其他支持的组件。 在许多方面上,模块看起来像一个 应用。主要的区别就是模块不能单独部署,它必须存在于一个应用里。 2.模块组织在一个目录中,目录的名字即模块的唯一 ID 。 模块目录的结构跟 应用基础目录 很相似。 | |
相关类 | CWebModule | |
声明形式 | return array( ...... 'modules'=>array('forum',...), ...... ); | 传参形式: return array( ...... 'modules'=>array( 'forum'=>array( 'postPerPage'=>20, ), ), ...... ); |
嵌套的模块(可以无限级嵌套) | 一个模块可以包含另一个模块,而这另一个模块又可以包含其他模块。我们称前者为 父模块 ,后者为 子模块. 子模块必须定义在其父模块的 modules 属性中,就像我们前面在应用配置中定义模块一样。 | |
要访问子模块中的控制器动作,我们应使用路由 parentModuleID/childModuleID/controllerID/actionID. |
路径别名
声明形式 | YiiBase::getPathOfAlias(), 别名可以被翻译为其相应的路径。 例如, system.web.CController 会被翻译为 yii/framework/web/CController。
通过调用 YiiBase::setPathOfAlias(),我们可以定义新的根路径别名。 |
常用别名 | Yii 预定义了以下几个根别名:Root Alias system: 表示 Yii 框架目录; zii: 表示 Zii 库 目录; application: 表示应用的 基础目录; webroot: 表示 入口脚本 文件所在的目录。此别名从版本 1.0.3 开始有效。 ext: 表示包含了所有第三方 扩展 的目录。此别名从版本 1.0.8 开始有效。 |
可用于模块 | 额外的,如果应用使用了 模块, (Yii) 也为每个模块ID定义了根别名,指向相应模块的跟目录。 此功能从版本 1.0.3 起有效。 |
Importing Classes 导入类的定义 | 例如,如果我们想包含 CController 类的定义,Yii::import('system.web.CController'); import 方法跟 include 和 require 不同,它更加高效。 导入(import)的类定义并不会真正被包含进来,直到它第一次被引用。 多次导入同样的名字空间也会比 include_once 和 require_once 快得多。 |
预导入 使用Class Map导入 | 从1.1.5版本开始,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.*'); |
其它用法 | 除 import 外, 别名还在其他许多地方指向类。 例如,路径别名可以传递给 Yii::createComponent() 以创建相应类的实例。 即使类文件在之前从未被包含。 |
URL
默认情况下,Yii 识别如下格式的 URL: http://hostname/index.php?r=ControllerID/ActionID r GET 变量意为 路由(route) ,它可以被Yii解析为 控制器和动作。 如果 ActionID 被省略,控制器将使用默认的动作(在CController::defaultAction中定义); 如果 ControllerID 也被省略(或者 r 变量不存在),应用将使用默认的控制器 (在CWebApplication::defaultController中定义)。
|
验证器
1.指定验证器 | 示例:改写model(CFormModel或CActiveRecord类)的rules()方法即可指定验证器,如用于登录的表单model class LoginForm extends CFormModel{ public function rules() { return array( array('username, password', 'required'), array('rememberMe', 'boolean'), array('password', 'authenticate'), ); } } |
有三种方式可在验证规则中指定 Validator: 第一, Validator 可以是模型类中一个方法的名字,就像上面示例中的 authenticate 。验证方法必须是下面的结构: public function ValidatorName($attribute,$params) { ... } 第二,Validator 可以是一个验证器类的名字,当此规则被应用时, 一个验证器类的实例将被创建以执行实际验证。规则中的附加选项用于初始化实例的属性值。 验证器类必须继承自 CValidator。 第三,Validator 可以是一个预定义的验证器类的别名。一般, required 名字是 CRequiredValidator 的别名。 | |
系统预定义的验证器在framework/validators/CXxxValidator.php | |
2.触发数据验证 | 一旦模型被用户提交的数据填充,就可以调用 CModel::validate() 触发数据验证进程。此方法返回一个指示验证是否成功的值。 对 CActiveRecord 模型来说的验证也可以在我们调用其 CActiveRecord::save() 方法时自动触发。 |
3.提取验证错误 | 验证完成后,任何可能产生的错误将被存储在模型对象中。 我们可以通过调用 CModel::getErrors() 和CModel::getError() 提取这些错误信息 |
一个简单的表单相关示例代码:
//表单模型protected\models\LoginForm.php class LoginForm extends CFormModel { rules(){…} attributeLabels(){…} }
|
//控制器位于protected\controllers\SiteController.php class SiteController extends Controller { ... public function actionLogin() //动作login { $model=new LoginForm; //生成model(这里是表单)实例
// if it is ajax validation request //如果是ajax方式的验证,处理方法是向客服端打印验证结果,而不是像普通验证那样进行页面跳转 if(isset($_POST['ajax']) && $_POST['ajax']==='login-form') { echo CActiveForm::validate($model); Yii::app()->end(); }//???那何时login呢
// collect user input data if(isset($_POST['LoginForm'])) { $model->attributes=$_POST['LoginForm']; //安全的特性赋值(给model(这里是表单)实例赋值)(attributes 属性由 CModel 定义) // validate user input and redirect to the previous page if valid if($model->validate() && $model->login()) //触发验证 $this->redirect(Yii::app()->user->returnUrl);//如果验证成功,重定向用户浏览器到之前需要身份验证的页面。 } // display the login form $this->render('login',array('model'=>$model)); //验证或登录失败,则跳回名为login的view,(当前的模型$model作为参数,以填充页面) }
|
//视图位于protected\views\site\login.php; protected\views\site\index.php等等 |
表单帮助者:CHtml和CActiveForm。
用于生成先进的表单html,能直接带有与服务端一致的客户端验证。
创建CHtml和CActiveForm实例时,需要将一个model实例(CFormModel)作为一个参数传给帮助者,原因是(1)以便帮助者知道有那些验证条件;(2)CFormModel的获取特性标签名的方法attributeLabels()为帮助者提供了显示label的字符串。
CHtml | Yii 提供了几个助手(helper)类简化视图编写。例如, 要创建一个文本输入域,我们可以调用 CHtml::textField() |
CActiveForm | 从版本 1.1.1 开始,提供了一个新的小物件 CActiveForm 以简化表单创建。 这个小物件可同时提供客户端及服务器端无缝的、一致的验证。 |
表单生成器CForm
一、Controller public function actionLogin() { $model = new LoginForm; $form = new CForm('application.views.site.loginForm', $model); //1.CForm构造时一定接收了$_POST['LoginForm'],2.指定表单配置文件,3.指定了model
if($form->submitted('login') && $form->validate())//这里,$form调用validate(),其实是引起$model自身的validate()调用 $this->redirect(array('site/index')); else $this->render('login', array('form'=>$form));//验证失败的话,则跳回名为login的view,(当前的$form作为参数,以填充页面) } |
二、view视图login.php,很简单,如下。(echo $form;相当于echo $form->render();) <h1>Login</h1>
<div class="form"> <?php echo $form; ?> </div> |
三、表单配置文件放在protected/views/ControllerID/xxx.php。它返回一个数组,指定表单中有哪些输入框,如 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', ), ), );
|
访问表单元素 和访问数组元素一样简单。CForm::elements 属性返回一个 CFormElementCollection 对象, 它扩展自 CMap 并允许以类似于一个普通数组的方式来访问它的元素。例如,要访问登录表单中的元素 username,我们可以使用下面的代码: $username = $form->elements['username']; $email = $form->elements['user']->elements['email'];//对于子表单的情况 或简化为 $username = $form['username']; $email = $form['user']['email']; //对于子表单的情况 |
特殊的表单配置文件形式
1.对于type为list的,包括 dropdownlist, checkboxlist 和 radiolist。 'gender'=>array( 'type'=>'dropdownlist', 'items'=>User::model()->getGenderOptions(),//利用模型model来返回array作为列表下拉选项 'prompt'=>'Please select:', ), //model文件 class User extends CActiveRecord { public function getGenderOptions() { return array( 0 => 'Male', 1 => 'Female', ); } |
2.除了内置的类型, type 选项也可以是一个 widget 类名字或 widget 类的路径别名。 widget 类必须扩展自 CInputWidget 或 CJuiInputWidget。当渲染输入元素时, 一个指定 widget 类的实例将被创建并渲染。 |
3.指定静态文本 可以在 CForm::elements 集合中指定HTML 代码作为静态文本。只要指定静态文本字符串作为一个数组元素,在 CForm::elements 恰当的位置。例如, return array( 'elements'=>array( ...... 'password'=>array( 'type'=>'password', 'maxlength'=>32, ),
'<hr />',
'rememberMe'=>array( 'type'=>'checkbox', ) ), ...... ); |
4.嵌套表单(指定子表单) 子表单被用来分离一个长的表单为几个逻辑部分。 例如,我们可以分离用户注册表单为两部分:登录信息和档案信息。 每个子表单和一个数据模型有无关联均可。若一个子表单需要关联一个数据模型,我们也可以配置它的 CForm::model 属性。 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', ), ), ), 对于嵌套表单的Controller、view、表单配置文件的写法请参见yiichina.com |
表格输入验证(注意:这里yiichina.com的写法可能有点错误,我这里已修正)
//控制器处理 public function actionBatchUpdate() { // 假设每一项(item)是一个 'Item' 类的实例, // 提取要通过批量模式更新的项 $items=$this->getItemsToUpdate(); if(isset($_POST['Item'])) { $valid=true; foreach($items as $i=>$item) { if(isset($_POST['Item'])) $item->attributes=$_POST['Item']; $valid=$valid && $item->validate();//所有条目有效才算有效 } if($valid) // 如果所有项目有效 // ...则在此处做一些操作 } // 显示视图收集表格输入 $this->render('batchUpdate',array('items'=>$items));//转发到视图batchUpdate } |
//视图batchUpdate显示。("[$i]name"的形式) <?php echo CHtml::beginForm(); ?> <table> <tr><th>Name</th><th>Price</th><th>Count</th><th>Description</th></tr> <?php foreach($items as $i=>$item): ?> <tr> <td><?php echo CHtml::activeTextField($item,"[$i]name"); ?></td> <td><?php echo CHtml::activeTextField($item,"[$i]price"); ?></td> <td><?php echo CHtml::activeTextField($item,"[$i]count"); ?></td> <td><?php echo CHtml::activeTextArea($item,"[$i]description"); ?></td> </tr> <?php endforeach; ?> </table>
<?php echo CHtml::submitButton('Save'); ?> <?php echo CHtml::endForm(); ?> </div><!-- form --> |
数据
使用数据库连接
法一: $connection=new CDbConnection($dsn,$username,$password); // 建立连接。你可以使用 try...catch 捕获可能抛出的异常 $connection->active=true; ...... $connection->active=false; // 关闭连接 |
法二: 由于 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 ), ), ) |
执行sql
$command=$connection->createCommand($sql); |
一条 SQL 语句会通过 CDbCommand 以如下两种方式被执行:
1.execute(): 执行一个无查询 (non-query)SQL语句, 例如 INSERT, UPDATE 和 DELETE 。如果成功,它将返回此执行所影响的行数。
2.query(): 执行一条会返回若干行数据的 SQL 语句,例如 SELECT。 如果成功,它将返回一个 CDbDataReader 实例,通过此实例可以遍历数据的结果行。为简便起见, (Yii)还实现了一系列 queryXXX() 方法以直接返回查询结果。
$rowCount=$command->execute(); // 执行无查询 SQL $dataReader=$command->query(); // 执行一个 SQL 查询,返回CDbDataReader实例 $rows=$command->queryAll(); // 查询并返回结果中的所有行,直接返回数据(数组) $row=$command->queryRow(); // 查询并返回结果中的第一行,直接返回数据(数组) $column=$command->queryColumn(); // 查询并返回结果中的第一列,直接返回数据(数组) $value=$command->queryScalar(); // 查询并返回结果中第一行的第一个字段,直接返回数据(数组) |
获取查询结果
在 CDbCommand::query() 生成 CDbDataReader 实例之后,你可以通过重复调用 CDbDataReader::read()获取结果中的行。你也可以在PHP 的 foreach 语言结构中使用 CDbDataReader 一行行检索数据。
$dataReader=$command->query(); while(($row=$dataReader->read())!==false) { ... }// 重复调用 read() 直到它返回 false
//或foreach($dataReader as $row) { ... }// 使用 foreach 遍历数据中的每一行
// 一次性提取所有行到一个数组 $rows=$dataReader->readAll();
|
使用事务
1.开始事务.
2.一个个执行查询。任何对数据库的更新对外界不可见。
3.提交事务。如果事务成功,更新变为可见。
4.如果查询中的一个失败,整个事务回滚。
$transaction=$connection->beginTransaction(); try { $connection->createCommand($sql1)->execute(); $connection->createCommand($sql2)->execute(); //.... other SQL executions $transaction->commit(); } catch(Exception $e) // 如果有一条查询失败,则会抛出异常 { $transaction->rollBack(); }
|
绑定参数
参数绑定必须在 SQL 语句执行之前完成。参数占位符可以是命名的 (表现为一个唯一的标记) 或未命名的 (表现为一个问号) 以使用实际参数替换这些占位符。
1要避免 SQL 注入攻击
2.提高重复执行的 SQL 语句的效率:在查询执行前,改变参数的实际值,可以改变查询结果。这在循环代码块中相当有用。
CDbCommand::bindParam()//使用一个 PHP 变量绑定参数。对于那些内存中的大数据块参数,处于性能的考虑,应优先使用此。
CDbCommand::bindValue() //使用一个值
绑定列
是对输出的绑定,函数为bindColumn。这样在每次获取查询结果中的一行时就会自动使用最新的值填充。
$sql="SELECT username, email FROM tbl_user"; $dataReader=$connection->createCommand($sql)->query(); $dataReader->bindColumn(1,$username); // 使用 $username 变量绑定第一列 (username) $dataReader->bindColumn(2,$email); // 使用 $email 变量绑定第二列 (email)
while($dataReader->read()!==false) { // $username 和 $email 含有当前行中的 username 和 email } |
使用表前缀
表前缀是指在当前连接的数据库中的数据表的名字前面添加的一个字符串。 它常用于共享的服务器环境,这种环境中多个应用可能会共享同一个数据库,要使用不同的表前缀以相互区分。 例如,一个应用可以使用 tbl_ 作为表前缀而另一个可以使用 yii_。
配置 CDbConnection::tablePrefix 属性为所希望的表前缀。 然后,在 SQL 语句中使用{{TableName}} 代表表的名字,其中的 TableName是指不带前缀的表名。
Query Builder
提供了一个面向对象的方法来书写SQL语句,好处有
It allows building complex SQL statementsprogrammatically.
It automatically quotes table names andcolumn names to prevent conflict with SQL reserved words and specialcharacters.
It also quotes parameter values and usesparameter binding when possible, which helps reduce risk of SQL injectionattacks.
It offers certain degree of DB abstraction,which simplifies migration to different DB platforms.
一、Building Data Retrieval Queries
$user = Yii::app()->db->createCommand() ->select('id, username, profile') ->from('tbl_user u') ->join('tbl_profile p', 'u.id=p.user_id') ->where('id=:id', array(':id'=>$id)) ->queryRow(); |
以下是几个补充的可用功能
Retrieving SQLs 提取sql文本,执行了CDbCommand::getText(). $sql = Yii::app()->db->createCommand() ->select('*') ->from('tbl_user') ->text; |
Alternative Syntax for Building Queries 使用赋值的形式 Sometimes, using method chaining to build a query may not be the optimal choice. The Yii Query Builder allows a query to be built using simple object property assignments. In particular, for each query builder method, there is a corresponding property that has the same name.
$command->select(array('id', 'username')); $command->select = array('id', 'username'); |
the CDbConnection::createCommand() method can take an array as the parameter. 使用array做createCommand()的参数。 $row = Yii::app()->db->createCommand(array( 'select' => array('id', 'username'), 'from' => 'tbl_user', 'where' => 'id=:id', 'params' => array(':id'=>1), ))->queryRow(); |
Building Multiple Queries多次查询 the CDbCommand::reset() method must be invoked to clean up the previous query.
$command = Yii::app()->db->createCommand(); $users = $command->select('*')->from('tbl_users')->queryAll(); $command->reset(); // clean up the previous query $posts = $command->select('*')->from('tbl_posts')->queryAll(); |
二、Building Data Manipulation Queries
insert(): inserts a row into a table update(): updates the data in a table delete(): deletes the data from a table |
三、Building Schema Manipulation Queries
Besides normal data retrieval andmanipulation queries, the query builder also offers a set of methods forbuilding and executing SQL queries that can manipulate the schema of adatabase. In particular, it supports the following queries:
createTable(): creates a table renameTable(): renames a table dropTable(): drops a table truncateTable(): truncates a table addColumn(): adds a table column renameColumn(): renames a table column alterColumn(): alters a table column dropColumn(): drops a table column createIndex(): creates an index dropIndex(): drops an index 注,这种方式可能需要用到抽象数据类型Abstract Data Types。The following abstract data types are supported by the query builder.
pk: a generic primary key type, will be converted into int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY for MySQL; string: string type, will be converted into varchar(255) for MySQL; text: text type (long string), will be converted into text for MySQL; integer: integer type, will be converted into int(11) for MySQL; float: floating number type, will be converted into float for MySQL; decimal: decimal number type, will be converted into decimal for MySQL; datetime: datetime type, will be converted into datetime for MySQL; timestamp: timestamp type, will be converted into timestamp for MySQL; time: time type, will be converted into time for MySQL; date: date type, will be converted into date for MySQL; binary: binary data type, will be converted into blob for MySQL; boolean: boolean type, will be converted into tinyint(1) for MySQL; money: money/currency type, will be converted into decimal(19,4) for MySQL. This type has been available since version 1.1.8. |
Active Record
使对象操作直接转换成对数据库操作
可能的步骤是: 1.继承Active Record类 2.覆盖请覆盖 tableName() 方法 3.覆盖 primaryKey() 方法指定哪一列或哪几列作为主键。 4.要向数据表中插入新行,我们要创建一个相应 AR 类的实例,设置其与表的列相关的属性,然后调用 save() 方法完成插入。 |
读取记录,可以通过如下方式调用 find 系列方法中的一种: // 查找满足指定条件的结果中的第一行 $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->save(); |
删除记录 如果一个 AR 实例被一行数据填充,我们也可以删除此行数据。 $post=Post::model()->findByPk(10); // 假设有一个帖子,其 ID 为 10 $post->delete(); // 从数据表中删除此行 |
数据验证 当调用 save() 时, AR 会自动执行数据验证。 |
对比记录 类似于表记录,AR 实例由其主键值来识别。 因此,要对比两个 AR 实例,假设它们属于相同的 AR 类, 我们只需要对比它们的主键值。 然而,一个更简单的方式是调用 CActiveRecord::equals()。 |
使用 AR 处理事务 每个 AR 实例都含有一个属性名叫 dbConnection ,是一个 CDbConnection 的实例,这样我们可以在需要时配合 AR 使用由 Yii DAO 提供的 事务 功能: $model=Post::model(); $transaction=$model->dbConnection->beginTransaction(); …… |
命名范围:详见yiichina.com |