Autowiring

Mautic 5 添加了对 Symfony 的 :xref:`symfony-autowiring:xref:`symfony-autoconfigure 服务支持。

早期版本需要将服务定义在 *Bundle/Config/config.php 文件中。这在 Mautic 5 中仍然有效,但在 Mautic 6 中将被移除。

请按照以下步骤从硬编码的服务迁移到自动注入的服务。

优点

  • 新服务不再需要在 app/bundles/*Bundle/Config/config.php 文件中有任何定义。插件也一样。Symfony 会根据构造函数的参数类型推断所需的服务。

  • 如果服务未在其他服务中用作依赖项,则它们将被删除,例如 订阅者命令**和 **表单类型

  • 您可以将现有的服务定义简化为仅设置字符串别名,以保持向后兼容性并使控制器正常工作。

  • app/config/services.php 会自动配置所有 bundle,包括插件,因此如果 bundle 没有进行任何特殊的修改,它应该可以开箱即用。

  • 传统的服务定义位于 *Bundle/Config/config.php 文件中,但在 Mautic 6 中将被移除。

这同样适用于插件以及核心 bundle。

介绍 services.php

*Bundle/Config/services.php 是存储 PHP 服务特殊情况的正确位置。通过这样做,Mautic 更接近 Symfony 的默认应用程序。在 Mautic 5 的生命周期结束时,理想状态是所有服务都已自动注入或定义在 services.php 文件中。

如果您的插件没有此文件,它将使用 :xref:`default_services_config。 它排除了 Mautic bundle 应排除的经典目录,并为您自动将实体存储库作为服务注册。

但是,每个 bundle 都应该有自己的 services.php 文件,因为每个 bundle 负责定义其自身的服务。一个基本的 services.php 文件如下所示:

<?php

declare(strict_types=1);

use Mautic\CoreBundle\DependencyInjection\MauticCoreExtension;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

return function (ContainerConfigurator $configurator) {
    $services = $configurator->services()
        ->defaults()
        ->autowire()
        ->autoconfigure()
        ->public();

    $excludes = [
        'SomeDirectoryYouWantToExclude',
    ];

    $services->load('MauticPlugin\\[YourPluginName]Bundle\\', '../')
        ->exclude('../{'.implode(',', array_merge(MauticCoreExtension::DEFAULT_EXCLUDES, $excludes)).'}');
};

Note

[YourPluginName] 替换为您的插件名称。

这允许您声明 bundle 服务是自动注入和自动配置的。默认情况下,所有服务都应该是私有的,但 Controller 和其他旧服务直接从容器加载依赖项,而不是通过构造函数使用依赖注入。关于这一点,请参见下面的内容。

您可以在 $excludes 数组中定义希望排除的目录或文件。稍后,它们将与默认排除的所有目录合并,并存储在 MauticCoreExtension::DEFAULT_EXCLUDES 常量中。

如果您希望将实体仓库作为服务加载,可以使用以下行:

$services->load('MauticPlugin\\[YourPluginName]Bundle\\Entity\\', '../Entity/*Repository.php');

大多数 Mautic bundle 将实体(DTO 对象)和存储库(服务)放在同一个目录中,因此采用这种方法。默认情况下排除 Entity 目录,现在您必须仅选择以 Repository.php 结尾的文件,并将它们作为服务加载。

要使用 services.php 文件,您必须为您的 bundle 创建一个 Extension 文件。以下是一个示例:

// *Bundle/DependencyInjection/[YourPluginName]Extension.php
<?php

declare(strict_types=1);

namespace MauticPlugin\[YourPluginName]Bundle\DependencyInjection;

use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\Extension;
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;

class Mautic[YourPluginName]Extension extends Extension
{
    /**
     * @param mixed[] $configs
     */
    public function load(array $configs, ContainerBuilder $container): void
    {
        $loader = new PhpFileLoader($container, new FileLocator(__DIR__.'/../Config'));
        $loader->load('services.php');
    }
}

