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

Shopware 6前台加载非接入点分类菜单树的正确方法

解决Shopware 6加载非销售渠道接入点分类树的问题

问题背景

需在Shopware 6前台头部添加两级深度的下拉菜单,展示页脚三类菜单中的两类。由于页脚服务导航为硬编码一级导航且已占用,单独创建了顶级分类树(子分类为页脚导航的内部链接),但当前代码仅能加载销售渠道接入点下的分类,选择其他分类会触发CategoryNotFoundException——根源是/src/Core/Content/Category/SalesChannel/NavigationRoute.php的validate()函数限制了仅能加载接入点关联分类。

解决方案

方案1:直接通过CategoryRepository构建分类树

绕过NavigationLoader的接入点限制,直接使用仓储查询分类并手动构建树结构。

1. 修改服务注入配置

更新主题的services.xml,将NavigationLoader替换为CategoryRepository:

<services>
    <service id="MyExample\Subscriber\HeaderPageletLoadedSubscriber">
        <argument type="service" id="Shopware\Core\System\SystemConfig\SystemConfigService"/>
        <argument type="service" id="Shopware\Core\Content\Category\CategoryRepository"/>
        <tag name="kernel.event_subscriber"/>
    </service>
</services>

2. 重写订阅者代码

<?php declare(strict_types=1);

namespace MyExample\Subscriber;

use Shopware\Core\Content\Category\CategoryEntity;
use Shopware\Core\Content\Category\CategoryRepositoryInterface;
use Shopware\Core\Content\Category\Tree\Tree;
use Shopware\Core\Content\Category\Tree\TreeItem;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Sorting\FieldSorting;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Shopware\Core\System\SystemConfig\SystemConfigService;
use Shopware\Storefront\Pagelet\Header\HeaderPageletLoadedEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class HeaderPageletLoadedSubscriber implements EventSubscriberInterface
{
    private CategoryRepositoryInterface $categoryRepository;
    private SystemConfigService $systemConfigService;

    public function __construct(
        SystemConfigService $systemConfigService,
        CategoryRepositoryInterface $categoryRepository
    ) {
        $this->systemConfigService = $systemConfigService;
        $this->categoryRepository = $categoryRepository;
    }

    public static function getSubscribedEvents(): array
    {
        return [
            HeaderPageletLoadedEvent::class => 'onHeaderPageletLoaded'
        ];
    }

    public function onHeaderPageletLoaded(HeaderPageletLoadedEvent $event)
    {
        $context = $event->getSalesChannelContext();
        $salesChannelId = $context->getSalesChannelId();
        
        $rootCategoryId = $this->systemConfigService->get('MyExample.config.serviceRoot', $salesChannelId);
        if (!$rootCategoryId) {
            return;
        }

        // 加载根分类
        $rootCriteria = new Criteria([$rootCategoryId]);
        $rootCategory = $this->categoryRepository->search($rootCriteria, $context->getContext())->first();
        if (!$rootCategory) {
            return;
        }

        // 加载一级子分类及二级子分类
        $criteria = new Criteria();
        $criteria->addFilter(new EqualsFilter('parentId', $rootCategoryId));
        $criteria->addFilter(new EqualsFilter('active', true));
        $criteria->addSorting(new FieldSorting('position'));
        $criteria->addAssociation('children');
        $criteria->getAssociation('children')
            ->addFilter(new EqualsFilter('active', true))
            ->addSorting(new FieldSorting('position'));

        $childCategories = $this->categoryRepository->search($criteria, $context->getContext())->getEntities();

        // 构建Tree结构
        $treeItems = [];
        foreach ($childCategories as $child) {
            $childTreeItem = $this->buildTreeItem($child);
            // 添加二级子分类
            $grandchildren = $child->getChildren();
            if ($grandchildren) {
                $childTreeItem->setChildren(array_map(fn(CategoryEntity $cat) => $this->buildTreeItem($cat), $grandchildren->getElements()));
            }
            $treeItems[] = $childTreeItem;
        }

        $tree = new Tree();
        $tree->setTree($treeItems);
        $tree->setRoot($rootCategoryId);

        $event->getPagelet()->addExtension('MyMenu', $tree);
    }

    private function buildTreeItem(CategoryEntity $category): TreeItem
    {
        $item = new TreeItem();
        $item->setId($category->getId());
        $item->setName($category->getName());
        $item->setUrl($category->getSeoUrl() ?? $category->getInternalLink());
        $item->setActive(false);
        $item->setChildren([]);

        return $item;
    }
}

方案2:自定义NavigationLoader绕过验证

基于核心NavigationLoader扩展,跳过接入点验证逻辑。

1. 创建自定义加载器类

<?php declare(strict_types=1);

namespace MyExample\Service;

use Shopware\Core\Content\Category\Service\NavigationLoader;
use Shopware\Core\Content\Category\Tree\Tree;
use Shopware\Core\System\SalesChannel\SalesChannelContext;

class UnrestrictedNavigationLoader extends NavigationLoader
{
    public function load(string $navigationId, SalesChannelContext $context, ?string $rootId = null, int $depth = 2): Tree
    {
        // 直接调用核心树构建逻辑,跳过接入点验证
        return $this->buildTree(
            $this->loadCategories($navigationId, $context, $depth),
            $navigationId,
            $rootId ?? $navigationId,
            $context
        );
    }
}

2. 配置服务注入

更新services.xml,注入自定义加载器:

<services>
    <service id="MyExample\Service\UnrestrictedNavigationLoader">
        <argument type="service" id="Shopware\Core\Content\Category\CategoryRepository"/>
        <argument type="service" id="Shopware\Core\Content\Category\Tree\TreeBuilder"/>
        <argument type="service" id="Shopware\Core\Content\Seo\SeoUrlPlaceholderHandler"/>
        <argument type="service" id="Shopware\Core\System\SalesChannel\SalesChannelRepository"/>
    </service>

    <service id="MyExample\Subscriber\HeaderPageletLoadedSubscriber">
        <argument type="service" id="Shopware\Core\System\SystemConfig\SystemConfigService"/>
        <argument type="service" id="MyExample\Service\UnrestrictedNavigationLoader"/>
        <tag name="kernel.event_subscriber"/>
    </service>
</services>

原订阅者代码无需修改,直接使用自定义加载器即可加载任意分类树。


注意事项

  • 确保加载的分类处于active状态,避免展示未发布内容
  • 若需SEO友好URL,需确保分类的SEO配置完整
  • 自定义加载器需保持与Shopware核心逻辑对齐,降低版本升级兼容风险

内容的提问来源于stack exchange,提问作者Jürgen Hörmann

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 22:17:04