使用 Symfony 4 每个项目存储库有多个应用程序
More than one application per project repository with Symfony 4
我有三个旧应用程序(运行 在 Symfony 2 上),每个应用程序都是在单独的 git 存储库中开发并在各自的虚拟主机中配置的:
company.com
公司网站。
admin.company.com
网站管理。
api.company.com
API公司服务。
尽管如此,他们共享同一个数据库。所以我们决定(公司)将所有这些统一到一个应用程序中,采用 Symfony 4 结构和方法,主要是为了删除大量重复数据并改善其维护。
现在,我正在按照计划将所有内容集成到一个 application/repository 中,但我开始处理一些性能和结构问题:
- 因为我只有一个入口点
index.php
我做了两个路由前缀以便能够访问 company.com/admin/
和 company.com/api/
子应用程序,所以每次都会加载所有路由:(
- 为每个请求不必要地加载和处理所有包和配置。例如:当我访问 API 路径时,也会加载
SonataAdminBundle
:(
- 缓存清除命令需要很长时间才能完成。
- 测试正在崩溃,现在也需要很长时间才能完成。
我想保留早期的虚拟主机并只加载每个域所需的捆绑包和配置:
company.com
仅为公司网站加载包、路由和配置(SwiftmailerBundle
,...)
admin.company.com
仅为网站管理加载包、路由和配置(SecurityBundle
、SonataAdminBundle
、...)
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 定义变量)我想为每个 "sub-app" 等清除缓存。
我一直在研究这个主题,但到目前为止我找不到任何对我的场景有帮助的东西 (Symfony 4)。
是否可以在一个项目存储库下独立地拥有多个应用程序 运行(如单个应用程序)但共享一些配置?实现它的最佳方法是什么?
提前致谢。
您可以创建新环境,例如:admin
、website
、api
。然后通过 apache/nginx 提供环境变量 SYMFONY_ENV
,您将能够 运行 专用应用程序并仍然使用子域 company.com
、admin.company.com
、api.company.com
.此外,您将能够轻松地仅加载所需的路由。
根据您要基于此方法创建的应用程序的数量,您可以添加条件以在 AppKernel
class 中按项目加载指定的包,或者为每个创建单独的 classes项目。
你也应该看看这篇文章https://jolicode.com/blog/multiple-applications-with-symfony2
multiple kernels 方法可能是解决此类项目的一个不错的选择,但现在考虑使用环境变量、结构和内核实现的 Symfony 4 方法,它可以得到改进。
基于名称的虚拟内核
术语"Virtual Kernel"指的是在单个项目存储库上运行安装多个应用程序(例如api.example.com
和admin.example.com
)的做法。虚拟内核是 "name-based",这意味着您在每个应用程序上有多个内核名称 运行ning。他们 运行 在同一个物理项目存储库上的事实对最终用户来说并不明显。
简而言之,每个内核名称对应一个应用程序。
基于应用程序的配置
首先,您需要为 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
class 的完整示例:
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
class 需要一个额外的参数 (name
) 来定义要加载的应用程序。为了让自动加载器找到您的新 \VirtualKernel
class,确保将其添加到 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 相同的 filosofy,而环境变量决定应该使用哪个开发环境和调试模式 运行 您的应用程序,您可以添加一个新的 APP_NAME
环境变量来设置要执行的应用程序:
public/index.php
// ...
$kernel = new \VirtualKernel(getenv('APP_ENV'), getenv('APP_DEBUG'), getenv('APP_NAME'));
// ...
现在,您可以使用 PHP 的内置 Web 服务器,在新的应用程序环境变量前加上前缀:
$ 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
以便能够 运行 来自不同应用程序的命令:
bin/console
// ...
$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
您还可以在.env
文件中配置默认的APP_NAME
环境变量。
运行 每个应用程序测试
├── 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
class 以便一起执行所有测试:
test/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
:
<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/applications:
- 向
config
、src
和 tests
目录添加一个新文件夹,其中包含应用程序的 <name>
及其内容。
- 至少将
bundles.php
文件添加到 config/<name>/
目录。
- 为
src/<Name>/
和 tests/<Name>
目录添加新的 PSR-4 命名空间 composer.json
autoload/autoload-dev 部分并更新自动加载配置文件。
检查新应用运行ning 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
不同于多个内核文件的方法,这个版本减少了很多代码重复和文件;由于环境变量和虚拟内核 class.
,所有应用程序只需一个内核 index.php
和 console
基于 Symfony 4 框架的示例:https://github.com/yceruto/symfony-skeleton-vkernel
灵感来自 https://symfony.com/doc/current/configuration/multiple_kernels.html
另外,当你想要 运行 Behat 测试时,你应该 运行 使用这个命令:
对于 windows:
set APP_NAME=web&& vendor\bin\behat
对于linux:
export APP_NAME='web' && vendor\bin\behat
其中 "web" 是您想要 运行 的内核名称。
KernelInterface::getName() 方法和 kernel.name 参数已被弃用。他们别无选择,因为这是一个在 Symfony 应用程序中不再有意义的概念。
如果您需要应用程序内核的独特 ID,可以使用 KernelInterface::getContainerClass() 方法和 kernel.container_class 参数。
同样,getRootDir() 方法和 kernel.root_dir 参数也已弃用。另一种方法是使用 Symfony 3.3
中引入的 getProjectdir() 和 kernel.project_dir 方法
我有三个旧应用程序(运行 在 Symfony 2 上),每个应用程序都是在单独的 git 存储库中开发并在各自的虚拟主机中配置的:
company.com
公司网站。admin.company.com
网站管理。api.company.com
API公司服务。
尽管如此,他们共享同一个数据库。所以我们决定(公司)将所有这些统一到一个应用程序中,采用 Symfony 4 结构和方法,主要是为了删除大量重复数据并改善其维护。
现在,我正在按照计划将所有内容集成到一个 application/repository 中,但我开始处理一些性能和结构问题:
- 因为我只有一个入口点
index.php
我做了两个路由前缀以便能够访问company.com/admin/
和company.com/api/
子应用程序,所以每次都会加载所有路由:( - 为每个请求不必要地加载和处理所有包和配置。例如:当我访问 API 路径时,也会加载
SonataAdminBundle
:( - 缓存清除命令需要很长时间才能完成。
- 测试正在崩溃,现在也需要很长时间才能完成。
我想保留早期的虚拟主机并只加载每个域所需的捆绑包和配置:
company.com
仅为公司网站加载包、路由和配置(SwiftmailerBundle
,...)admin.company.com
仅为网站管理加载包、路由和配置(SecurityBundle
、SonataAdminBundle
、...)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 定义变量)我想为每个 "sub-app" 等清除缓存。
我一直在研究这个主题,但到目前为止我找不到任何对我的场景有帮助的东西 (Symfony 4)。
是否可以在一个项目存储库下独立地拥有多个应用程序 运行(如单个应用程序)但共享一些配置?实现它的最佳方法是什么?
提前致谢。
您可以创建新环境,例如:admin
、website
、api
。然后通过 apache/nginx 提供环境变量 SYMFONY_ENV
,您将能够 运行 专用应用程序并仍然使用子域 company.com
、admin.company.com
、api.company.com
.此外,您将能够轻松地仅加载所需的路由。
根据您要基于此方法创建的应用程序的数量,您可以添加条件以在 AppKernel
class 中按项目加载指定的包,或者为每个创建单独的 classes项目。
你也应该看看这篇文章https://jolicode.com/blog/multiple-applications-with-symfony2
multiple kernels 方法可能是解决此类项目的一个不错的选择,但现在考虑使用环境变量、结构和内核实现的 Symfony 4 方法,它可以得到改进。
基于名称的虚拟内核
术语"Virtual Kernel"指的是在单个项目存储库上运行安装多个应用程序(例如api.example.com
和admin.example.com
)的做法。虚拟内核是 "name-based",这意味着您在每个应用程序上有多个内核名称 运行ning。他们 运行 在同一个物理项目存储库上的事实对最终用户来说并不明显。
简而言之,每个内核名称对应一个应用程序。
基于应用程序的配置
首先,您需要为 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
class 的完整示例:
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
class 需要一个额外的参数 (name
) 来定义要加载的应用程序。为了让自动加载器找到您的新 \VirtualKernel
class,确保将其添加到 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 相同的 filosofy,而环境变量决定应该使用哪个开发环境和调试模式 运行 您的应用程序,您可以添加一个新的 APP_NAME
环境变量来设置要执行的应用程序:
public/index.php
// ...
$kernel = new \VirtualKernel(getenv('APP_ENV'), getenv('APP_DEBUG'), getenv('APP_NAME'));
// ...
现在,您可以使用 PHP 的内置 Web 服务器,在新的应用程序环境变量前加上前缀:
$ 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
以便能够 运行 来自不同应用程序的命令:
bin/console
// ...
$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
您还可以在.env
文件中配置默认的APP_NAME
环境变量。
运行 每个应用程序测试
├── 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
class 以便一起执行所有测试:
test/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
:
<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/applications:
- 向
config
、src
和tests
目录添加一个新文件夹,其中包含应用程序的<name>
及其内容。 - 至少将
bundles.php
文件添加到config/<name>/
目录。 - 为
src/<Name>/
和tests/<Name>
目录添加新的 PSR-4 命名空间composer.json
autoload/autoload-dev 部分并更新自动加载配置文件。
检查新应用运行ning 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
不同于多个内核文件的方法,这个版本减少了很多代码重复和文件;由于环境变量和虚拟内核 class.
,所有应用程序只需一个内核index.php
和 console
基于 Symfony 4 框架的示例:https://github.com/yceruto/symfony-skeleton-vkernel 灵感来自 https://symfony.com/doc/current/configuration/multiple_kernels.html
另外,当你想要 运行 Behat 测试时,你应该 运行 使用这个命令:
对于 windows:
set APP_NAME=web&& vendor\bin\behat
对于linux:
export APP_NAME='web' && vendor\bin\behat
其中 "web" 是您想要 运行 的内核名称。
KernelInterface::getName() 方法和 kernel.name 参数已被弃用。他们别无选择,因为这是一个在 Symfony 应用程序中不再有意义的概念。
如果您需要应用程序内核的独特 ID,可以使用 KernelInterface::getContainerClass() 方法和 kernel.container_class 参数。
同样,getRootDir() 方法和 kernel.root_dir 参数也已弃用。另一种方法是使用 Symfony 3.3
中引入的 getProjectdir() 和 kernel.project_dir 方法