- PVSM.RU - https://www.pvsm.ru -
Как предлагается создавать проект на Yii2 сейчас? Выбираете шаблон проекта: basic или advanced, форкаете себе, потом пишете и комитите туда. Бам! Случилась копипаста, ваш проект и шаблон теперь развиваются отдельно. Вам не получить исправлений, внесенных, в шаблон, а в yii2-app-basic
, естественно, не возьмут доработок специфических для вашей задачи. Это проблема номер один.
Как расширяется проект на Yii2? Выбираете подходящие расширения и подключаете их с помощью композера. Находите пример конфига этого расширения в README и копипастите в конфиг своего приложения. Оопс… Опять копипаста. Вылазящяя разными боками, в том числе таким: в большом проекте используется много расширений — конфиг приложения становится огромным и просто нечитаемым. Это проблема номер два.
Как эти проблемы связаны? Первая решается так: выделяем переиспользуемый код и превращаем в расширение. И снова здравствуйте: у расширения есть свой конфиг — получили вторую проблему.
Наиболее остро эти проблемы стоят для повторно используемых решений, когда надо поднимать много/несколько, в принципе одинаковых проектов, но с большими/маленькими изменениями. Плюс избавление от копипасты и переиспользование кода ещё никому не мешало.
Хочу поделиться своим вариантом решения этих проблем.
Не буду томить душу, изложу суть сразу, чтоб, если-что, было удобненько расплеваться сразу, не дочитывая до конца.
Итак, решение такое: использовать систему плагинов — с самого начала создавать свой проект как плагин (расширение вместе с конфигом), делить проект на плагины и собирать конфиг приложения автоматически из конфигов плагинов.
Тут я должен приостановиться и объяснить что я называю плагином. В yii2 предусмотрены расширения (yii2 extension) и они дают возможность организовывать переиспользуемый код и подключать его к проекту композером. Но мало-мальски сложное расширение нуждается в конфигурации. И тут фреймворк не помогает. У создателя расширения есть два варианта:
Первый вариант я уже покритиковал в самом начале, возьмусь за второй:
Application
уже создан и что-то уже просто не получится сконфигурировать;controllerMap
,… (я пробовал — так счастья не видать);В общем, пройдя несколько итераций, намучавшись с разными вариантами родилось радикальное решение — собирать конфиг за пределами приложения и ещё до его запуска (хм, звучит просто и очевидно, но, как известно, хорошая мысля приходит опосля). Собирать оказалось удобнее всего плагином к композеру, из него есть удобный доступ
ко всей иерархии зависимостей проекта. Так получился composer-config-plugin [2].
Composer-config-plugin работает довольно просто:
extra
секции их composer.json
;В composer.json
расширения (которое превращается в плагин) добавляются такие строчки:
"extra": {
"config-plugin": {
"web": "src/config/web.php"
}
}
Это значит замержить в конфиг под названием web
содержимое файла src/config/web.php
. А в файле этом будет просто то, что плагин хочет добавить в конфиг приложения, например, конфиг интернационализации:
<?php
return [
'components' => [
'i18n' => [
'translations' => [
'my-category' => [
'class' => yiii18nPhpMessageSource::class,
'basePath' => '@myvendor/myplugin/messages',
],
],
],
],
];
Конфигов может быть сколько угодно, включая специальные: dotenv
, defines
и params
. Конфиги обрабатываются в таком порядке:
dotenv
;defines
;params
;common
, console
, web
, ...Таким образом, чтобы значения полученные на предыдущих шагах могли быть использованы на всех ппоследующих.
То есть: переменные окружения могут использоваться для назначения констант. Константы и переменные окружения могут использоваться для назначения параметров. И весь набор: параметры, константы и переменные окружения могут использоваться в конфигах.
В общем-то всё! composer-config-plugin
просто мержит все массивы конфигов аналогом функции yiibasehelpersArrayHelper::merge
. Естественно, конфиги мержатся в правильном порядке — с учётом кто кого реквайрит — таким образом, чтобы конфиг каждого пакета мержился после своих зависимостей и мог перезаписать значения заданные ими. Т.е. самый верхний пакет имеет полный контроль над конфигом и управляет всеми значениями, а плагины только задают дефолтные значения. В целом, процесс повторяет сборку конфигов в yii2-app-advanced
, только более масштабно.
Изпользовать в приложении тривиально — добавляем в web/index.php
:
$config = require hiqdevcomposerconfigBuilder::path('web');
(new yiiwebApplication($config))->run();
Найти больше информации и примеров, а также задать вопросы можно на гитхабе: hiqdev/composer-config-plugin [2].
Очень простой пример плагина hiqdev/yii2-yandex-plugin [3]. Но он наглядно демонстрирует возможности этого подхода. Чтобы получить счётчик Яндекс.Метрики достаточно зареквайрить плагин и задать параметр yandexMetrika.id
. Всё! Не надо ничего копипастить в свой конфиг, не надо добавлять виджет в layout — не надо касаться рабочего кода. Плагин — это цельный кусок функционала, который позволяет расширять систему не внося изменений в существующий код.
— Что? Можно написать новую фичу, не поломав старые?!
— Да.
— Крутяк! Теперь можно не писать тесты?
— Нет… Так не бывает…
Итого, composer-config-plugin
даёт систему плагинов и решает вопрос повторного использования так сказать "малых архитектурных форм". Пора вернуться к главному — организации больших переиспользуемых проектов. Повторю и уточню предлагаемое решение: создавать проект как систему плагинов, организованную в правильную иерархию.
Самый простой вариант организации проекта такой — наш проект реквайрит композером фреймворк и сторонние расширения ("сторонними" я называю не являющиеся частью нашего проекта), т.е. получается такая простая иерархия пакетов (репозиториев):
Пропускаю все промежуточные варианты организации, проверенные и отброшенные по итогам практической эксплуатации, и перехожу сразу к оптимальной иерархии, которой мы придерживаемся сейчас:
Иерархия отображает кто кого реквайрит, т.е. корень реквайрит основной проект, тот в свою очередь — базовый проект, а базовый проект — фреймворк.
— Воу-воу! Полегче! Что за "корень" и "базовый проект"?
Извиняюсь, всё придумал сам, терминологии подходящей не нашёл, пришлось велосипедить, буду признателен за лучшие варианты.
"Корнем" я называю самый внешний пакет, содержащий код, конфиг и другие файлы специфические для данного конкретного варианта реализации вашего проекта — то, чем этот вариант отличается от основного проекта. В идеале содержит буквально несколько файлов, об этом ниже.
"Базовый проект" это то, во что превращается yii2-app-basic
в этой схеме. Т.е. переиспользуемая основа приложения реализующая некоторый базовый функционал и оформленная в виде плагина. Эта запчасть не обязательна, но очень полезна. Вам не надо её делать самому, она может разрабатываться сообществом как сейчас разрабатывается yii2-app-basic
. Мы разрабатываем HiSite, об этом ниже.
Таким образом пакеты образуют иерархию композиции — более внешний пакет использует внутренний, в основном переиспользуя его поведение, но переопределяя свою специфику: "корень" использует и уточняет основной проект, основной проект — базовый, базовый проект — фреймворк.
Необходимо уточнить, что речь идёт только об организации кода, т.е. про разделение кода по пакетам/плагинам. Архитектурное деление проекта на слои, естественно, независимо от деления на пакеты, но они могут дополнять друг друга. Например, доменная логика может быть вынесена в отдельный пакет для переиспользования между разными проектами.
— Аааа! Нужен пример!
Например, Вы делаете на потоке сайты визитки. Базовый функционал везде одинаковый, но есть фичи за дополнительну плату, например каталог и, естественно, сайты отличаются внешним видом (темой) и кучей параметров. Это можно организовать в такую иерархию пакетов:
business-card-no42.com
— "корень";
myvendor/yii2-theme-cool
— плагин, специфичный для данного сайта;myvendor/business-card-catalog
— плагин проекта, подключенный на данном сайте;myvendor/business-card
— основной проект;
myvendor/business-card-contacts
— плагин проекта, используемый на всех сайтах;othervendor/yii2-cool-stuff
— сторонний плагин;hiqdev/hisite
— базовый проект;
yiisoft/yii2-swiftmail
— плагин, необходимый для работы базового проекта;yiisoft/yii2
— фреймворк.Надеюсь, не открыл Америки, и все более менее так и делят свои проекты на части. Только, наверно, без "корня". Попытаюсь донести его полезность.
В "корне" достаточно всего пару файлов, которые подлежат копированию из шаблона и настройке под данную инсталяцию проекта. Можно и желательно обойтись всего тремя файлами:
.env
— переменные окружения, например,ENV=prod
;composer.json
— тут подключается основной проект и специфичные для него плагины;src/config/params.php
— явки, пароли, параметры проекта и используемых плагинов.Пароли можно положить в .env
и потом использовать их в params.php
так:
return [
'db.password' => $_ENV['DB_PASSWORD'],
];
Учитывая "легкоусвояемость" .env
лучшими претендентами на вынос в .env
являются параметры используемые другими (не PHP) технологиями.
Конечно, можно и нужно класть в "корень" некоторый конфиг и даже код, специфичный сугубо для данной инсталяции, не подлежащий копипастингу. Как только вижу копипасту, страшно её не люблю — уношу в какой-нибудь плагин.
Остальные файлы и каталоги необходимые для функционирования приложения (web/assets/
, web/index.php
) стандартны, их нужно создавать и назначать права "сборщиком" (build tool, task runner) мы велосипедим свой, но это уже совсем другая история.
По сути, "корень" — это params-local.php
на стероидах. В нём концентрируется отличие конкретной инсталяции проекта от общего переиспользуемого кода. Мы создаём репозиторий под корень и храним его на нашем приватном git-сервере, поэтому комитим туда даже секреты (но это холиварная тема). Все остальные пакеты — в публичном доступе на GitHub. Мы комитим composer.lock
в корне, поэтому перенос проекта на другой сервер делается просто composer create-project
(я знаю — Docker получше будет, но об этом в следующий раз).
— А можно ещё конкретнее? Покажите мне код наконец!
Одно из "базовых приложений", которые мы развиваем — HiSite hiqdev/hisite [4] — это основа для типичного сайта, как yii2-app-basic,
только сделанная как плагин, что даёт все преимущества переиспользования кода над копипастингом:
Шаблон "корня" для проекта на HiSite здесь — hiqdev/hisite-template [5].
Иерархия зависимостей выглядит так:
В README [5] корня описано как поднять проект у себя — composer create-project
плюс настройка конфигурации. Благодаря реализации тем как плагинов и библиотеке тем [hiqdev/yii2-thememanager] в composer.json
корня можно поменять yii2-theme-flat
на yii2-theme-original
запустить composer update
и сайт переоденется в новую тему. Вот так просто.
Ещё один реальный рабочий проект, подходящий в качестве примера, сделанный, используя этот подход и полностью доступный на GitHub — Asset Packagist [9] — packagist-совместимый репозиторий, который позволяет устанавливать Bower и NPM пакеты как нативные composer пакеты.
Иерархия зависимостей выглядит так:
Подробности как поднять проект у себя описаны в README [10] корня.
Тема обширная, множество подробностей пришлось опустить. Надеюсь получилось донести общую идею. Ещё раз, используя введенную терминологию:
Мы используем описанный подход около года, впечатления самые положительные — волосы стали мягкие и шелковистые: разделяем и властвуем, клепаем плагины легко и непринуждённо, 100+ [13] и останавливаться не собираемся, нужен новый функционал — делаем новый плагин.
Подход, в той или иной мере, применим для других фреймворков и даже языков… Ой, Остапа понесло… На сегодня хватит! Спасибо за внимание. Продолжение следует.
На написание таких объёмов текста сподвигла серия [14] статей [15] Фабьена Потенсьера [16] (автора Symfony) про грядущий Symfony 4. Стыдно сказать, не до конца понял как именно всё работает, но уловил идеи и цели: система бандлов будет доработана в сторону их автоматической конфигурации, что в итоге даёт:
new way to create and evolve your applications with ease
новый способ создавать и развивать ваши приложения с лёгкостью
© Fabien Potencier
В общем, не один я считаю поднятые вопросы очень важными для фреймворка.
Я люблю Yii. Давайте сделаем в Yii лучше!
Автор: hiqsol
Источник [17]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/plugins/255973
Ссылки в тексте:
[1] bootstrap: http://www.yiiframework.com/doc-2.0/guide-structure-extensions.html#bootstrapping-classes
[2] composer-config-plugin: https://github.com/hiqdev/composer-config-plugin
[3] hiqdev/yii2-yandex-plugin: https://github.com/hiqdev/yii2-yandex-plugin
[4] hiqdev/hisite: https://github.com/hiqdev/hisite
[5] hiqdev/hisite-template: https://github.com/hiqdev/hisite-template
[6] hiqdev/yii2-theme-flat: https://github.com/hiqdev/yii2-theme-flat
[7] hiqdev/yii2-thememanager: https://github.com/hiqdev/yii2-thememanager
[8] yiisoft/yii2: https://github.com/yiisoft/yii2
[9] Asset Packagist: https://asset-packagist.org
[10] hiqdev/asset-packagist.dev: https://github.com/hiqdev/asset-packagist.dev
[11] hiqdev/yii2-theme-original: https://github.com/hiqdev/yii2-theme-original
[12] hiqdev/asset-packagist: https://github.com/hiqdev/asset-packagist
[13] 100+: https://hiqdev.com/packages
[14] серия: http://fabien.potencier.org/symfony4-compose-applications.html
[15] статей: http://fabien.potencier.org/symfony4-monolith-vs-micro.html
[16] Фабьена Потенсьера: http://fabien.potencier.org/
[17] Источник: https://habrahabr.ru/post/329286/?utm_source=habrahabr&utm_medium=rss&utm_campaign=sandbox
Нажмите здесь для печати.