Shopware 6自定义CMS元素:如何从两个仓库获取数据到CMS区块
推荐方案:为每个CMS元素配置独立DataResolver(解决覆盖问题)
1. 每个Resolver需返回唯一元素标识
多个Resolver互相覆盖的核心原因是getType()返回了重复的类型字符串,每个CMS元素的Resolver必须返回唯一的元素类型标识(与你定义CMS元素时的type一致)。
第一个元素的Resolver实现
// ExampleOneCmsElementResolver.php namespace YourPlugin\Cms\Resolver; use Shopware\Core\Content\Cms\Aggregate\CmsSlot\CmsSlotEntity; use Shopware\Core\Content\Cms\DataResolver\CriteriaCollection; use Shopware\Core\Content\Cms\DataResolver\ResolverContext; use Shopware\Core\Content\Cms\DataResolver\Element\CmsElementResolver; use Shopware\Core\Content\Cms\DataResolver\Element\ElementDataCollection; use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria; use YourPlugin\Entity\Entity1Definition; use YourPlugin\Repository\Entity1Repository; use YourPlugin\Struct\ExampleOneElementStruct; class ExampleOneCmsElementResolver extends CmsElementResolver { public function __construct( private readonly Entity1Repository $entity1Repository ) {} // 返回第一个CMS元素的唯一类型标识 public function getType(): string { return 'example-one-element'; } public function getStruct(): \Shopware\Core\Framework\Struct\Struct { return new ExampleOneElementStruct(); } public function collect(CmsSlotEntity $slot, ResolverContext $resolverContext): ?CriteriaCollection { $config = $slot->getFieldConfig(); // 从元素配置中获取需要查询的实体ID(根据你的配置字段调整) $entityId = $config->get('entity1Id')?->getValue(); if (!$entityId) { return null; // 无配置则无需查询 } $criteria = new Criteria([$entityId]); // 可添加关联、过滤等条件,例如:$criteria->addAssociation('relatedEntity'); $criteriaCollection = new CriteriaCollection(); // 用唯一键区分查询结果(元素类型+实体名) $criteriaCollection->add( sprintf('%s-%s', $this->getType(), Entity1Definition::ENTITY_NAME), Entity1Definition::class, $criteria ); return $criteriaCollection; } public function enrich(CmsSlotEntity $slot, ResolverContext $resolverContext, ElementDataCollection $result): void { // 通过collect阶段的唯一键获取查询结果 $entityData = $result->get(sprintf('%s-%s', $this->getType(), Entity1Definition::ENTITY_NAME)); if (!$entityData) { return; } $struct = $this->getStruct(); // 将数据赋值到Struct(需在Struct类中定义对应的setter) $struct->setEntity1($entityData->first()); // 将Struct绑定到CMS Slot的data属性 $slot->setData($struct); } }
第二个元素的Resolver实现
只需替换类型标识、仓库、实体定义和Struct即可:
// ExampleTwoCmsElementResolver.php namespace YourPlugin\Cms\Resolver; use Shopware\Core\Content\Cms\Aggregate\CmsSlot\CmsSlotEntity; use Shopware\Core\Content\Cms\DataResolver\CriteriaCollection; use Shopware\Core\Content\Cms\DataResolver\ResolverContext; use Shopware\Core\Content\Cms\DataResolver\Element\CmsElementResolver; use Shopware\Core\Content\Cms\DataResolver\Element\ElementDataCollection; use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria; use YourPlugin\Entity\Entity2Definition; use YourPlugin\Repository\Entity2Repository; use YourPlugin\Struct\ExampleTwoElementStruct; class ExampleTwoCmsElementResolver extends CmsElementResolver { public function __construct( private readonly Entity2Repository $entity2Repository ) {} public function getType(): string { return 'example-two-element'; } public function getStruct(): \Shopware\Core\Framework\Struct\Struct { return new ExampleTwoElementStruct(); } public function collect(CmsSlotEntity $slot, ResolverContext $resolverContext): ?CriteriaCollection { $config = $slot->getFieldConfig(); $entityId = $config->get('entity2Id')?->getValue(); if (!$entityId) { return null; } $criteria = new Criteria([$entityId]); $criteriaCollection = new CriteriaCollection(); $criteriaCollection->add( sprintf('%s-%s', $this->getType(), Entity2Definition::ENTITY_NAME), Entity2Definition::class, $criteria ); return $criteriaCollection; } public function enrich(CmsSlotEntity $slot, ResolverContext $resolverContext, ElementDataCollection $result): void { $entityData = $result->get(sprintf('%s-%s', $this->getType(), Entity2Definition::ENTITY_NAME)); if (!$entityData) { return; } $struct = $this->getStruct(); $struct->setEntity2($entityData->first()); $slot->setData($struct); } }
2. 正确注册Resolver服务
在插件的src/Resources/config/services.xml中注册两个Resolver,必须指定type属性对应各自的元素类型,Shopware会自动匹配:
<services> <!-- 第一个元素的Resolver --> <service id="YourPlugin\Cms\Resolver\ExampleOneCmsElementResolver"> <argument type="service" id="Entity1.repository"/> <tag name="shopware.cms.data_resolver" type="example-one-element"/> </service> <!-- 第二个元素的Resolver --> <service id="YourPlugin\Cms\Resolver\ExampleTwoCmsElementResolver"> <argument type="service" id="Entity2.repository"/> <tag name="shopware.cms.data_resolver" type="example-two-element"/> </service> </services>
备选方案:整合到单个DataResolver(不推荐)
如果一定要将逻辑整合到一个Resolver,需在方法中判断当前Slot的类型,分别处理:
// CombinedCmsElementResolver.php namespace YourPlugin\Cms\Resolver; use Shopware\Core\Content\Cms\Aggregate\CmsSlot\CmsSlotEntity; use Shopware\Core\Content\Cms\DataResolver\CriteriaCollection; use Shopware\Core\Content\Cms\DataResolver\ResolverContext; use Shopware\Core\Content\Cms\DataResolver\Element\CmsElementResolver; use Shopware\Core\Content\Cms\DataResolver\Element\ElementDataCollection; use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria; use YourPlugin\Entity\Entity1Definition; use YourPlugin\Entity\Entity2Definition; use YourPlugin\Repository\Entity1Repository; use YourPlugin\Repository\Entity2Repository; use YourPlugin\Struct\ExampleOneElementStruct; use YourPlugin\Struct\ExampleTwoElementStruct; class CombinedCmsElementResolver extends CmsElementResolver { public function __construct( private readonly Entity1Repository $entity1Repository, private readonly Entity2Repository $entity2Repository ) {} public function getType(): string { return 'combined-resolver'; } public function collect(CmsSlotEntity $slot, ResolverContext $resolverContext): ?CriteriaCollection { $criteriaCollection = new CriteriaCollection(); $slotType = $slot->getType(); // 处理第一个元素 if ($slotType === 'example-one-element') { $config = $slot->getFieldConfig(); $entityId = $config->get('entity1Id')?->getValue(); if ($entityId) { $criteria = new Criteria([$entityId]); $criteriaCollection->add( sprintf('%s-%s', $slotType, Entity1Definition::ENTITY_NAME), Entity1Definition::class, $criteria ); } } // 处理第二个元素 if ($slotType === 'example-two-element') { $config = $slot->getFieldConfig(); $entityId = $config->get('entity2Id')?->getValue(); if ($entityId) { $criteria = new Criteria([$entityId]); $criteriaCollection->add( sprintf('%s-%s', $slotType, Entity2Definition::ENTITY_NAME), Entity2Definition::class, $criteria ); } } return $criteriaCollection->all() ? $criteriaCollection : null; } public function enrich(CmsSlotEntity $slot, ResolverContext $resolverContext, ElementDataCollection $result): void { $slotType = $slot->getType(); $struct = null; if ($slotType === 'example-one-element') { $struct = new ExampleOneElementStruct(); $entityData = $result->get(sprintf('%s-%s', $slotType, Entity1Definition::ENTITY_NAME)); if ($entityData) { $struct->setEntity1($entityData->first()); } } elseif ($slotType === 'example-two-element') { $struct = new ExampleTwoElementStruct(); $entityData = $result->get(sprintf('%s-%s', $slotType, Entity2Definition::ENTITY_NAME)); if ($entityData) { $struct->setEntity2($entityData->first()); } } if ($struct) { $slot->setData($struct); } } public function getStruct(): \Shopware\Core\Framework\Struct\Struct { return new \Shopware\Core\Framework\Struct\EmptyStruct(); } }
注册服务时需添加多个type标签:
<service id="YourPlugin\Cms\Resolver\CombinedCmsElementResolver"> <argument type="service" id="Entity1.repository"/> <argument type="service" id="Entity2.repository"/> <tag name="shopware.cms.data_resolver" type="example-one-element"/> <tag name="shopware.cms.data_resolver" type="example-two-element"/> </service>
关键注意事项
- 确保CMS元素的
type与Resolver的getType()、服务标签的type完全一致。 CriteriaCollection的添加键必须唯一,避免查询结果互相覆盖。- 自定义Struct需继承
Shopware\Core\Framework\Struct\Struct,并定义对应的数据属性和setter方法。
内容的提问来源于stack exchange,提问作者Noah
相关产品推荐
相关产品推荐

