由 yiic
工具生成的 Post
日志模型类主要需要做如下两处修改:
rules()
方法:指定对模型属性的验证规则;relations()
方法:指定关联的对象;信息: 模型 包含了一系列属性,每个属性关联到数据表中相应的列。属性可以在类成员变量中显式定义,也可以隐式定义,不需要事先声明。
rules()
方法 ¶我们先来指定验证规则,它可以确保用户输入的信息在保存到数据库之前是正确的。例如, Post
的 status
属性应该是 1, 2 或 3 中的任一数字。 yiic
工具其实也为每个模型生成了验证规则,但是这些规则是基于数据表的列信息的,可能并不是非常恰当。
基于需求分析,我们把 rules()
做如下修改:
public function rules()
{
return array(
array('title, content, status', 'required'),
array('title', 'length', 'max'=>128),
array('status', 'in', 'range'=>array(1,2,3)),
array('tags', 'match', 'pattern'=>'/^[\w\s,]+$/',
'message'=>'Tags can only contain word characters.'),
array('tags', 'normalizeTags'),
array('title, status', 'safe', 'on'=>'search'),
);
}
在上面的代码中,我们指定了 title
, content
和 status
属性是必填项;title
的长度不能超过 128;status
属性值应该是 1 (草稿), 2 (已发布) 或 3 (已存档);tags
属性应只允许使用单词字母和逗号。另外,我们使用 normalizeTags
来规范化用户输入的Tag,使Tag是唯一的且整齐地通过逗号分隔。最后的规则会被搜索功能用到,这个我们后面再讲。
像 required
, length
, in
和 match
这几个验证器(validator)是Yii提供的内置验证器。normalizeTags
验证器是一个基于方法的验证器,我们需要在 Post
类中定义它。关于如何设置验证规则的更多信息,请参考 指南。
public function normalizeTags($attribute,$params)
{
$this->tags=Tag::array2string(array_unique(Tag::string2array($this->tags)));
}
其中的 array2string
和 string2array
是在 Tag
模型类中定义的新方法。详情请参考 /wwwroot/yii/demos/blog/protected/models/Tag.php
文件。
rules()
方法中定义的规则会在模型实例调用其 validate() 或 save() 方法时逐一执行。
注意: 请务必记住
rules()
中出现的属性必须是那些通过用户输入的属性。其他的属性,如Post
模型中的id
和create_time
,是通过我们的代码或数据库设定的,不应该出现在rules()
中。详情请参考 属性的安全赋值(Securing Attribute Assignments).
作出这些修改之后,我们可以再次访问日志创建页检查新的验证规则是否已生效。
relations()
方法 ¶最后我们来自定义 relations()
方法,以指定与日志相关的对象。通过在 relations()
中声明这些相关对象,我们就可以利用强大的 Relational ActiveRecord (RAR) 功能来访问日志的相关对象,例如它的作者和评论。不需要自己写复杂的 SQL JOIN 语句。
我们自定义 relations()
方法如下:
public function relations()
{
return array(
'author' => array(self::BELONGS_TO, 'User', 'author_id'),
'comments' => array(self::HAS_MANY, 'Comment', 'post_id',
'condition'=>'comments.status='.Comment::STATUS_APPROVED,
'order'=>'comments.create_time DESC'),
'commentCount' => array(self::STAT, 'Comment', 'post_id',
'condition'=>'status='.Comment::STATUS_APPROVED),
);
}
我们还在 Comment
模型类中定义了两个在上面的方法中用到的常量。
class Comment extends CActiveRecord
{
const STATUS_PENDING=1;
const STATUS_APPROVED=2;
......
}
relations()
中声明的关系表明:
User
,它们的关系建立在日志的 author_id
属性值之上;Comment
,它们的关系建立在评论的 post_id
属性值之上。这些评论应该按它们的创建时间排列,且评论必须已通过审核;commentCount
关系有一点特别,它返回一个关于日志有多少条评论的一个聚合结果。通过以上的关系声明,我们现在可以按下面的方式很容易的访问日志的作者和评论信息。
$author=$post->author;
echo $author->username;
$comments=$post->comments;
foreach($comments as $comment)
echo $comment->content;
关于如何声明和使用关系的更多详情,请参考 指南.
url
属性 ¶日志是一份可以通过一个唯一的URL访问的内容。我们可以在 Post
模型中添加一个 url
属性,这样同样的创建URL的代码就可以被复用,而不是在代码中到处调用 CWebApplication::createUrl 。 稍后讲解怎样美化 URL 的时候,我们将看到添加这个属性给我们带来了超拽的便利。
要添加 url
属性,我们可以按如下方式给 Post
类添加一个 getter 方法:
class Post extends CActiveRecord
{
public function getUrl()
{
return Yii::app()->createUrl('post/view', array(
'id'=>$this->id,
'title'=>$this->title,
));
}
}
注意我们除了使用日志的ID之外,还添加了日志的标题作为URL中的一个 GET 参数。这主要是为了搜索引擎优化 (SEO) 的目的,在 美化 URL 中将会讲述。
由于 CComponent 是 Post
的最顶级父类,添加 getUrl()
这个 getter 方法使我们可以使用类似 $post->url
这样的表达式。当我们访问 $post->url
时,getter 方法将会被执行,它的返回结果会成为此表达式的值。关于这种组件的更多详情,请参考 指南。
由于日志的状态在数据库中是以一个整型数字存储的,我们需要提供一个文本话的表现形式,这样在它显示给最终用户时会更加直观。在一个大的系统中,类似的需求是很常见的。
作为一个总体的解决方案,我们使用 tbl_lookup
表存储数字值和被用于其他数据对象的文本值的映射。为了更简单的访问表中的文本数据,我们按如下方式修改 Lookup
模型类:
class Lookup extends CActiveRecord
{
private static $_items=array();
public static function items($type)
{
if(!isset(self::$_items[$type]))
self::loadItems($type);
return self::$_items[$type];
}
public static function item($type,$code)
{
if(!isset(self::$_items[$type]))
self::loadItems($type);
return isset(self::$_items[$type][$code]) ? self::$_items[$type][$code] : false;
}
private static function loadItems($type)
{
self::$_items[$type]=array();
$models=self::model()->findAll(array(
'condition'=>'type=:type',
'params'=>array(':type'=>$type),
'order'=>'position',
));
foreach($models as $model)
self::$_items[$type][$model->code]=$model->name;
}
}
我们的新代码主要提供了两个静态方法: Lookup::items()
和 Lookup::item()
。前者返回一个属于指定的数据类型的字符串列表,后者按指定的数据类型和数据值返回一个具体的字符串。
我们的博客数据库已经预置了两个查询类别: PostStatus
和 CommentStatus
。前者代表可用的日志状态,后者代表评论状态。
为了使我们的代码更加易读,我们还定义了一系列常量,用于表示整数型状态值。我们应该在涉及到相应的状态值时在代码中使用这些常量。
class Post extends CActiveRecord
{
const STATUS_DRAFT=1;
const STATUS_PUBLISHED=2;
const STATUS_ARCHIVED=3;
......
}
这样,我们可以通过调用 Lookup::items('PostStatus')
来获取可用的日志状态列表(按相应的整数值索引的文本字符串),通过调用 Lookup::item('PostStatus', Post::STATUS_PUBLISHED)
来获取发布状态的文本表现形式。
Found a typo or you think this page needs improvement?
Edit it on github !
Signup or Login in order to comment.