跳到内容

使用工厂来创建服务

编辑此页

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: 'fabien@symfony.com'

也可以使用 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' => 'fabien@symfony.com'], 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 许可协议授权。
目录
    版本