Porting a blog from yii1.1. * To yii2

When I studied yii, of course, the standard blog tutorial helped me in developing the new framework. Having mastered a little, I began to switch to a more modern version - Yii2, and in this case I did not find such a wonderful tool. Having figured out the basics, I thought how good it would be for beginners, or migrating from the first version of yii, to have the exact same blog, but implemented using the yii2 tool. Of course, the network has already implemented a large number of blogs on yii2, there are ready-made extensions for creating support for tags, comments, etc. But from this the entrance to yii2 does not become easier. In this connection, I decided to implement a blog port from yii to yii2 (beta: https: //github.com/tilhom/myblog_yii2).
You can see a working example here.

Highlights of the implementation:

According to standard recipes, which are already many on the network, install the Advanced template in a folder accessible from the network. As a result, we will have a framework of a finished web application with frontend and backend parts. Further, you can use the yii1.1. * Tutorial to create a blog to deploy an application similar to the block of the first version of yii.

We create tables for our block from the finished yii 1.1. * Model (located here: yii / demos / blog / protected / data). Before starting sql, you need to make the following corrections:
- to simplify removing the tbl_ prefix,
- exclude the tbl_user table, since the advanced template already has a User model out of the box
- make the appropriate corrections in creating foreign keys in the post and comments tables:

sql tables
CREATE TABLE lookup
(
id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
name VARCHAR (128) NOT NULL,
code INTEGER NOT NULL,
type VARCHAR (128) NOT NULL,
position INTEGER NOT NULL
) ENGINE = InnoDB DEFAULT CHARSET = utf8 COLLATE = utfc_unic;

CREATE TABLE post
(
id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
title VARCHAR (128) NOT NULL,
content TEXT NOT NULL,
tags TEXT,
status INTEGER NOT NULL,
create_time INTEGER,
update_time INTEGER,
author_id INTEGER NOT NULL,
CONSTRAINT FK_post_author FOROR )
REFERENCES user (id) ON DELETE CASCADE ON UPDATE RESTRICT
) ENGINE = InnoDB DEFAULT CHARSET = utf8 COLLATE = utf8_unicode_ci;

CREATE TABLE comment
(
id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
content TEXT NOT NULL,
status INTEGER NOT NULL,
create_time INTEGER,
author VARCHAR (128) NOT NULL,
email VARCHAR (128) NOT NULL,
url VARCHAR (128),
post_id INTEGER NOT NULL,
CONSTRAINT FK_comment_post FOREIGN KEY (post_id)
REFERENCES post (id) ON DELETE CASCADE ON UPDATE RESTRICT
) ENGINE = InnoDB DEFAULT CHARSET = utf8 COLLATE = utf8_unicode_ci;

CREATE TABLE tag
(
id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
name VARCHAR (128) NOT NULL,
frequency INTEGER DEFAULT 1
) ENGINE = InnoDB DEFAULT CHARSET = utf8 COLLATE = utf8_unicode_ci;

We create models of our tables and generate CRUD operation codes. To do this, run the gii generator in backend -e: Accordingly, fill in the fields: Table Name: * Namespace: common \ models Click [Preview] and uncheck the creation of the Migration.php model, we do not need it, making sure that the overwrite item of the User model is not checked .php click on [Generate] As a result, we get the following models in the common \ models folder: common / models / Comment.php common / models / Lookup.php common / models / Post.php common / models / Tag.phplocalhost/blog/backend/web/index.php?r=gii
















After the models have been created, we can use the Crud Generator to generate CRUD operation code for them. Let's do it in the backend, since CRUD is an operation for the admin panel. Create the CRUD models of Post and Comment.

For Post:
Model Class: common \ models \ Post
Search Model Class: common \ models \ PostSearch
Controller Class: backend \ controllers \ PostController

For Comment:
Model Class: common \ models \ Comment
Search Model Class: common \ models \ CommentSearch
Controller Class : backend \ controllers \ CommentController

Further, authentication is configured in the tutorial of the first version, here you may have noticed that the advanced application framework already implements authentication through the database user table and User model.

Refinement of the Post model.

Changing the rules () method:

