Pull to refresh

Знакомство с Yii 2 на основе создания простого блога

Yii *
Sandbox
Здравствуйте, уважаемые хабрапользователи!

Как вы уже поняли из заголовка, в данной статье пойдёт речь о новой версии Yii. Я попробую коротко, на живом примере, познакомить вас с замечательным Yii 2.

Писать полную инструкцию по созданию блога, наверное, не имеет смысла, так как многие шаги схожи с первой версией, но я буду писать именно про те нюансы, которые отличаются, и буду делать акцент на новинках Yii2, которые я заметил в процессе создания приложения. Всё же остальное вы сможете быстро посмотреть в демо приложении, что, наверное, будет более эффективно, чем просто написанный текст.

Основные моменты


В Yii2, как вы уже заметили, все построено на namespace, это, пожалуй, основная «изюминка» в новой версии.
Также очень важный момент: «C» префикс из имён файлов был удалён.

Установка и создание первого приложения


За основу мы будем использовать basic приложение.
Как её установить очень хорошо описано на Github по этому сразу же переходим к следующим пунктам.

Структура приложения


Для примера я выбрал модульную структуру. Это позволит нам узнать и понять принцип работы модулей в Yii2.
Итак, для того, чтобы реализовать такую структуру, нам, как и в первой версии, нужно создать основную папку всех модулей «modules», в которой мы уже будем создавать отдельные папки для отдельных модулей. Тут все, как в первой версии.

Единственное что поменялось в модулях – это название основного класса модуля. Как я понял, теперь уже не обязательно писать суффикс «Module», поэтому наши основные файлы будут иметь понятные имена без суффикса.

Пример:
Site.php, Users.php, Blogs.php, и.т.д.
Структуру вы сможете посмотреть уже на готовом примере, и останавливаться больше на этом не имеет смысла.

Настройка приложения: config/main.php


Как и в Yii 1.0, приложение настраивается через файла main.php, и многие элементы остались без изменений, но всё-таки есть много переименованных параметров, что следует помнить в случае неработоспособности приложения после конфигурации.

Несколько примеров таких изменений и особенностей:
  • Приркутка модулей теперь содержит обязательный параметр «class», который хранит имя основного класса модуля, и без которого подключить модуль не получится.
    'modules' => array(
    ...
    		'users' => array(
    			'class' => 'app\modules\users\Users'
    		),
    ...
    	),
    

  • Параметр «defaultController» убрали, и вместо него используется «defaultRoute».
  • При настройке путей к директориям, можно использовать заданные алиасы "@app, @www, @wwwroot", что очень удобно.
  • В UrlManager тоже переименовали несколько параметров. Теперь «urlFormat» заменили на «enablePrettyUrl», «useStrictParsing» на «enableStrictParsing».
  • В компоненте «db» переименовали «connectionString» на «dsn».

В случае неработоспособности каких-либо параметров, можно легко подсмотреть все в исходниках фрейма.
Как только мы настроили наше приложение и подключили наши модули, можем приступить к написанию нашего «CRUD».

CRUD


Подробный пример есть в демо коде, но, если честно, сильного отличия от первой версии нет. Единственное, что появилось новое, это ранее метод контроллера: populate(), который был перенесён на днях в модель и переименован в load().

Пример:
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'];
}

Других особых различий в работе с CRUD нет.

Модель


Основным нововведением является метод «scenarios() за счёт которого можно настроить валидаторы модели в зависимости от указанного сценария.
public function scenarios()
{
    return array(
        'backend' => array('email', 'role'),
        'frontend' => array('email', '!name'),
    );
}

Привязка модели делается за счёт функции formName() которая в итоге возвращает имя Класса модели, к которой относится форма.
Также немаловажный момент — model() убрали, и теперь работа с моделью происходит, как и с другим обычным классом:
 MyModel::getAuthor();

ActiveRecord


Тут все переписали, теперь в Yii2 новый AR, который реально радует.

Примеры нескольких нововведений:
«scopes()» были заменены на обычные методы AR модели, которые сейчас имеют такой вид:
public function active($query)
	{
		return $query->andWhere('status = ' . self::STATUS_ACTIVE);
	}

Также радикально были изменены «relations()», которые сейчас задаются в виде гетерров, что более правильно. Доступны два типа relations: «hasOne()», «hasMany()».
public function getAuthor()
	{
		return $this->hasOne('app\modules\users\models\User', array('id' => 'author_id'));
                // Первый параметр – это у нас имя класса, с которым мы настраиваем связь.
               // Во втором параметре в виде массива задаётся имя удалённого PK ключа  (id) и FK из текущей таблицы модели (author_id), которые связываются между собой
	}

Также были переписаны функции выборки из базы, и дополнены новыми.
$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();

И последнее, AR теперь делает автоматическую привязку модели к базе, за счёт функции «tableName()» которая по умолчанию возвращает такое значение «tbl_MODEL_NAME». Для примера, модель «User», будет привязана к таблице «tbl_user». Если же имя таблицы отличается, можно просто переопределить функцию.

События


В новой версии работать с событиями стало максимально просто. В демо приложении я привёл пример собственного события, — при добавлении нового пользователя, событие вызывается в afterSave(), хотя там можно использовать стандартные события, которые доступны в Yii, как например «EVENT_AFTER_INSERT».

