Introducing Yii 2 through Creating a Simple Blog

Hello, dear Habrausers!

As you already understood from the title, this article will discuss the new version of Yii. I’ll try briefly, using a living example, to introduce you to the wonderful Yii 2.

Writing a complete instruction for creating a blog probably does not make sense, since many steps are similar to the first version, but I will write about those nuances that differ, and I will focus on the new Yii2, which I noticed in the process of creating the application. All the rest, you can quickly see in the demo application , which, probably, will be more effective than just written text.

Highlights


In Yii2, as you already noticed, everything is built on namespace, this is perhaps the main "highlight" in the new version.
Also a very important point: the “C” prefix from the file names has been removed.

Install and create the first application


As a basis, we will use the basic application.
How to install it is very well described on Github , so immediately go to the following points.

Application structure


For an example, I chose a modular structure. This will allow us to learn and understand the principle of operation of modules in Yii2.
So, in order to implement such a structure, we, as in the first version, need to create the main folder of all modules, in which we will already create separate folders for individual modules. That's all, as in the first version.

The only thing that has changed in the modules is the name of the main class of the module. As I understand it, now it is no longer necessary to write the suffix "Module", so our main files will have clear names without a suffix.

Example:
Site.php, Users.php, Blogs.php, etc.
You can already see the structure with a ready-made example, and there is no point in dwelling on this anymore.

Application Settings: config / main.php


As in Yii 1.0, the application is configured through the main.php file, and many elements remain unchanged, but still there are many renamed parameters, which should be remembered if the application does not work after configuration.

A few examples of such changes and features:
  • The module flavor now contains the required parameter “class”, which stores the name of the main class of the module, and without which the module cannot be connected.
    'modules' => array(
    ...
    		'users' => array(
    			'class' => 'app\modules\users\Users'
    		),
    ...
    	),
    

  • The defaultController parameter has been removed, and the defaultRoute is used instead.
  • When configuring paths to directories, you can use the specified aliases " @app, @www, @wwwroot", which is very convenient.
  • UrlManager also renamed several parameters. Now urlFormat has been replaced with enablePrettyUrl, useStrictParsing with enableStrictParsing.
  • In the db component, renamed connectionString to dsn.

In case of inoperability of any parameters, you can easily spy everything in the source code of the frame.
As soon as we set up our application and plug in our modules, we can start writing our “CRUD”.

CRUD


A detailed example is in the demo code, but to be honest, there is no strong difference from the first version. The only thing new has appeared is the controller's method earlier: populate (), which was recently transferred to the model and renamed to load ().

Example:
public function actionCreate()
	{
		$model = new Blog();
		if ($model->load($_POST) && $model->save()) {
				return Yii::$app->response->redirect(array('view', 'id' => $model->id));
		} else {
			echo $this->render('create', array('model' => $model));
		}
	}

$model->load($_POST)
// тоже самое что
if (isset($_POST['Blog'])) {
    $model->attributes = $_POST['Blog'];
}

There are no other special differences in working with CRUD.

Model


The main innovation is the method “scenarios () due to which you can configure the model validators depending on the specified scenario.
public function scenarios()
{
    return array(
        'backend' => array('email', 'role'),
        'frontend' => array('email', '!name'),
    );
}

The model is bound by the formName () function, which ultimately returns the name of the Class of the model to which the form belongs.
Also, an important point - model () was removed, and now work with the model occurs, as with another ordinary class:
 MyModel::getAuthor();

Activerecord


Everything was rewritten here, now in Yii2 a new AR, which really pleases.

Examples of several innovations:
“scopes ()” have been replaced by the usual methods of AR models, which now look like this:
public function active($query)
	{
		return $query->andWhere('status = ' . self::STATUS_ACTIVE);
	}

Also, “relations ()” were radically changed, which are now set in the form of heteros, which is more correct. Two types of relations are available: "hasOne ()", "hasMany ()".
public function getAuthor()
	{
		return $this->hasOne('app\modules\users\models\User', array('id' => 'author_id'));
                // Первый параметр – это у нас имя класса, с которым мы настраиваем связь.
               // Во втором параметре в виде массива задаётся имя удалённого PK ключа  (id) и FK из текущей таблицы модели (author_id), которые связываются между собой
	}

