Список всех статей:

  1. Вступление

  2. Вспомогательные инструменты разработки

  3. Обзор app-* шаблонов и demo

Текущая статья будет посвящена обзору app, app-api и app-console шаблонов, а также demo приложений.

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

Начну с главного – различия app-* шаблонов и demo.

App

App-* – шаблоны приложения, готовые для использования в собственных проектах. Это просто болванка, скелетон или template готового приложения.

App – шаблон для классического PHP web + console приложения с server side rendering. То есть весь HTML страницы будет генерироваться на сервере.

App-api – шаблон для PHP web + console приложения с client side rendering. Обычно такие шаблоны используют для создания бэкендов для Single Page Application или различного рода “апишек”.

App-console – шаблон для написания console приложения. Из шаблона удалены все web-related things, однако вы можете в дальнейшем добавить туда их самостоятельно или воспользоваться одним из шаблонов выше.

На момент написания статьи в Yii3 существуют только 3 шаблона. В планах добавить еще несколько шаблонов для быстрого старта с асинхронными фреймворками, такими как AMPHP, ReactPHP, Swoole, RoadRunner, а также Temporal.

Demo

Demo – монорепозиторий с уже готовыми приложениями. В demo приложениях мы будем показывать вариации использования тех или иных библиотек. Приложения в demo предназначены просто для демонстрации работы фреймворка. Чтобы начать писать на Yii3, лучше создайте проект используя app-* шаблон.

На момент написания статьи demo содержит два приложения: blog и blog-api. В планах добавить еще интеграцию с RoadRunner и Temporal.

Если у вас есть желание создать новое приложение или улучшить существующее, то создайте Issue или PR, или просто напишите в чат, и мы обязательно рассмотрим любые доработки.

Начнем разбор фреймворка с app шаблона.

Установка

composer create-project yiisoft/app app --stability=dev

Флаг --stability=dev нужен потому, что шаблоны приложения не имеют релизов, так как зависят от других пакетов, которые не имеют релизов.

Внимательный читатель может заметить, что в первой статье я обещал обозревать только стабильные пакеты. Простите меня за мою ложь. Я больше так не буду. Но не обещаю :)

После выполнения команды по установке в текущей директории появится директория с именем app. Там и лежит наш “пациент”.

cd app – переходим в директорию с проектом.

Первым делом запускаем тесты:

./vendor/bin/codecept run

Тесты проходят. Всё отлично.

Структура

Общая структура app-* шаблонов и demo приложений покажется весьма знакомой современному разработчику.

Из непонятного – директории resources и runtime.

Runtime – директория, отвечающая за хранение промежуточных данных: кэша приложения, записей дебагера, ошибок от Codeception, кеша схемы Cycle и прочих пользовательских файлов. Подобные директории, как правило, стоит скрывать от индексирования в IDE. В PHPStorm, например, нужно в контекстном меню выбрать Mark Directory as → Excluded .

Resources – директория для дополнительных ресурсов, таких как web assets, translator messages, layout + views, mail templates, rbac settings и любые другие ресурсы, которые вы захотите хранить у себя в приложении вне src и config.

Давайте заглянем глубже в директорию config.

С первого взгляда может показаться, что ничего не понятно – но давайте разбираться.

Директории web и console, а также файлы с суффиксами *-web и *-console подключаются, когда вы запускаете приложение из соответствующего окружения. yiisoft/config позволяет вам вручную манипулировать всеми этими файлами, но в yiisoft/yii-runner-* репозиториях все настройки по умолчанию прописаны за вас.

Директория common и файлы без префиксов будут подключаться во всех окружениях. Сначала они, потом *-webили *-console файлы, перезаписывая и дополняя то, что находится в общих конфигах.

Директории web и console отвечают за конфигурацию DI непосредственно для web и console приложений.

Например, конфигурация web содержит несколько файлов, которые объединяться в единый, и ими будет конфигурироваться контейнер зависимостей.

Также в директории config можно заметить 3 дочерние директории dev, prod и test. Они будут подключаться, когда переменная окружения YII_ENV будет равняться dev, prod или test соответственно.

Структура подключаемых конфигов

У вас может появиться потребность изменить конфиг так, чтобы с ним было удобнее работать – переименовать директорию config, поменять расположение файлов внутри неё, хранить конфигурации модулей внутри самих модулей или помимо dev, prod или test окружений создать дополнительные окружения.

