Что такое Yii

Yii — full-stack PHP-фреймворк с сильным уклоном в быстрые CRUD-приложения, админки, кабинеты, каталоги, формы и REST API. В классическом Yii-проекте из коробки есть MVC-структура, routing, controllers, views, models, validation, Active Record, migrations, widgets, RBAC, debug toolbar, консольный скрипт yii и генератор кода Gii.

В этой статье речь в основном о Yii 2, потому что именно он чаще встречается в существующих full-stack проектах с Gii, ActiveRecord-моделями, GridView и типовыми админками. Если Laravel часто продаёт «единый framework-way», а Symfony — компонентность и явные границы, Yii занимает более прагматичную нишу: быстро поднять приложение вокруг базы данных, форм и административного интерфейса.

Yii ставится через Composer, Packagist и composer.json, использует namespaces и autoloading, поэтому хорошо ложится на темы Неймспейсы и use и Autoloading и PSR-4. Но его стиль заметно отличается от PSR-first microframework-подхода из Slim и Mezzio: Yii даёт готовую application structure и много собственных abstractions.

MVC и request lifecycle

Типовой Yii 2 basic project имеет знакомую структуру: config/, controllers/, models/, views/, runtime/, vendor/, web/, yii. Веб-доступным должен быть только каталог web/; входная точка — web/index.php. Остальной PHP-код находится вне document root, что важно для безопасности и деплоя.

project/
├── config/        # конфигурация приложения и компонентов
├── controllers/   # controller classes и actions
├── models/        # Active Record и form models
├── views/         # PHP-шаблоны views
├── runtime/       # cache, logs, временные файлы
├── vendor/        # Composer-зависимости
├── web/           # document root: index.php, assets
└── yii            # консольный entry script

Yii реализует MVC: controller принимает route, получает данные из request, вызывает model, затем рендерит view и возвращает response. Это та же предметная область, что в SAPI и суперглобалы, GET, POST и фильтрация ввода и HTTP-заголовки, ответы и редиректы, только завернутая в framework components.

flowchart TD A[HTTP request] --> B[web/index.php] B --> C[Yii application] C --> D[Request component resolves route] D --> E[Controller] E --> F[Action filters] F --> G[Action executes] G --> H[Model / Active Record] G --> I[View / widget rendering] I --> J[Response component] H --> I J --> K[HTTP response]
flowchart TD
    A[HTTP request] --> B[web/index.php]
    B --> C[Yii application]
    C --> D[Request component resolves route]
    D --> E[Controller]
    E --> F[Action filters]
    F --> G[Action executes]
    G --> H[Model / Active Record]
    G --> I[View / widget rendering]
    I --> J[Response component]
    H --> I
    J --> K[HTTP response]
Упрощённый request lifecycle Yii: входной скрипт создаёт application, route приводит к controller action, action работает с model и view, затем response отправляется клиенту.

Минимальный controller action выглядит так:

namespace app\controllers;

use yii\web\Controller;
use yii\web\Response;

final class HealthController extends Controller
{
    public function actionIndex(): array
    {
        \Yii::$app->response->format = Response::FORMAT_JSON;

        return ['ok' => true];
    }
}

По умолчанию route в Yii часто следует схеме controller/action: например, health/index. Можно настраивать pretty URLs, rules и REST-контроллеры, но базовая mental model остаётся простой: route выбирает controller action, action готовит данные, view или response отправляет результат.

Быстрое повторение
Почему в Yii 2 веб-сервер должен отдавать только каталог `web/`, а не весь проект?

Active Record и работа с базой

Одна из центральных частей Yii — yii\db\ActiveRecord. Active Record class связан с таблицей, объект — со строкой, attribute — с колонкой. Поэтому Yii особенно удобен, когда приложение естественно растёт вокруг реляционной базы: пользователи, заказы, товары, статусы, справочники, права доступа.

namespace app\models;

use yii\db\ActiveRecord;

final class Product extends ActiveRecord
{
    public static function tableName(): string
    {
        return '{{%product}}';
    }

    public function rules(): array
    {
        return [
            [['title', 'price'], 'required'],
            ['title', 'string', 'max' => 255],
            ['price', 'number', 'min' => 0],
        ];
    }
}

Сохранение выглядит лаконично:

$product = new Product();
$product->title = 'Клавиатура';
$product->price = 7900;

if (!$product->save()) {
    var_dump($product->getErrors());
}

Важно не путать Active Record с магической защитой от всех SQL-проблем. Для обычных значений Yii строит параметризованные запросы, но архитектурные правила из PDO и подключение к базе и Prepared statements и SQL injection всё равно остаются актуальны: имена таблиц и колонок нельзя принимать из пользовательского ввода без allowlist, сложные отчёты иногда лучше писать через Query Builder или SQL, а массовые выборки стоит делать через batch(), each() или asArray(), чтобы не раздувать память объектами.

Отношения описываются методами hasOne() и hasMany():

final class Order extends ActiveRecord
{
    public function getCustomer()
    {
        return $this->hasOne(Customer::class, ['id' => 'customer_id']);
    }
}

$order = Order::find()->with('customer')->where(['id' => 10])->one();
echo $order->customer->email;
Быстрое повторение
Как в Yii Active Record соотносятся class, object, attribute и база данных?

Gii: генерация моделей, CRUD и админок

