📜 ⬆️ ⬇️

Creating a simple CRUD application using Yii2

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:


Forward!

I will assume that you already have a configured web server. In the lesson I will use the following paths and addresses:


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); // Change this to your actual Yii path require '/path/to/yii2/framework/yii.php'; // Change __DIR__ to __FILE__ so we can load our config file $config = require dirname(__FILE__).'/protected/config/main.php'; $config['basePath'] = dirname(__FILE__).'/protected'; $app = new \yii\web\Application($config); $app->run(); 

Let's look at what we changed:

 // Change this to your actual Yii path require '/path/to/yii2/framework/yii.php'; 


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.

c4ca4238a0b923820dcc509a6f75849b.png
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( // uncomment the following to use a MySQL database 'db' => array( 'class' => 'yii\db\Connection', 'dsn' => 'mysql:host=localhost;dbname=yii2', 'username' => 'yii2', 'password' => '<password>', ), 'cache' => array( 'class' => 'yii\caching\DummyCache', ), ), ); 

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 { /** * Returns the static model of the specified AR class. * @param string $className active record class name. * @return Comments the static model class */ public static function model($className=__CLASS__) { return parent::model($className); } /** * @return string the associated database table name */ public static function tableName() { return 'posts'; } /** * @return array primary key of the table **/ public static function primaryKey() { return array('id'); } /** * @return array customized attribute labels (name=>label) */ 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)) { //Then do something } 

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 code
Demo
Original article

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


All Articles