1.创建YII
2.YII规范
Yii典型的工作流
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 - 管理主题。
$event
是 CEvent
或其子类的实例。
fourm
的模块的典型的目录结构:
ucfirst($id).'Module'
确定, 其中的 $id
代表模块的 ID (或者说模块的目录名字)。 模块类是存储模块代码间可共享信息的中心位置。例如,我们可以使用 CWebModule::params 存储模块参数,使用 CWebModule::components 分享模块级的 应用组件 .
modules
中。 然后在应用的 modules
属性中声明模块 ID 。例如,为了使用上面的
forum
模块, 我们可以使用如下 应用配置
:(main.php)
moduleID/controllerID/actionID
访问。 例如,假设上面的
forum
模块有一个名为
PostController
的控制器,我们就可以通过 路由
forum/post/create
访问此控制器中的
create
动作。 此路由对应的 URL 即
http://www.example.com/index.php?r=forum/post/create
.
system.web.CController
会被翻译为
yii/framework/web/CController
。
为方便起见,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
。
product_order
)。
tbl_
。这样当应用所使用的表和另一个应用说使用的表共存于同一个数据库中时就特别有用。 这两个应用的表可以通过使用不同的表前缀很容易地区别开。
array('AttributeList', 'Validator', 'on'=>'ScenarioList', ...附加选项)
public function ValidatorName($attribute,$params) { ... }
Validator
可以是一个预定义的验证器类的别名。在上面的例子中,
required
名字是 CRequiredValidator
的别名,它用于确保所验证的特性值不为空。
$model=new LoginForm;$model->attributes=$_POST['LoginForm'];
array('username, password', 'required', 'on'=>'login, register'), array('email', 'required', 'on'=>'register'),
array('content', 'safe')
login
动作中,我们使用 Yii::app()->user->returnUrl
获取之前需要身份验证的页面URL。 组件 Yii::app()->user
是一种 CWebUser (或其子类) ,它表示用户会话信息(例如 用户名,状态)。更多详情, 请参考 验证与授权.
<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 -->
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)); }
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', ), ), );
<h1>Login</h1> <div class="form"> <?php echo $form; ?> </div>
'username'=>array(
'type'=>'text', 'maxlength'=>32, ),
maxlength
不是一个 CFormInputElement
属性,它被渲染作为 HTML 文本输入框的
maxlength
属性。
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', ); }}
return array(
'elements'=>array( ...... 'password'=>array(
'type'=>'password', 'maxlength'=>32, ), '<hr />', 'rememberMe'=>array(
'type'=>'checkbox', )
), ...... );
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', ...... ), ...... ), ...... );
username
,我们可以使用下面的代码
$username = $form['username']; $email = $form['user']['email'];
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)); }
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', ), ), );
<h1>Register</h1> <div class="form"> <?php echo $form; ?> </div>
$connection=new CDbConnection($dsn,$username,$password); $connection->active=true; ...... $connection->active=false; // 关闭连接
Yii::app()->db
访问数据库连接了。它已经被自动激活了,除非我们特意配置了CDbConnection::autoConnect 为 false。通过这种方式,这个单独的DB连接就可以在我们代码中的很多地方共享。
$connection=Yii::app()->db; // 假设你已经建立了一个 "db" 连接// 如果没有,你可能需要显式建立一个连接:
// $connection=new CDbConnection($dsn,$username,$password);
$command=$connection->createCommand($sql); // 如果需要,此 SQL 语句可通过如下方式修改:// $command->text=$newSQL;
execute(): 执行一个无查询 (non-query)SQL语句, 例如 INSERT
, UPDATE
和 DELETE
。如果成功,它将返回此执行所影响的行数。
SELECT
。 如果成功,它将返回一个 CDbDataReader 实例,通过此实例可以遍历数据的结果行。为简便起见, (Yii)还实现了一系列
queryXXX()
方法以直接返回查询结果。
foreach
语言结构中使用 CDbDataReader 一行行检索数据。
$transaction=$connection->beginTransaction(); try{
$connection->createCommand($sql1)->execute(); $connection->createCommand($sql2)->execute(); //.... other SQL executions
$transaction->commit(); }catch(Exception $e) // 如果有一条查询失败,则会抛出异常{
$transaction->rollBack(); }
{{TableName}}
代表表的名字,其中的
TableName
是指不带前缀的表名。 例如,如果数据库含有一个名为
tbl_user
的表,而
tbl_
被配置为表前缀,那我们就可以使用如下代码执行用户相关的查询。
$sql='SELECT * FROM {{user}}'; $users=$connection->createCommand($sql)->queryAll();
$post=new Post; $post->title='sample post'; $post->content='post body content'; $post->save();
return array(
'import'=>array(
'application.models.*', ), );
public function tableName(){
return '{{post}}'; }
$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);
$post=Post::model()->find('postID=:postID', array(':postID'=>10));
find
方法传递一个数组。 数组的键和值各自对应标准(criterion)的属性名和值,上面的例子可以重写为如下:
$post=Post::model()->find(array(
'select'=>'title', 'condition'=>'postID=:postID', 'params'=>array(':postID'=>10), ));
// 查找满足指定条件的所有行 $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);
// 获取满足指定条件的行数 $n=Post::model()->count($condition,$params); // 通过指定的 SQL 获取结果行数 $n=Post::model()->countBySql($sql,$params); // 检查是否至少有一行复合指定的条件 $exists=Post::model()->exists($condition,$params);
$post=Post::model()->findByPk(10); $post->title='new post title'; $post->save(); // 将更改保存到数据库
new
操作符创建的,调用 save()
将会向数据表中插入一行新数据; 如果 AR 实例是某个
find
或
findAll
方法的结果,调用 save()
将更新表中现有的行。 实际上,我们是使用 CActiveRecord::isNewRecord
说明一个 AR 实例是不是新的。
// 更新符合指定条件的行 Post::model()->updateAll($attributes,$condition,$params); // 更新符合指定条件和主键的行 Post::model()->updateByPk($pk,$attributes,$condition,$params); // 更新满足指定条件的行的计数列 Post::model()->updateCounters($counters,$condition,$params);
// 删除符合指定条件的行 Post::model()->deleteAll($condition,$params); // 删除符合指定条件和主键的行 Post::model()->deleteByPk($pk,$condition,$params);
$post->attributes=$_POST['Post'];// 假设 $_POST['Post'] 是一个以列名索引列值为值的数组
$post->save();
CActiveRecord 提供了几个占位符方法,它们可以在子类中被覆盖以自定义其工作流。
beforeSave 和 afterSave: 这两个将在保存 AR 实例之前和之后被调用。
beforeDelete 和 afterDelete: 这两个将在一个 AR 实例被删除之前和之后被调用。
afterConstruct: 这个将在每个使用 new
操作符创建 AR 实例后被调用。
beforeFind: 这个将在一个 AR 查找器被用于执行查询(例如 find()
, findAll()
)之前被调用。 1.0.9 版本开始可用。
afterFind: 这个将在每个 AR 实例作为一个查询结果创建时被调用。
$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(); }
我们已经了解了怎样使用 Active Record (AR) 从单个数据表中获取数据。 在本节中,我们讲解怎样使用 AR 连接多个相关数据表并取回关联(join)后的数据集。
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
.
'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'), ); }}
$author
代表一个
User
AR 实例, 我们可以使用
$author->posts
访问其关联的
Post
实例。
// 获取 ID 为 10 的帖子 $post=Post::model()->findByPk(10);
// 获取帖子的作者(author): 此处将执行一个关联查询。 $author=$post->author;
N
个帖子的作者,使用这种懒惰式加载将会导致执行
N
个关联查询。 这种情况下,我们应该改为使用
渴求式加载(eager loading)
方式。
$posts=Post::model()->with('author')->findAll();
上述代码将返回一个 Post
实例的数组。与懒惰式加载方式不同,在我们访问每个 Post
实例中的 author
属性之前,它就已经被关联的 User
实例填充了。 渴求式加载通过 一个 关联查询返回所有帖子及其作者,而不是对每个帖子执行一次关联查询。
我们可以在 with() 方法中指定多个关系名字,渴求式加载将一次性全部取回他们。例如,如下代码会将帖子连同其作者和分类一并取回。
$posts=Post::model()->with('author','categories')->findAll();
$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', ));
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_MANY
relations. 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'), ); }}
$posts=Post::model()->with('comments')->findAll(array(
'order'=>'t.create_time, comments.create_time'));
$user=User::model()->findByPk(1); $posts=$user->posts(array('condition'=>'status=1'));
public function relations(){
return array(
'comments' => array(self::HAS_MANY, 'Comment', 'post_id', 'together'=>false), ); }
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)'), ); }}
commentCount
calculates the number of comments belonging to a post, and
categoryCount
calculates the number of categories that a post belongs to.
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 版开始支持。
// 值$value 在缓存中最多保留30秒Yii::app()->cache->set($id, $value, 30);
// 此值将在30秒后失效// 也可能因依赖的文件发生了变化而更快失效Yii::app()->cache->set($id, $value, 30, new CFileCacheDependency('FileName'));
$value
,依赖关系将被检查,如果发生改变,我们将会得到一个 false 值,表示数据需要被重新生成。
如下是可用的缓存依赖的简要说明:
CFileCacheDependency: 如果文件的最后修改时间发生改变,则依赖改变。
CDirectoryCacheDependency: 如果目录和其子目录中的文件发生改变,则依赖改变。
CDbCacheDependency: 如果指定 SQL 语句的查询结果发生改变,则依赖改变。
CGlobalStateCacheDependency: 如果指定的全局状态发生改变,则依赖改变。全局状态是应用中的一个跨请求,跨会话的变量。它是通过 CApplication::setGlobalState() 定义的。
CChainedCacheDependency: 如果链中的任何依赖发生改变,则此依赖改变。
CExpressionDependency: 如果指定的 PHP 表达式的结果发生改变,则依赖改变。此类从版本 1.0.4 起可用。
...别的HTML内容... <?php if($this->beginCache($id)) { ?> ...被缓存的内容... <?php $this->endCache(); } ?> ...别的HTML内容...
if
语句内的内容将被执行并在 endCache()
触发时缓存。
也许是最常见的选项是duration,指定了内容在缓存中多久有效。和CCache::set()过期参数有点类似。下面的代码缓存内容片段最多一小时:
...其他HTML内容... <?php if($this->beginCache($id, array('duration'=>3600))) { ?> ...被缓存的内容... <?php $this->endCache(); } ?> ...其他HTML内容...
像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内容...
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.
有时候,我们希望片段缓存只对某些类型的请求启用。例如,对于某张网页上显示表单,我们只想要缓存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内容...
嵌套缓存可以设定不同的缓存选项。例如, 在上面的例子中内部缓存和外部缓存可以设置时间长短不同的持续值。当数据存储在外部缓存无效,内部缓存仍然可以提供有效的内部片段。 然而,反之就不行了。如果外部缓存包含有效的数据, 它会永远保持缓存副本,即使内容中的内部缓存已经过期。
如果想要缓存整个页面,我们应该跳过产生网页内容的动作执行。我们可以使用COutputCache作为动作 过滤器来完成这一任务。下面的代码演示如何配置缓存过滤器:
public function filters(){
return array(
array(
'COutputCache', 'duration'=>100, 'varyByParam'=>array('id'), ), ); }
上述过滤器配置会使过滤器适用于控制器中的所有行动。 我们可能会限制它在一个或几个行动通过使用插件操作器。 更多的细节中可以看过滤器。
当使用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()方法中。回调将返回动态内容而不是仅仅显示它。
适用扩展通常包含了以下三步:
extensions/xyz
下,这里的 xyz
是扩展的名称.xyz
,我们也可以使用路径别名定位到包含了
xyz
所有文件的基目录.
$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
属性里.
return array(
'components'=>array(
'db'=>array(
'class'=>'CDbConnection', 'behaviors'=>array(
'xyz'=>array(
'class'=>'ext.xyz.XyzBehavior', 'property1'=>'value1', 'property2'=>'value2', ), ), ), //....
), );
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', ), ); }
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(); ?>
动作 被 控制器 用于响应指定的用户请求.假设动作的类 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
来访问.
过滤器 也被 控制器 使用.过滤器主要用于当其被 动作 挂起时预处理,提交处理用户请求.假设过滤器的类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');
来包含它的类文件.然后,我们既可以创建一个类的实例,配置它的属性,也可以调用它的方法.我们还可以创建一个新的子类来扩展它.
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));