Disclaimer
This lesson will help you get to know Yii2. At the moment, Yii2 is absolutely not ready for production. I do not recommend using it in work projects.
Let's start
Today, the authors of the Yii Framework announced a public preview of Yii2. There are quite a few changes between Yii1 and Yii2.
This tutorial describes how to create a simple blog on Yii2. In the process, we will download and install Yii2, create a basic application, connect to the database and describe the logic of creating, updating, reading and deleting posts.
')
To continue, you will need:
- Web server, for example Apache or Nginx. I will use nginx. Using Apache will differ slightly, so don’t worry if you don’t have a Nginx server nearby.
- North database for our application. I chose MySQL 5.5
- Basic knowledge of PHP. I will try to explain everything as simple as possible, but the better you know PHP, the easier it will be to move on.
- Basic knowledge of Yii or MVC. If you have no experience with MVC, then I recommend you read the basics of MVC . You can read this lesson without MVC knowledge, but it will be much easier for you to understand what is happening, I understand the topic of MVC.
Forward!
I will assume that you already have a configured web server. In the lesson I will use the following paths and addresses:
- / var / www / yii2 as DocumentRoot
- yii2.erianna.com as a hosting address
Also, at the end of the lesson you will be able to see an example of the application that we will do at
yii2.erianna.com .
Downloading Yii2
You can get a copy of Yii2 from Github either by cloning the repository or by downloading the archive.
git clone git@github.com: yiisoft / yii2.git / dir / to / yii2
or
wget https://github.com/yiisoft/yii2/archive/master.zip
unzip master.zip / dir / to / yii2
After unpacking Yii2, go to the
/ dir / to / yii2 / framework folder
cd / dir / to / yii2 / framework
And run the following command to create a basic web application, answering Yes to the first question.
php yiic.php app / create / var / www / yii2
yes
This is equivalent to creating a web application in Yii 1.x. Now go to / var / www / yii2. You will see one folder and one file.
$ ls -l
total 8
-rwxrwxrwx 1 user www-data 265 May 4 09:30 index.php
drwxrwsr-x 5 user www-data 4096 May 4 09:07 protected
Before we launch our site, we need to make some modifications to the index.php file. In my opinion, there are several controversial decisions. Hopefully they will be fixed before the final release of Yii2, to make it more user friendly to install.
Modify index.php as follows:
<?php define('YII_DEBUG', true);
Let's look at what we changed:
First, we need to change the path to the framework / yii.php file. By default, it is considered that it lies in the same directory. Perhaps it is, but we need to specify the exact path to Yii2.
$config = require dirname(__FILE__).'/protected/config/main.php'; $config['basePath'] = dirname(__FILE__).'/protected';
Secondly, we update the path to the configuration file so that it uses
__FILE__ instead of
__DIR__ . This is necessary so that your application can run.
Before we continue, it is important to note something new in Yii -
Namespaces $app = new \yii\web\Application($config);
The meaning of namespaces in encapsulating code into logical units to avoid collisions between different code bases. Suppose you have two classes, both are called
Foo and both have a
Bar method. If they are located in different namespaces, then you will be able to call them independently from each other without any collisions.
$foo = new \namespace\Foo; $foo2 = new \namespace2\Foo;
Namespaces are a simple way to avoid collisions in code. I recommend you
read about them , since Yii2 will be fully built on this principle.
Now you have created the first application! Go to the address where you have yii2 and you will see the following page.

