Symfony 4,从自定义 class(不是控制器 class)获取项目的根路径

Symfony 4, get the root path of the project from a custom class (not a controller class)

在 src/Utils 目录中,我为各种东西创建了自定义 class Foo。我正在寻找一种方法来获取 symfony 4 项目的绝对根路径

从控制器,它很容易:

$webPath = $this->get('kernel')->getProjectDir();

但是从我在 src/Utils 目录中创建的自定义 class,我如何才能获得根路径目录?

我可以将路径从控制器传递到 Foo class :

$webPath = $this->get('kernel')->getProjectDir();
$faa = new Foo($webPath);
$faa->doSomething();

但我认为将此信息存储在 Foo class 中并且在控制器中只有 "controller logic" class

更合适

这是工作:

// from Foo class
use Symfony\Component\HttpKernel\KernelInterface;
...
class Foo{
    private $rootDir;
    public function __construct(KernelInterface $kernel)
    {
        $this->rootDir = $kernel->getProjectDir();
    }
    public function myfoomethod(){
        return $this->getRootDir();
    }
    public function getRootDir(){
        return $this->rootDir;
    }
}


// from the controller class 
use App\Utils\Foo;
...
class FaaController extends AbstractController
{
    /**
     * @Route("/scenario", name="scenario")
     */
    public function new(Foo $foo)
    {
        dump($foo->myfoomethod()); //show the dir path !
        return $this->render('faa/index.html.twig');
    }
}

在 Symfony 中 AppKernel class 正在处理方法 getProjectDir() 下的项目根目录。要在控制器中获取它,您可以执行以下操作:

$projectRoot = $this->get('kernel')->getProjectDir();

它将return你一个项目根目录。

如果您需要 classes 之一中的项目根目录,您有两个选择,我将向您展示。首先是传递 AppKernel 作为依赖项:

class Foo 
{
    /** KernelInterface $appKernel */
    private $appKernel;

    public function __construct(KernelInterface $appKernel)
    {
        $this->appKernel = $appKernel;
    }
}

多亏了 Symfony 4 自动装配依赖项,它会自动注入到你的 class 中,你可以通过以下方式访问它:

$this->appKernel->getProjectDir();

但是请注意:我不认为这是一个好主意,除非你有真正的需要并且更多地使用 AppKernel class获取项目根目录。特别是如果您稍后考虑为 class 创建单元测试。例如,您需要创建 AppKernel 的 mock 会自动增加复杂性。

第二个选项和恕我直言更好的方法是只传递一个带有目录路径的字符串。您可以通过在 config/services.yaml 中定义服务来实现此目的,如下所示:

services:
    (...)
    MyNamespace\Foo:
        arguments:
            - %kernel.project_dir%

你的构造函数看起来像:

class Foo 
{
    /** string $rootPath */
    private $rootPath;

    public function __construct(string $rootPath)
    {
        $this->rootPath = $rootPath;
    }
}

没有Kernel注入

config/services.yaml

services:
    _defaults:
        autowire: true
        autoconfigure: true
        bind:
            $projectDir: '%kernel.project_dir%'

.....

class Foo
{
    private $projectDir;

    public function __construct(string $projectDir)
    {
        $this->projectDir = $projectDir;
    }
}

如果您的 class 正在扩展: Symfony\Bundle\FrameworkBundle\Controller\AbstractController,那么你可以得到根目录为

$projectRoot = $this->getParameter('kernel.project_dir');

ContainerBagInterface 注入您的控制器

protected $projectRoot;

public function __construct(ContainerBagInterface $containerBag)
{
    $this->projectRoot = $containerBag->get('kernel.project_dir');;
}

更好的推荐方法

root_dir 注入您的 Foo class。在 services

下将以下内容添加到您的配置中
services:
    foo:
        class:     App\Path\To\Foo
        arguments: ['%kernel.project_dir%']

容器应在解析时将参数传递给您的 class,Foo class 应如下所示

<?php

namespace App\Path\To;

class Foo {

  private $projectDir;

  public function __construct($projectDir) 
  {
     $this->projectDir = $projectDir;
  }
}

在不注入无用依赖项的情况下执行此操作的最简单方法是创建这样的实用方法(在我的例子中,它在 src\Utils\Utils.php 中):

namespace App\Utils;

public static function getRootDir(): string
{
    return __DIR__ . '/../..';
}

然后只需使用

在任何地方调用它
Utils::getRootDir()

它适用于 Windows 和 Linux,但如果您愿意,可以使用 DIRECTORY_SEPARATOR 常数代替 '/'


此外,它比 Kernel::getProjectDir() 方法更有效,后者实例化一个 ReflectionObject,然后进行递归,直到找到带有 composer.json 的文件夹,并进行多次检查,涉及I/O 如 is_file()。使用 __DIR__ costant 更好,因为它的值已经在内存中可用,无需访问物理磁盘。