Gii — web-based code generator, который в dev-окружении подключается как module. Он умеет генерировать Active Record models, CRUD controllers, views, forms и search models. В типичном сценарии вы создаёте таблицу миграцией, затем через Gii получаете модель Product, ProductController, ProductSearch и views для списка, просмотра, создания и редактирования.

models/Product.php
models/ProductSearch.php
controllers/ProductController.php
views/product/index.php
views/product/view.php
views/product/create.php
views/product/update.php
views/product/_form.php
views/product/_search.php
php yii gii/model --tableName=product --modelClass=Product
php yii gii/crud \
  --modelClass="app\\models\\Product" \
  --controllerClass="app\\controllers\\ProductController"

Это не значит, что сгенерированный код надо оставлять как есть. Gii хорош как стартовая точка: быстро получить рабочий CRUD, затем убрать лишнее, вынести бизнес-логику из controller, настроить validation, access rules, labels, search filters и UI. Для внутренней админки это часто экономит часы. Для публичного продукта с тонкой UX-логикой Gii-код обычно становится черновиком, а не финальной архитектурой.

Быстрое повторение
Почему Gii в Yii полезен для админки, но опасен как финальная архитектура проекта?

Forms, validation и widgets

В Yii model — это не только Active Record. Для login form, contact form, фильтров и одноразовых сценариев часто создают класс от yii\base\Model, где есть attributes и rules(), но нет таблицы в базе.

use yii\base\Model;

final class LoginForm extends Model
{
    public string $username = '';
    public string $password = '';

    public function rules(): array
    {
        return [
            [['username', 'password'], 'required'],
            ['username', 'string', 'max' => 64],
        ];
    }
}

Во view форма обычно строится через yii\widgets\ActiveForm:

use yii\helpers\Html;
use yii\widgets\ActiveForm;

$form = ActiveForm::begin(['id' => 'login-form']);

echo $form->field($model, 'username');
echo $form->field($model, 'password')->passwordInput();
echo Html::submitButton('Войти', ['class' => 'btn btn-primary']);

ActiveForm::end();

ActiveForm связывает HTML-поля с model attributes и validation rules. Это удобно, но не отменяет правил из XSS, экранирование вывода и шаблоны: вывод всё равно нужно экранировать по контексту, а HTML helpers использовать осознанно.

Widgets — переиспользуемые блоки во views. В Yii это не «виджеты на дашборде», а PHP-классы, которые генерируют UI: ActiveForm, GridView, ListView, DetailView, Menu, LinkPager, Pjax-обёртки и сторонние widgets. Именно поэтому Yii хорошо подходит для админок: список с фильтрами, сортировкой и пагинацией можно собрать поверх ActiveDataProvider и GridView быстрее, чем писать весь HTML вручную.

RBAC и доступы

Для авторизации Yii предлагает access control filters и RBAC. Access Control Filter подходит для простых правил вроде «только авторизованные пользователи могут открыть /admin». RBAC нужен, когда есть роли, permissions и наследование: author может createPost, admin наследует права author и дополнительно может updatePost.

Yii поддерживает PhpManager для хранения auth data в PHP-файле и DbManager для хранения в базе. В реальных админках чаще нужен DbManager, потому что роли и назначения меняются без деплоя. При этом RBAC не должен превращаться в бизнес-логику, разбросанную по views. Проверки доступа лучше держать рядом с controller actions, policies/services или явно выделенными permission checks.

if (\Yii::$app->user->can('updateProduct', ['product' => $product])) {
    // показать кнопку редактирования или выполнить действие
}

Где Yii особенно уместен

Yii уместен в проектах, где база данных и CRUD — центр приложения: back-office, CRM-подобные системы, каталоги, внутренние панели, legacy-приложения, где важны понятные forms, grids, роли, миграции и быстрый scaffolding. Он также встречается в long-lived проектах, где команда уже накопила свои widgets, modules и conventions.

Главные риски Yii — переоценить Gii и недооценить архитектуру. Сгенерированный CRUD легко начинает жить как «вся система»: толстые controllers, Active Record с бизнес-логикой, view-файлы с условиями доступа, сложно тестируемые сценарии. Если проект растёт, Yii-коду нужны те же инженерные практики, что и любому PHP-приложению: ясные классы из Классы, объекты и видимость, интерфейсы там, где есть сменяемые реализации из Наследование, интерфейсы и трейты, статический анализ из PHPDoc, generics и статический анализ, тесты из PHPUnit, моки и стабы и аккуратный deploy pipeline.

Если нужен современный ecosystem-first фреймворк с большим количеством готовых продуктовых пакетов, сравните Yii с Laravel. Если важна компонентная архитектура и enterprise-инфраструктура, смотрите Symfony. Если приложение — тонкий HTTP API с несколькими middleware, Yii может быть тяжелее, чем Slim и Mezzio. А для выбора по команде, найму, legacy constraints и сроку поддержки полезна отдельная статья Выбор PHP-фреймворка.

См. также

Источники

  1. Yii 2 Guide: Running Applications
  2. Yii 2 Guide: Creating Forms
  3. Yii 2 Guide: Generating Code with Gii
  4. Yii 2 Guide: Active Record
  5. Yii 2 Guide: Authorization
  6. Yii 2 Guide: Widgets
  7. Yii Framework official homepage
  8. Yii 3 documentation: What is Yii