0 follower

Создание и редактирование записей

После того, как мы закончили с моделью Post, займёмся контроллером PostController и его отображениями. В данном разделе мы настроим правила доступа операций CRUD. Затем изменим код, отвечающий за создание(create) и обновление(update). В завершение мы реализуем предварительный просмотр для обеих операций.

1. Настройка правил доступа

Первое, что мы запланировали — настройка прав доступа. Код, сгенерированный при помощи yiic нам не подойдёт.

Необходимо изменить метод accessRules() в файле /wwwroot/blog/protected/controllers/PostController.php следующим образом:

public function accessRules()
{
    return array(
        array('allow',  // все пользователи могут использовать действия 'list' и 'show'
            'actions'=>array('list', 'show'),
            'users'=>array('*'),
        ),
        array('allow', // аутентифицированные пользователи могут всё
            'users'=>array('@'),
        ),
        array('deny',  // все остальные не могут ничего
            'users'=>array('*'),
        ),
    );
}

Описанные выше правила разрешают всем пользователям выполнять действия list и show. Аутентифицированным — любые действия, включая admin. Всем остальным пользователям запрещено всё. Стоит отметить, что правила применяются в порядке их описания. Первое сработавшее правило определяет, давать доступ или не давать. К примеру, если текущий пользователь является владельцем системы и пытается зайти на страницу создания записи, будет применено второе правило и доступ будет разрешён.

2. Правки в действиях create и update

Операции create и update довольно похожи. В обоих случаях требуется вывести HTML форму для сбора данных, вводимых пользователем. Также требуется валидация и сохранение данных в БД. Главное отличие в том, что при update форма будет заполняться данными о редактируемой записи. По этой причине yiic генерирует вложенное отображение /wwwroot/blog/protected/views/post/_form.php, которое включается как в отображение create, так и отображение update для вывода HTML формы.

Для начала изменим файл _form.php таким образом, чтобы форма собирала только нужные нам данные: title, content и status. Для первых двух атрибутов мы используем текстовые поля. Для status — выпадающий список с всеми возможными состояниями записи:

<?php echo CHtml::activeDropDownList($post,'status',Post::model()->statusOptions); ?>

Подсказка: Для получения состояний можно вместо Post::model()->statusOptions использовать Post::model()->getStatusOptions(). Это возможно так как Post является компонентом, что даёт нам возможность использовать свойства, определённые как методы (геттеры).

Далее изменим класс Post таким образом, чтобы он автоматически выставлял некоторые атрибуты (такие, как createTime и authorId) непосредственно перед сохранением записи в БД. Перекроем метод beforeValidate():

protected function beforeValidate($on)
{
    $parser=new CMarkdownParser;
    $this->contentDisplay=$parser->safeTransform($this->content);
    if($this->isNewRecord)
    {
        $this->createTime=$this->updateTime=time();
        $this->authorId=Yii::app()->user->id;
    }
    else
        $this->updateTime=time();
    return true;
}

В данном методе мы используем CMarkdownParser для того, чтобы конвертировать текст в формате Markdown в HTML и сохранить результат в contentDisplay. Сделано это для того, чтобы не конвертировать текст каждый раз при отображении записи. Если запись новая, мы выставляем время создания(createTime) и автора(authorId). Иначе мы выставляем время обновления(updateTime) как текущее время. Стоит отметить, что данный метод вызывается автоматически при вызове методов модели validate() или save().

Так как мы хотим сохранить теги записи в таблицу Tag, нам понадобится добавить в класс Post метод, который будет вызываться автоматически после сохранения записи:

protected function afterSave()
{
    if(!$this->isNewRecord)
        $this->dbConnection->createCommand(
            'DELETE FROM PostTag WHERE postId='.$this->id)->execute();
 
    foreach($this->getTagArray() as $name)
    {
        if(($tag=Tag::model()->findByAttributes(array('name'=>$name)))===null)
        {
            $tag=new Tag(array('name'=>$name));
            $tag->save();
        }
        $this->dbConnection->createCommand(
            "INSERT INTO PostTag (postId, tagId) VALUES ({$this->id},{$tag->id})")->execute();
    }
}
 
public function getTagArray()
{
    // break tag string into a set of tags
    return array_unique(
        preg_split('/\s*,\s*/',trim($this->tags),-1,PREG_SPLIT_NO_EMPTY)
    );
}

Сначала удаляем все записи, связанные с данной из таблицы PostTag. Затем вставляем новые теги в таблицу Tag и добавляем соответствующую запись в PostTag. Логика в данном случае немного сложная, поэтому вместо использования ActiveRecord, мы пишем выражения SQL и выполняем их напрямую через соединение с БД.

Подсказка: Хорошим тоном является отделение бизнес-логики, такой как beforeValidate() и afterSave(), описанные выше и размещение её в моделях, а не в контроллерах.

3. Реализация предварительного просмотра

Кроме изменений, описанных выше, нам необходимо добавить возможность предварительного просмотра, которая позволит нам оценить запись до её сохранения в БД.

Для того, чтобы добавить кнопку «предварительный просмотр» и само отображение записи, изменим представление _form.php. Просмотр отображается только при нажатии кнопки и если не произошло ошибок валидации.

<?php echo CHtml::submitButton('Предварительный просмотр',array('name'=>'previewPost')); ?>
......
<?php if(isset($_POST['previewPost']) && !$post->hasErrors()): ?>
...отображаем предварительный просмотр модели $post...
<?php endif; ?>

Добавим в методы actionCreate() и actionUpdate() контроллера PostController обработку запроса на предварительный просмотр. Ниже приведён код actionCreate(), который очень похож на то, что необходимо сделать и в actionUpdate():

public function actionCreate()
{
    $post=new Post;
    if(isset($_POST['Post']))
    {
        $post->attributes=$_POST['Post'];
        if(isset($_POST['previewPost']))
            $post->validate();
        else if(isset($_POST['submitPost']) && $post->save())
            $this->redirect(array('show','id'=>$post->id));
    }
    $this->render('create',array('post'=>$post));
}

При нажатии на кнопку «предварительный просмотр» мы вызываем $post->validate() для выполнения валидации введённых данных. Если же нажали на кнопку «сохранить»(submit) — пробуем сохранить запись при помощи $post->save(), который также выполняет валидацию данных. Если сохранение прошло успешно, (не возникли ошибки валидации и данные сохранились в БД без ошибки) — перенаправляем пользователя на страницу только что созданной записи.