За работу с конфигами отвечает пакет yiisoft/config. А все необходимые параметры подключения этих файлов лежат в корневом composer.json. Они выглядят примерно так:

"extra": {
    "config-plugin-options": {
        "source-directory": "config"
    },
    "config-plugin-environments": {
        "dev": {
            "params": [
                "test/params.php"
            ]
        },
        "prod": {
            "params": [
                "test/params.php"
            ]
        },
        "test": {
            "params": [
                "test/params.php"
            ]
        }
    },
    "config-plugin": {
        "common": "common/*.php",
        "params": [
            "params.php",
            "?params-local.php"
        ],
        "web": [
            "$common",
            "web/*.php"
        ],
        "console": [
            "$common",
            "console/*.php"
        ],
        ...
    }
},

Большую часть из примера я вырезал из-за одинаковости настройки.

Полную документацию работы пакета yiisoft/config вы можете найти в README репозитория. Более подробный разбор пакета будет в одной и следующих статей.

Точки входа в приложение

Точкой доступа для запуска приложения в web окружении является файл public/index.php.

В нем есть запуск так называемого “раннера”, который конфигурирует, инициализирует и запускает основное приложение.

<?php
/// ...

// Run HTTP application runner
$runner = (new HttpApplicationRunner(dirname(__DIR__), $_ENV['YII_DEBUG'], $_ENV['YII_ENV']))
    ->withTemporaryErrorHandler(new ErrorHandler(
        new Logger([new FileTarget(dirname(__DIR__) . '/runtime/logs/app.log')]),
        new JsonRenderer(),
    ))
;
$runner->run();

Консольная версия раннера лежит в файле yii в корневом каталоге.

Запуск консольного приложения выглядит следующим образом:

<?php
/// ...

// Run console application runner
$runner = new ConsoleApplicationRunner(__DIR__, $_ENV['YII_DEBUG'], $_ENV['YII_ENV']);
$runner->run();

Интерфейсы использования раннеров довольно простые. Достаточно указать всего несколько аргументов и вызвать $runner->run() для его запуска. При желании раннеры можно сконфигурировать иначе. Разные раннеры предоставляют различные методы конфигурирования.

Полный разбор раннеров будет также в одной из следующих статей. Для справки, сейчас существуют следующие раннеры:

Также существует базовый раннер с общей функциональностью – yiisoft/yii-runner

Немного пояснений про withTemporaryErrorHandler()

При запуске HTTP раннера можно указать temporary error handler. Это обработчик, который будет обрабатывать все ошибки произошедшие до запуска основного приложения. Например, ошибка может произойти в момент конфигурирования DI контейнера. Так как контейнер еще не инициализирован, из него невозможно получить ErrorHandler. Для этих целей будет использоваться “временный”. Наличие такого временного обработчика ошибок позволит получить ответ в запрашиваемом формате – html, json или другом, который вы можете также сконфигурировать вручную.

Однако, использование “невременного” вам может тоже понадобиться. В нем можно подписаться на события приложения, заинжектить в него какой-то класс или что-то на ваш вкус. После запуска контейнера, временный обработчик отключается, и подключается обработчик из контейнера зависимостей.

Основные директории src и tests

Я уверен, что код в src вам покажется простым и понятным.

В src или tests нет каких-то Yii-specific мест. Максимум – это сконфигурированные классы по умолчанию. Всё взаимодействие классов происходит как во многих других фреймворках, с помощью ООП и Dependency Injection. Любые конфигурации классов вы можете переписать с помощью params.php . Об этом будет рассказано в следующих статьях.

По умолчанию в шаблонах app-* и demo приложениях мы инициализируем Codeception с базовыми настройками, потому как считаем, что он будет чаще использоваться при разработке реальных проектов. Этот тестовый фреймворк никак не привязан к Yii3, и вы спокойно можете удалить его и оставить классический PHPUnit, который тоже идет вместе с шаблонами.

Пример контроллера

Рассмотрим пример контроллера на основе файла src/Controller/SiteController.php:

<?php

declare(strict_types=1);

namespace App\Controller;

use Psr\Http\Message\ResponseInterface;
use Yiisoft\Yii\View\ViewRenderer;

final class SiteController
{
    public function __construct(private ViewRenderer $viewRenderer)
    {
        $this->viewRenderer = $viewRenderer->withControllerName('site');
    }

