如何在Shopware 6中正确实现插件的自动化更新?
如何在Shopware 6中正确实现插件的自动化更新?
针对你遇到的官方Migration和生命周期方法的局限性,可通过自定义版本跟踪机制 + 生命周期方法结合的方案解决,同时也可扩展官方Migration系统实现需求,以下是具体实现思路:
核心痛点回顾
- Migration局限性:仅支持SQL操作,无法注入服务,无法处理文件系统、API调用等复杂逻辑
- 生命周期方法局限性:
plugin:refresh会直接标记插件为最新版本,导致无法按版本梯度执行更新脚本
可行解决方案
方案1:自定义版本跟踪 + 生命周期方法(推荐)
自己维护版本执行记录,结合插件的install和update方法,按版本梯度执行更新脚本,同时利用容器获取所需服务处理复杂操作。
步骤1:创建自定义版本跟踪表
在插件的install方法中,先创建用于记录已执行更新版本的表:
public function install(InstallContext $context): void { parent::install($context); // 创建版本跟踪表 $connection = $this->container->get(Connection::class); $connection->executeStatement(' CREATE TABLE IF NOT EXISTS my_custom_plugin_version ( id INT AUTO_INCREMENT PRIMARY KEY, version VARCHAR(255) NOT NULL UNIQUE, executed_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 '); // 执行版本检测与更新脚本 $this->processVersionUpdates($context); }
步骤2:实现版本检测与脚本执行逻辑
新增通用方法处理版本对比和脚本执行,同时兼容首次安装和版本升级场景:
private function processVersionUpdates(InstallContext $context): void { $connection = $this->container->get(Connection::class); $currentPluginVersion = $this->getVersion(); // 获取已执行的最高版本,首次安装则为空 $lastExecutedVersion = $connection->fetchOne('SELECT MAX(version) FROM my_custom_plugin_version'); // 定义所有版本梯度(按升级顺序排列) $versionList = [ '1.0.0', '1.0.1', '1.0.2', // ... 中间版本 '2.9.2', '2.9.3' ]; // 确定需要执行的版本范围 $startIndex = $lastExecutedVersion ? array_search($lastExecutedVersion, $versionList) + 1 : 0; $endIndex = array_search($currentPluginVersion, $versionList); // 依次执行每个版本的更新脚本 for ($i = $startIndex; $i <= $endIndex; $i++) { $targetVersion = $versionList[$i]; $this->runUpdateScriptForVersion($targetVersion, $context); // 记录已执行的版本 $connection->executeStatement( 'INSERT INTO my_custom_plugin_version (version) VALUES (?) ON DUPLICATE KEY UPDATE executed_at = CURRENT_TIMESTAMP', [$targetVersion] ); } } private function runUpdateScriptForVersion(string $version, InstallContext $context): void { switch ($version) { case '1.0.1': // 示例:创建目录 + 调用外部API $fileSystem = $this->container->get(FileSystem::class); $fileSystem->mkdir($this->getPath() . '/data'); $externalApiService = $this->container->get(ExternalApiService::class); $externalApiService->syncInitialData(); break; case '1.0.2': // 示例:添加数据库字段 $connection = $this->container->get(Connection::class); $connection->executeStatement( 'ALTER TABLE my_custom_entity ADD COLUMN `new_field` VARCHAR(255) NULL AFTER `existing_field`', [], [Connection::PARAM_STR] ); break; case '2.9.3': // 示例:调用其他插件的服务 $otherPluginService = $this->container->get('other.plugin.service.id'); $otherPluginService->performAction(); break; // 其他版本的更新逻辑 } }
步骤3:适配update方法
在插件的update方法中复用版本处理逻辑,支持从旧版本升级:
public function update(UpdateContext $context): void { parent::update($context); $this->processVersionUpdates($context); }
注意事项
- 版本列表必须严格按升级顺序排列,确保脚本执行顺序正确
- 所有更新脚本需保证幂等性(如使用
IF NOT EXISTS、ON DUPLICATE KEY等),避免重复执行出错 - 在
uninstall方法中添加版本跟踪表的删除逻辑,清理残留数据
方案2:扩展官方Migration系统
若想保留官方Migration的版本跟踪机制,可自定义Migration运行器,允许注入服务:
- 创建自定义Migration类,继承官方
Migration并添加容器注入逻辑 - 自定义
MigrationRuntime的实现,将容器传递给Migration实例 - 在自定义Migration中即可使用服务处理复杂操作
此方案需对Shopware内核有一定了解,适合需要深度定制的场景。
内容的提问来源于stack exchange,提问作者Matheus Gontijo
相关产品推荐
相关产品推荐