The functions of selecting from the database were also rewritten, and supplemented with new ones.
$customers = Customer::find()
    ->where(array('status' => $active))
    ->orderBy('id')
    ->all();
// return the customer whose PK is 1
$customer = Customer::find(1);
$customers = Customer::find(array('status'=>$active));
$customers = Customer::find()->asArray()->all();
$customers = Customer::find()->active()->asArray()->all();

And lastly, AR now automatically binds the model to the database, due to the “tableName ()” function which by default returns such a value “tbl_MODEL_NAME”. For example, the User model will be bound to the tbl_user table. If the table name is different, you can simply override the function.

Events


In the new version, working with events has become as simple as possible. In the demo application, I gave an example of my own event - when adding a new user, the event is called in afterSave (), although there you can use standard events that are available in Yii, such as “EVENT_AFTER_INSERT”.

In order to determine the event, it is enough to call the “trigger ()” function in the right place, and only then to set the handler for the event in the right place.

Example:
файл app\modules\users\models\User
...
public function afterSave($insert)
	{
               // Создаём событие
		$event = new ModelEvent;
		$this->trigger(self::EVENT_NEW_USER, $event);
		parent::afterSave($insert);
	}
...
файл app\modules\users\controllers\DefaultController
...
public function actionSignup()
	{
		$model = new User();
		$model->scenario = 'signup';
		if ($model->load($_POST)) {
			if (!$this->module->activeAfterRegistration)
                                // Задаём наш обработчик событий, для события [[EVENT_NEW_USER]]
				$model->on($model::EVENT_NEW_USER, array($this->module, 'onNewUser'));
			if ($model->save()) {
				Yii::$app->session->setFlash('success');
				return Yii::$app->response->refresh();
			}
		} else {
			echo $this->render('signup', array('model' => $model));
		}
	}
...

There are several ways to bind a handler:
function ($event) { ... }         // Анонимная функция
array($object, 'handleClick')    // $object->handleClick()
array('Page', 'handleClick')     // Page::handleClick()
'handleClick'                    // глобальная  функция handleClick()

View


A new class has appeared in Yii2, which is responsible for all representations of the application, and which directly outputs information.
Now in the view files the variable "$ this" no longer refers to the controller, but to the new class "yii \ base \ View".
In order to call a certain function of the controller or widget to which the view belongs, you need to turn to the method: "context".

Example:
// Файл app\modules\blogs\views\default\index
// $this->context относится к файлу app\modules\blogs\controllers\DefaultController
// Простой вызов параметра модуля, к которому относится контроллер из представления
echo $this->context->module->recordsPerPage; // Результат 10
//Файл app\modules\comments\widgets\comments\views\index
// $this->context относится к файлу app\modules\comments\widgets\comments\Comments
if ($this->context->model['id'] == 10 ) {...}

Widgets


Widgets were supplemented with new methods, plus now they should be directly displayed via "echo".

Example:
// Note that you have to "echo" the result to display it
echo \yii\widgets\Menu::widget(array('items' => $items));
// Passing an array to initialize the object properties
$form = \yii\widgets\ActiveForm::begin(array(
    'options' => array('class' => 'form-horizontal'),
    'fieldConfig' => array('inputOptions' => array('class' => 'input-xlarge')),
));
... form inputs here ...
\yii\widgets\ActiveForm::end();

Action filters


In the new version, controller filters are implemented in the form of “behaviors”.
public function behaviors()
	{
		return array(
			'access' => array(
				'class' => \yii\web\AccessControl::className(),
				'rules' => array(
				    // allow authenticated users
					array(
						'allow' => true,
						'actions' => array('login', 'signup', 'activation'),
						'roles' => array('?')
					),
					array(
						'allow' => true,
						'actions' => array('logout'),
						'roles' => array('@')
					),
					array(
						'allow' => true,
						'actions' => array('index', 'view'),
						'roles' => array('guest')
					),
					array(
						'allow' => true,
						'actions' => array('edit', 'delete'),
						'roles' => array('@')
					),
					// deny all - можно не писать, он по умолчанию всё запрещает
					array(
						'allow' => false
					)
				)
			)
		);
	}

