Более одного приложения на репозиторий проекта с Symfony 4

У меня есть три старых приложения (работающих на Symfony 2), каждое из которых было разработано в отдельных репозиториях git и сконфигурировано в их соответствующих vhosts:

  1. company.com Вебсайт компании.
  2. admin.company.com Администрация сайта.
  3. api.company.com API сервис компании.

Хотя они и используют одну и ту же базу данных. Поэтому мы решили (Компания) объединить их всех в одном приложении со структурой Symfony 4 & Подход, в основном, для удаления большого количества дублированных данных и улучшения их обслуживания.

Прямо сейчас я интегрирую все в одном приложении / репозитории, как было запланировано, но я начинаю иметь дело с некоторой производительностью & вопросы структуры:

  • Поскольку у меня только одна точка входа index.php Я сделал два префикса маршрутов, чтобы иметь доступ к company.com/admin/ а также company.com/api/ вспомогательное приложение, поэтому все маршруты загружаются каждый раз 🙁
  • Все пакеты и конфигурации загружаются и обрабатываются без необходимости для каждого запроса. Например: когда я получаю доступ к пути API, SonataAdminBundle тоже грузится sad
  • Выполнение команды очистки кеша занимает много времени.
  • Испытания заканчиваются, и теперь для их завершения требуется много времени.

Я хотел бы сохранить ранний vhost и загрузить только необходимые пакеты и конфигурацию для доменов:

  1. company.com Загрузка пакетов, маршрутов и конфигурации только для веб-сайта компании (SwiftmailerBundle…)
  2. admin.company.com Загружает пакеты, маршруты и конфигурацию только для администрирования сайта (SecurityBundle, SonataAdminBundle…)
  3. api.company.com Загружает только пакеты, маршруты и конфигурацию, чтобы обеспечить быстрый сервис API компании (SecurityBundle, FOSRestBundle, NelmioApiDocBundle…)

Это то, что я делаю до сих пор:

// public/index.php

// ...

$request = Request::createFromGlobals();
$kernel = new Kernel(getenv('APP_ENV'), getenv('APP_DEBUG'));

// new method implemented in my src/kernel.php
$kernel->setHost($request->server->get('HTTP_HOST'));

$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response);

Я проверил текущий префикс хоста в Kernel::registerBundles() метод, и я загрузил только необходимые пакеты, но все еще у меня проблемы с bin/console файл (он не работает как HTTP_HOST переменная не определена для CLI) Я хотел бы очистить кэш для каждого «подпрограммы» и так далее.

Я проводил некоторые исследования по этой теме, но до сих пор не смог найти ничего полезного для своего сценария (Symfony 4).

Возможно ли иметь много приложений в одном репозитории проектов, работающих независимо (например, отдельные приложения), но совместно использовать некоторую конфигурацию? Каков наилучший подход для достижения этого?

Заранее спасибо.

1

Решение

Скорее всего несколько ядер подход мог бы стать хорошим вариантом для решения такого рода проектов, но теперь, если рассмотреть подход Symfony 4 с переменными среды, структурой и реализацией ядра, его можно улучшить.

Основанное на имени виртуальное ядро

Термин «виртуальное ядро» относится к практике запуска более одного приложения (например, api.example.com а также admin.example.com) в одном репозитории проекта. Виртуальные ядра «основаны на именах», что означает, что в каждом приложении работает несколько имен ядер. Тот факт, что они работают в одном и том же физическом репозитории проекта, не очевиден для конечного пользователя.

Короче говоря, каждое имя ядра соответствует одному приложению.

Конфигурация на основе приложения

Во-первых, вам нужно будет повторить структуру одного приложения для config, src, var каталоги и оставить корневую структуру для общих пакетов и конфигурации. Это должно выглядеть так:

├── config/
│   ├── admin/
│   │   ├── packages/
│   │   ├── bundles.php
│   │   ├── routes.yaml
│   │   ├── security.yaml
│   │   └── services.yaml
│   ├── api/
│   ├── site/
│   ├── packages/
│   ├── bundles.php
├── src/
│   ├── Admin/
│   ├── Api/
│   ├── Site/
│   └── VirtualKernel.php
├── var/
│   ├── cache/
│   │   ├── admin/
│   │   │   └── dev/
│   │   │   └── prod/
│   │   ├── api/
│   │   └── site/
│   └── log/