命名对于 Mautic 能够使用此文件非常重要。它必须位于 DependencyInjection 目录中,并且您必须将其命名为 bundle 的根目录中的主 bundle 类相同的名称。因此,如果您的文件名例如是 MauticConfigBundle.php,那么您必须将此新文件的名称设置为 DependencyInjection/MauticConfigExtension.php

Bundle.php 替换为 Extension.php,即可获得此新文件的名称。

为什么从自动注入中排除目录?

如果您不从自动注入中排除目录,Symfony 会尝试自动注入所有 PHP 类,这在某些情况下会失败。例如,DTO 或值对象类不应该被自动注入。它们是在应用程序执行期间创建的,并且不是旨在成为服务的。此类包括实体或事件。以下是完整的目录列表:xref:excluded_directories

如何迁移?

只要您喜欢删除不必要代码,您就会享受这个过程。

移除 Commands、Subscribers 和 Forms

一个有趣的事实是,**命令**自从 Mautic 3 开始就支持自动注入。因此,您可以从 config.php 中删除它们,清除缓存,它们将像以前一样工作。

在 Mautic 5 中,您可以删除 订阅者表单 的服务定义,因为它们不被用作其他服务的依赖项。

关于核心模块的服务兼容性

对于核心模块,重要的是要考虑向后兼容性并维护服务别名。如果您是插件开发者,可以直接使用完全限定类名 (FQCN)。如果这样做,请将新版本的插件发布为主要版本,以警告您的插件的用户。您永远不知道用户如何在生产环境中使用的插件。

在核心模块中,保留服务别名。例如,对于以下服务定义:

```php // config.php ‘mautic.campaign.model.campaign’ => [

‘class’ => MauticCampaignBundleModelCampaignModel::class, ‘arguments’ => [

‘mautic.lead.model.list’, ‘mautic.form.model.form’, ‘mautic.campaign.event_collector’, ‘mautic.campaign.membership.builder’, ‘mautic.tracker.contact’,

],

],

您可以删除所有前面的代码,并像这样创建别名:

`php // services.php $services->alias('mautic.campaign.model.campaign', \Mautic\CampaignBundle\Model\CampaignModel::class); `

您也可以跳过创建别名的步骤,删除 config.php 中的所有服务定义,并将所有字符串服务定义替换为直接从容器加载的完全限定类名:

`diff - $container->get('mautic.campaign.model.campaign'); + $container->get(\Mautic\CampaignBundle\Model\CampaignModel::class); `

使用接口而不是实现

Symfony 会在您的服务中使用例如 Http\Adapter\Guzzle7\Client 作为依赖项,而不是其接口 Psr\Http\Client\ClientInterface 时发出警告。不用担心,错误消息会明确告知您这一点。

特殊情况

某些服务不仅需要其他服务作为依赖项,有时还需要参数。例如,以下服务:

```php // config.php ‘mautic.config.form.escape_transformer’ => [

‘class’ => MauticConfigBundleFormTypeEscapeTransformer::class, ‘arguments’ => [

‘%mautic.config_allowed_parameters%’,

],

],

如果您删除此定义并让自动注入来处理它,您将收到以下错误消息:

无法自动注入服务 “MauticConfigBundleFormTypeEscapeTransformer”: __construct() 方法的参数 “$allowedParameters” 提示类型为 “array”,您应该显式配置其值。

它不知道要传递哪个数组参数。因此,您必须定义它:

```php

// services.php

$services->get(MauticConfigBundleFormTypeEscapeTransformer::class)->arg(‘$allowedParameters’, ‘%mautic.config_allowed_parameters%’);

最大的优势是,Mautic正在变得更接近一个标准的Symfony应用程序,它具有自动依赖注入功能,因此所有这些特殊情况都已在 :xref:`manually_wiring_arguments`中进行了详细记录。

控制器中的依赖注入

控制器可以使用经典的依赖注入或:xref:action_based_di。 如果您只需要在单个操作中使用依赖项,请使用后一种选项。 对于新的控制器,请考虑:xref:invokable_controllers