Drupal – Custom breadcrumb for different content type
     发布在:PHP      浏览:19      评论:2 条评论

在 Drupal 中,系统默认的 Breadcrumb 结构过于单调,

有时候为了满足业务需求,需要根据 Content Type 自定义不同的 Breadcrumb。

这里,我将演示如何将 Menu 层级作为 Breadcrumb 的层级结构。

代码统一写在 Custom Modules 内

demo_breadcrumb.info.yml

name: 'Demo Breadcrumb'
description: 'Provides breadcrumb for Demo website.'
type: module
core: 8.x
package: Demo

demo_breadcrumb.services.yml

services:
  demo_breadcrumb.node_breadcrumb:
    class: Drupal\demo_breadcrumb\DemoNodeBreadcrumbBuilder
    arguments: ['@request_stack', '@title_resolver', '@entity.manager', '@language_manager', '@entity.repository', '@menu.link_tree']
    tags:
      - { name: breadcrumb_builder, priority: 1002 }

DemoNodeBreadcrumbBuilder.php

<?php

namespace Drupal\demo_breadcrumb;

use Drupal\Core\Link;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Url;

/**
 * Build default breadcrumb for default node page.
 *
 * Add node title to the breadcrumb. And if the node is on the menus, add menu title before the node title.
 * Home->Menu first level Title->Menu second level Title->...->Node Title.
 *
 * Class DemoNodeBreadcrumbBuilder
 *
 * @package Drupal\demo_breadcrumb
 */
class DemoNodeBreadcrumbBuilder extends DemoMenuBreadcrumbBaseBuilder {

    /**
     * {@inheritdoc}
     */
    public function applies(RouteMatchInterface $route_match) {
        return TRUE; // default node.
        // If for the different content types.
        // We can use the following code.
        /**
        if ($bundle = $this->getContentTypeByRoute($route_match)) {
            return $bundle == 'demo_content_type';
        }
        return FALSE;
        **/
    }

    /**
     * {@inheritdoc}
     */
    public function build(RouteMatchInterface $route_match) {
        $this->buildBreadcrumbFirstLevel();

        // Get active menus.
        $perMenus = $curMenu = NULL;
        $this->getActiveMenus('main', $perMenus, $curMenu);

        if (!$curMenu) {
            // The page not on the menus.
            $this->buildBreadcrumbLastTextLevel($route_match);
        }
        else {
            // On the menus.
            $this->buildMenuBreadcrumb($curMenu['link'], $perMenus);

            // Current menu.
            $this->breadcrumb->addLink(Link::fromTextAndUrl($curMenu['title'], Url::fromRoute('<none>')));
        }

        // Expire the cache per url.
        $this->breadcrumb->addCacheContexts(['url.path']);

        return $this->breadcrumb;
    }
}

DemoMenuBreadcrumbBaseBuilder.php