A small nuance - only 2 roles remained out of the box:
  • @ - authorized
  • ? - guests
  • * - was deleted.

Static helpers


Yii2 added many new helpers. For example, like the same SecurityHelper, which speeds up work with passwords and generated codes, ArrayHelper, Html, and others, which again pleases.

Activeform


Forms are now created even faster and more conveniently, due to the ActiveField class, which simplifies the style of writing code, which is good news.
 array('class' => 'form-horizontal')));
echo $form->field($model, 'username')->textInput($model->isNewRecord ? array() : array('readonly' => true));
echo $form->field($model, 'email')->textInput();
if (!$model->isNewRecord) {
    if (Yii::$app->user->checkAccess('editProfile')) {
        echo $form->field($model, 'status')->dropDownList(array(
        	User::STATUS_ACTIVE => 'Active',
        	User::STATUS_INACTIVE => 'Inactive',
        	User::STATUS_DELETED => 'Deleted'
        ));
        echo $form->field($model, 'role')->dropDownList(array(
        	User::ROLE_USER => 'User',
        	User::ROLE_ADMIN => 'Admin'
        ));
    }
	echo $form->field($model, 'oldpassword')->passwordInput();
}
echo $form->field($model, 'password')->passwordInput();
echo $form->field($model, 'repassword')->passwordInput();
?>
isNewRecord ? 'Register' : 'Update', array('class' => 'btn btn-primary')); ?>

User and Identity


The robot with users is now implemented through the class "yii \ web \ User" and the interface "yii \ web \ Identity", which is more flexible in use.
Because of these changes, user attributes can be obtained through the user’s “identity” method.

Example:
echo Yii::$app->user->identity->username;

URL Management


Here, the styles for recording rules were slightly modified.

Example:
...
array(
  'dashboard' => 'site/index',
 'PUT post/' => 'post/update',
 'POST,PUT post/index' => 'post/create',
  'POST s' => '/create',
  's' => '/index',
 'PUT /'    => '/update',
 'DELETE /' => '/delete',
 '/'        => '/view',
);
...

RBAC


Work with user rights is now available initially from the box without adding your own code, as, for example, in the case of using the file version in the first version.
Below I will write briefly instructions on RBAC and a description of the roles in the file.

First, we need to create our own class, which will inherit from "\ yii \ rbac \ PhpManager".
In our example, it is located in the rbac module in the components folder under the name PhpManager.php.
The code in it is simple. We simply set the path to our file with the described roles, and bind the user to the desired role.
authFile === NULL)
            $this->authFile = Yii::getAlias('@app/modules/rbac/components/rbac') . '.php';
        parent::init();
        if (!Yii::$app->user->isGuest)
            $this->assign(Yii::$app->user->identity->id, Yii::$app->user->identity->role);
    }
}

After that, in the same folder, we create the “rbac.php” file where we describe the roles we need. (The code can be seen in the demo application in the folder: @app/modules/rbac/components/rbac)

And, finally, we just have to set up «authManager» in the configuration file:
...
'authManager' => array(
			'class' => 'app\modules\rbac\components\PhpManager',
			'defaultRoles' => array('guest'),
		),
...

After that, in the place we need, we can safely do user checks for the necessary rights:
if (Yii::$app->user->checkAccess('editOwnBlog', array('blog' => $model)) || Yii::$app->user->checkAccess('editBlog')) {
...
}

This is all that I managed to find out, but I’m sure there are still a lot of things that did not succeed.

At the moment, I did not manage to deal with ajax requests in Yii 2, namely, how to correctly create the right answer when validating the model. In 1.0, this is done like this:
echo CActiveForm::validate($model);

if anyone already knows, write, I'm sure many will be interested.

Total


Despite the unfinished look, Yii 2 already looks very good. I am sure only the best is ahead of us.

Yii 2 really liked it. To be honest, there is already a great desire to start writing working projects on the new version, although before that it is still very early.

The article turned out to be a big one, but in another way I just don’t know how.

Thank you all for your attention. Good luck

Demo blog on Github. Installation instructions are present.
A working blog example.

Also popular now: