Пользователи обычно:
- Читают имеющиеся конфиги.
- Меняют существующий конфиг сервиса.
- Добавляют конфиг для нового сервиса (обычно копипастой из ридми).
Одна из целей Yii 3 — возможность конфигурировать и использовать любой PHP-код без обёрток. Широко используется DI-контейнер и композиция, поэтому конфиги получаются немного более сложными.
В общем в настоящий момент процесс выглядит примерно так (красная стрелка):
Приложение запрашивает из контейнера экземпляр определённого интерфейса. Контейнер создаёт его или на основе конфига напрямую или при помощи фабрики. Конфиг фабрики обычно хранится в params.php, рядом с которым лежат соответственно common.php, web.php и console.php. Эти файлы мержатся и кешируются с помощью composer-config-plugin.
Концепт
Уже имеющийся подход довольно дружественный к пользователю, поэтому я предлагаю это оставить. Дополнения же следующие:
- Мы вводим объекты-конфиги (на диаграмме это зелёные стрелки и DB Config), описывая в них имена параметров и значения по умолчанию. Сама конфигурация при этом остаётся декларативной, как и ранее. В общем, конфижить придётся создавая объекты, но за нас это может и будет делать контейнер.
- Если мы используем params.php, то в качестве ключа конфига используем имя класса, описывающего конфиг (пример).
- Конфиг в виде объекта можно запросить из контейнера.
- Независимость от $params (или даже плагина composer-config-plugin) — можно конфижить вручную или через фабрики контейнера, создавая конфиг-объекты.
- Из предыдущего пункта следует, что интеграция модуля в сторонние фреймворки может быть проще.
- При клике по имени класса можно быстро перейти к его описанию и посмотреть, какие поля можно указать в конфиге, что они означают и какие значения принимают. Это сильно улучшает общую читаемость конфига.
- Валидацию или преобразование переданных значений можно производить прямо конфиг-классом. Этим также решиться проблема с описками в конфиге при его изменении. Сейчас описки проходят незамеченными, настройка откатывается к умолчанию.
Разбор реализации
Начнём с конфигурации в params.php:
Код: Выделить всё
# было
'cycle.migration' => [
'directory' => '@root/migrations',
'namespace' => 'App\\Migration',
'table' => 'migration',
'safe' => false,
],
# стало
CycleMigrationConfig::class => [
'directory' => '@root/migrations',
'namespace' => 'App\\Migration',
'table' => 'migration',
'safe' => false,
],
Поскольку мы изменили params.php, нужно пересобрать конфигурационные файлы (обновить в кеше), как того требует composer-config-plugin.
Код: Выделить всё
> composer du
Код: Выделить всё
'Yiisoft\\Yii\\Cycle\\CycleMigrationConfig' => [
'__class' => 'Yiisoft\\Yii\\Cycle\\CycleMigrationConfig',
'configure()' => [
[
'directory' => '@root/migrations',
'namespace' => 'App\\Migration',
'table' => 'migration',
'safe' => false,
],
],
],
Кроме того, на этапе сборки конфигов имеется потенциал для валидации конфигурируемых значений. В реализации концепта валидацию я не делал.
Для каждого поля (свойства) конфига могут быть определены методы-сеттеры и методы-геттеры. Сеттеры будут использованы во время заполнения конфига-объекта значениями (если сеттер для поля не определен, то значение будет записано в свойство напрямую). Геттеры автоматически вызываются при получении непубличных свойств, в том числе при получении конфига в виде массива методом toArray(). Если геттер для какого-либо поля не описан, его всё-равно можно вызвать для получения значения. Этим объясняется наличие магии и аннотаций @method для геттеров в базовом классе конфигов.
Пример сеттера в классе CycleDbalConfig:
Если указать в настройках соединения путь до файла с использованием альяса, то альяс в этом сеттере будет преобразован в реальный путь.
Код: Выделить всё
'connection' => 'sqlite:@runtime/database.db'
Код: Выделить всё
protected $directory = '@root/migrations';
/** @var Yiisoft\Aliases\Aliases */
private $objAliases;
protected function getDirectory(): string
{
return $this->convertAlias($this->directory);
}
protected function convertAlias(string $alias): string
{
return $this->objAliases->get($alias, true);
}
О валидации
Валидация очень хорошо вписывается на этапе сборки и кеширования конфиг-файлов. Представьте: вы меняете конфиги проекта, запускаете процесс их сборки и вам сразу выводятся сообщения о косяках и рекомендации по их устранению. Удобно? Удобно. Даже веб-приложение запускать не надо.
Правила валидации, на мой взгляд, было бы удобно указывать в аннотациях к свойствам. Причём набор правил можно будет сколь угодно расширять, чтобы валидировать более сложные значения, начиная от email и ip адресов/диапазонов, заканчивая кастомными встраиваемыми объектами с более сложной структурой.
Валидация конфигов не обязательно должна приводить к бросанию исключения в случае малейшей ошибки и предотвращению сборки невалидных конфигов. Предупредительный характер работы валидатора позволит с одной стороны обнаружить ошибки в конфиге, а с другой не падать в продакшене, когда лишь незначительная часть конфига написана с ошибкой.
- Понятен ли концепт?
- Нравятся ли вам такие конфиги?
- Как вы относитесь к аннотациям для описания правил валидации с позиции разработчика модуля и с позиции пользователя модуля?