You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

API更新自定义实体时自动失效Shopware 6分类页面缓存的实现方案

解决Shopware 6自定义CMS元素分类页面缓存自动失效问题

我刚好碰到过类似的场景,你当前的思路方向是对的(关联缓存标签),但实现方式需要调整,核心是要通过事件订阅正确绑定自定义条目到分类页面的缓存标签,同时确保自定义条目更新时能触发缓存清除。下面一步步来:

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 17:32:43