<?php
namespace Drupal\demo_breadcrumb;
use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface;
use Drupal\Core\Controller\TitleResolverInterface;
use Drupal\Core\Entity\EntityRepositoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Menu\MenuLinkTreeInterface;
use Drupal\Core\Menu\MenuLinkInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Symfony\Component\HttpFoundation\RequestStack;
use Drupal\Core\Link;
use Drupal\Core\Url;
/**
* Demo menu breadcrumb base builder.
*/
abstract class DemoMenuBreadcrumbBaseBuilder implements BreadcrumbBuilderInterface {
use StringTranslationTrait;
use TaxonomyChainLinkTrait;
/**
* The menu link tree.
*
* @var \Drupal\Core\Menu\MenuLinkTree
*/
protected $menuLinkTree;
/**
* Constructs the DemoMenuBreadcrumbBaseBuilder.
*/
public function __construct(
RequestStack $request_stack,
TitleResolverInterface $title_resolver,
EntityTypeManagerInterface $entity_type_manager,
LanguageManagerInterface $language_manager,
EntityRepositoryInterface $entity_repository,
MenuLinkTreeInterface $menu_link_tree
) {
$this->requestStack = $request_stack;
$this->titleResolver = $title_resolver;
$this->entityTypeManager = $entity_type_manager;
$this->languageManager = $language_manager;
$this->entityRepository = $entity_repository;
$this->menuLinkTree = $menu_link_tree;
$this->curLangCode = $this->languageManager->getCurrentLanguage()->getId();
}
/**
* Build breadcrumb last level.
*/
protected function buildBreadcrumbLastTextLevel(RouteMatchInterface $route_match) {
$request = $this->requestStack->getCurrentRequest();
$route = $route_match->getRouteObject();
$title = $this->titleResolver->getTitle($request, $route);
$title && $this->breadcrumb->addLink(Link::fromTextAndUrl($title, Url::fromRoute('<none>')));
}
/**
* Get active menu.
*/
protected function getActiveMenu(String $menuMachineName, &$perMenus, &$curMenu) {
$main_menus = $this->menuLinkTree->load(
$menuMachineName,
$this->menuLinkTree->getCurrentRouteMenuTreeParameters($menuMachineName)
);
$this->recursiveActiveMenuFilter($main_menus, $perMenus);
$perMenus && array_multisort(array_column($perMenus, 'depth'), SORT_ASC, $perMenus);
$curMenu = $perMenus ? array_pop($perMenus) : NULL;
}
/**
* Recursive filter active menu.
*/
protected function recursiveActiveMenuFilter(array $tree, &$menus) {
if (isset($tree) && !empty($tree)) {
foreach ($tree as $item) {
$link_title = $item->link->getTitle();
if ($item->inActiveTrail && $link_title != ' ') {
$menus[] = [
'title' => $link_title,
'link' => $item->link,
'depth' => $item->depth,
];
}
$this->recursiveActiveMenuFilter($item->subtree, $menus);
}
}
}
/**
* Get field value by MenuLinkContent.
*/
protected function getValueByLink(String $field, MenuLinkInterface $link) {
$uuid = $link->getDerivativeId();
$entity = $this->entityRepository->loadEntityByUuid('menu_link_content', $uuid);
$entity = $entity->hasTranslation($this->curLangCode) ? $entity->getTranslation($this->curLangCode) : $entity;
return $entity->get($field)->getValue();
}
/**
* Build custom breadcrumb.
*/
protected function buildMenuBreadcrumb(MenuLinkInterface $currentMenuLink, array &$menus) {
//Custom Breadcrumb.
$custom_breadcrumb = $this->getValueByLink('field_custom_breadcrumb', $currentMenuLink);
if (!empty($custom_breadcrumb)) {
$depth = 1;
$menus = array_map(function ($value) use (&$depth) {
return [
'title' => $value['title'],
'url' => Url::fromUri($value['uri']),
'depth' => $depth++,
];
}, $custom_breadcrumb);
}
// Sort.
array_multisort(array_column($menus, 'depth'), SORT_ASC, $menus);
// Add Menu Breadcrumb.
foreach ($menus as $menu) {
$url = isset($menu['link']) && $menu['link'] ? $menu['link']->getUrlObject() : $menu['url'];
$this->breadcrumb->addLink(Link::fromTextAndUrl($menu['title'], $url));
}
}
}

TaxonomyChainLinkTrait.php

