当为产品使用多个类别时,产品页面上的面包屑不反映当前 url/slug
Breadcrumbs on product page don't reflect current url/slug when using multiple categories for product
我的 Wordpress 网站使用 Woocommerce 来销售产品。但是,当您将多个产品类别分配给一个产品并访问产品页面(将 Product Permalinks
设置为 Shop base with category
)时,会发生意想不到的事情。 (父)类别的面包屑导航 link 始终相同,不反映导航 url。 (这与 here 描述的问题相同。)
问题总结:
Navigate to
Breadcrumb link
Expected breadcrumb link
/shop/category-1/sample-product/
category-2
category-1
/shop/category-2/sample-product/
category-2
category-2
我在任何解决这个问题的来源上都没有找到真正的答案。 (我认为它应该被修复,但有些人可能会说这是出于 SEO 原因,防止重复的网站内容。)所以为了帮助任何人寻找答案,这里是:
(wr)on(g) 发生了什么事?
产品页面上的面包屑由 woocommerce_breadcrumb()
function 生成。依次通过 WC_Breadcrumb->generate()
.
进一步遍历代码,您最终进入 add_crumbs_single()
函数,其中 woocommerce_breadcrumb_product_terms_args
过滤器应用于产品术语提取的排序。 (有关详细信息,请参阅 wp_get_post_terms
and WP_Term_Query::__construct()
。)
从代码中可以清楚地看出,他们优先考虑具有最高 parent
值的术语,换句话说,就是数据库中最新添加的父术语。
解决方案 1
由于此产品始终相同,您可能需要向主题的 function.php
添加过滤器(或使用自定义插件)以覆盖此行为。我用这个让我的工作:
function prioritize_current_url_cat_in_breadcrumbs( $args ) {
// Only if we're on a product page..
if ( is_product() ) {
// ..and there's a product category slug in the navigated url..
$product_cat_slug = get_query_var( 'product_cat', '' );
if ( ! empty($product_cat_slug) ) {
// ..which we can find
$product_cat = get_term_by( 'slug', $product_cat_slug, 'product_cat', ARRAY_A );
if ( ! empty($product_cat) ) {
// Then only get that current product category to start the breadcrumb trail
$args['term_taxonomy_id'] = $product_cat['term_taxonomy_id'];
}
}
}
return $args;
}
add_filter( 'woocommerce_breadcrumb_product_terms_args', 'prioritize_current_url_cat_in_breadcrumbs' );
add_crumbs_single()
函数的其余部分将负责遍历类别的父项等,直到 Home。
解决方案 2
或者,您可以使用 woocommerce_breadcrumb_main_term
过滤器来更改用于痕迹导航的 'main term'。过滤器函数接收 2 个参数:主要术语(如 WP_Term
)和包含找到的所有产品类别的 WP_Term
数组。它 returns 一个术语,因此您可以搜索整个数组并选择您想要开始面包屑的正确术语。
function prioritize_current_url_cat( $first_term, $all_terms ) {
if ( is_product() ) {
$product_cat_slug = get_query_var( 'product_cat', '' );
if ( ! empty($product_cat_slug) ) {
// Get the WP_Term with the current slug
$filtered_terms = array_values( array_filter($all_terms, function($v) use ($product_cat_slug) {
return $v->slug === $product_cat_slug;
}) );
if ( ! empty($filtered_terms) ) {
return $filtered_terms[0];
}
}
}
// Fallback to default
return $first_term;
}
add_filter( 'woocommerce_breadcrumb_main_term', 'prioritize_current_url_cat', 10, 2 );
希望这可以帮助任何搜索时间和研究源代码行的人..!
额外:修复存档页面上的 permalinks
为了解决产品存档页面上的相同问题,您可以挂钩 post_type_link
过滤器并预先将 permalink 的 %product_cat%
部分替换为正确的鼻涕虫像这样:
/**
* Set product link slugs to match the page context,
* for products with multiple associated categories.
*
* For example:
* when viewing Category A, use '/category-a/this-product/'
* when viewing Category B, use '/category-b/this-product/'
*/
function my_plugin_product_url_use_current_cat( $post_link, $post, $leavename, $sample ) {
// Get current term slug (used in page url)
$current_product_term_slug = get_query_var( 'product_cat', '' );
if ( empty ( $current_product_term_slug ) ) {
return $post_link;
}
if ( is_product_category() ) {
// Get current term object
$current_product_term = get_term_by( 'slug', $current_product_term_slug, 'product_cat' );
if ( FALSE === $current_product_term ) {
return $post_link;
}
// Get all terms associated with product
$all_product_terms = get_the_terms( $post->ID, 'product_cat' );
// Filter terms, taking only relevant terms for current term
$matching_or_descendant_terms = array_filter( array_map( function( $term ) use ( $current_product_term ) {
// Return term if it is the current term
if ( $term->term_id === $current_product_term->term_id ) {
return [ TRUE, $term ];
}
// Return term if one of its ancestors is the current term (highest hierarchy first)
$parent_terms = array_reverse( get_ancestors( $term->term_id, 'product_cat' ) );
foreach ( $parent_terms as $parent_term_id ) {
if ( $parent_term_id === $current_product_term->term_id ) {
return [ FALSE, $term ];
}
}
// Leave out all others
return NULL;
}, $all_product_terms ) );
if ( count( $matching_or_descendant_terms ) > 0 ) {
// Sort terms (directly associated first, descendants next)
usort( $matching_or_descendant_terms, function( $a, $b ) {
if ( $a[0] === $b[0] ) {
return 0;
} else if ( TRUE === $a[0] ) {
return -1;
} else {
return 1;
}
} );
// Get entire slug (including ancestors)
$slug = get_term_parents_list( $matching_or_descendant_terms[0][1]->term_id, 'product_cat', [
'format' => 'slug',
'separator' => '/',
'link' => false,
'inclusive' => true,
] );
// Set url slug to closest child term of current term
// or current term (if directly associated)
$post_link = str_replace('%product_cat%/', $slug, $post_link);
}
} else if ( is_product() ) {
$post_link = str_replace('%product_cat%', $current_product_term_slug, $post_link);
}
return $post_link;
}
add_filter( 'post_type_link', 'my_plugin_product_url_use_current_cat', 10, 4 );
解释
正如@Markos 提到的,我们试图找出最合乎逻辑和最直观的方法来做到这一点。
基本上,它会检查当前产品类别(来自 url 的别名)是否与显示的产品直接关联。如果是这样,请使用该 slug 作为产品页面的 link。 (使用上述解决方案之一,面包屑反映了直观导航的 url 路径。)
如果当前查看的产品类别与产品没有直接关联,它会查找第一个匹配的后代(子)类别,并将该 slug 用于 link 到产品页面。
这样,用户始终可以看到他们是如何进入当前产品页面的,并且可以轻松导航回该类别。
注意: 如果此代码段没有任何改变,请尝试更早的挂钩优先级 and/or 检查 $post_link
变量是否包含 %product_cat%
模板部分。
在与 Philip 进行了长时间的交谈并解决了商店具有多层类别的情况后(谢谢 Philip),我发现以下永久链接方法对我有用。
function my_plugin_product_url_use_current_cat( $post_link, $post, $leavename, $sample ) {
if ( is_product_category() || is_product() ) {
$product_cat_slug = get_query_var( 'product_cat', '' );
// get all the category of products
$terms = get_the_terms ( $post->ID, 'product_cat' );
foreach($terms as $term){
$term_slug = $term->slug;
// check if category page belongs to the category of the product
if($term_slug == $product_cat_slug){
$post_link = str_replace('%product_cat%', $term_slug, $post_link);
return $post_link;
}
// or category page is a parent category of the product
else{
$all_parents = get_ancestors($term->term_id, 'product_cat');
foreach($all_parents as $cat_id){
$cat_term = get_term( $cat_id, 'product_cat' );
if($cat_term->slug == $product_cat_slug){
$post_link = str_replace('%product_cat%', $term_slug, $post_link);
return $post_link;
}
}
}
}
}
return $post_link;
}
add_filter( 'post_type_link', 'my_plugin_product_url_use_current_cat', 9, 4 );
我的 Wordpress 网站使用 Woocommerce 来销售产品。但是,当您将多个产品类别分配给一个产品并访问产品页面(将 Product Permalinks
设置为 Shop base with category
)时,会发生意想不到的事情。 (父)类别的面包屑导航 link 始终相同,不反映导航 url。 (这与 here 描述的问题相同。)
问题总结:
Navigate to | Breadcrumb link | Expected breadcrumb link |
---|---|---|
/shop/category-1/sample-product/ |
category-2 | category-1 |
/shop/category-2/sample-product/ |
category-2 | category-2 |
我在任何解决这个问题的来源上都没有找到真正的答案。 (我认为它应该被修复,但有些人可能会说这是出于 SEO 原因,防止重复的网站内容。)所以为了帮助任何人寻找答案,这里是:
(wr)on(g) 发生了什么事?
产品页面上的面包屑由 woocommerce_breadcrumb()
function 生成。依次通过 WC_Breadcrumb->generate()
.
进一步遍历代码,您最终进入 add_crumbs_single()
函数,其中 woocommerce_breadcrumb_product_terms_args
过滤器应用于产品术语提取的排序。 (有关详细信息,请参阅 wp_get_post_terms
and WP_Term_Query::__construct()
。)
从代码中可以清楚地看出,他们优先考虑具有最高 parent
值的术语,换句话说,就是数据库中最新添加的父术语。
解决方案 1
由于此产品始终相同,您可能需要向主题的 function.php
添加过滤器(或使用自定义插件)以覆盖此行为。我用这个让我的工作:
function prioritize_current_url_cat_in_breadcrumbs( $args ) {
// Only if we're on a product page..
if ( is_product() ) {
// ..and there's a product category slug in the navigated url..
$product_cat_slug = get_query_var( 'product_cat', '' );
if ( ! empty($product_cat_slug) ) {
// ..which we can find
$product_cat = get_term_by( 'slug', $product_cat_slug, 'product_cat', ARRAY_A );
if ( ! empty($product_cat) ) {
// Then only get that current product category to start the breadcrumb trail
$args['term_taxonomy_id'] = $product_cat['term_taxonomy_id'];
}
}
}
return $args;
}
add_filter( 'woocommerce_breadcrumb_product_terms_args', 'prioritize_current_url_cat_in_breadcrumbs' );
add_crumbs_single()
函数的其余部分将负责遍历类别的父项等,直到 Home。
解决方案 2
或者,您可以使用 woocommerce_breadcrumb_main_term
过滤器来更改用于痕迹导航的 'main term'。过滤器函数接收 2 个参数:主要术语(如 WP_Term
)和包含找到的所有产品类别的 WP_Term
数组。它 returns 一个术语,因此您可以搜索整个数组并选择您想要开始面包屑的正确术语。
function prioritize_current_url_cat( $first_term, $all_terms ) {
if ( is_product() ) {
$product_cat_slug = get_query_var( 'product_cat', '' );
if ( ! empty($product_cat_slug) ) {
// Get the WP_Term with the current slug
$filtered_terms = array_values( array_filter($all_terms, function($v) use ($product_cat_slug) {
return $v->slug === $product_cat_slug;
}) );
if ( ! empty($filtered_terms) ) {
return $filtered_terms[0];
}
}
}
// Fallback to default
return $first_term;
}
add_filter( 'woocommerce_breadcrumb_main_term', 'prioritize_current_url_cat', 10, 2 );
希望这可以帮助任何搜索时间和研究源代码行的人..!
额外:修复存档页面上的 permalinks
为了解决产品存档页面上的相同问题,您可以挂钩 post_type_link
过滤器并预先将 permalink 的 %product_cat%
部分替换为正确的鼻涕虫像这样:
/**
* Set product link slugs to match the page context,
* for products with multiple associated categories.
*
* For example:
* when viewing Category A, use '/category-a/this-product/'
* when viewing Category B, use '/category-b/this-product/'
*/
function my_plugin_product_url_use_current_cat( $post_link, $post, $leavename, $sample ) {
// Get current term slug (used in page url)
$current_product_term_slug = get_query_var( 'product_cat', '' );
if ( empty ( $current_product_term_slug ) ) {
return $post_link;
}
if ( is_product_category() ) {
// Get current term object
$current_product_term = get_term_by( 'slug', $current_product_term_slug, 'product_cat' );
if ( FALSE === $current_product_term ) {
return $post_link;
}
// Get all terms associated with product
$all_product_terms = get_the_terms( $post->ID, 'product_cat' );
// Filter terms, taking only relevant terms for current term
$matching_or_descendant_terms = array_filter( array_map( function( $term ) use ( $current_product_term ) {
// Return term if it is the current term
if ( $term->term_id === $current_product_term->term_id ) {
return [ TRUE, $term ];
}
// Return term if one of its ancestors is the current term (highest hierarchy first)
$parent_terms = array_reverse( get_ancestors( $term->term_id, 'product_cat' ) );
foreach ( $parent_terms as $parent_term_id ) {
if ( $parent_term_id === $current_product_term->term_id ) {
return [ FALSE, $term ];
}
}
// Leave out all others
return NULL;
}, $all_product_terms ) );
if ( count( $matching_or_descendant_terms ) > 0 ) {
// Sort terms (directly associated first, descendants next)
usort( $matching_or_descendant_terms, function( $a, $b ) {
if ( $a[0] === $b[0] ) {
return 0;
} else if ( TRUE === $a[0] ) {
return -1;
} else {
return 1;
}
} );
// Get entire slug (including ancestors)
$slug = get_term_parents_list( $matching_or_descendant_terms[0][1]->term_id, 'product_cat', [
'format' => 'slug',
'separator' => '/',
'link' => false,
'inclusive' => true,
] );
// Set url slug to closest child term of current term
// or current term (if directly associated)
$post_link = str_replace('%product_cat%/', $slug, $post_link);
}
} else if ( is_product() ) {
$post_link = str_replace('%product_cat%', $current_product_term_slug, $post_link);
}
return $post_link;
}
add_filter( 'post_type_link', 'my_plugin_product_url_use_current_cat', 10, 4 );
解释
正如@Markos 提到的,我们试图找出最合乎逻辑和最直观的方法来做到这一点。
基本上,它会检查当前产品类别(来自 url 的别名)是否与显示的产品直接关联。如果是这样,请使用该 slug 作为产品页面的 link。 (使用上述解决方案之一,面包屑反映了直观导航的 url 路径。)
如果当前查看的产品类别与产品没有直接关联,它会查找第一个匹配的后代(子)类别,并将该 slug 用于 link 到产品页面。
这样,用户始终可以看到他们是如何进入当前产品页面的,并且可以轻松导航回该类别。
注意: 如果此代码段没有任何改变,请尝试更早的挂钩优先级 and/or 检查 $post_link
变量是否包含 %product_cat%
模板部分。
在与 Philip 进行了长时间的交谈并解决了商店具有多层类别的情况后(谢谢 Philip),我发现以下永久链接方法对我有用。
function my_plugin_product_url_use_current_cat( $post_link, $post, $leavename, $sample ) {
if ( is_product_category() || is_product() ) {
$product_cat_slug = get_query_var( 'product_cat', '' );
// get all the category of products
$terms = get_the_terms ( $post->ID, 'product_cat' );
foreach($terms as $term){
$term_slug = $term->slug;
// check if category page belongs to the category of the product
if($term_slug == $product_cat_slug){
$post_link = str_replace('%product_cat%', $term_slug, $post_link);
return $post_link;
}
// or category page is a parent category of the product
else{
$all_parents = get_ancestors($term->term_id, 'product_cat');
foreach($all_parents as $cat_id){
$cat_term = get_term( $cat_id, 'product_cat' );
if($cat_term->slug == $product_cat_slug){
$post_link = str_replace('%product_cat%', $term_slug, $post_link);
return $post_link;
}
}
}
}
}
return $post_link;
}
add_filter( 'post_type_link', 'my_plugin_product_url_use_current_cat', 9, 4 );