Symfony - 使用静态默认值从 `service.yaml` 配置类

问题描述 投票:0回答:1

我正在尝试创建一个可以从代码中的任何位置调用的类。
它接受可以从构造函数(或设置器)配置的不同参数。

该类将在多个项目之间共享,因此我需要能够轻松配置一次并多次使用相同的配置(或不同/特定的配置)。

这是我的课程:

namespace Allsoftware\SymfonyBundle\Utils;

class GdImageConverter
{
    public function __construct(
        ?int $width = null,
        ?int $height = null,
        int|array|null $dpi = null,
        int $quality = 100,
        string $resizeMode = 'contain',
    ) {
        $this->width  = $width ? \max(1, $width) : null;
        $this->height = $height ? \max(1, $height) : null;

        $this->dpi = $dpi ? \is_int($dpi) ? [\max(1, $dpi), \max(1, $dpi)] : $dpi : null;

        $this->quality = \max(-1, \min(100, $quality));

        $this->resizeMode = $resizeMode;
    }
}

大多数时候,一个应用程序的构造函数参数是相同的。
所以我想到使用一个与其自身相对应但已经配置好的

private static
变量。

所以我添加了

$default
变量:

namespace Allsoftware\SymfonyBundle\Utils;

class GdImageConverter
{
    private static GdImageConverter $default;

    public function __construct(
        ?int $width = null,
        ?int $height = null,
        int|array|null $dpi = null,
        int $quality = 100,
        string $resizeMode = 'contain',
    ) {
        // ...
    }

    public static function setDefault(self $default): void
    {
        self::$default = $default;
    }

    public static function getDefault(): self
    {
        return self::$default ?? self::$default = new self();
    }
}

看起来像单例,但实际上并非如此。
要设置一次并使用

GdImageConverter::getDefault()
来获取它,我在
service.yaml
文件中写入了这些行:

services:
    default.gd_image_converter:
        class: Allsoftware\SymfonyBundle\Utils\GdImageConverter
        arguments:
            $width: 2000
            $height: 2000
            $dpi: 72
            $quality: 80
            $resizeMode: contain

    Allsoftware\SymfonyBundle\Utils\GdImageConverter:
        calls:
            -   setDefault: [ '@default.gd_image_converter' ]

拨打

GdImageConverter::getDefault()
时ATE,不对应
default.gd_image_converter
服务。

$default = GdImageConverter::getDefault();
$imageConverter = new GdImageConverter(2000, 2000, 72, 80);
dump($default);
dump($imageConverter);
die();

dump of variables

并且在调试

self::$default
内部
getDefault()
时,它是空的。

我做错了什么?

注意:当我将

calls
方法
setDefault
更改为不存在的方法
setDefaults
时,symfony 告诉我该方法未定义。

无效服务“Allsoftware\SymfonyBundle\Utils\GdImageConverter”:方法“setDefaults()”不存在。

谢谢!

symfony service configuration symfony5 php-8
1个回答
2
投票

决定发布一个新的且希望更加连贯的答案。

基本问题是

GdImageConverter::getDefault();
返回一个所有参数都为空的实例。 这是因为 Symfony 容器仅在请求(也称为注入)时创建服务。 setDefault 从未被调用,因此使用 new self() 。

有一个名为 MimeTypes 的 Symfony 类,它采用类似的模式,但它不会尝试自定义服务,所以这并不重要。

第二个问题是GdImageConverter服务的配置方式。 即使它确实正确设置了默认时刻,它基本上也会注入一个“空”版本。

要解决第二个问题,您需要使用当前服务调用 setDefault 并删除 default.gd_image_converter ,除非您需要它用于其他用途:

services:
   App\Service\GdImageConverter:
        class: App\Service\GdImageConverter
        public: true
        arguments:
            $width: 2000
            $height: 2000
            $dpi: 72
            $quality: 80
            $resizeMode: contain
        calls:
            -   setDefault: [ '@App\Service\GdImageConverter' ]

顺便说明一下,静态方法 setDefault 将被动态调用。 这有点不寻常,但它在 PHP 中是合法的,并且 Symfony 对其他类也这样做。

接下来我们需要确保服务始终被实例化。 这是一个罕见的要求,我认为没有默认的方法可以做到这一点。 但使用 Kernel::boot 可以工作:

# src/Kernel.php
class Kernel extends BaseKernel
{
    use MicroKernelTrait;

    public function boot()
    {
        parent::boot();

        $this->container->get(GdImageConverter::class);
    }
}

这可确保为命令和 Web 应用程序设置默认服务。

GdImageConverter::getDefault();
现在可以随时调用,并将返回初始化的服务。 请注意,必须将服务声明为公共才能让 Container::get 正常工作。

您可以停在这里,但总是创建一个服务,即使您通常可能不需要它,这有点烦人。 通过将容器本身注入到您的类中可以避免这样做。

这绝对违反了 Symfony 推荐的做法,如果读者觉得他们需要否决答案,甚至建议它,那么就做你需要做的事情。 然而,Laravel 框架在常规基础上使用这种方法(称为外观),并且这些应用程序以某种方式设法工作。

use Psr\Container\ContainerInterface;

class GdImageConverter
{
    private static GdImageConverter $default;

    private static ContainerInterface $container; // Add this

    public static function setContainer(ContainerInterface $container)
    {
        self::$container = $container;
    }
    public static function getDefault(): self
    {
      //return self::$default ?? self::$default = new self();
      return self::$default ?? self::$default = self::$container->get(GdImageConverter::class);
    }
}
# Kernel.php
   public function boot()
    {
        parent::boot();

        GdImageConverter::setContainer($this->container);
    }

现在我们又回到了惰性实例化。

虽然我不会提供详细信息,但您可以消除注入容器以及通过注入 GdImageConverterServiceLocater 公开服务的需要。

© www.soinside.com 2019 - 2024. All rights reserved.