it-bens/shopware-code-based-plugin-configuration

提供基于代码和类型的方式创建 Shopware 配置的软件包,除了 XML 文件。

v0.2.0 2024-07-27 23:40 UTC

This package is auto-updated.

Last update: 2024-09-28 00:06:03 UTC


README

Tests codecov

Shopware 提供了一种简单的方法来创建插件配置,而无需实现任何前端代码:[链接](https://developer.shopware.com/docs/guides/plugins/plugins/plugin-fundamentals/add-plugin-configuration.html)

尽管底层系统对于内部配置生成非常灵活,但 config.xml 文件是利用此系统的唯一方式。本软件包旨在提供另一种方法:基于代码的配置生成。

这是一个 Shopware 插件吗?

不是。虽然此软件包与 Shopware 相关联,但它不会对其自身的事件做出反应。它旨在由 Shopware 插件使用。

如何在 Shopware 插件中添加此软件包?

首先,必须通过 composer 安装此软件包

composer require it-bens/shopware-code-based-plugin-configuration

这可以在插件的 composer.json 文件中完成。但请记住在插件类中启用 composer。

public function executeComposerCommands(): bool
{
    return true;
}

此软件包提供了两个 Symfony 编译器通过。`ConfigurationCardConfigReaderPass` 将添加所有必要的配置生成服务到服务容器中。它可以通过插件的 `build` 方法添加。

use ITB\ShopwareCodeBasedPluginConfiguration\DependencyInjection\ConfigurationCardConfigReaderPass;

public function build(ContainerBuilder $container): void
{
    // ...
    parent::build($container);

    $container->addCompilerPass(new ConfigurationCardConfigReaderPass());
}

为什么默认配置值缺失?

虽然提到的编译器通过足够在运行时生成插件配置,但缺少了一个方便的功能:默认值。需要 `ConfigurationCardConfigSaverPass` 来“黑客”Shopware 插件配置持久性。

use ITB\ShopwareCodeBasedPluginConfiguration\DependencyInjection\ConfigurationCardConfigReaderPass;
use ITB\ShopwareCodeBasedPluginConfiguration\DependencyInjection\ConfigurationCardConfigSaverPass;

public function build(ContainerBuilder $container): void
{
    // ...
    parent::build($container);

    $container->addCompilerPass(new ConfigurationCardConfigReaderPass());
    $container->addCompilerPass(new ConfigurationCardConfigSaverPass());
}

有关为什么这是必要的以及为什么使用是“黑客式”的详细信息,请参阅[此处](#user-content-shopware-plugin-configuration-persistence)。

我如何使用此软件包?

将配置编码到卡片中

此软件包提供了定义配置卡片(如在 Shopware 插件管理中看到)的类。

一个 ConfigurationCard 有英文标题,德文标题和一个 ConfigurationInputField 对象列表。支持的 ConfigurationInputField 实现包括

  • BoolField
  • IntegerField
  • SingleSelectField
  • TextField

这些实现中的一些在所需信息方面有所不同。但由于它们主要需要相同的信息,所有都需要一个 GeneralFieldInputInformation 对象。以下是一个包含所有类型输入字段的配置卡片的示例

use ITB\ShopwareCodeBasedPluginConfiguration\ConfigurationCard;
use ITB\ShopwareCodeBasedPluginConfiguration\ConfigurationInputField\BoolField;
use ITB\ShopwareCodeBasedPluginConfiguration\ConfigurationInputField\IntegerField;
use ITB\ShopwareCodeBasedPluginConfiguration\ConfigurationInputField\SingleSelectField;
use ITB\ShopwareCodeBasedPluginConfiguration\ConfigurationInputField\SingleSelectFieldOption;
use ITB\ShopwareCodeBasedPluginConfiguration\ConfigurationInputField\TextField;
use ITB\ShopwareCodeBasedPluginConfiguration\GeneralFieldInputInformation;

$enabledFieldInformation = new GeneralFieldInputInformation(
    name: 'enabled', 
    labelInEnglish: 'Enabled', 
    labelInGerman: 'Aktiviert', 
    helpTextInEnglish: 'Enable or disable the feature', 
    helpTextInGerman: 'Aktiviere oder deaktiviere das Feature'
);
$enabledField = new BoolField(new GeneralFieldInformation(generalInformation: $enabledFieldInformation, defaultValue: false);

$integerFieldInformation = new GeneralFieldInputInformation(
    name: 'position', 
    labelInEnglish: 'Position', 
    labelInGerman: 'Position', 
    helpTextInEnglish: 'The position in the list', 
    helpTextInGerman: 'Die Position in der Liste'
);
$integerField = new IntegerField(new GeneralFieldInformation(generalInformation: $integerFieldInformation, defaultValue: 0);

$selectFieldInformation = new GeneralFieldInputInformation(
    name: 'color', 
    labelInEnglish: 'Color', 
    labelInGerman: 'Farbe', 
    helpTextInEnglish: 'The color of the item', 
    helpTextInGerman: 'Die Farbe des Elements'
);
$selectField = new SingleSelectField(
    new GeneralFieldInformation(generalInformation: $selectFieldInformation, defaultValue: 'red'),
    [
        new SingleSelectFieldOption(id: 'red', nameInEnglish: 'Red', nameInGerman: 'Rot'),
        new SingleSelectFieldOption(id: 'blue', nameInEnglish: 'Blue', nameInGerman: 'Blau'),
        new SingleSelectFieldOption(id: 'green', nameInEnglish: 'Green', nameInGerman: 'Grün'),
    ]
);

$textFieldInformation = new GeneralFieldInputInformation(
    name: 'description', 
    labelInEnglish: 'Description', 
    labelInGerman: 'Beschreibung', 
    helpTextInEnglish: 'A description of the item', 
    helpTextInGerman: 'Eine Beschreibung des Elements'
);
$textField = new TextField(new GeneralFieldInformation(generalInformation: $textFieldInformation, defaultValue: '');

$configurationCard = new ConfigurationCard(
    titleInEnglish: 'General Settings',
    titleInGerman: 'Allgemeine Einstellungen',
    inputFields: [$enabledField, $integerField, $selectField, $textField]
);

将卡片注入到配置中

该软件包使用实现 `ConfigurationCardProvider` 接口的服务。您可以创建尽可能多的服务实例实现。由于 Shopware 为所有插件使用相同的加载机制,因此必须声明 `ConfigurationCardProvider` 的范围。这是在插件类的 `getBundleClasses` 方法中完成的。

/**
 * @return class-string<Bundle>[]
 */
public function getBundleClasses(): array {
    return [
        YourPluginClass::class
    ];
}

使用 getPriority 方法对注册了同一插件的多个提供者进行排序。

public function getPriority(): int {
    return 100;
}

最后,使用 getConfigurationCards 方法返回配置卡片。

/**
 * @return ConfigurationCard[]
 */
public function getConfigurationCards(): array {
    return $this->configurationCards;
}

软件包是如何工作的?

该软件包装饰了Shopware的Shopware\Core\System\SystemConfig\Util\ConfigReader服务。此服务用于读取插件的config.xml文件。它将XML文件解码为数组。该软件包保留了插件基于XML的配置,并通过匹配配置卡片提供者的数据扩展它。

装饰通过编译器步骤分三步进行

  1. 收集并标记实现ConfigurationCardProvider接口的所有服务。
  2. 创建一个ConfigurationCardProviderProvider定义,它接收(1)中的所有服务。
  3. 创建ConfigurationCardConfigReader定义,并装饰了Shopware\Core\System\SystemConfig\Util\ConfigReader服务,并将ConfigurationCardProviderProvider注入到ConfigurationCardConfigReader中。
  4. ConfigurationCardConfigReader设置为Shopware\Core\System\SystemConfig\Util\ConfigReader服务的别名以替换它。

Shopware插件配置持久化

Shopware将其数据库中的插件配置保存为键值对。当安装插件时,Shopware使用Shopware\Core\System\SystemConfig\Util\ConfigReader读取配置并持久化默认值。然而,插件的服务在插件激活期间而不是在安装期间添加到DI容器中。这意味着允许基于代码配置生成的服务装饰在安装期间未使用。因此,只持久化config.xml文件中的默认值。

该软件包提供了一个服务订阅者,它订阅Shopware的PluginPostActivateEventPluginPostUpdateEvent。该服务需要插件/包实例来执行配置持久化。订阅者通过使用在注册的ConfigurationCardProvider实例中定义的包基类以及从KernelPluginCollection服务中获取插件实例来检查是否需要配置持久化。编译器步骤负责服务定义、参数注入和订阅标记。

Shopware会检查已持久化的配置值,并且不会覆盖它们。因此,使用此软件包的插件的多次安装/卸载/激活/停用不会重置配置值,除非使用“删除所有数据”选项。

插件激活后,HTTP缓存可能会被重置。为了避免新持久化的配置值的问题,事件订阅者以100的优先级注册,以便尽可能早地执行(至少在优先级为0的其他事件订阅者之前)。

贡献

我很高兴看到软件开发商社区喜欢开源,就像我一样!♥

这就是为什么我感激每一个提交的问题(最好是建设性的)以及每个提供其他甚至更好的代码到这个软件包的拉取请求。

你们都太棒了!