php - Drupal 8:当内容属于多个分类时如何管理面包屑?
问题描述
我在 Drupal 8 中有一个博客,它使用类别分类法来显示面包屑:主页 > 博客 > [类别] > 帖子标题。我正在添加标签分类,这意味着每个帖子都可以有一个类别和多个标签。
自从我添加标签分类后,它们就接管了各个博客帖子的面包屑:主页 > 博客 > [标签] > 帖子标题。
在 Drupal 管理员中(开箱即用,或通过模块):
在显示面包屑时,是否可以控制哪个分类法应该具有优先权?
是否可以根据用户来自哪里来控制在博客文章中显示的分类?例如,如果用户在单个标签页面上:主页 > 博客 > [标签] 并单击属于该标签的博客文章,当博客文章打开时,它应该使用标签分类法显示面包屑:主页 > 博客 > [标签] > 帖子标题。但是,如果用户在单个类别页面:主页 > 博客 > [类别] 并单击属于该类别的博客文章,则当博客文章打开时,它应该使用类别分类法显示面包屑:主页 > 博客 > [类别] > 帖子标题。
解决方案
这是我的解决方案。请记住,我对 Drupal 很陌生,所以这可能有点过于复杂,但希望它能给有类似问题的人一些方向。在接下来的几天里,我可能会重构这个类,但现在我只需要从所有这些混乱中休息一下。
function [YOUR-MODULE-NAME]_system_breadcrumb_alter(\Drupal\Core\Breadcrumb\Breadcrumb &$breadcrumb, \Drupal\Core\Routing\RouteMatchInterface $route_match, array $context) {
// Control Blog Post Breadcrumb Taxonomy Trail
$blogPostBreadcrumb = new BlogPostBreadcrumb();
$blogPostBreadcrumb->alter($breadcrumb);
}
use Drupal\Core\Breadcrumb\Breadcrumb;
use Drupal\Core\Entity\EntityBase;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Url;
use Drupal\node\Entity\Node;
use Drupal\taxonomy\Entity\Term;
use Exception;
use Symfony\Component\HttpFoundation\Request;
/**
* Class BlogPostBreadcrumb
*
* The purpose of this class is to control Blog post breadcrumbs, as described here:
*
* RULE 1:
* When the user is on the Tag page (Home > Blog > Tag) and clicks on a blog post, the blog post breadcrumbs will
* display: Home > Blog > [Tag Name] > Blog Post Title
*
* RULE 2:
* When the user is on the Category page (Home > Blog > Category) and clicks on a blog post, the blog post breadcrumbs
* will display: Home > Blog > [Category Name] > Blog Post Title
*
* RULE 3:
* When the user loads the blog post directly (i.e. Home > Blog, or comes from other sources unrelated to
* Category or Tag), the blog post breadcrumbs should default to Category:
* Home > Blog > [Category Name] > Blog Post Title
*
*/
class BlogPostBreadcrumb
{
/**
* @param Breadcrumb $breadcrumb
*/
public function alter(Breadcrumb &$breadcrumb): void
{
try {
// current node
$current_node = $this->currentNode();
// nothing found, nothing to do
if (empty($current_node)) {
return;
}
// what is the current content type
$content_type = $this->getField($current_node, 'type');
// apply rule only to blog_post content type, otherwise let's stop the madness right here
if ($content_type !== 'blog_post') {
return;
}
// determine if the tag can be extracted from referrer. If not, default it to college_tag
$altered_breadcrumb = $this->getTaxonomyLabelAndUrlByReferrer()
?? $this->getTaxonomyLabelAndUrlByCollege($current_node);
// nothing matched - just let it be. Display blog_tags rather than nothing.
if (empty($altered_breadcrumb)) {
return;
}
// modify existing breadcrumb list by replacing the third link with proper tag
// (Home > Blog > [college_tag/blog_tag] > Blog Title)
$breadcrumb->getLinks()[2]->setText($altered_breadcrumb['label']);
$breadcrumb->getLinks()[2]->setUrl($altered_breadcrumb['url']);
// we have to disable caching, otherwise whichever taxonomy opens first, it will get cached!
// load menu_link_content entity
$entity = \Drupal::entityTypeManager()
->getStorage('menu_link_content')
->load($current_node->getEntityTypeId());
// disable caching
$breadcrumb->addCacheableDependency($entity);
} catch (Exception $e) {
// if any exception is thrown, we don't want to stop the page from loading.
// Worse case scenario, it will load blog_tags into breadcrumbs.
}
}
/**
* Get Field from node
* Helper method for retrieving field value from given node.
*
* @param EntityInterface $node
* @param string $fieldName
* @return mixed|null
*/
protected function getField(EntityInterface $node, string $fieldName)
{
return $node->get($fieldName)->getValue()[0]['target_id'] ?? null;
}
/**
* Load node from current url
*
* @return EntityBase|null
*/
protected function currentNode():? EntityBase
{
if ($nid = \Drupal::routeMatch()->getParameter('node')) {
return Node::load($nid->id()) ?? null;
}
return null;
}
/**
* Determine Taxonomy Breadcrumb Label and Url by referrer
* We want to display Breadcrumb category depending from where the user is coming from:
* Blog College (category) or Blog tag page
*
* @return array|null
*/
protected function getTaxonomyLabelAndUrlByReferrer():? array
{
// capture referrer the "Drupal way"
$route_parameters = $this->referrer();
// if type is not a taxonomy term, it's not used to us
if (empty($route_parameters['taxonomy_term'])) {
return null;
}
// load node (taxonomy term)
$term = Term::load($route_parameters['taxonomy_term']);
// get taxonomy type
$taxonomy = $this->getField($term, 'vid');
// check if taxonomy belongs to college_tags or blog_tags
if (empty($taxonomy) && !in_array($taxonomy, ['college_tags', 'blog_tags'])) {
return null;
}
// if we can't find the url or the label
if (empty($term->url() || empty($term->label()))) {
return null;
}
return [
'label' => $term->label(),
'url' => Url::fromUserInput($term->url())
];
}
/**
* Determine Taxonomy Breadcrumb Label and Url by college
* This method will be used when referrer page doesn't exist, or the referrer is not Blog Category or Blog Tag page
*
* @param EntityInterface $node
* @return array|null
*/
protected function getTaxonomyLabelAndUrlByCollege(EntityInterface $node):? array
{
// blog can have one college tag (category)
$taxonomy_id = $this->getField($node, 'field_college');
$term = Term::load($taxonomy_id);
// if we can't find the url or the label
if (empty($term->url() || empty($term->label()))) {
return null;
}
return [
'label' => $term->label(),
'url' => Url::fromUserInput($term->url())
];
}
/**
* Referrer node
* Load referrer node the "drupal way"
*
* @return mixed
*/
protected function referrer()
{
// this would be college_tag or blog_tag page
$previousUrl = \Drupal::request()->server->get('HTTP_REFERER');
// fake the request to previous page, so we can get more info
$fake_request = Request::create($previousUrl);
// build url object.
$url_object = \Drupal::service('path.validator')->getUrlIfValid($fake_request->getRequestUri());
if ($url_object) {
return $url_object->getRouteParameters();
} else {
return null;
}
}
}
推荐阅读
- python - 字典数据可以随机分成测试集和训练集吗?
- material-ui - 将 material-ui 组件包装在库中以在多个其他项目中重新使用它会破坏样式
- android - Kotlin / Android Studio - 如何将变量从覆盖乐趣传递到应用程序的其余部分?
- python - 熊猫:如果数据框中的两个字段匹配,则添加布尔列
- javascript - 在文本框中输入数字应该是十进制的,两位数
- postgresql - 如何更改 postgresql 数据库连接的默认用户名?
- scala - 键值对映射 - 从数据框到 cassandra 表
- git - 如何最好使用 git 同步两个相关项目?
- html - 使 @Html.RadioButtonFor 为必需
- excel - 需要将基于行的转换/转置为基于列的