Wordpress - Apply the_title() filter in post & page title, but not in menu title
Problem Description:
Let me rephrase the question first. You want to:
Set new title to all
post
andpage
type from a meta field.You want this to happen everywhere (home page, single page, widgets etc.)
However, you don't want this title change to happen if the title is on the Navigation Menu.
Solution:
Before I give you the CODE, let me explain a few points first (based on your CODE):
How to change titles of all posts and pages:
You already know the use of the_title
filter. However, if you want to target all post
and page
type titles (but not custom post types), then your condition:
is_singular(array('post','page')) || is_archive() || is_home()
will not work. For example, it'll change custom post type on an archive page or the home page as well. This condition doesn't check if the title we are filtering is a page
or post
type. Instead, it checks if the page itself is either singular (post
or page
) or it's an archive (category, tag etc.) page or the home page. So custom post types in these pages also gets affected. Additionally, if there is a widget in a custom post type (singular) page, then by this logic, page
or post
titles in that widget will not be affected there.
To fix that, we need a different check, like:
$post = get_post( $id );
if ( $post instanceof WP_Post && ( $post->post_type == 'post' || $post->post_type == 'page' ) )
Why Navigation Menu title is also changed & how to stop it:
WordPress applies the_title
filter twice on the navigation menu items' title (if the menu items correspond to existing posts or pages).
First as the corresponding post or page title. This happens in the
wp_setup_nav_menu_item()
function ofwp-includes/nav-menu.php
file.Then as the Menu item title itself. This happens in the
Walker_Nav_Menu
class.
For your requirement, we need to stop the the_title
filter both the times.
Fortunately, WordPress has two filters: pre_wp_nav_menu
fires before filtering menu titles and wp_nav_menu_items
fires after filtering menu titles. So we can use these two filters to first remove the the_title
filter for nav menu item titles and then add the the_title
filter back again for other titles.
CODE
You may use the following CODE in the theme's functions.php
file or as a separate plugin:
function wpse309151_title_update( $title, $id = null ) {
if ( ! is_admin() && ! is_null( $id ) ) {
$post = get_post( $id );
if ( $post instanceof WP_Post && ( $post->post_type == 'post' || $post->post_type == 'page' ) ) {
$new_titile = get_post_meta( $id, 'pp_new_title', true );
if( ! empty( $new_titile ) ) {
return $new_titile;
}
}
}
return $title;
}
add_filter( 'the_title', 'wpse309151_title_update', 10, 2 );
function wpse309151_remove_title_filter_nav_menu( $nav_menu, $args ) {
// we are working with menu, so remove the title filter
remove_filter( 'the_title', 'wpse309151_title_update', 10, 2 );
return $nav_menu;
}
// this filter fires just before the nav menu item creation process
add_filter( 'pre_wp_nav_menu', 'wpse309151_remove_title_filter_nav_menu', 10, 2 );
function wpse309151_add_title_filter_non_menu( $items, $args ) {
// we are done working with menu, so add the title filter back
add_filter( 'the_title', 'wpse309151_title_update', 10, 2 );
return $items;
}
// this filter fires after nav menu item creation is done
add_filter( 'wp_nav_menu_items', 'wpse309151_add_title_filter_non_menu', 10, 2 );
WordPress navigation editor has ability to change menu title regardless of the title of the post/page.
If you need more automated solution code below will replace title of the post/page everywhere using the_title
filter but restore default title of the menu item using nav_menu_item_title
filter.
/**
* Replace post/page title on home, single and archive pages.
*
* @param string $title Post title
* @param int $post_id Post ID
*
* @return string New post tilte
*/
function wpse_309151_get_replace_default_title_from_meta( $title, $post_id ) {
$post_type = get_post_type( $post_id );
if( !is_admin() && ( $post_type === 'post' || $post_type === 'page' ) ) {
$new_title = get_post_meta( $post_id, 'wpse_309151_post_title', true);
if( $new_title && !empty( $new_title ) ) {
return $new_title;
}
}
return $title;
}
add_filter( 'the_title', 'wpse_309151_get_replace_default_title_from_meta', 10, 2 );
/**
* Restore default post/page title in navigation
*
* @param string $title The menu item's title.
* @param WP_Post $item The current menu item.
* @param stdClass $args An object of wp_nav_menu() arguments.
* @param int $depth Depth of menu item. Used for padding.
*
* @return string Restored post title
*/
function wpse_309151_get_restore_default_title_for_navigation( $title, $item, $args, $depth ) {
// Remove filter to not affect title
remove_filter( 'the_title', 'wpse_309151_get_replace_default_title_from_meta', 10, 2 );
$post_id = $item->object_id;
$title = get_the_title( $post_id );
// Add the title filter back
add_filter( 'the_title', 'wpse_309151_get_replace_default_title_from_meta', 10, 2 );
return $title;
}
add_filter( 'nav_menu_item_title', 'wpse_309151_get_restore_default_title_for_navigation', 10, 4 );