return [
            [['title', 'content', 'status'], 'required'],
            ['title','string','max'=>128],
            ['status','in', 'range'=>[1,2,3]],
            ['tags', 'match', 'pattern'=>'/^[\w\s,]+$/',
            'message'=>'В тегах можно использовать только буквы.'],
            ['tags', function($attribute,$params){
                $this->tags=Tag::array2string(array_unique(Tag::string2array($this->tags)));
            }],
        ];


Changing the relations () method:

     public function getComments()
    {
        return $this->hasMany(Comment::className(), ['post_id' => 'id'])
        ->where('status = '. Comment::STATUS_APPROVED)
                    ->orderBy('create_time DESC');
    }
    public function getCommentCount()
    {
        return $this->hasMany(Comment::className(), 
            ['post_id' => 'id'])->where(['status'=>Comment::STATUS_APPROVED])->count();
    }
    public function getAllCommentCount()
    {
        return $this->hasMany(Comment::className(), 
            ['post_id' => 'id'])->where(['status'=>Comment::STATUS_APPROVED])->count();
    }


Add helpers to the model:

- url property:

    public function getUrl()
    {
        return Yii::$app->urlManager->createUrl([ 'post/view', 
            'id'=>$this->id,
            'title'=>$this->title]);
    }


- textual representation for status:

   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]=[];
        $models=self::find()->where(['type'=>$type])->orderBy('position')->all();
        foreach ($models as $model)
            self::$_items[$type][$model->code]=$model->name;
    }


Creating and editing entries takes place in the backend application, so that only authorized users can use the interface, we will change the access rules in the controllers.

Setting Access Rules

  'access' => [
                    'class' => AccessControl::className(),
                    'rules' => [
                        [
                            'allow' => true,
                            'roles' => ['@'],
                        ],
                    ],
                ],


Edits in the create and update
actions We follow the instructions of the tutorial with corrections: for status - a drop-down list with all possible recording states:

 field($model, 'status')->dropDownList(Lookup::items('PostStatus'),['prompt'=>'Select...']) ?>


In the above code, a call to Lookup :: items ('PostStatus') is used to get a list of statuses.

Next, we will modify the Post class so that it automatically exposes some attributes (such as create_time and author_id) immediately before saving the record to the database.

 'timestamp' => [
                        'class' => TimestampBehavior::className(),
                        'attributes' => [
                            ActiveRecord::EVENT_BEFORE_INSERT => ['create_time', 'update_time'],
                            ActiveRecord::EVENT_BEFORE_UPDATE => ['update_time'],
                        ],
                    ],
                    [
                        'class' => BlameableBehavior::className(),
                        'createdByAttribute' => 'author_id',
                        'updatedByAttribute' => 'author_id',
                    ],

When saving the record, we also want to update the frequency of tags in the tag table. We can implement this in the afterSave () method, which is automatically called after the record is successfully saved to the database.

    public function afterSave($insert, $changedAttributes)
    {
        parent::afterSave($insert, $changedAttributes);
        Tag::updateFrequency($this->_oldTags, $this->tags);
    }
    public function afterFind()
    {
        parent::afterFind();
        $this->_oldTags=$this->tags;
    }


Display of records will occur in the frontend part. To do this, in this part of the application, using gii, create a CRUD for the Post model and delete the unnecessary create, update, delete actions. Introducing changes to the index and view actions. When viewing posts, page layout is divided into two columns. For this, two layouts column1.php and column2.php are created, which are switched in the application, for example, index - column2, view - column1.

Manage comments
We are finalizing the Comment model. We make the appropriate changes to the rules () method.

public function rules()
    {
        return [
        [['content', 'author', 'email'], 'required'],
        [['author', 'email', 'url'], 'string', 'max' => 128],
        ['email','email'],
        [['content'], 'string'],
        ['url','url'],
        [['status', 'create_time', 'post_id'], 'integer'],
        ];
    }


To automatically register the date the comment was created, create the behavior in the model:

public function behaviors(){
        return [
        'timestamp' => [
        'class' => TimestampBehavior::className(),
        'attributes' => [
        ActiveRecord::EVENT_BEFORE_INSERT => ['create_time'],
        ],
        ]   
        ];
    }


