API更新自定义实体时自动失效Shopware 6分类页面缓存的实现方案
我刚好碰到过类似的场景,你当前的思路方向是对的(关联缓存标签),但实现方式需要调整,核心是要通过事件订阅正确绑定自定义条目到分类页面的缓存标签,同时确保自定义条目更新时能触发缓存清除。下面一步步来:
1. 正确通过CategoryRouteCacheTagsEvent添加缓存标签
你之前硬修改CachedCategoryRoute::extractProductIds的方式不可取(核心代码不能直接改,而且这个方法是专门处理产品的),应该用官方提供的CategoryRouteCacheTagsEvent来注入自定义缓存标签。
创建一个事件订阅者类:
<?php namespace YourPlugin\Subscriber; use Shopware\Core\Content\Category\SalesChannel\Event\CategoryRouteCacheTagsEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; class CategoryCacheTagsSubscriber implements EventSubscriberInterface { public static function getSubscribedEvents(): array { return [ CategoryRouteCacheTagsEvent::class => 'addCustomJobCacheTags' ]; } public function addCustomJobCacheTags(CategoryRouteCacheTagsEvent $event): void { $page = $event->getPage(); $tags = $event->getTags(); // 找到所有自定义的jobs类型CMS插槽 $jobSlots = $page->getElementsOfType('jobs'); foreach ($jobSlots as $slot) { $slotData = $slot->getData(); // 确保你的slot数据里能正确获取到job条目ID数组 if (!empty($slotData['jobs']->getIds())) { // 按照Shopware的缓存标签格式添加:"job:{id}" foreach ($slotData['jobs']->getIds() as $jobId) { $tags[] = 'job:' . $jobId; } } } // 更新事件里的标签集合 $event->setTags($tags); } }
然后在插件的services.xml里注册这个订阅者:
<service id="YourPlugin\Subscriber\CategoryCacheTagsSubscriber"> <tag name="kernel.event_subscriber"/> </service>
2. 确保自定义条目更新时触发缓存清除
接下来要保证当你的自定义job条目被创建/更新/删除时,Shopware会清除所有带有job:{id}标签的缓存。分两种情况处理:
情况一:自定义实体
如果你的job是Shopware自定义实体,需要在实体定义里配置缓存标签:
<?php namespace YourPlugin\Core\Content\Job; use Shopware\Core\Framework\DataAbstractionLayer\EntityDefinition; use Shopware\Core\Framework\DataAbstractionLayer\FieldCollection; use Shopware\Core\Framework\DataAbstractionLayer\Field\IdField; use Shopware\Core\Framework\DataAbstractionLayer\Field\StringField; use Shopware\Core\Framework\DataAbstractionLayer\EntityInterface; class JobDefinition extends EntityDefinition { public const ENTITY_NAME = 'job'; public function getEntityName(): string { return self::ENTITY_NAME; } public function getEntityClass(): string { return JobEntity::class; } public function getCollectionClass(): string { return JobCollection::class; } protected function defineFields(): FieldCollection { return new FieldCollection([ (new IdField('id', 'id'))->addFlags(new PrimaryKey(), new Required()), new StringField('title', 'title'), // 其他字段... ]); } // 关键:添加缓存标签配置 public function getCacheTags(EntityInterface $entity): array { return ['job:' . $entity->getId()]; } }
情况二:外部API关联数据
如果你的job是通过API关联的外部数据,需要在更新条目的逻辑里手动触发缓存清除:
// 在你的job更新服务里,注入CacheInvalidator public function updateJob(string $jobId, array $data): void { // 你的外部API更新逻辑... // 清除对应缓存标签 $this->cacheInvalidator->invalidate(['job:' . $jobId]); }
这里的$cacheInvalidator是Shopware\Core\Framework\Adapter\Cache\CacheInvalidator类,通过依赖注入注入即可。
3. 为什么产品盒子元素没问题?
产品盒子元素之所以能自动失效,是因为:
- 分类页面渲染时,核心代码已经通过相关逻辑添加了产品的缓存标签(
product:{id}) - 产品实体本身已经配置了
getCacheTags方法,当产品更新时,会自动清除所有带有对应标签的缓存
你的自定义元素就是缺少了这两步的绑定。
关于核心代码重复的问题
你提到的CachedCategoryRoute::extractProductIds和CachedLandingPageRoute::extractIds逻辑重复,这确实是Shopware核心里的历史实现冗余问题,属于官方代码的小瑕疵,不需要我们处理,专注于自己的缓存标签绑定即可。
内容的提问来源于stack exchange,提问作者Alex

