Доопрацювання моделі Post

Модель 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).


Для чого реклама?