Нам очень приятно сообщить о выходе бета-версии PHP фреймворка Yii 2. Вы можете загрузить его с yiiframework.com.
Бета включает в себя сотни новшеств, изменений и исправлений с релиза альфы.
Далее мы сделаем обзор наиболее важного, но сначала ответим на часто задаваемые вопросы.
Часто задаваемые вопросы
- Что значит бета? Бета означает стабильные набор возможностей и архитектуру. Начиная с беты и до GA (General Availability, стабильного релиза), мы будем заниматься, в основном, исправлением ошибок и документацией. Мы не собираемся изменять архитектуру фреймворка или добавлять какие-либо новые возможности. Стоит отметить, что поломки обратной совместимости всё ещё могут случаться, но мы попытаемся делать их как можно меньше, а сделанные детально описывать.
- Когда будет стабильный релиз? Пока что мы не можем назвать точную дату. Так как следующий этап заключается, главным образом, в исправлении ошибок и доработке документации, мы думаем, что сильно много времени это не займёт.
- Могу ли я использовать бету в моих проектах? Не стоит использовать бету в проектах с сжатыми сроками или если вы ещё не знакомы с Yii 2.0. Если это не про вас и вас не пугают изменения в фреймворке, можете попробовать. Мы слышали, что довольно много проектов уже запущены и работают на ветке `master`. Также помните, что минимальная версия PHP для работы фреймворка — 5.4.
- Есть ли документация по 2.0? Да, есть The Definitive Guide и API documentation. Гайд постоянно дополняется. Перевод на русский будет как только оригинал будет более-менее завершён.
- Как мне обновить приложения с 1.1 на 2.0? На эту тему написан отдельнй раздел
Upgrading from Yii 1.1. Стоит отметить, что так как 2.0 переписан полностью, обновление не будет тривиальной задачей. Тем не менее, если вы работали с 1.1, вам будет намного проще потому как между 1.1 и 2.0 много общего. - Как обновиться с альфы 2.0? Если вы обновляете альфу через Composer, стоит удалить всё, кроме
.gitignore
из директорииvendor
и запустить composer ещё раз. Сделать это необходимо лишь один раз и вам не придётся так поступать с каждым новым релизом. Полный список изменений, в том чисте и ломающих обратную совместимость, доступен в файле CHANGELOG. - Как следить за разработкой 2.0? Вся разработка и обсуждения происходят на GitHub:
https://github.com/yiisoft/yii2. Вы можете подписаться на обновления или поставить звёздочку чтобы получать уведомления об обновлениях. Также можно следить за нашим twitter https://twitter.com/yiiframework.
Большие изменения с альфа-версии
Полный список изменений вы можете найти в CHANGELOG, а ниже мы опишем самое важное.
Структура
Yii 2 начал использовать PSR-4
для загрузки классов. То есть:
- Структура директорий фреймворка стала проще.
- Структура директорий расширений стала проще.
- Фреймворк больше не загружает классы, именованные в стиле PEAR. Загрузка классов от этого упростилась и стала быстрее.
Классы контроллеров теперь обязательно должны находится в пространстве имён, указанном в Module::controllerNamespace
, если конечно вы не используете Module::controllerMap
.
Также мы вернули поддержку группировки контроллеров в субдиректории, которая была в 1.1.
Удобство использования
Удобство использования — один из наивысших приоритетов нашей команды. Именно поэтому мы уделяем много внимания именованию и тому, чтобы код нормально работал с различными IDE. Всё это позволяет сделать повседневную разработку более приятной.
Мы перешли на стили кода PSR-1 и PSR-2 и получили поддержку многих IDE, автоматизированной проверки кода и
автоматического форматирования.
Производительность
Одно из самых значительных изменений состоит в том, что сессия не стартует до тех пор, пока она действительно не понадобится. Это позволяет приложениям не тратить лишние ресурсы.
Если в своих проектах вы используете Markdown, вы заметите, что скорость преобразования в HTML существенно возрасла. Это стало возможным после того, как Carsten Brandt (cebe), после рассмотрения существующих решений, написал с нуля свою библиотеку для этих целей. Кроме производительности, она лучше расширяется и поддерживает вариант markdown, используемый GitHub.
Безопасность
Теперь Yii использует *маскированые* CSRF-токены для предотвращения атак типа BREACH.
RBAC был переписан, вследствие чего стал проще. Бизнес-правила теперь оформляются в виде классов без всякого eval
.
RESTful API framework
Долгожданная возможность, появившаяся в бете — встроенный фреймворк для создания REST API. В данном обзоре мы не будем описывать его в деталях, а лишь перечислим основные особенности. Полное описание доступно в The Definitive Guide.
- Быстрое прототипирование частых операций через ActiveRecord;
- Response format negotiation (по умолчанию поддерживается JSON и XML);
- Настраиваемая сериализация объектов. Можно выбрать сериализуемые поля.
- Правильное форматирование коллекций данных и ошибок валидации;
- Эффективный роутинг с полной поддержкой HTTP;
- Поддержка
OPTIONS
иHEAD
; - Аутентификация;
- Авторизация;
- Поддержка HATEOAS;
- HTTP-кеширование;
- Ограничение количества запросов.
Dependency Injection и Service Locator
Многие спрашивали, почему в Yii нет контейнера Dependency Injection (DI). На самом деле Yii всегда предоставлял похожую функциональность в виде Service Locator — экземпляра приложения Yii. Тепереь мы выделили service locator в отдельный компонент yiidiServiceLocator
. Как и раньше, и приложение и модули являются service locator-ами. Вы можете получить сервис (в 1.1 он назвался компонент приложения) используя Yii::$app->get('something')
.
Кроме Service Locator, мы также реализовали DI контейнер yiidiContainer
. Он помогает создавать менее связанный код. По предварительным данным, наш контейнер является одним из самых быстрых контейнеров DI на PHP. Вы можете использовать Yii::$container->set()
для задания начальных значений классов. Старый метод Yii::$objectConfig
удалён.
Тестирование
Yii был интегрирован с Codeception. Это позволяет тестировать приложение в целом путём эмуляции действий пользователя и проверки правильности генерируемых ответов. В отличие от поддержки selenium в PHPUnit, Codeception не требует браузера, его проще поставить на CI-сервер и отрабатывает он гораздо быстрее.
В Yii также добавлена поддержка фикстур, решающая проблему инициализации тестовых данных — одной из самых долгих и рутинных задач при написании тестов. Поддержка фикстур покрывает как их создание, так и применение. Также было реализовано расширение faker, которое помогает создавать близкие к реальным наборы данных.
Оба шаблона приложений, как «basic», так и «advanced», поставляются вместе с тестами: модульными, функциональными и приёмочными. Надеемся, это будет хорошим стимулом применять разработку через тестирование.
Валидация моделей
В валидации моделей произошло довольно много интересных улучшений.
Валидаторы UniqueValidator
и ExistValidator
теперь поддерживают проверку нескольких столбцов. Ниже приведены несколько примеров для unique
:
// a1 должен быть уникальным
['a1', 'unique']
// a1 должен быть уникален, но сравнивать будем с a2
['a1', 'unique', 'targetAttribute' => 'a2']
// a1 и a2 вместе должны быть уникальны и оба поля получат сообщения об ошибке
[['a1', 'a2'], 'unique', 'targetAttribute' => ['a1', 'a2']]
// a1 и a2 вместе должны быть уникальны, но только a1 получит сообщение об ошибке
['a1', 'unique', 'targetAttribute' => ['a1', 'a2']]
// a1 должен быть уникален при сравнении его значения как с a2, так и с a3
['a1', 'unique', 'targetAttribute' => ['a2', 'a1' => 'a3']]
Валидацию можно производить по условиям, которые задаются через свойства when
и whenClient
. Далее показан пример обязательности поля «state» только в том случае, если выбранная страна «USA»:
['state', 'required',
'when' => function ($model) {
return $model->country == Country::USA;
},
'whenClient' => "function (attribute, value) {
return $('#country').value == 'USA';
}",
]
Иногда требуется проверить данные, не привязанные к модели. Это можно сделать используя новый класс yiibaseDynamicModel
:
public function actionSearch($name, $email)
{
$model = DynamicModel::validateData(compact('name', 'email'), [
[['name', 'email'], 'string', 'max' => 128],
['email', 'email'],
]);
if ($model->hasErrors()) {
// неудачная валидация
} else {
// всё хорошо
}
}
База данных и Active Record
Всё связанное с базами данных — сильная сторона Yii. Набор возможностей был уже довольно интересен в альфа-версии, а бета принесла улучшения и новые возможности. В числе реализаций Active Record есть elasticsearch, redis и Sphinx search. А в бете появилась поддержка mongodb.
Поддержка вложенных транзакций
Yii теперь поддерживает вложенные транзакции. Вы можете начать транзакцию не беспокоясь о том, начата ли внешняя по отношению к данной транзакции.
Join
Мы добавили ActiveQuery::joinWith()
для поддержки создания SQL-запросов с JOIN использую уже объявленные связи AR. Это особенно полезно, если вы хотите отфильтровать или отсортировать данные по полям из связанных таблиц. К примеру:
// найдёт все заказы и отсортирует их по id клиента и id заказа. При этом для "customer" будет использована жадная загрузка
$orders = Order::find()->joinWith('customer')->orderBy('customer.id, order.id')->all();
// найдёт все заказы, в которых есть книги. При этом для "books" будет использована жадная загрузка
$orders = Order::find()->innerJoinWith('books')->all();
Это особенно полезно при работе с связанными столбцами в GridView. Теперь, используя joinWith()
, очень легко сделать по ним сортировку или отфильтровать.
Преобразование типов данных
ActiveRecord теперь конвертирует данные, полученные из базы в соответствующие типы. К примеру, если у вас в базе столбец type
типа integer
, после получения соответствующего экземпляра ActiveRecord, type
будет типа integer
в PHP.
Поиск
Для того, чтобы упростить задачу поиска мы добавили метод Query::filterWhere()
, который автоматически убирает пустые значения. К примеру, если у вас есть форма поиска с фильтром по полям name
и email
, то вы можете использовать приведённый код для построения поискового запроса. Без данного метода вам бы пришлось проверять, ввёл ли пользователь что-либо в поле и добавлять условие поиска только в этом случае. filterWhere()
делает такую проверку за вас.
$query = User::find()->filterWhere([
'name' => Yii::$app->request->get('name'),
'email' => Yii::$app->request->get('email'),
]);
Пакетные запросы
Мы добавили поддержку пакетных запросов для работы с крупными объёмами данных. При использовании данной возможности мы получаем данные порциями, а не сразу. Это позволяет значительно экономить память. К примеру:
use yiidbQuery;
$query = (new Query())
->from('user')
->orderBy('id');
foreach ($query->batch() as $users) {
// $users — массив из 100 или менее строк из таблицы user
}
// или, если хотите получать по одной строке за раз
foreach ($query->each() as $user) {
// $user — одна строка данных из таблицы user
}
Вы можете использовать пакетные запросы и с ActiveRecord:
// выбираем по 10 клиентов за раз
foreach (Customer::find()->batch(10) as $customers) {
// $customers — массив 10 или менее объектов Customer
}
// выбираем по 10 клиентов за раз, перебираем по одному
foreach (Customer::find()->each(10) as $customer) {
// $customer — объект Customer
}
// пакетный запрос с жадной загрузкой
foreach (Customer::find()->with('orders')->each() as $customer) {
}
Поддержка подзапросов
В постоитель запросов была добавлена поддержка вложенных запросов. Вы можете составить подзапрос как обычный объект Query
и далее использовать его в другом Query
:
$subQuery = (new Query())->select('id')->from('user')->where('status=1');
$query->select('*')->from(['u' => $subQuery]);
Обратные связи
Связи часто могут объявляться парами. К примеру, Customer
может содержать связь orders
, в Order
может содержать связь customer
. В примере ниже можно заметить, что customer
заказа не тот же самый объект, что и изначальный, для
которого ищутся заказы. Вызов customer->orders
приведёт к новому SQL-запросу также, как и как вызов customer
заказа:
// SELECT * FROM customer WHERE id=1
$customer = Customer::findOne(1);
// echoes "not equal"
// SELECT * FROM order WHERE customer_id=1
// SELECT * FROM customer WHERE id=1
if ($customer->orders[0]->customer === $customer) {
echo 'equal';
} else {
echo 'not equal';
}
Чтобы избежать лишнего SQL запроса можно объявить обратную связь для связей customer
и orders
. Делается это при помощи метода inverseOf()
:
class Customer extends ActiveRecord
{
// ...
public function getOrders()
{
return $this->hasMany(Order::className(), ['customer_id' => 'id'])->inverseOf('customer');
}
}
Теперь, если мы выполним приведённый код ещё раз, мы получим следующее:
// SELECT * FROM customer WHERE id=1
$customer = Customer::findOne(1);
// echoes "equal"
// SELECT * FROM order WHERE customer_id=1
if ($customer->orders[0]->customer === $customer) {
echo 'equal';
} else {
echo 'not equal';
}
Единообразные API для Relational Query
В альфе 2.0 в ActiveRecord мы реализовали поддержку как реляционных (например, MySQL), так
и noSQL (например, redis, elasticsearch, MongoDB) хранилищ данных. В бете мы отрефакторили этот код и достигли большего единообразия интерфейсов. А именно, был удалён ActiveRelation
, а ActiveQuery
стал единой точкой входа для выполнения запросов по связям и описания связей. Также мы добавили методы ActiveRecord::findOne()
и findAll()
.
Эти методы позволяют делать запросы по ключам или значениям столбцов, используя короткий синтаксис. Ранее это можно было делать параметрами метода ActiveRecord::find()
, что вызывало путаницу из за возврата методом разных типов данных.
Поддержка AJAX
Мы решили использовать отличную библиотеку Pjax и создали виджет yiiwidgetsPjax
. Это общий виджет, позволяющий добавить AJAX ко всему, что в него обёрнуто. К примеру, в него можно обернуть GridView
для
того, чтобы получить постраничную навигацию и сортировку без перезагрузки страницы:
use yiiwidgetsPjax;
use yiigridGridView;
Pjax::begin();
echo GridView::widget([ /*...*/ ]);
Pjax::end();
Request и response
Кроме множества внутренних исправлений и улучшений, компоненты request и response подверглись и множеству изменений. Самое заметное заключается в том, что работа с request теперь выглядит так:
// получаем GET параметр из request, если его там нет, умолчание равно 1
$page = Yii::$app->request->get('page', 1);
// получаем POST параметр из request, если его там нет, умлчание равно null
$name = Yii::$app->request->post('name');
Ещё одно фундаментальное изменение состоит в том, что response теперь отсылается в самом конце работы приложения, что позволяет модифицировать заголовки и тело ответа как вы пожелаете и где вы пожелаете.
Класс request теперь умеет разбирать тело запроса в различных форматах, например JSON.
Фильтры
Весь механизм фильтрации был переосмыслен. Теперь вы можете включить фильтрацию action-ов на как уровне контроллера, так и на уровнях приложения или модуля. Это позволяет вам организовать иерархичные фильтры. К примеру, вы можете установить фильтр для модуля и он будет применяться ко всем его action-ам. В дополнение можно установить ещё один фильтр на некоторые контроллеры модуля. Этот фильтр будет применяться только к action-ом в этих контроллерах.
Код был реорганизован и фильтры теперь находятся в пространстве имён yiifilters
. К примеру, вы можете использовать фильтр yiifiltersHttpBasicAtuh
для того, чтобы включить аутентификацию HTTP Basic Auth. Для этого он описывается в контроллере или модуле:
public function behaviors()
{
return [
'basicAuth' => [
'class' => yiifiltersauthHttpBasicAuth::className(),
'exclude'=> ['error'], // не применяем к action "error"
],
];
}
Инициализация компонент
Мы ввели в жизненный цикл приложения важный шаг инициализации («bootstrap»). Расширения могут зарегистрировать bootstrap-классы путём объявления их в composer.json
. Обычный компонент также может быть зарегистрирован для bootstrap если его объявить в Application::$bootstrap
.
Компонент bootstrap будет инстранциирован до того, как приложение начнёт обработку запроса. Это даёт компоненту возможность зарегистрировать обработчики событий и принять участие в жизненном цикле приложения.
Работа с URL
Так как разработчики очень много работают с URL, мы вынесли большинство относящихся к URL методов в хелпер Url
и получили более приятный API:
use yiihelpersUrl;
// текущий активный маршрут
// например: /index.php?r=management/default/users
echo Url::to('');
// тот же контроллер, другой action
// например: /index.php?r=management/default/page&id=contact
echo Url::toRoute(['page', 'id' => 'contact']);
// тот же модуль, другие контроллер и action
// например: /index.php?r=management/post/index
echo Url::toRoute('post/index');
// абсолютный маршрут не зависит от того, какой контроллер делает запрос
// например: /index.php?r=site/index
echo Url::toRoute('/site/index');
// url для регистрозависимого имени `actionHiTech` текущего контроллера
// например: /index.php?r=management/default/hi-tech
echo Url::toRoute('hi-tech');
// url для action в контроллере с регистрозависимым именем, `DateTimeController::actionFastForward`
// например: /index.php?r=date-time/fast-forward&id=105
echo Url::toRoute(['/date-time/fast-forward', 'id' => 105]);
// получаем URL из alias
Yii::setAlias('@google', 'http://google.com/');
echo Url::to('@google/?q=yii');
// получаем канонический URL для текущей страницы
// например: /index.php?r=management/default/users
echo Url::canonical();
// получаем домашний URL
// например: /index.php?r=site/index
echo Url::home();
Url::remember(); // сохраняем URL
Url::previous(); // получаем сохранённый URL
В правилах URL тоже имеются улучшения. Вы можете использовать новый класс yiiwebGroupUrlRule
для того, чтобы группировать правила. Для каждоый группы можно задать общий префикс, который будет применяться ко всем URL в группе.
new GroupUrlRule([
'prefix' => 'admin',
'rules' => [
'login' => 'user/login',
'logout' => 'user/logout',
'dashboard' => 'default/dashboard',
],
]);
// правило выше эквивалентно следующему:
[
'admin/login' => 'admin/user/login',
'admin/logout' => 'admin/user/logout',
'admin/dashboard' => 'admin/default/dashboard',
]
Контроль доступа на основе ролей (RBAC)
Мы переделали реализацию RBAC. Теперь она основана на исходной модели RBAC NIST, а именно мы выкинули концепцию операций и задач. Вместо них теперь разрешения, что соответствует термину NIST.
Как было упомянуто выше, редизайн постиг и biz rule. Тепереь правила описываются в отдельных классах.
Переводы
Сперва мы хотим сказать спасибо всем членам сообщества, которые участвовали в переводе сообщений фреймворка. На данный момент они переведены на 26 языков. Хорошее число, правда?
Компонент перевода сообщений теперь пытается взять сообщения из альтернативного источника, если используемый не содержит нужного сообщения. К примеру, если ваше приложение использует fr-CA
в качестве языка, а переводы есть только для fr
, то Yii сначала будет искать перевод в fr-CA
и, если он там не найден, поиск продолжится в fr
.
В генератор кода Gii была добавлена новая опция, которая позволяет вам генерировать код с сообщениями, обёрнутыми в Yii::t()
.
Команда для поиска сообщений для перевода научилась писать строки в .po
и базы данных.
Расширения и инструменты
Мы реализовали расширение генератор документации yii2-apidoc
, которое можно использовать для создания документации по API и основной документации из markdown. Генератор легко настраивается и расширяется под ваши нужды. Мы используем его для генерации официальной документации и API. Вы можете посмотреть на результат тут: http://www.yiiframework.com/doc-2.0/.
Дебаггер получил множество мелких улучшений и панель отладки почты. Также он научился показывать количество запросов к базе данных и количество отправленных писем в тулбаре.
Кроме улучшений по части переводов, упомянутых выше, в Gii был добавлен новый генератор расширений. Кроме того, вы можете заметить улучшений в предварительном просмотре генерируемого кода. Вы можете быстро переключаться между файлами и обновлять diff, в том числе и с клавиатуры. Также теперь при копи-пасте из diff в буфер не попадает ничего лишнего.
Попробуйте!
Спасибо!
Бета-версия Yii 2.0 — большая веха, которая была достигнута большими совместными усилиями. У нас бы ничего не получилось без огромного количества ценного кода, полученного от нашего замечательного сообщества.
Спасибо вам за то, что этот релиз состоялся.
Автор: SamDark