    public function index(): ResponseInterface
    {
        return $this->viewRenderer->render('index');
    }
}

\Yiisoft\Yii\View\ViewRenderer – класс, который рендерит представление.

Метод ViewRenderer::withControllerName() меняет базовую директорию в контексте класса с @views на @views/site.

Метод ViewRenderer::render() вызывает непосредственно рендеринг представления @views/site/index.php. Помимо PHP шаблонов Yii3 поддерживает еще и Twig.

Полный список всех поддерживаемых шаблонизаторов вы можете найти здесь.

В Telegram чате недавно подняли вопрос поддержки Smarty. Если вы любитель Smarty и хотите поддержку этого или любого другого шаблонизатора в Yii3, то приносите ваши предложения в Issue или в наш Telegram чат.

withControllerName() – простой хелпер. Этот метод создан для того, чтобы не дублировать базовую директорию при каждом вызове метода render().

Вы можете не использовать этот метод и воспользоваться функцией внедрения зависимостей в action. В этом случае контроллер будет выглядеть следующим образом:

<?php

declare(strict_types=1);

namespace App\Controller;

use Psr\Http\Message\ResponseInterface;
use Yiisoft\Yii\View\ViewRenderer;

final class SiteController
{
    public function index(ViewRenderer $viewRenderer): ResponseInterface
    {
        return $viewRenderer->render('site/index');
    }
}

Пример функционального теста

Рассмотрим пример функционального теста на основе файла tests/Functional/SiteControllerTest.php:

<?php

declare(strict_types=1);

namespace App\Tests\Functional;

use PHPUnit\Framework\TestCase;
use Yiisoft\Yii\Testing\FunctionalTester;

final class SiteControllerTest extends TestCase
{
    private ?FunctionalTester $tester;

    protected function setUp(): void
    {
        $this->tester = new FunctionalTester();
    }

    public function testGetIndex()
    {
        $method = 'GET';
        $url = '/';

        $this->tester->bootstrapApplication('web', dirname(__DIR__, 2));
        $response = $this->tester->doRequest($method, $url);

        $this->assertStringContainsString(
            'Don\'t forget to check the guide',
            $response->getContent()
        );
    }
}

В данной реализации используется пакет yiisoft/yii-testing.

Данный пакет предоставляет тестовый раннер приложения с возможностью подставлять моки сервисов непосредственно в контейнер зависимостей.

В примере выше приложение инициализируется строчкой $this->tester->bootstrapApplication('web', dirname(__DIR__, 2)); . web указывает на окружение определений контейнера. Помните *-web и *-console файлы и директории? Это оно.

Далее в тесте происходит вызов маршрута GET / строчкой $response = $this->tester->doRequest($method, $url). В переменной $response будет объект класса \Yiisoft\Yii\Testing\ResponseAccessor . Этот объект создан для упрощения работы с объектом ответа сервера. В дальнейшем этот класс будет расширяться дополнительными удобными методами для работа с объектом ответа, но и сейчас вы можете выполнить все необходимые проверки.

Для того, чтобы подставить сервис-заглушку в ваше приложение, вы можете вызвать метод mockService$this->tester->mockService($className, $definition); . Вот реальный пример подстановки сервиса-заглушки. 

О том, какими могут быть $className и $definition вы узнаете в следующей статье.

Инструменты для разработки

В шаблонах мы предустанавливаем вспомогательные для разработки инструменты.

Такие инструменты, как Psalm, Codeception, Infection, PHPUnit и PHPUnit-watcher добавлены в каждый из шаблонов.

Наличие сразу и Codeception, и PHPUnit обусловлено тем, что Codeception не ограничивает в написании юнит тестов напрямую при помощи PHPUnit.


Несмотря на то, что мы стараемся придерживаться OCP в разработке, мы открыты к изменениям. Если вы считаете, что шаблон приложения будет удобнее с вашими идеями, то создайте Issue, и мы постараемся воплотить их в коде. Это относится как и к шаблонам, так и к вспомогательным инструментам, и к фреймворку в целом.

Полезные ссылки

Комментарии (2)


  1. alexeydg
    30.11.2022 08:17
    +5

    вы когда-нибудь его допишите? ощущение когда это произойдет не только Yii будет не актуален, а сам пых уже будет похоронен


  1. Khaperets
    30.11.2022 15:24
    +1

    Выглядит как попытка Yii догнать Symfony, только Yii очень сильно отстает