<?php
namespace Drupal\demo_breadcrumb;
use Drupal\Core\Breadcrumb\Breadcrumb;
use Drupal\Core\Link;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Url;
/**
* Trait TaxonomyChainLinkTrait.
*
* @package Drupal\demo_breadcrumb
*/
trait TaxonomyChainLinkTrait {
/**
* Inject entityTypeManager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* Current Breadcrumb.
*
* @var \Drupal\Core\Breadcrumb\Breadcrumb
*/
protected $breadcrumb;
/**
* The request stack.
*
* @var \Symfony\Component\HttpFoundation\RequestStack
*/
protected $requestStack;
/**
* The title resolver.
*
* @var \Drupal\Core\Controller\TitleResolverInterface
*/
protected $titleResolver;
/**
* The language manager.
*
* @var \Drupal\Core\Language\LanguageManagerInterface
*/
protected $languageManager;
/**
* The entity repository.
*
* @var \Drupal\Core\Entity\EntityRepositoryInterface
*/
protected $entityRepository;
/**
* Current language code.
*
* @var string
*/
protected $curLangCode;
/**
* Get node bundle by route.
*/
protected function getContentTypeByRoute(RouteMatchInterface $route_match) {
$node = $route_match->getParameter('node');
if ($node) {
return $node->bundle();
}
return FALSE;
}
/**
* Build breadcrumb first level.
*/
protected function buildBreadcrumbFirstLevel() {
$this->breadcrumb = new Breadcrumb();
// Home - 1st level.
$this->breadcrumb->addLink(Link::createFromRoute('Home',
'<front>'));
}
/**
* Build breadcrumb second level with type.
*/
protected function buildBreadcrumbSecondLevel($type) {
// 2st level menuLinkManager.
$fragment = $this->handleBaseLangPath();
$url = Url::fromUri('base:' . $fragment . $type);
return $url;
}
/**
* Url Base Path transform.
*/
protected function handleBaseLangPath() {
$url_fragment = "";
if ($this->languageManager->getCurrentLanguage()->getId() == 'ja') {
// @Todo Use a more flexiable way to get language prefix
$url_fragment = "/jp/";
}
return $url_fragment;
}
/**
* Build breadcrumb last text level.
*/
protected function buildBreadcrumbLastTextLevel(RouteMatchInterface $route_match) {
$request = $this->requestStack->getCurrentRequest();
$route = $route_match->getRouteObject();
$title = $this->titleResolver->getTitle($request, $route);
// Get Page Title first.
$node = \Drupal::routeMatch()->getParameter('node');
if ($node) {
$nodeStorage = $this->entityTypeManager->getStorage('node');
$node = $nodeStorage->load($node->id());
$node = $this->entityRepository->getTranslationFromContext($node, $this->curLangCode);
$page_title = $node->get('field_page_title')->getValue();
if ($page_title) {
$title = $page_title[0]['value'];
$noneUrl = Url::fromRoute('<none>');
$this->breadcrumb->addLink(Link::fromTextAndUrl($title, $noneUrl));
return;
}
}
if ($title) {
$noneUrl = Url::fromRoute('<none>');
$this->breadcrumb->addLink(Link::fromTextAndUrl($title, $noneUrl));
}
}
/**
* Get parent taxonomy term.
*/
protected function getParentTerm($term) {
/** @var \Drupal\taxonomy\TermStorageInterface $termStorage */
$termStorage = $this->entityTypeManager->getStorage('taxonomy_term');
$termParents = $termStorage->loadParents($term->id());
if ($termParents) {
$termParent = reset($termParents);
return $termParent;
}
return FALSE;
}
/**
* Get child taxonomy term.
*/
protected function getChildTerm($term) {
/** @var \Drupal\taxonomy\TermStorageInterface $termStorage */
$termStorage = $this->entityTypeManager->getStorage('taxonomy_term');
$termChildren = $termStorage->loadChildren($term->id());
if ($termChildren) {
$termChild = reset($termChildren);
return $termChild;
}
return FALSE;
}
/**
* Add Term Link to breadcrumb as a chain with plain text.
*/
protected function addTermChainLinkText($term) {
// Look for a parent of this term.
/** @var \Drupal\taxonomy\Entity\Term $parentTerm */
if ($parentTerm = $this->getParentTerm($term)) {
// Recursive call this method for the parent.
$this->addTermChainLinkText($parentTerm);
}
if ($this->term !== $term) {
$this->addLinkToTerm($term);
}
else {
$this->addTextToTerm($term);
}
}
/**
* Add Term Link to breadcrumb as a chain.
*/
protected function addTermChainLink($term) {
// Look for a parent of this term.
/** @var \Drupal\taxonomy\Entity\Term $parentTerm */
if ($parentTerm = $this->getParentTerm($term)) {
// Recursive call this method for the parent.
$this->addTermChainLink($parentTerm);
}
$this->addLinkToTerm($term);
}
/**
* Add Term Link to breadcrumb.
*/
protected function addLinkToTerm($term) {
/** @var \Drupal\Core\Url $termUrl */
$term = $this->entityRepository->getTranslationFromContext($term, $this->curLangCode);
$termUrl = $term->toUrl();
$this->breadcrumb->addLink(Link::fromTextAndUrl($term->label(), $termUrl));
}
/**
* Add Term Link to breadcrumb as text.
*/
protected function addTextToTerm($term) {
$termUrl = Url::fromRoute('<none>');
$term = $this->entityRepository->getTranslationFromContext($term, $this->curLangCode);
$this->breadcrumb->addLink(Link::fromTextAndUrl($term->label(), $termUrl));
}
/**
* Add Term Link to breadcrumb with Text.
*/
protected function addLinkToTermText($term, $string = "") {
$termUrl = Url::fromRoute('<none>');
$term = $this->entityRepository->getTranslationFromContext($term, $this->curLangCode);
$this->breadcrumb->addLink(Link::fromTextAndUrl($term->label() . ' ' . $string,
$termUrl));
}
}
Responses

    加载中...
  1. Drupal 国外用户比较多,国内的好像用的很少

    回复
    • OceanicKang博主

      对的呐,中文社区不是很发达,没个英语四六级文档都看的费劲。

      回复