📜 ⬆️ ⬇️

Porting the blog from yii1.1. * To yii2

When I studied yii, of course, I was helped in mastering a new framework, a standard tutorial on creating a blog. Having become accustomed to a little, I began to switch to a more modern version - Yii2, and in this case I did not find such a wonderful guide. Having dealt with the basics, I thought, as if it were good for beginners, or migrating from the first version of yii, the existence of exactly the 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 this does not make entrance to yii2 easier. In connection with what he planned to implement the blog port from yii to yii2 (beta: https: //github.com/tilhom/myblog_yii2).
A working example can be found here.

Highlights of implementation:

According to standard recipes, which are already a lot on the network, we install the Advanced template in a folder accessible from the network. As a result, we will have the framework of the finished web application with frontend and backend parts. Further, it is possible according to the textbook yii1.1. * Of creating a blog to deploy an application similar to the block of the first version of yii.
')
Create tables for our block from the finished model yii 1.1. * (Located here: yii / demos / blog / protected / data). Before you run sql, you need to make corrections:
- for simplicity, remove the tbl_ prefix,
- exclude the tbl_user table, since the Advanced template already has a User model out of the box
- make corresponding corrections in the creation of 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 = utf8_unicode_ci;

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 FOREIGN KEY (author_id)
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,
VARCHAR url (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 the backend -e: localhost/blog/backend/web/index.php?r=gii


Accordingly, we fill in the fields:

Table Name: *
Namespace: common \ models

Click [Preview] and remove the check mark from the creation of the model Migration.php, we do not need it, making sure that the overwrite option of the User.php model is not checked, then click on [Generate]

As a result, we have the following models in the common \ models folder:

common / models / Comment.php
common / models / Lookup.php
common / models / Post.php
common / models / Tag.php

After the models have been created, we can use the Crud Generator to generate a CRUD operation code for them. We will do this in the backend, since CRUD is an operation for the admin. Create a CRUD Post and Comment models.

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, in the tutorial of the first version, authentication is configured, here you may have noticed that the advanced application framework already implements authentication using the database user table and the User model.

Refinement model Post.

Change 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 method of relations ():

  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]); } 


- text view 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 records occurs in the backend part of the 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' => ['@'], ], ], ], 


Edit in create and update actions
By following the instructions of the textbook with amendments: for status, a drop-down list with all possible write states:

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


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

Next, we change 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 information on the frequency of use of tags in the tag table. We can do this in the afterSave () method, which is automatically called after successfully saving the record 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; } 


The display of entries 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 unnecessary actions create, update, delete. We make changes to the index and view actions. When viewing posts, the page layout is divided into two columns. What created two layouts column1.php and column2.php, which are switched in the application, for example index - column2, view - column1.

Comment management
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 set the date of the comment creation automatically, create the behavior in the model:

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


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

 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, )); } 


Presentation Code:

 <?php use yii\helpers\Html; use yii\widgets\DetailView; /* @var $this yii\web\View */ /* @var $model common\models\Post */ $this->title = $model->title; $this->params['breadcrumbs'][] = ['label' => 'Posts', 'url' => ['index']]; $this->params['breadcrumbs'][] = $this->title; ?> <div class="post-view"> <?php echo $this->context->renderPartial('_item', array( 'model'=>$model ));?> <div id="comments" class="row-fluid"> <?php if($model->commentCount>=1): ?> <h4> <?php echo $model->commentCount>1 ? $model->commentCount . ' comments' : 'One comment'; ?> </h4> <?php echo $this->context->renderPartial('_comments',array( 'post'=>$model, 'comments'=>$model->comments, )); ?> <?php endif; ?> <?php echo $this->context->renderPartial('/comment/_form',array( 'model'=>$comment, )); ?> </div><!-- comments --> </div> 


_Item.php code:

 <?php use \yii\helpers\Html; ?> <div> <h1><?php echo Html::a(Html::encode($model->title), $model->url); ?></h1> <p class="meta">Posted by <?php echo $model->author->username . ' on ' . date('F j, Y',$model->create_time); ?></p> <p class='lead'> <?php echo $model->content; ?> <p> <div> <p> <strong>Tags:</strong> <?php echo $model->tags; ?> </p> <?php echo Html::a('Permalink', $model->url); ?> | <?php echo Html::a("Comments ({$model->commentCount})",$model->url.'#comments'); ?> | Last updated on <?php echo date('F j, Y',$model->update_time); ?> </div> </div> 


Code _comments.php:

 <?php use \yii\helpers\Html; foreach($comments as $comment): ?> <div class="well" id="c<?php echo $comment->id; ?>"> <div class="row"> <div class="col-md-8"> <h4><?php echo $comment->authorLink; ?> says:</h4> </div> <div class="col-md-4 text-right"> <?php echo Html::a("#{$comment->id}", $comment->getUrl(),[ 'class'=>'cid', 'title'=>'Permalink to this comment!', ]); ?> </div> </div> <hr style="margin:2px 0px;"> <p class='lead'> <?php echo nl2br(Html::encode($comment->content)); ?> </p> <h5> <?php echo date('F j, Y \a\th:i a',$comment->create_time); ?> </h5> </div><!-- comment --> <?php endforeach; ?> 


And the form for creating a new comment:

 <?php use \yii\helpers\Html; use \yii\widgets\ActiveForm; ?> <div class="panel panel-success"> <div class="panel-heading"> <h3 class="panel-title">Leave a Comment</h3> </div> <div class="panel-body"> <?php $form = ActiveForm::begin(); ?> <?php echo $form->field($model,'author')->textInput(); ?> <?php echo $form->field($model,'email')->textInput(); ?> <?php echo $form->field($model,'url')->textInput(); ?> <?php echo $form->field($model,'content')->textArea(array('rows'=>6, 'cols'=>50)); ?> <div class="form-actions text-center"> <?php echo Html::submitButton('Save',['class' => 'btn btn-success btn-block']); ?> </div> <?php ActiveForm::end(); ?> </div> </div> 


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

 <?= GridView::widget([ 'dataProvider' => $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, the widget of the list of recent comments is 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 the 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 submission:

 <div class="panel panel-default"> <div class="panel-heading"> <h3 class="panel-title">Recent Comments</h3> </div> <div class="panel-body"> <ul class="list-unstyled"> <?php foreach($this->context->comments as $comment): ?> <li><span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> <?php echo $comment->authorLink; ?> on <?php echo Html::a(Html::encode($comment->post->title), $comment->getUrl()); ?> </li> <?php endforeach; ?> </ul> </div> </div> 


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

Source: https://habr.com/ru/post/269085/


All Articles