Далее, используя Kernel::$name свойство, которое вы можете выделить приложение для запуска с выделенными файлами проекта (var/cache/<name>/<env>/*):

  • <name><Env>DebugProjectContainer*
  • <name><Env>DebugProjectContainerUrlGenerator*
  • <name><Env>DebugProjectContainerUrlMatcher*

Это будет ключом к производительности, поскольку каждое приложение по определению имеет свой собственный контейнер DI, маршруты и файлы конфигурации. Вот полный образец VirtualKernel класс, который поддерживает предыдущую структуру:

SRC / VirtualKernel.php

// WITHOUT NAMESPACE!

use Symfony\Component\HttpKernel\Kernel;

class VirtualKernel extends Kernel
{
use MicroKernelTrait;

private const CONFIG_EXTS = '.{php,xml,yaml,yml}';

public function __construct($environment, $debug, $name)
{
$this->name = $name;

parent::__construct($environment, $debug);
}

public function getCacheDir(): string
{
return $this->getProjectDir().'/var/cache/'.$this->name.'/'.$this->environment;
}

public function getLogDir(): string
{
return $this->getProjectDir().'/var/log/'.$this->name;
}

public function serialize()
{
return serialize(array($this->environment, $this->debug, $this->name));
}

public function unserialize($data)
{
[$environment, $debug, $name] = unserialize($data, array('allowed_classes' => false));

$this->__construct($environment, $debug, $name);
}

public function registerBundles(): iterable
{
$commonBundles = require $this->getProjectDir().'/config/bundles.php';
$kernelBundles = require $this->getProjectDir().'/config/'.$this->name.'/bundles.php';

foreach (array_merge($commonBundles, $kernelBundles) as $class => $envs) {
if (isset($envs['all']) || isset($envs[$this->environment])) {
yield new $class();
}
}
}

protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader): void
{
$container->setParameter('container.dumper.inline_class_loader', true);

$this->doConfigureContainer($container, $loader);
$this->doConfigureContainer($container, $loader, $this->name);
}

protected function configureRoutes(RouteCollectionBuilder $routes): void
{
$this->doConfigureRoutes($routes);
$this->doConfigureRoutes($routes, $this->name);
}

private function doConfigureContainer(ContainerBuilder $container, LoaderInterface $loader, string $name = null): void
{
$confDir = $this->getProjectDir().'/config/'.$name;
if (is_dir($confDir.'/packages/')) {
$loader->load($confDir.'/packages/*'.self::CONFIG_EXTS, 'glob');
}
if (is_dir($confDir.'/packages/'.$this->environment)) {
$loader->load($confDir.'/packages/'.$this->environment.'/**/*'.self::CONFIG_EXTS, 'glob');
}
$loader->load($confDir.'/services'.self::CONFIG_EXTS, 'glob');
if (is_dir($confDir.'/'.$this->environment)) {
$loader->load($confDir.'/'.$this->environment.'/**/*'.self::CONFIG_EXTS, 'glob');
}
}

private function doConfigureRoutes(RouteCollectionBuilder $routes, string $name = null): void
{
$confDir = $this->getProjectDir().'/config/'.$name;
if (is_dir($confDir.'/routes/')) {
$routes->import($confDir.'/routes/*'.self::CONFIG_EXTS, '/', 'glob');
}
if (is_dir($confDir.'/routes/'.$this->environment)) {
$routes->import($confDir.'/routes/'.$this->environment.'/**/*'.self::CONFIG_EXTS, '/', 'glob');
}
$routes->import($confDir.'/routes'.self::CONFIG_EXTS, '/', 'glob');
}
}

Теперь ваш \VirtualKernel класс требует дополнительного аргумента (name), который определяет приложение для загрузки. Чтобы автозагрузчик нашел ваш новый \VirtualKernel класс, не забудьте добавить его в composer.json раздел автозагрузки:

"autoload": {
"classmap": [
"src/VirtualKernel.php"],
"psr-4": {
"Admin\\": "src/Admin/",
"Api\\": "src/Api/",
"Site\\": "src/Site/"}
},

Затем беги composer dump-autoload сбросить новый конфиг автозагрузки.

Сохранение единой точки входа для всех приложений

├── public/
│   └── index.php

Следуя той же философии Symfony 4, в то время как переменные среды решают, какую среду разработки и режим отладки следует использовать для запуска вашего приложения, вы можете добавить новую APP_NAME Переменная окружения для установки приложения на выполнение:

общественности / index.php

// ...

$kernel = new \VirtualKernel(getenv('APP_ENV'), getenv('APP_DEBUG'), getenv('APP_NAME'));
// ...

Сейчас вы можете поиграть с ним, используя встроенный веб-сервер PHP, добавив префикс новой переменной среды приложения:

$ APP_NAME=site php -S 127.0.0.1:8000 -t public
$ APP_NAME=admin php -S 127.0.0.1:8001 -t public
$ APP_NAME=api php -S 127.0.0.1:8002 -t public

Выполнение команд для приложения

├── bin/
│   └── console.php

Добавить новый параметр консоли --kernel чтобы иметь возможность запускать команды из разных приложений:

бен / консоль

// ...
$name = $input->getParameterOption(['--kernel', '-k'], getenv('APP_NAME') ?: 'site');

