Creating and Updating Posts

With the Post model ready, we need to fine-tune the actions and views for the controller PostController. In this section, we first customize the access control of CRUD operations; we then modify the code implementing the create and update operations.

1. Customizing Access Control

The first thing we want to do is to customize the access control because the code generated by gii does not fit our needs.

We modify the accessRules() method in the file /wwwroot/blog/protected/controllers/PostController.php as follows,

public function accessRules()
{
    return array(
        array('allow',  // allow all users to perform 'list' and 'show' actions
            'actions'=>array('index', 'view'),
            'users'=>array('*'),
        ),
        array('allow', // allow authenticated users to perform any action
            'users'=>array('@'),
        ),
        array('deny',  // deny all users
            'users'=>array('*'),
        ),
    );
}

The above rules state that all users can access the index and view actions, and authenticated users can access any actions, including the admin action. The user should be denied access in any other scenario. Note that these rules are evaluated in the order they are listed here. The first rule matching the current context makes the access decision. For example, if the current user is the system owner who tries to visit the post creation page, the second rule will match and it will give the access to the user.

2. Customizing create and update Operations

The create and update operations are very similar. They both need to display an HTML form to collect user inputs, validate them, and save them into database. The main difference is that the update operation will pre-populate the form with the existing post data found in the database. For this reason, gii generates a partial view /wwwroot/blog/protected/views/post/_form.php that is embedded in both the create and update views to render the needed HTML form.

We first change the _form.php file so that the HTML form only collects the inputs we want: title, content, tags and status. We use plain text fields to collect inputs for the first three attributes, and a dropdown list to collect input for status. The dropdown list options are the text displays of the possible post statuses:

<?php echo $form->dropDownList($model,'status',Lookup::items('PostStatus')); ?>

In the above, we call Lookup::items('PostStatus') to bring back the list of post statuses.

We then modify the Post class so that it can automatically set some attributes (e.g. create_time, author_id) before a post is saved to the database. We override the beforeSave() method as follows,

protected function beforeSave()
{
    if(parent::beforeSave())
    {
        if($this->isNewRecord)
        {
            $this->create_time=$this->update_time=time();
            $this->author_id=Yii::app()->user->id;
        }
        else
            $this->update_time=time();
        return true;
    }
    else
        return false;
}

When we save a post, we want to update the tbl_tag table to reflect the change of tag frequencies. We can do this work in the afterSave() method, which is automatically invoked by Yii after a post is successfully saved into the database.

protected function afterSave()
{
    parent::afterSave();
    Tag::model()->updateFrequency($this->_oldTags, $this->tags);
}
 
private $_oldTags;
 
protected function afterFind()
{
    parent::afterFind();
    $this->_oldTags=$this->tags;
}

In the implementation, because we want to detect if the user changes the tags in case he is updating an existing post, we need to know what the old tags are. For this reason, we also write the afterFind() method to keep the old tags in the variable _oldTags. The method afterFind() is invoked automatically by Yii when an AR record is populated with the data from database.

We are not going to give details of the Tag::updateFrequency() method here. Interested readers may refer to the file /wwwroot/yii/demos/blog/protected/models/Tag.php.

$Id$

Total 2 comments

#15158 report it
Don Khan at 2013/10/14 12:30am
General Error 8: Attempt to write a readonly database (SQLite)

If you're using SQLite in Linux and you get the above error, note that permissions must be granted to the .db file and its parent folder (probably /data) otherwise you'll get the above error.

#7148 report it
Dan O&#39;Donnell at 2012/02/27 09:36pm
updateFrequency and other Tag functions

At this point your demo will stop working unless you get three functions from the yii/demos/blog/protected/models/Tag.php class: - updateFrequency - addTags - removeTags

Rather than make you go there, here they are here:

public function updateFrequency($oldTags, $newTags)
    {
        $oldTags=self::string2array($oldTags);
        $newTags=self::string2array($newTags);
        $this->addTags(array_values(array_diff($newTags,$oldTags)));
        $this->removeTags(array_values(array_diff($oldTags,$newTags)));
    }
 
    public function addTags($tags)
    {
        $criteria=new CDbCriteria;
        $criteria->addInCondition('name',$tags);
        $this->updateCounters(array('frequency'=>1),$criteria);
        foreach($tags as $name)
        {
            if(!$this->exists('name=:name',array(':name'=>$name)))
            {
                $tag=new Tag;
                $tag->name=$name;
                $tag->frequency=1;
                $tag->save();
            }
        }
    }
 
    public function removeTags($tags)
    {
        if(empty($tags))
            return;
        $criteria=new CDbCriteria;
        $criteria->addInCondition('name',$tags);
        $this->updateCounters(array('frequency'=>-1),$criteria);
        $this->deleteAll('frequency<=0');
    }

Leave a comment

Please to leave your comment.