Rename the 'Shop' Breadcrumb and change the link in Woocommerce - php

I tried using this code below to change the URL of the word "Shop" but it seems to do nothing:
add_filter( 'woocommerce_page_title', 'woo_shop_page_title');
function woo_shop_page_title( $page_title ) {
if( 'Shop' == $page_title) {
return "My new title";
}
}
I don't want users to go to the main "Shop" page and setup many categories.
Unfortunately I have not been able to find a way to remove or change the URL of that "Shop" breadcrumb. The official docs only mention how to change the "Home" URL, not "Shop".
You're help is appreciated.

To rename any breadcrumb item and change its link, use woocommerce_get_breadcrumb filter hook this way:
add_filter( 'woocommerce_get_breadcrumb', 'custom_get_breadcrumb', 20, 2 );
function custom_get_breadcrumb( $crumbs, $breadcrumb ){
if( ! is_shop() ) return $crumbs; // Only shop page
// The Crump item to target
$target = __( 'Shop', 'woocommerce' );
foreach($crumbs as $key => $crumb){
if( $target === $crumb[0] ){
// 1. Change name
$crumbs[$key][0] = __( 'Name', 'woocommerce' );
// 2. Change URL (you can also use get_permalink( $id ) with the post Id
$crumbs[$key][1] = home_url( '/my-link/' );
}
}
return $crumbs;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.

Please check this code in on home replace Start chenag in woocommerce
function woocommerce_breadcrumb( $args = array() ) {
$args = wp_parse_args( $args, apply_filters( 'woocommerce_breadcrumb_defaults', array(
'delimiter' => ' / ',
'wrap_before' => '',
'wrap_after' => '',
'before' => '',
'after' => '',
'home' => _x( 'Home', 'breadcrumb', 'woocommerce' ),
) ) );
$breadcrumbs = new WC_Breadcrumb();
$args['home'] = "Start"; // home replace start set
if ( ! empty( $args['home'] ) ) {
$breadcrumbs->add_crumb( $args['home'], apply_filters( 'woocommerce_breadcrumb_home_url', home_url() ) );
}
$args['breadcrumb'] = $breadcrumbs->generate();

The 'shop' breadcrumb is coming from you 'Shop' pages'. Simply change your shop page slug to whatever you want, or if it is another page altogether then in the woocommerce settings you set the shop page to your new page. The url will always be the page's slug.

Related

Change removing cart item url to checkout to avoid wrong checkout

My simple question is that how I can make this code working
esc_url( WC()->cart->get_cart_url->get_remove_url( $cart_item_key ) ),
the above i have tried and the current code is below
esc_url( $woocommerce->cart->get_remove_url( $cart_item_key ) ),
so instead my current url : example.com/?removed_item=1
should be something like example.com/checkout/?removed_item=1
Thanks for any suggestion
Try the below code. code will goes in your active theme functions.php file.
add_filter( 'woocommerce_get_remove_url', 'change_item_remove_url_to_checkout', 10, 1 );
function change_item_remove_url_to_checkout( $remove_url ) {
$cart_page_url = wc_get_page_permalink( 'cart' );
$replacement_url = wc_get_page_permalink( 'checkout' ); // Shop page
$remove_url = str_replace( $cart_page_url, $replacement_url, $remove_url );
return $remove_url;
}

How to change WooCommerce My Account page titles?

I want to change the page titles (entry titles) for each page in the WooCommerce My Account section so that they relate to the actual page you are on rather than outputting a generic "My Account" on each page.
I have looked around and seen this solution in several places:
function wpb_woo_endpoint_title( $title, $id ) {
if ( is_wc_endpoint_url( 'downloads' ) && in_the_loop() ) { // add your endpoint urls
$title = "Download MP3s"; // change your entry-title
}
elseif ( is_wc_endpoint_url( 'orders' ) && in_the_loop() ) {
$title = "My Orders";
}
elseif ( is_wc_endpoint_url( 'edit-account' ) && in_the_loop() ) {
$title = "Change My Details";
}
return $title;
}
add_filter( 'the_title', 'wpb_woo_endpoint_title', 10, 2 );
This does not work unless you remove the in_the_loop check, which obviously isn't ideal as then it ends up changing other things on the page too.
Then I found this answer, as an example on how to change the title for the "Account Details" page:
add_filter( 'woocommerce_endpoint_edit-account_title', 'change_my_account_edit_account_title', 10, 2 );
function change_my_account_edit_account_title( $title, $endpoint ) {
$title = __( "Edit your account details", "woocommerce" );
return $title;
}
But this didn't work, it didn't even seem to go into the function at all.
Is there a way to do this that actually works?
Changing main my account page title and My account page title sub sections:
1) For "My Account: dashbord (main page):
To change the main "My account" page title, you just need to change the title of the page in backend directly because it's not an endpoint, and to check in Settings > Advanced section that the page (with the renamed title) is still assigned to "My account page".
2) For the other "endpoints" page titles in My account section:
Use woocommerce_endpoint_{$endpoint}_title composite dedicated filter hook, where {$endpoint} need to be replaced by the targeted endpoint (see available endpoints on Settings > Advanced section)
Example: To change My account "orders" endpoint page title you will use:
add_filter( 'woocommerce_endpoint_orders_title', 'change_my_account_orders_title', 10, 2 );
function change_my_account_orders_title( $title, $endpoint ) {
$title = __( "Your orders", "woocommerce" );
return $title;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
If it doesn't work, it's because something else is making trouble, like your own customizations, your theme's customizations, a plugin or something else…
Related StackOverFlow answers:
Changing the titles on My Account pages in Woocommerce
Rename My account tabbed menu items in Woocommerce
Reorder menu items in Woocommerce My Account section
Display custom content for a custom account menu item in Woocommerce 3
You can change you MyAccount item titles this way:
/**
* Rename WooCommerce MyAccount menu items
*/
add_filter( 'woocommerce_account_menu_items', 'rename_menu_items' );
function rename_menu_items( $items ) {
$items['downloads'] = 'Download MP3s';
$items['orders'] = 'My Orders';
$items['edit-account'] = 'Change My Details';
return $items;
}
To change the title on each account page as well, you need to add this too:
/**
* Change page titles
*/
add_filter( 'the_title', 'custom_account_endpoint_titles' );
function custom_account_endpoint_titles( $title ) {
global $wp_query;
if ( isset( $wp_query->query_vars['downloads'] ) && in_the_loop() ) {
return 'Download MP3s';
}
if ( isset( $wp_query->query_vars['orders'] ) && in_the_loop() ) {
return 'My Orders';
}
if ( isset( $wp_query->query_vars['edit-account'] ) && in_the_loop() ) {
return 'Change My Details';
}
return $title;
}
If you're using Yoast SEO, you need to add another function to set the correct page titles in the browser tab. If you also need this, I'll expand my answer.
Put this into you functions.php file. Tested and works.
I notice I had some issues implementing the is_wc_endpoint_url() && in_the_loop solution, it did not work out with the && in_the_loop(), removing is not a viable option since it created even more conflicts.
So instead I chose to work on the template file directly, I am sharing this because I noticed that some people had the same issues and this solution might help others achieve the similar results using a different approach.
I basically replaced the header in page-my-account.php with this code:
<header>
<?php
$main_title = get_the_title();
$endpoint = WC()->query->get_current_endpoint();
$endpoint_title = __( WC()->query->get_endpoint_title( $endpoint ), 'text-domain' );
if ( $endpoint_title ){
//If the endpoint exists use itself as the new custom title
$custom_title = $endpoint_title;
}
else{
// Here we can define the custom title for each endpoint we can use home_url() or strpos + add_query_arg()
global $wp;
if( basename( home_url($wp->request) ) == 'points-and-rewards' || ( strpos( (add_query_arg( $wp->query_vars)) , 'points-and-rewards' ) !== false ) ){
$custom_title = __( 'Points and rewards', 'text-domain' );
}
elseif( basename(home_url($wp->request)) == 'payment-methods' ){
$custom_title = __( 'Payment methods', 'text-domain' );
}
elseif( basename(home_url($wp->request)) == 'my-account' ){
$custom_title = __( 'Dashboard', 'text-domain' );
}
//Add more custom titles here
}
if ( !empty( $custom_title ) ){
//If there is a custom title
$new_endpoint_title = sprintf( '<h2>%s > %s</h2>', $main_title, $custom_title );
}
else{
//If there is no custom title default to get_title()
$new_endpoint_title = sprintf( '<h2>%s</h2>' , $main_title );
}
?>
<?php //Echo's the resulting title ?>
<?php echo $new_endpoint_title; ?>
</header>

Displays Woocommerce My Account Orders instead of Dashboard

I am trying to make it so that the url.com/my-account or the shortcode [woocommerce_my_account] displays the orders instead of the dashboard that displays "Hello User (not user)?".
The only thing I have is for after logging in which redirects to the orders instead of the dashboard, but I then going to the /my-account still displays the dashboard which I don't want.
The closest code I found that does what I want is...
function woocommerce_orders() {
$user_id = get_current_user_id();
if ($user_id == 0) {
return do_shortcode('[woocommerce_my_account]');
}else{
ob_start();
wc_get_template( 'myaccount/my-orders.php', array(
'current_user' => get_user_by( 'id', $user_id),
'order_count' => $order_count
) );
return ob_get_clean();
}
}
add_shortcode('woocommerce_orders', 'woocommerce_orders');
However, if there are no orders placed then it comes out blank(doesn't display the "No order has been made yet." with shop button) and the my account nav-sidebar doesn't show up. Would I have to make a custom page-template for this to add in the woocommerce account nav-sidebar?
Edit: If I use the orders.php instead of my-orders.php then I am able to get the "No order has been made yet." But still no sidebar-nav
You could try the following code (that is not perfect as it removes the access to the dashboard):
add_action( 'woocommerce_account_content', 'remove_dashboard_account_default', 5 );
function remove_dashboard_account_default() {
remove_action( 'woocommerce_account_content', 'woocommerce_account_content', 10 );
add_action( 'woocommerce_account_content', 'custom_account_orders', 10 );
}
function custom_account_orders( $current_page ) {
global $wp;
if ( ! empty( $wp->query_vars ) ) {
foreach ( $wp->query_vars as $key => $value ) {
// Ignore pagename param.
if ( 'pagename' === $key ) {
continue;
}
if ( has_action( 'woocommerce_account_' . $key . '_endpoint' ) ) {
do_action( 'woocommerce_account_' . $key . '_endpoint', $value );
return;
}
}
}
$current_page = empty( $current_page ) ? 1 : absint( $current_page );
$customer_orders = wc_get_orders( apply_filters( 'woocommerce_my_account_my_orders_query', array(
'customer' => get_current_user_id(),
'page' => $current_page,
'paginate' => true,
) ) );
wc_get_template(
'myaccount/orders.php',
array(
'current_page' => absint( $current_page ),
'customer_orders' => $customer_orders,
'has_orders' => 0 < $customer_orders->total,
)
);
}
Code goes in function.php file of your active child theme (or active theme). Tested and work.
There's a much easier way: simply catch WordPress's parse_request, check if the request is for my-account (or whatever your account page slug is) and perform a redirect:
function vnm_wc_redirect_account_dashboard( $wp ) {
if ( !is_admin() ) {
// Uncomment the following line if you want to see what the current request is
//die( $wp->request );
// The following will only match if it's the root Account page; all other endpoints will be left alone
if ( $wp->request === 'my-account' ) {
wp_redirect( site_url( '/my-account/orders/' ) );
exit;
}
}
}
add_action( 'parse_request', 'vnm_wc_redirect_account_dashboard', 10, 1 );
I used code of LoicTheAztec and another complementary snippet, which removes dashboard tab:
// Remove or rename my account page navigation links (removes downloads and dashboard).
add_filter ( 'woocommerce_account_menu_items', 'my_account_menu_order' );
function my_account_menu_order() {
$menuOrder = array(
'orders' => __( 'Orders', 'woocommerce' ),
// 'downloads' => __( 'Download', 'woocommerce' ),
'edit-address' => __( 'Addresses', 'woocommerce' ),
'edit-account' => __( 'Account details', 'woocommerce' ),
'customer-logout' => __( 'Logout', 'woocommerce' ),
// 'dashboard' => __( 'Dashboard', 'woocommerce' )
);
return $menuOrder;
}
I also created a snippet which causes orders tab to be highlighted by default. It's done through adding active class and then CSS opacity: 1 highlights it. Script will show only in account section to avoid bloat where it isn't needed:
// Make orders link highlighted by default in my account section.
add_action('wp_footer', 'taisho_dashboard_orders_highlight');
function taisho_dashboard_orders_highlight() {
if (!is_account_page()) return; // Account section only
global $wp;
$acc_url = get_permalink( get_option( 'woocommerce_myaccount_page_id' ));
$my_acc = rtrim( $acc_url , '/' );
$my_acc = explode( '/', $my_acc );
?>
<script type="text/javascript">
var dashboard_active = <?php echo $wp->request === end($my_acc) ?>;
jQuery(document).ready(function($) {
$('.woocommerce-MyAccount-navigation-link--orders').toggleClass('is-active', dashboard_active);
});
</script>
<?php
}
Using parse_request action:
add_action( 'parse_request', function ( $wp ) {
// Prevent the redirection, in the case,
// the user is not logged in (no login, no orders)
if (!is_user_logged_in()) return false;
if ( $wp->request === 'my-account' ) {
wp_redirect( home_url( '/my-account/orders/' ) );
exit;
}
}, 10, 1 );
As of today there's only one workaround to set another My Account tab as default and also preserve the "Dashboard" tab.
Let's say we want to default to the "Downloads" tab. We need to:
replace the “Dashboard” tab content with the “Downloads” tab content
rename the “Dashboard” tab to “Downloads”
hide the original “Downloads” tab as we already have it now
readd the “Dashboard” tab
This is a little complex to paste here so I'll cover point 1 only which is the most difficult one. The rest can be easily found on StackOverflow or this Business Bloomer tutorial as it's basic rename/remove/add My Account tabs work.
As per point 1, we need to overwrite the woocommerce_account_content() function which is responsible to display the "Dashboard" content by default in case we're on the My Account and there are no query vars or there is a non-empty query var called "pagename".
I will therefore remove the WooCommerce function, and add my version instead:
add_action( 'woocommerce_account_content', 'bbloomer_myaccount_replace_dashboard_content', 1 );
function bbloomer_myaccount_replace_dashboard_content() {
remove_action( 'woocommerce_account_content', 'woocommerce_account_content', 10 );
add_action( 'woocommerce_account_content', 'bbloomer_account_content' );
}
Now I go define my custom bbloomer_account_content() function, and I basically tell the system that:
if there is no query var or if there is and a query var called "pagename" is not empty, I echo my custom content (the "Downloads" tab content in my case"
otherwise, it means I am on an endpoint URL, and therefore I copy whatever was inside woocommerce_account_content() to return the tab content
Code:
function bbloomer_account_content() {
global $wp;
if ( empty( $query_vars = $wp->query_vars ) || ( ! empty( $query_vars ) && ! empty( $query_vars['pagename'] ) ) ) {
woocommerce_account_downloads();
} else {
foreach ( $wp->query_vars as $key => $value ) {
if ( 'pagename' === $key ) {
continue;
}
if ( has_action( 'woocommerce_account_' . $key . '_endpoint' ) ) {
do_action( 'woocommerce_account_' . $key . '_endpoint', $value );
return;
}
}
}
}
In this way, if I go to "example.com/my-account" I will see the "Downloads" tab content; if I switch tab and go to "Dashboard" or "Orders", I will see their original content instead.
Remember that this is only part 1 and therefore additional workaround is needed with parts 2, 3 and 4 to really make it work.

Wordpress custom post type rewrite rule matches all pages

I've created a custom post type with a rewrite to use the grandparent relationship as the URL like so:
function cpt_child(){
$args = array(
//code
'rewrite' => array( 'slug' => '%grandparent%/%parent%', 'with_front' => false),
);
register_post_type( 'child', $args );
}
add_action( 'init', 'cpt_child' );
Then I update the permalink:
add_filter( 'post_type_link', 'filter_the_post_type_link', 1, 2 );
function filter_the_post_type_link( $post_link, $post ) {
switch( $post->post_type ) {
case 'child':
$post_link = get_bloginfo( 'url' );
$relationship_child = p2p_type('children_to_parents')->get_adjacent_items($post->ID);
$parent = $relationship['parent']->post_name;
$relationship_parent = p2p_type('parents_to_grandparents')->get_adjacent_items($parent['parent']->ID);
$grandparent = $relationship_parent['parent']->post_name;
$post_link .= "/{$grandparent}/";
$post_link .= "{$parent}/";
$post_link .= "{$post->post_name}";
break;
}
return $post_link;
}
This all works great, but unfortunately the rewrite rule matches regular pages as well which makes them 404.
I can prevent this by adding a custom slug, for example 'relationship': http://example.com/relationship/grandparent/parent/child
But I'd really like to use http://example.com/grandparent/parent/child and have it not break regular pages.
I might have to resort to using non native Wordpress rewrites using htaccess.
Thanks in advance!
I managed to find a solution thanks to Milo's previous answers on other related questions. I found this particular post Remove base slug in CPT & CT, use CT in permalink to be extremely helpful.
My initial CPT with grandparent relationship rewrite remains the same:
function cpt_child(){
$args = array(
//code
'rewrite' => array( 'slug' => '%grandparent%/%parent%', 'with_front' => false),
);
register_post_type( 'child', $args );
}
add_action( 'init', 'cpt_child' );
Then I update the permalink:
add_filter( 'post_type_link', 'filter_the_post_type_link', 1, 2 );
function filter_the_post_type_link( $post_link, $post ) {
switch( $post->post_type ) {
case 'child':
$post_link = get_bloginfo( 'url' );
$relationship_child = p2p_type('children_to_parents')->get_adjacent_items($post->ID);
$parent = $relationship['parent']->post_name;
$relationship_parent = p2p_type('parents_to_grandparents')->get_adjacent_items($parent['parent']->ID);
$grandparent = $relationship_parent['parent']->post_name;
$post_link .= "/{$grandparent}/";
$post_link .= "{$parent}/";
$post_link .= "{$post->post_name}";
break;
}
return $post_link;
}
Hooked into request and changed query based on request vars that match my needs. Added is_admin() check to prevent request filter from changing the CPT's back end.
// URL rewrite pages 404 fix
if ( ! is_admin() ) {
function svbr_fix_requests( $request ){
// if it's not a section request and request is not empty treat request as page or post
if( ( ! array_key_exists( 'section' , $request ) ) && ( ! empty($request) ) ){
$request['post_type'] = array( 'post', 'page' );
}
// return request vars
return $request;
}
add_filter( 'request', 'svbr_fix_requests' );
}
Because we changed the query vars we have to add template functionality.
// Use single_template filter to properly redirect to page.php and custom page templates
function svbr_get_template_file($single_template) {
global $post;
$page_custom_template = get_post_meta( $post->ID, '_wp_page_template', true );
if ($post->post_type == 'page') {
if($page_custom_template != 'default') {
// We are using a child theme, so get_stylesheet_directory()
$single_template = get_stylesheet_directory() . '/' . $page_custom_template;
}
else {
$single_template = get_template_directory() . '/page.php';
}
}
return $single_template;
}
add_filter( 'single_template', 'svbr_get_template_file' );
Last but not least we have to add the template class to the body for styling purposes.
// Add template class to body
add_filter( 'body_class', 'template_class_name' );
function template_class_name( $classes ) {
global $post;
$page_custom_template = get_post_meta( $post->ID, '_wp_page_template', true );
$page_custom_template = str_replace('.php','',$page_custom_template);
$page_custom_template = 'page-template-' . $page_custom_template;
// add 'class-name' to the $classes array
$classes[] = $page_custom_template;
// return the $classes array
return $classes;
}

Wordpress pagination not working in home page with Post name Permalink

EDIT : Even after making the Permalink Settings to Post name, http://domain.com/?paged=3 still works.
I have posts collection in my home page which has pagination. Let's say I have
domain.com
If my Permalink settings is in default which is Plain Format like
http://domain.com/?p=123
Then my pagination http://domain.com/?paged=3 will work.
How ever if I want my permalink settings to be in Post Name Format like
http://domain.com/sample-post/
Then my pagination in my home page will not work anymore. I tried inspect element in the link of pagination if set in post name permalink and it's
http://domain.com/page/23/
Then what happened is it will not go to page 23. It will always redirect to my home page
http://domain.com
I already tried this
https://wordpress.stackexchange.com/questions/134339/pagination-on-custom-post-type-not-working-if-permalinks-set-to-rewrite-url
Which I put this code in my functions.php
add_filter( 'redirect_canonical','custom_disable_redirect_canonical' );
function custom_disable_redirect_canonical( $redirect_url ){
if ( is_singular('your_custom_post_type') ) $redirect_url = false;
return $redirect_url;
}
And it didn't work.
Update I checked the functions.php and i see this
/* -----------------------------------------------------------------------------
Woo commerce
*/
if (in_array( 'woocommerce/woocommerce.php', apply_filters( 'active_plugins', get_option( 'active_plugins' )))) { // check if we have woo commerce installed
// breadcrumb
add_filter( 'woocommerce_breadcrumb_defaults', 'td_woocommerce_breadcrumbs' );
function td_woocommerce_breadcrumbs() {
return array(
'delimiter' => ' <span class="td-sp td-sp-breadcrumb-arrow td-bread-sep"></span> ',
'wrap_before' => '<div class="entry-crumbs" itemprop="breadcrumb">',
'wrap_after' => '</div>',
'before' => '',
'after' => '',
'home' => _x( 'Home', 'breadcrumb', 'woocommerce' ),
);
}
// number of products to display on shop page
add_filter('loop_shop_per_page', create_function('$cols', 'return 8;'));
if (!function_exists('woocommerce_pagination')) {
// pagination
function woocommerce_pagination(){
echo td_page_generator::get_pagination();
}
}
if (!function_exists('woocommerce_output_related_products')) {
// number of related product
function woocommerce_output_related_products() {
woocommerce_related_products(4,4); // Display 4 products in rows of 4
}
}
}
/**
* Add prev and next links to a numbered link list - the pagination on single.
*/
function wp_link_pages_args_prevnext_add($args)
{
global $page, $numpages, $more, $pagenow;
if (!$args['next_or_number'] == 'next_and_number')
return $args; # exit early
$args['next_or_number'] = 'number'; # keep numbering for the main part
if (!$more)
return $args; # exit early
if($page-1) # there is a previous page
$args['before'] .= _wp_link_page($page-1)
. $args['link_before']. $args['previouspagelink'] . $args['link_after'] . '</a>'
;
if ($page<$numpages) # there is a next page
$args['after'] = _wp_link_page($page+1)
. $args['link_before'] . $args['nextpagelink'] . $args['link_after'] . '</a>'
. $args['after']
;
return $args;
}
add_filter('wp_link_pages_args', 'wp_link_pages_args_prevnext_add');
add_theme_support('woocommerce');
It doesn't have woo commerce plugin but It has that code. Hope it helps you find out whats going on?
Is is not possible to have a page name and a custom post type with the same name. If you do, the page url-slug of the page name and custom post type slug are the same and will cause a not found error.
If you are using WP_Query for retrieving posts then change your code to below
$paged = ( $_REQUEST['paged'] ) ? absint( $_REQUEST['paged'] ) : 1;
$args = array(
'posts_per_page' => 5,
'post_type' => 'post',
'paged' => $paged,
);
$the_query = new WP_Query( $args );
while ( $the_query->have_posts() ) : $the_query->the_post();
the_title();
echo '<br>';
endwhile;
$big = 999999999;
echo paginate_links( array(
'format' => '?paged=%#%',
'current' => max( 1, $paged ),
'total' => $the_query->max_num_pages
) );
Beside other mentioned scenarios, Make sure you do not have the slug of the template you are using the loop on similar to any of the post types exist in the theme. If a page slug is similar to the post type slug, the pagination will not work properly.

Categories