We tweak the attributeLabels () method according to the textbook.
And then, exactly according to the textbook, we implement the creation and display of comments in the controller and in the view frontend of the application part:

namespace frontend\controllers;
/* * */
class PostController extends Controller
{
/* * */
public function actionView($id)
    {
        $this->layout='column1';
        $model = $this->findModel($id);
        //$comment=$this->newComment($model);
        $comment=new Comment();
        if($comment->load($_POST) && $model->addComment($comment))
            {
                if($comment->status==Comment::STATUS_PENDING){
                    Yii::$app->getSession()->setFlash('warning','Thank you for your comment. Your comment will be posted once it is approved.');
                }
                return $this->refresh();
            }
        return $this->render('view',array(
            'model'=>$model,
            'comment'=>$comment,
        ));
    }


Submission Code:

title = $model->title;
$this->params['breadcrumbs'][] = ['label' => 'Posts', 'url' => ['index']];
$this->params['breadcrumbs'][] = $this->title;
?>
context->renderPartial('_item', array( 'model'=>$model ));?>
commentCount>=1): ?>

commentCount>1 ? $model->commentCount . ' comments' : 'One comment'; ?>

context->renderPartial('_comments',array( 'post'=>$model, 'comments'=>$model->comments, )); ?> context->renderPartial('/comment/_form',array( 'model'=>$comment, )); ?>


Code _item.php:

title), $model->url); ?>

Posted by author->username . ' on ' . date('F j, Y',$model->create_time); ?>

content; ?>

Tags: tags; ?>

url); ?> | commentCount})",$model->url.'#comments'); ?> | Last updated on update_time); ?>


Code _comments.php:

authorLink; ?> says:

id}", $comment->getUrl(),[ 'class'=>'cid', 'title'=>'Permalink to this comment!', ]); ?>

content)); ?>

create_time); ?>


And the form for creating a new comment:

Leave a Comment

field($model,'author')->textInput(); ?> field($model,'email')->textInput(); ?> field($model,'url')->textInput(); ?> field($model,'content')->textArea(array('rows'=>6, 'cols'=>50)); ?>
'btn btn-success btn-block']); ?>


To manage comments from the admin panel, we modify the previously created CRUD for the Comment model. The index action is created using the GridView widget, in the column of which the approval of comments is implemented using the approve () method:

 $dataProvider,
        'filterModel' => $searchModel,
        'columns' => [
            ['class' => 'yii\grid\SerialColumn'],
            [
                'attribute'=>'id',
                'contentOptions'=>['style'=>'width:64px;'],
            ],
            'content:ntext',
           [
                'attribute'=>'status',
                'format'=>'raw',
                'value'=>function ($model){
                    $text=\common\models\Lookup::item('CommentStatus',$model->status);
                    $url=Url::to(["comment/approve","id"=>$model->id]);
                    Url::remember();
                    return $text=='Pending Approval'?Html::a($text,$url):$text;
                },
                'contentOptions'=>['style'=>'width:136px;'],               
            ],
            'create_time:datetime',
            'author',
            [
                'class' => 'yii\grid\ActionColumn',
                'header' => 'Actions',
                'contentOptions'=>['style'=>'width:96px;'],
            ],
        ],
    ]); ?> 


And finally, for now, a widget for the list of recent comments has been implemented, which is displayed in the frontend application when viewing posts.
Add a static method to the Comment model:

public static function findRecentComments($limit=10)
    {
        return static::find()->where('status='.self::STATUS_APPROVED)
                    ->orderBy('create_time DESC')
                    ->limit($limit)
                    ->with('post')->all();
    }


And we implement widget:

class RecentComments extends Widget
{
	public $comments;
	public function init()
	{
		parent::init();
		$this->comments = Comment::findRecentComments();
	}
	public function run()
	{
		return $this->render('recent-comments');
	}
}


With a view:

Recent Comments

    context->comments as $comment): ?>
  • authorLink; ?> on post->title), $comment->getUrl()); ?>


Of course, this is not a detailed guide, especially since there is a ready-made tutorial on creating a god for the first version of yii, here are the main points of coding for Yii2. I hope this will be useful for beginners to implement their blog on Yii2 for educational purposes.

Also popular now: