如何获取应用程序中可用邮件传输的列表



我在获取为mailer组件定义的所有传输的列表时遇到问题。

在我的config/packages/mailer.yaml中,我定义了三种传输:

framework:
mailer:
transports:
default: '%env(MAILER_DEFAULT_DSN)%'
first: '%env(MAILER_FIRST_DSN)%'
second: '%env(MAILER_SECOND_DSN)%'

现在,在我的代码中,我想以关联数组的方式获得定义的传输列表:

[
'default' => 'default_mailer_dsn',
'first' => 'first_mailer_dsn',
'second' => 'second_mailer_dsn',
]

知道怎么做吗?

因此,这并不能完全回答问题,但会产生如下结果:

array:3 [▼
"default" => SymfonyComponentMailerTransportSmtpEsmtpTransport {#401 ▶}
"first" => SymfonyComponentMailerTransportSmtpEsmtpTransport {#407 ▶}
"second" => SymfonyComponentMailerTransportSmtpEsmtpTransport {#413 ▶}
]

您可以获得所需的数组索引以及实际的传输对象。

使用bin/console调试:container mailer.transports揭示transports对象的存在:

namespace SymfonyComponentMailerTransport;
final class Transports implements TransportInterface
{
private $transports;

$transports是我们所追求的,但它在没有反射的情况下是不可访问的,并且类是最终类,所以我们无法真正扩展它。一个强力解决方案是将完整的类克隆(即复制(到应用程序命名空间中,并说服邮寄者使用它:

namespace AppMailer;
final class Transports implements TransportInterface
{
public $transports; // Change from private to public.  
the rest stays the same
# config/services.yaml
AppMailerTransports: '@mailer.transports' # define alias
# HomeController.php
use AppMailerTransports;
class HomeController extends AbstractController
{
public function index(Transports $transports): Response
{
dump($transports->transports);

说服邮件系统使用我们的运输工具是困难的部分。通常,您只需添加一个编译器传递,然后将mailer.transports服务的类更改为应用程序传输。遗憾的是,这不起作用,因为实际的服务定义使用工厂来创建传输,而且似乎没有更改传输类的特定点。具体而言:

namespace SymfonyComponentMailer;
class Transport # This is actually a factory class, poor name
{
# and the factory method
public function fromStrings(array $dsns): Transports
{
$transports = [];
foreach ($dsns as $name => $dsn) {
$transports[$name] = $this->fromString($dsn);
}
return new Transports($transports);
}

正如您所看到的,Transports类名是硬编码的。我试着扩展这个类并说服它使用不同的Transports类,但Transports的返回类型使它变得困难。作为概念验证,我决定将整个类复制到App命名空间中,看看会发生什么。您通常不会这样做,因为类中有相当多的代码。代码可能会随着时间的推移而更新。我还将类重命名为TransportFactory,因为Transport这个名称似乎是一个但被过度使用的名称。然后更改服务定义以使用新类。

namespace AppMailer;
class TransportFactory
{
# src/Kernel.php
class Kernel extends BaseKernel implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$container->getDefinition('mailer.transport_factory')->setClass(TransportFactory::class);
}

在这一点上,您应该有一个工作示例,产生我在答案开头给出的数组。对于本应是一个简单的请求来说,这是一项相当艰巨的工作。也许其他人现在会过来说,嘿,就这么做吧。

显然,如果你真的需要这种能力,你应该仔细考虑。最好直接从应用程序的扩展中读取config/packages/mailer.yaml。

在services.yml中定义您的传输参数,如下所示:

parameters:
mailDefault: '%env(MAILER_DEFAULT_DSN)%'
mailFirst: '%env(MAILER_FIRST_DSN)%'
mailSecond: '%env(MAILER_SECOND_DSN)%'

然后在服务中,在构造函数中获取ParameterBagInterface,并在变量中实例化以获取params:

<?php
...
use SymfonyComponentDependencyInjectionParameterBag;
...
private ParameterBagInterface $parameter;
public function __contructor(ParameterBagInterface $parameter ) {
$this->parameter = $parameter;
}
private function getTransport() {
return [
'default' => $this->parameter->get('mailDefault'),
'first' => $this->parameter->get('mailFirst'),
'second' => $this->parameter->get('mailSecond'),
];
}
?>

我在控制台命令中也遇到了类似的问题-列出传输,我可以通过查看debug:config命令的编写方式来解决这个问题。我不是通过SymfonyComponentConsoleCommandCommand而是通过SymfonyBundleFrameworkBundleCommandConfigDebugCommand扩展我的命令,并复制私有方法compileContainer。然后我可以使用这种方法:

private function listTransports(){
$extension = $this->findExtension('framework');
$container = $this->compileContainer();
$extensionAlias = $extension->getAlias();
$extensionConfig = [];
foreach ($container->getCompilerPassConfig()->getPasses() as $pass) {
if ($pass instanceof ValidateEnvPlaceholdersPass) {
$extensionConfig = $pass->getExtensionConfig();
break;
}
}
if (!isset($extensionConfig[$extensionAlias])) {
throw new LogicException(sprintf('The extension with alias "%s" does not have configuration.', $extensionAlias));
}
$config = $container->resolveEnvPlaceholders($extensionConfig[$extensionAlias]);
return array_keys($config["mailer"]["transports"]);
}

正如您所猜测的,$config包含配置树。

最新更新