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; and finally we implement the preview feature for both operations.
The first thing we want to do is to customize the access control because the code generated by yiic
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('list', 'show'),
'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 list
and show
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.
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, the yiic
tool 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
and status
. We use plain text fields to collect inputs for the first two attributes, and a dropdown list to collect input for status
. The dropdown list options are the text displays of the possible post statuses:
echo CHtml::activeDropDownList($post,'status',Post::model()->statusOptions);
Tip: In the above, we can also use
Post::model()->getStatusOptions()
instead ofPost::model()->statusOptions
to return the possible status options. The reason that we can use the latter expression is becausePost
is a component which allows us to access properties defined in terms of getter methods.
We then modify the Post
class so that it can automatically set some attributes (e.g. createTime
, authorId
) before a post is saved to the database. We override the beforeValidate()
method as follows,
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;
}
In this method, we use CMarkdownParser to convert the content from Markdown format into HTML and save the result to contentDisplay
. This avoids repeated format conversion when we display a post. If the post is new, we set its createTime
and authorId
attributes; otherwise we set its updateTime
to be the current time. Note that this method will be invoked automatically when we call validate()
or save()
method of the model.
Because we want to save post tags to the Tag
table, we also need the following method in the Post
class, which is invoked automatically after a post is saved to the database:
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)
);
}
In the above, we first clean up the PostTag
table for rows related with the current post. We then insert new tags into the Tag
table and add a reference in the PostTag
table. The logic here is a bit complex. Instead of using ActiveRecord, we write raw SQL statements and execute them with the database connection.
Tip: It is good practice to keep business logic, such as the above
beforeValidate()
andafterSave()
code, in models instead of controllers.
Besides the above customizations, we also want to add the preview feature that would allow us to preview a post before we save it to the database.
We first change the _form.php
view file to add a preview
button and a preview display. The preview is only displayed when the preview button is clicked and there is not validation error.
echo CHtml::submitButton('Preview',array('name'=>'previewPost')); ...... if(isset($_POST['previewPost']) && !$post->hasErrors()): ...display preview of $post here... endif;
We then change the actionCreate()
and actionUpdate()
methods of PostController
to respond to the preview request. Below we show the updated code of actionCreate()
, which is very similar to that in 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));
}
In the above, if the preview button is clicked, we call $post->validate()
to validate the user input; otherwise if the submit button is clicked, we try to save the post by calling $post->save()
which implicitly performs validation. If the saving is successful (no validation errors and the data is saved to the database without error), we redirect the user browser to show the newly created post.
Signup or Login in order to comment.