Модель Post
, сгенерированная при помощи Gii
, нуждается в следующих изменениях:
rules()
: задаёт правила валидации атрибутов модели;relations()
: задаёт связи с другими объектами.Информация: Модель состоит из набора атрибутов, каждый из которых ассоциируется с соответствующим полем в таблице БД. Атрибуты могут быть описаны явно как переменные класса, либо использоваться без какого-либо описания.
rules()
¶В первую очередь необходимо определить правила валидации, которые позволят
убедиться в том, что данные, введённые пользователем, корректны до их сохранения в БД.
К примеру, атрибут status
модели Post
должен быть целым числом, равным 1, 2 или 3.
Консоль Gii
генерирует правила валидации для каждой модели. При этом
используется структура БД, поэтому некоторые правила могут оказаться неточными.
Основываясь на анализе требований, изменим метод 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'=>'В тегах можно использовать только буквы.'),
array('tags', 'normalizeTags'),
array('title, status', 'safe', 'on'=>'search'),
);
}
В коде выше мы определили, что атрибуты title
, content
и status
являются
обязательными для заполнения. Длина title
не должна превышать 128 символов.
Значение status
может быть 1 (черновик), 2 (опубликовано) или 3 (в архиве).
В tags
могут содержаться только буквы, запятые и пробелы. Вводимые пользователем
теги дополнительно нормализуются при помощи normalizeTags
. Это делается для того,
чтобы теги были уникальными и правильно разделялись запятыми. Последнее правило
используется поиском и будет описано позже.
Валидаторы, такие как required
, length
, in
и match
являются стандартными
валидаторами Yii. Валидатор normalizeTags
использует определённый в классе Post
метод. За дополнительной информацией о том, как описывать правила валидации вы
можете обратиться к полному руководству.
public function normalizeTags($attribute,$params)
{
$this->tags=Tag::array2string(array_unique(Tag::string2array($this->tags)));
}
где array2string
и string2array
- новые методы, которые мы должны
определить в классе модели Tag
:
public static function string2array($tags)
{
return preg_split('/\s*,\s*/',trim($tags),-1,PREG_SPLIT_NO_EMPTY);
}
public static function array2string($tags)
{
return implode(', ',$tags);
}
Правила, описанные в методе rules()
, вызываются по очереди при вызове методов модели
validate() или save().
Примечание: Важно помнить, что атрибуты, описываемые в
rules()
должны вводиться пользователем. Другие атрибуты моделиPost
, такие какid
илиcreate_time
, заполняемые в коде или напрямую в БД не должны присутствовать вrules()
. Подробнее это описано в разделе Безопасное присваивание значений атрибутам.
После того, как мы сделали описанные изменения, мы можем зайти на страницу создания записи и проверить, что новые правила валидации работают.
relations()
¶Далее укажем в методе relations()
связанные с записью объекты. После этого мы
сможем использовать реляционную 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. Вместо повсеместного
вызова CWebApplication::createUrl для формирования этого URL, мы можем
добавить свойство url
модели Post
и повторно использовать код для генерации URL.
Позже мы опишем, как получить красивые URL. Использование свойства модели позволит
реализовать это максимально удобно.
Для того, чтобы добавить свойство url
, мы добавляем геттер в класс Post
:
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()
позволяет
нам писать код вроде $post->url
. При обращении к $post->url
будет вызван геттер
и мы получим результат его выполнения. Более подробно это описано
в полном руководстве.
Так как статус записи хранится в БД в виде числа, нам необходимо получить его текстовое представление для отображения пользователям. Для больших систем такое требование является довольно типичным.
Для хранения связей между целыми числами и их текстовым представлением, необходимым
другим объектам данных, мы используем таблицу 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.