Managing Posts

Managing posts mainly refers to listing posts in an administrative view that allows us to see posts with all statuses, updating them and deleting them. They are accomplished by the admin operation and the delete operation, respectively. The code generated by Gii does not need much modification. Below we mainly explain how these two operations are implemented.

1. Listing Posts in Tabular View

The admin operation shows posts with all statuses in a tabular view. The view supports sorting and pagination. The following is the actionAdmin() method in PostController:

public function actionAdmin()
{
    $model=new Post('search');
    if(isset($_GET['Post']))
        $model->attributes=$_GET['Post'];
    $this->render('admin',array(
        'model'=>$model,
    ));
}

The above code is generated by the Gii tool without any modification. It first creates a Post model under the search scenario. We will use this model to collect the search conditions that the user specifies. We then assign to the model the user-supplied data, if any. Finally, we render the admin view with the model.

Below is the code for the admin view:

<?php
$this->breadcrumbs=array(
    'Manage Posts',
);
?>
<h1>Manage Posts</h1>
 
<?php $this->widget('zii.widgets.grid.CGridView', array(
    'dataProvider'=>$model->search(),
    'filter'=>$model,
    'columns'=>array(
        array(
            'name'=>'title',
            'type'=>'raw',
            'value'=>'CHtml::link(CHtml::encode($data->title), $data->url)'
        ),
        array(
            'name'=>'status',
            'value'=>'Lookup::item("PostStatus",$data->status)',
            'filter'=>Lookup::items('PostStatus'),
        ),
        array(
            'name'=>'create_time',
            'type'=>'datetime',
            'filter'=>false,
        ),
        array(
            'class'=>'CButtonColumn',
        ),
    ),
)); ?>

We use CGridView to display the posts. It allows us to sort by a column and paginate through the posts if there are too many to be displayed in a single page. Our change is mainly about how to display each column. For example, for the title column, we specify that it should be displayed as a hyperlink that points to the detailed view of the post. The expression $data->url returns the value of the url property that we define in the Post class.

Tip: When displaying text, we call CHtml::encode() to encode HTML entities in it. This prevents from cross-site scripting attack.

2. Deleting Posts

In the admin data grid, there is a delete button in each row. Clicking on the button should delete the corresponding post. Internally, this triggers the delete action implemented as follows:

public function actionDelete()
{
    if(Yii::app()->request->isPostRequest)
    {
        // we only allow deletion via POST request
        $this->loadModel()->delete();
 
        if(!isset($_GET['ajax']))
            $this->redirect(array('index'));
    }
    else
        throw new CHttpException(400,'Invalid request. Please do not repeat this request again.');
}

The above code is the one generated by the Gii tool without any change. We would like to explain a little bit more about the checking on $_GET['ajax']. The CGridView widget has a very nice feature that its sorting, pagination and deletion operations are all done in AJAX mode by default. That means, the whole page does not get reloaded if any of the above operations is performed. However, it is also possible that the widget runs in non-AJAX mode (by setting its ajaxUpdate property to be false or disabling JavaScript on the client side). It is necessary for the delete action to differentiate these two scenarios: if the delete request is made via AJAX, we should not redirect the user browser; otherwise, we should.

Deleting a post should also cause the deletion of all comments for that post. In addition, we should also update the tbl_tag table regarding the tags for the deleted post. Both of these tasks can be achieved by writing an afterDelete method in the Post model class as follows,

protected function afterDelete()
{
    parent::afterDelete();
    Comment::model()->deleteAll('post_id='.$this->id);
    Tag::model()->updateFrequency($this->tags, '');
}

The above code is very straightforward: it first deletes all those comments whose post_id is the same as the ID of the deleted post; it then updates the tbl_tag table for the tags of the deleted post.

Tip: We have to explicitly delete all comments for the deleted post here because SQLite does not really support foreign key constraints. In a DBMS that supports this constraint (such as MySQL, PostgreSQL), the foreign key constraint can be set up such that the DBMS automatically deletes the related comments if the post is deleted. In that case, we no longer this explicit deletion call in our code.

$Id$

Be the first person to leave a comment

Please to leave your comment.