跳到内容

使用工厂创建服务

编辑此页

Symfony 的服务容器提供了多种功能来控制对象的创建,允许您指定传递给构造函数的参数,以及调用方法和设置参数。

然而,有时您需要应用工厂设计模式,将对象创建委托给某个称为“工厂”的特殊对象。在这些情况下,服务容器可以调用工厂上的方法来创建对象,而不是直接实例化类。

静态工厂

假设您有一个工厂,它通过调用静态 createNewsletterManager() 方法来配置并返回一个新的 NewsletterManager 对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// src/Email/NewsletterManagerStaticFactory.php
namespace App\Email;

// ...

class NewsletterManagerStaticFactory
{
    public static function createNewsletterManager(): NewsletterManager
    {
        $newsletterManager = new NewsletterManager();

        // ...

        return $newsletterManager;
    }
}

为了使 NewsletterManager 对象作为服务可用,请使用 factory 选项来定义必须调用哪个类的哪个方法来创建其对象

1
2
3
4
5
6
7
# config/services.yaml
services:
    # ...

    App\Email\NewsletterManager:
        # the first argument is the class and the second argument is the static method
        factory: ['App\Email\NewsletterManagerStaticFactory', 'createNewsletterManager']

注意

当使用工厂创建服务时,为 class 选择的值对结果服务没有影响。实际的类名仅取决于工厂返回的对象。但是,配置的类名可能会被编译器传递使用,因此应将其设置为有意义的值。

将类本身用作工厂

当静态工厂方法与创建的实例在同一个类上时,可以从工厂声明中省略类名。假设 NewsletterManager 类有一个 create() 方法,需要调用该方法来创建对象,并且需要一个发送者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// src/Email/NewsletterManager.php
namespace App\Email;

// ...

class NewsletterManager
{
    private string $sender;

    public static function create(string $sender): self
    {
        $newsletterManager = new self();
        $newsletterManager->sender = $sender;
        // ...

        return $newsletterManager;
    }
}

您可以从工厂声明中省略类

1
2
3
4
5
6
7
8
# config/services.yaml
services:
    # ...

    App\Email\NewsletterManager:
        factory: [null, 'create']
        arguments:
            $sender: '[email protected]'

也可以使用 constructor 选项,而不是传递 null 作为工厂类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// src/Email/NewsletterManager.php
namespace App\Email;

use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;

#[Autoconfigure(bind: ['$sender' => '[email protected]'], constructor: 'create')]
class NewsletterManager
{
    private string $sender;

    public static function create(string $sender): self
    {
        $newsletterManager = new self();
        $newsletterManager->sender = $sender;
        // ...

        return $newsletterManager;
    }
}

非静态工厂

如果您的工厂使用常规方法而不是静态方法来配置和创建服务,也请将工厂本身实例化为一个服务。服务容器的配置如下所示

1
2
3
4
5
6
7
8
9
10
11
# config/services.yaml
services:
    # ...

    # first, create a service for the factory
    App\Email\NewsletterManagerFactory: ~

    # second, use the factory service as the first argument of the 'factory'
    # option and the factory method as the second argument
    App\Email\NewsletterManager:
        factory: ['@App\Email\NewsletterManagerFactory', 'createNewsletterManager']

可调用工厂

假设您现在将您的工厂方法更改为 __invoke(),以便您的工厂服务可以用作回调

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// src/Email/InvokableNewsletterManagerFactory.php
namespace App\Email;

// ...
class InvokableNewsletterManagerFactory
{
    public function __invoke(): NewsletterManager
    {
        $newsletterManager = new NewsletterManager();

        // ...

        return $newsletterManager;
    }
}

可以通过省略方法名称,使用可调用工厂来创建和配置服务

1
2
3
4
5
6
7
# config/services.yaml
services:
    # ...

    App\Email\NewsletterManager:
        class:   App\Email\NewsletterManager
        factory: '@App\Email\InvokableNewsletterManagerFactory'

在服务工厂中使用表达式

除了使用 PHP 类作为工厂外,您还可以使用表达式。这允许您例如根据参数更改服务

1
2
3
4
5
6
7
8
9
10
11
12
# config/services.yaml
services:
    App\Email\NewsletterManagerInterface:
        # use the "tracable_newsletter" service when debug is enabled, "newsletter" otherwise.
        # "@=" indicates that this is an expression
        factory: '@=parameter("kernel.debug") ? service("tracable_newsletter") : service("newsletter")'

    # you can use the arg() function to retrieve an argument from the definition
    App\Email\NewsletterManagerInterface:
        factory: "@=arg(0).createNewsletterManager() ?: service("default_newsletter_manager")"
        arguments:
            - '@App\Email\NewsletterManagerFactory'

将参数传递给工厂方法

提示

如果为您的服务启用了自动装配,则工厂方法的参数将自动装配。

如果您需要将参数传递给工厂方法,您可以使用 arguments 选项。例如,假设前面的示例中的 createNewsletterManager() 方法将 templating 服务作为参数

1
2
3
4
5
6
7
# config/services.yaml
services:
    # ...

    App\Email\NewsletterManager:
        factory:   ['@App\Email\NewsletterManagerFactory', createNewsletterManager]
        arguments: ['@templating']
本作品,包括代码示例,根据 Creative Commons BY-SA 3.0 许可协议获得许可。
目录
    版本