使用工厂创建服务
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']