//...
$kernel = new \VirtualKernel($env, $debug, $name);
$application = new Application($kernel);
$application
->getDefinition()
->addOption(new InputOption('--kernel', '-k', InputOption::VALUE_REQUIRED, 'The kernel name', $kernel->getName()))
;
$application->run($input);

Позже используйте эту опцию для запуска любой команды, отличной от команды по умолчанию (site).

$ bin/console about -k=api

Или, если хотите, используйте переменные окружения:

$ export APP_NAME=api
$ bin/console about                         # api application
$ bin/console debug:router                  # api application
$
$ APP_NAME=admin bin/console debug:router   # admin application

Также вы можете настроить по умолчанию APP_NAME переменная среды в .env файл.

Запуск тестов для одного приложения

├── tests/
│   ├── Admin/
│   │   └── AdminWebTestCase.php
│   ├── Api/
│   ├── Site/

tests каталог очень похож на src каталог, просто обновите composer.json сопоставить каждый каталог tests/<Name>/ с его пространством имен PSR-4:

"autoload-dev": {
"psr-4": {
"Admin\\Tests\\": "tests/Admin/",
"Api\\Tests\\": "tests/Api/",
"Site\\Tests\\": "tests/Site/"}
},

Опять беги composer dump-autoload заново сгенерировать конфигурацию автозагрузки.

Здесь вам может понадобиться создать <Name>WebTestCase класс для каждого приложения, чтобы выполнить все тесты вместе:

Тест / Admin / AdminWebTestCase

namespace Admin\Tests;

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

abstract class AdminWebTestCase extends WebTestCase
{
protected static function createKernel(array $options = array())
{
return new \VirtualKernel(
isset($options['environment']) ? $options['environment'] : 'test',
isset($options['debug']) ? $options['debug'] : true,
'admin'
);
}
}

Позже распространяется от AdminWebTestCase тестировать admin.company.com приложение (сделать то же самое для других).

Производство и вхосты

Установите переменную среды APP_NAME для каждой конфигурации vhost на вашем производственном сервере и машине разработки:

<VirtualHost company.com:80>
SetEnv APP_NAME site

# ...
</VirtualHost>

<VirtualHost admin.company.com:80>
SetEnv APP_NAME admin

# ...
</VirtualHost>

<VirtualHost api.company.com:80>
SetEnv APP_NAME api

# ...
</VirtualHost>

Добавление дополнительных приложений в проект

С помощью трех простых шагов вы сможете добавить новые vKernel / приложения в текущий проект:

  1. добавить в config, src а также tests каталоги новой папки с <name> приложения и его содержание.
  2. добавить в config/<name>/ по крайней мере, bundles.php файл.
  3. добавить в composer.json autoload / autoload-dev разделяет новые пространства имен PSR-4 для src/<Name>/ а также tests/<Name> каталоги и обновить файл конфигурации автозагрузки.

Проверьте, работает ли новое приложение bin/console about -k=<name>,

Конечная структура каталогов:

├── bin/
│   └── console.php
├── config/
│   ├── admin/
│   │   ├── packages/
│   │   ├── bundles.php
│   │   ├── routes.yaml
│   │   ├── security.yaml
│   │   └── services.yaml
│   ├── api/
│   ├── site/
│   ├── packages/
│   ├── bundles.php
├── public/
│   └── index.php
├── src/
│   ├── Admin/
│   ├── Api/
│   ├── Site/
│   └── VirtualKernel.php
├── tests/
│   ├── Admin/
│   │   └── AdminWebTestCase.php
│   ├── Api/
│   ├── Site/
├── var/
│   ├── cache/
│   │   ├── admin/
│   │   │   └── dev/
│   │   │   └── prod/
│   │   ├── api/
│   │   └── site/
│   └── log/
├── .env
├── composer.json

В отличие от несколько файлов ядра подход, эта версия уменьшает много дублирования кода и файлов; только одно ядро, index.php а также console для всех приложений, благодаря переменным среды и классу виртуального ядра.

Пример на основе скелета Symfony 4: https://github.com/yceruto/symfony-skeleton-vkernel
Вдохновленный в https://symfony.com/doc/current/configuration/multiple_kernels.html

9

Другие решения

Вы можете создавать новые среды, такие как: admin, website, api, Затем предоставьте переменную среды SYMFONY_ENV с помощью apache / nginx вы сможете запускать специализированное приложение и по-прежнему использовать субдомены company.com, admin.company.com, api.company.com, Также вы сможете легко загрузить только необходимую маршрутизацию.

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

Вы также должны прочитать эту статью https://jolicode.com/blog/multiple-applications-with-symfony2

1

Также, если вы хотите запустить тестирование Behat, вы должны запустить его с помощью этой команды:

для окон:

set APP_NAME=web&& vendor\bin\behat

для Linux:

export APP_NAME='web' && vendor\bin\behat

где «web» — это имя вашего ядра, которое вы хотите запустить.

0
По вопросам рекламы [email protected]