Your first application on Yii2!
Unlike Yii 1.x, the basic application of Yii2 is not so admiring. Let's teach him to do a little more.
First, open the
/protected/views/layout/main.php file and replace its contents:
<?php use yii\helpers\Html as Html; ?> <!doctype html> <html lang="<?php \Yii::$app->language?>"> <head> <meta charset="utf-8" /> <title><?php echo Html::encode(\Yii::$app->name); ?></title> <link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-combined.min.css" rel="stylesheet"> <script src="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/js/bootstrap.min.js"></script> </head> <body> <div class="container"> <div class="navbar navbar-inverse"> <div class="container"> <div class="navbar-inner"> <a class="brand" href="/"><?php echo Html::encode(\Yii::$app->name); ?></a> </div> </div> </div> <div class="content"> <?php echo $content?> </div> </div> </body> </html>
Now refresh the page. Do you see? Doesn't things get better with Twitter Bootstrap? Again, not so much has changed between Yii1 and Yii2. You still have the $ content variable to display the content in the view. However,
Yii :: app () was replaced by
Yii :: $ app . And let me remind you once again - everything in Yii2 is divided into namespaces, so it is important to remember to address everything in their namespace, and not just call “raw” classes.
Now let's do some real coding!
Connect to the database
For this application we will have only a simple table
Posts , in which we will store the posts of our blog.
Create a table in the database
Log in to MySQL and create a user and database named yii2. Then run the following query to update the structure:
DROP TABLE IF EXISTS `posts`; CREATE TABLE IF NOT EXISTS `posts` ( `id` int(11) NOT NULL, `title` varchar(255) NOT NULL, `content` text NOT NULL, `created` datetime NOT NULL, `updated` datetime NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=latin1; ALTER TABLE `posts` ADD PRIMARY KEY(`id`); INSERT INTO `yii2`.`posts` (`id`, `title`, `content`, `created`, `updated`) VALUES ('1', 'Example Title', 'New Post', NOW(), NOW());
We update a config
Go to the
/ var / www / yii2 / protected / folder and open the
config.php file in your favorite editor. Replace all its contents with this:
<?php return array( 'id' => 'webapp', 'name' => 'My Web Application', 'components' => array(
If you are familiar with Yii, you will notice a
strong improvement regarding the monstrous configs that Yii1 generated. Despite the fact that the structure remains the same, this is all we need to connect to the database.
Create a Post Model
Create a new
models folder in the protected folder, and then create a
Post.php file in it with the following code.
<?php namespace app\models; class Post extends \yii\db\ActiveRecord { public static function model($className=__CLASS__) { return parent::model($className); } public static function tableName() { return 'posts'; } public static function primaryKey() { return array('id'); } public function attributeLabels() { return array( 'id' => 'ID', 'title' => 'Title', 'content' => 'Content', 'created' => 'Created', 'updated' => 'Updated', ); } }
If you are familiar with Yii1, the only thing that has changed in ActiveRecord (at least in this example) is that the
primaryKey and
tableName functions are now static methods. Everything else basically remains the same. For the most part, ActiveRecord has remained untouched.
The most important part of the class is the inclusion of the
app \ models namespace. This tells Yii how we can reference this file.
Unlike Yii1, where you can simply call the class name, Yii2 uses a different type of file startup, which requires you to specify exactly what you are going to use. This may slow down the development somewhat (Attempts to remember to always connect \ yii \ framework \ web \ Html instead of just calling CHtml may tire), but at the same time it will make Yii2 much faster, because the autoloader now doesn’t have to search the entire framework to load some one class. At least in theory.
CRUD!
After we put the Post model in the namespace, we can start creating CRUD applications.
View all
First, let's update the index action so that we can see everything. I love to see all the messages right on the main page, so let's start with that. Open the
controllers / SiteController.php file and update the index action as shown below:
public function actionIndex() { $post = new Post; $data = $post->find()->all(); echo $this->render('index', array( 'data' => $data )); }
I will note a few things. First,
:: model () -> disappeared. Data from ActiveRecord and models are now available by direct method call. For example, $ post-> find () -> all (). Despite the fact that I personally love Post :: model () -> findAll (), the new way to access data looks more standard and easy to read.
Secondly, findAll has been replaced by find () -> all (). All search methods now come from find () or findBySql ().
Thirdly, $ this-> render () now requires echo at the beginning of the line. Personally, I hate it. This is
very similar to CakePHP and, in my opinion, redundant. The idea behind this change is that everything you want to show to the user should be sent via echo, otherwise it should just be placed in a variable for further action. I prefer the old way of rendering to a variable (passing a parameter to the rendering method), but maybe I’ve gotten to it.
Now refresh the page ...
If you are familiar with namespaces, you will probably shout at me asking why I didn’t turn on the Post model. If you are not familiar, then you may be surprised that you got an error. The reason is simple. _You must remember the namespace in Yii2_. Everything that you want to use should be clearly stated, if this has not been done before.
Add the following line at the beginning of the
SiteController file. Then refresh the page.
use app\models\Post;
Now add the markup to display posts. Open the
protected / views / site / index.php file and replace its contents with the following:
<?php use yii\helpers\Html; ?> <?php echo Html::a('Create New Post', array('site/create'), array('class' => 'btn btn-primary pull-right')); ?> <div class="clearfix"></div> <hr /> <table class="table table-striped table-hover"> <tr> <td>#</td> <td>Title</td> <td>Created</td> <td>Updated</td> <td>Options</td> </tr> <?php foreach ($data as $post): ?> <tr> <td> <?php echo Html::a($post->id, array('site/read', 'id'=>$post->id)); ?> </td> <td><?php echo Html::a($post->title, array('site/read', 'id'=>$post->id)); ?></td> <td><?php echo $post->created; ?></td> <td><?php echo $post->updated; ?></td> <td> <?php echo Html::a(NULL, array('site/update', 'id'=>$post->id), array('class'=>'icon icon-edit')); ?> <?php echo Html::a(NULL, array('site/delete', 'id'=>$post->id), array('class'=>'icon icon-trash')); ?> </td> </tr> <?php endforeach; ?> </table>
Hmmm, looks different, right? CHtml :: link () was gone, an Html helper appeared instead. Fortunately, the structure of CHtml :: link and Html :: a is no different. So just fill in the parameters.
We read
Reading is easy, so let's deal with it. Create a new method in SiteController with the following definition:
public function actionRead($id=NULL) { echo 'HelloWorld'; }
Now go to? R = site / read & id = 1. You will see
HelloWorld on the screen. Do you see? Hory. This means that our method has been called. Now update it to show data from the database.
First, let's add a HttpException to the SiteController so that we can throw a HttpException if the post is not found.
use \yii\base\HttpException;
Now we add the read action.
public function actionRead($id=NULL) { if ($id === NULL) throw new HttpException(404, 'Not Found'); $post = Post::find($id); if ($post === NULL) throw new HttpException(404, 'Document Does Not Exist'); echo $this->render('read', array( 'post' => $post )); }
For clarity, HttpException is essentially a CHttpException. All we do is request a post with the specified ID in the database and display it. If the post is not found or the ID was not specified, we throw a HttpException.
Next, we need to create a new file,
protected / views / site / read.php, and add the following code to it to display the post.
<?php use yii\helpers\Html; ?> <div class="pull-right btn-group"> <?php echo Html::a('Update', array('site/update', 'id' => $post->id), array('class' => 'btn btn-primary')); ?> <?php echo Html::a('Delete', array('site/delete', 'id' => $post->id), array('class' => 'btn btn-danger')); ?> </div> <h1><?php echo $post->title; ?></h1> <p><?php echo $post->content; ?></p> <hr /> <time>Created On: <?php echo $post->created; ?></time><br /> <time>Updated On: <?php echo $post->updated; ?></time>
Now, on the main page, click on "Example Post". Voila! Now you can view blog posts!
Deletion
Removing posts is just as easy, so we’ll do it. Create a new method with the following code:
public function actionDelete($id=NULL) { }
For this method, we need a slightly more complex syntax. We must redirect the user back to the main page after successfully deleting the post Let's start.
First, we describe the method
public function actionDelete($id=NULL) { if ($id === NULL) { Yii::$app->session->setFlash('PostDeletedError'); Yii::$app->getResponse()->redirect(array('site/index')); } $post = Post::find($id); if ($post === NULL) { Yii::$app->session->setFlash('PostDeletedError'); Yii::$app->getResponse()->redirect(array('site/index')); } $post->delete(); Yii::$app->session->setFlash('PostDeleted'); Yii::$app->getResponse()->redirect(array('site/index')); }
A few comments on Yii2. First, redirection is now done using
Yii :: $ app-> getResponse-> redirect () instead of
$ this-> redirect () . This solution makes sense in terms of code organization, but it takes so long to print! In addition, it creates a feeling of overloading the $ app. At the same time, the definition of the method remains the same.
Second, setFlash is now available via $ app instead of app (). You should now get used to it. =)
Now we are done with the deletion. Let's go back to
protected / views / site / index.php and catch the notifications that were sent.
Just add it after the first tag hr
<?php if(Yii::$app->session->hasFlash('PostDeletedError')): ?> <div class="alert alert-error"> There was an error deleting your post! </div> <?php endif; ?> <?php if(Yii::$app->session->hasFlash('PostDeleted')): ?> <div class="alert alert-success"> Your post has successfully been deleted! </div> <?php endif; ?>
Now try deleting the “Example Post”. Pretty simple, huh? Now you get the idea of ​​Yii :: $ app, right?
Create
We now turn to the fun broadcasting, the creation of new entries in our blog. We have to do a few things to create. First, we are going to use ActiveForm to work with the form. Secondly, we must catch and validate the data in $ _POST. And finally, after this we must save the data to the database. Let's start.
First, we will make view for the form. Create a
protected / views / site / create.php file . Since we are going to use the widget, it is necessary to create the “assets” folder in the application root and make it writable by the web server. Chmod 755 usually solves this issue. Then add the method definition to SiteController.
public function actionCreate() { $model = new Post; if (isset($_POST['Post'])) { $model->title = $_POST['Post']['title']; $model->content = $_POST['Post']['content']; if ($model->save()) Yii::$app->response->redirect(array('site/read', 'id' => $model->id)); } echo $this->render('create', array( 'model' => $model )); }
It looks more or less the same as in Yii1. But there are still a few differences. First, the Controller now has a “populate” method ($ this-> populate ($ ds, $ model)), which in theory should save us from all this misery with isset ($ _ POST). The code for creating a new post will look like this:
if ($this->populate($_POST, $model)) {
Unfortunately, I could not get it to work in the latest version. The data in my model remained unchanged. Secondly, I also could not make $ model-> attributes = $ _POST ['Post'] work. ActiveRecord looks somewhat damp, so for now, you have to enter data by hand.
Finally, I stumbled upon another obstacle - saving to the database with a unique ID. So that we have to do it manually. If someone finds out how to solve it - feel free to leave a comment.
Let's update the Post model to make the unique primary key work. Just add this code to the end of the file:
public function beforeSave($insert) { if ($this->isNewRecord) { $command = static::getDb()->createCommand("select max(id) as id from posts")->queryAll(); $this->id = $command[0]['id'] + 1; } return parent::beforeSave($insert); }
All it does is check whether a new record is created and if so, it gets the maximum ID from the database, increments it and uses it as an ID.
I tried several different combinations (NULL, 0, _ for $ model-> id, but for some reason ActiveRecord refused to keep the model with ID excellent 0. I have no idea why this does not work).
(
Actually, the author simply forgot to specify AUTO_INCREMENT for the id field. But I decided to leave this part as a small lesson of carelessness. Note the translator. )
With it sorted out, now we create view.
<?php use yii\helpers\Html; ?> <?php $form = $this->beginWidget('yii\widgets\ActiveForm', array( 'options' => array('class' => 'form-horizontal'), )); ?> <?php echo $form->field($model, 'title')->textInput(array('class' => 'span8')); ?> <?php echo $form->field($model, 'content')->textArea(array('class' => 'span8')); ?> <div class="form-actions"> <?php echo Html::submitButton('Submit', null, null, array('class' => 'btn btn-primary')); ?> </div> <?php $this->endWidget(); ?>
That's it, now you can save the model. But something strange is not it? For example, why is the creation and update time 0? What if we send an empty form?
Let's fix these two errors before we continue. Open the Post model and add the following method:
public function rules() { return array( array('title, content', 'required'), ); }
This method makes the title and content fields required. Now when you try to save the model, you get an error if one of these fields is empty. And since we use bootstrap, it is very easy to see what
exactly was the error. Try it!
Next, we must automatically set the correct time.
First, add another
use -string at the top of our model.
use \yii\db\Expression;
Second, update the beforeSave method to automate the process.
Inside the if ($ this-> isNewRecord) block, add a line:
$this->created = new Expression('NOW()');
Before return parent :: beforeSave ($ insert) add:
$this->updated = new Expression('NOW()');
As a result, the method should have the following form:
public function beforeSave($insert) { if ($this->isNewRecord) { $this->created = new Expression('NOW()'); $command = static::getDb()->createCommand("select max(id) as id from posts")->queryAll(); $this->id = $command[0]['id'] + 1; } $this->updated = new Expression('NOW()'); return parent::beforeSave($insert); }
Let's try to save again. Now the model validates the title and content fields, and also automatically fills in the creation and update times. Let's go to the update.
We update
The update action will be almost identical to the creation. The only difference is how we define the model we will use.
In the action of creation we did this:
$model = new Post;
In the update action, we do this:
$model = Post::find($id);
I like to throw exceptions when something is not found, so my action will do some error checking. After adding them, the code should look something like this:
public function actionUpdate($id=NULL) { if ($id === NULL) throw new HttpException(404, 'Not Found'); $model = Post::find($id); if ($model === NULL) throw new HttpException(404, 'Document Does Not Exist'); if (isset($_POST['Post'])) { $model->title = $_POST['Post']['title']; $model->content = $_POST['Post']['content']; if ($model->save()) Yii::$app->response->redirect(array('site/read', 'id' => $model->id)); } echo $this->render('create', array( 'model' => $model )); }
Noticed something interesting? We still use view to create, because they are
exactly the same . Cool, yeah?
findings
So we did it. In a few hours, we went from ignorance of Yii2 to a simple CRUD application on it. Using this knowledge, you can easily extend applications by adding user support, authentication, additional tables in the database, and even much more powerful features.
Yii2 is
very similar to Yii 1.x, but there are a lot of changes in it that you must learn to use. Since Yii2 is not well documented yet, I wrote this article only on the basis of the source code on Github. The framework code is very well documented. And since the methods are very similar to Yii1, it was easy to find what I needed.
As we found, there are still a few problems that need to be fixed (either through better documentation of ActiveRecord, or by fixing what is broken).
Links
Github source codeDemoOriginal article