Для того чтобы определить событие достаточно в нужном месте вызвать «trigger()» функцию, а уже потом в нужном месте задать для события обработчик.

Пример:
файл 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));
		}
	}
...

Есть несколько способов привязки обработчика:
function ($event) { ... }         // Анонимная функция
array($object, 'handleClick')    // $object->handleClick()
array('Page', 'handleClick')     // Page::handleClick()
'handleClick'                    // глобальная  функция handleClick()

View


В Yii2 появился новый класс, который отвечает за все представления приложения, и который выполняет непосредственно вывод информации.
Теперь в view файлах переменная "$this" относится уже не к контроллеру, а именно к новому классу «yii\base\View».
Для того, чтобы вызвать определённую функцию контроллера или виджета, к которому принадлежит представление, нужно обратится к методу: «context».

Пример:
// Файл 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


Виджеты были дополнены новыми методами, плюс теперь они должны быть непосредственно выведены через «echo».

Пример:
// 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


В новой версии фильтры контроллеров реализованы в виде «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
					)
				)
			)
		);
	}

Меленький нюанс — из коробки остались только 2 роли:
  • @ — авторизованные
  • ? — гости
  • * — была удалена.

Static Helpers


В Yii2 добавлены много новых хелперов. Например, как тот же SecurityHelper, который ускоряет работу с паролями и генерированными кодами, ArrayHelper, Html, и другие, что снова радует.

ActiveForm


Формы теперь создаются ещё быстрее и удобнее, за счёт ActiveField класса, который упрощает стиль написания кода, что не может не радовать.
<?php $form = ActiveForm::begin(array('options' => 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();
?>

<div class="form-actions">
	<?php echo Html::submitButton($model->isNewRecord ? 'Register' : 'Update', array('class' => 'btn btn-primary')); ?>
</div>

<?php ActiveForm::end(); ?>

User and Identity


Робота с пользователями теперь осуществляется через класс «yii\web\User» и интерфейс «yii\web\Identity», что более гибко в использовании.
Из-за этих изменений атрибуты пользователя можно получить через метод «identity» пользователя.

Пример:
echo Yii::$app->user->identity->username;

URL Management


Тут чуток видоизменили стили записи правил.

Пример:
...
array(
  'dashboard' => 'site/index',
 'PUT post/<id:\d+>' => 'post/update',
 'POST,PUT post/index' => 'post/create',
  'POST <controller:\w+>s' => '<controller>/create',
  '<controller:\w+>s' => '<controller>/index',
 'PUT <controller:\w+>/<id:\d+>'    => '<controller>/update',
 'DELETE <controller:\w+>/<id:\d+>' => '<controller>/delete',
 '<controller:\w+>/<id:\d+>'        => '<controller>/view',
);
...

RBAC


Работа с пользовательскими правами теперь доступна изначально из коробки без добавление своего кода, как, например, в случае с использованием файлового варианта в первой версии.
Ниже я напишу коротко инструкцию про RBAC и описание ролей в файле.

Для начала нам нужно создать свой класс, который будет наследоваться от "\yii\rbac\PhpManager".
В нашем примере он находится в модуле «rbac» в папке «components» под названием «PhpManager.php».
Код в нем простой. Мы просто задаём путь к нашему файлу с описанными ролями, и делаем привязку пользователя к нужной роли.
<?php 
namespace app\modules\rbac\components;

use Yii;

class PhpManager extends \yii\rbac\PhpManager
{
    public function init()
    {
        if ($this->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);
    }
}

После, в той же папке мы создаём файл «rbac.php» где описываем нужные нам роли. (Код можно увидеть в демо приложении в папке: @app/modules/rbac/components/rbac)

Ну и, напоследок, нам осталось только настроить «authManager» в конфигурационном файле:
...
'authManager' => array(
			'class' => 'app\modules\rbac\components\PhpManager',
			'defaultRoles' => array('guest'),
		),
...

После чего в нужном нам месте мы можем смело делать проверки пользователя на наличие нужных прав:
if (Yii::$app->user->checkAccess('editOwnBlog', array('blog' => $model)) || Yii::$app->user->checkAccess('editBlog')) {
...
}

Это все что я успел узнать, но, уверен, ещё есть много чего не успелось.

На данный момент я не успел разобраться с ajax запросами в Yii 2, а именно – как правильно создавать нужный ответ при валидации модели. В 1.0 это делается так:
echo CActiveForm::validate($model);

если кто уже знает, пишите, уверен многим будет интересно.

Итог


Несмотря на ещё недоделанный вид, Yii 2 выглядит уже очень хорошо. Уверен, впереди нас ждёт только лучшее.

Самому Yii 2 очень понравился. Если честно, уже есть огромное желание начать писать рабочий проекты на новой версии, хотя до этого ещё очень рано.

Статья получилось большая, но по-другому просто не знаю как.

Спасибо всем за внимание. Удачи!

Демо блог на Github. Инструкция по установке присутствует.
Рабочий пример блога.
Tags:
Hubs:
Total votes 42: ↑36 and ↓6 +30
Views 183K
Comments Comments 33