I have a custom walker for my navigation menu, inside which I want to get the menu item custom field value to further customize my menu items. Here is my code..
My menu item custom field is menu-item-mymenucolor
and I want to get its value in $menu_color (currently I get blank!)
wp_nav_menu( array(
'menu' => 'primary',
'walker' => new WPDocs_Walker_Nav_Menu()
)
);
// Custom Nav Menu walker class.
class WPDocs_Walker_Nav_Menu extends Walker_Nav_Menu {
function start_lvl( &$output, $depth = 0, $args = array() ) {
// Depth-dependent classes.
$indent = ( $depth > 0 ? str_repeat( "\t", $depth ) : '' ); // code indent
$display_depth = ( $depth + 1); // because it counts the first submenu as 0
// Custom nav menu item custom field value i want to get in the $classes.
$menu_color = '"style="background-color: '.get_post_meta($item->ID, 'menu-item-mymenucolor', true).';';
// Combine values of classes.
$classes = array(
'dropdown-menu',
( $display_depth % 2 ? 'menu-odd' : 'menu-even' ),
( $display_depth >=2 ? 'sub-sub-menu' : '' ),
'menu-depth-' . $display_depth,
''. $menu_color
);
$class_names = implode( ' ', $classes );
// Build HTML for output.
$output .= ($depth == 0) ? "\n" . $indent . '<ul class="' . $class_names . '">' . "\n" . "\n <div class=\"megamenu-content\">\n" . "\n<div class=\"row\">\n" : "\n<ul class=\"elementy-ul\">\n";
}
// Start the element output codes from here.....
}
Actually I can easily get the value of Woocommerce in the $menu_color place by just inserting the $woocommerce global variable, so is there anything to do with global variables? here take a look at this,
global $woocommerce;
// get cart quantity
$cart_qty = $woocommerce->cart->get_cart_contents_count();
$classes = array(
'dropdown-menu',
( $display_depth % 2 ? 'menu-odd' : 'menu-even' ),
( $display_depth >=2 ? 'sub-sub-menu' : '' ),
'menu-depth-' . $display_depth,
''. $cart_qty
);
Whereas, its much easier to get the menu item custom field value inside start element of this walker section, I will just post an important part of the code to make it short. I hope you understand where it must have been..
$item_output .= '<a'. $attributes .' style="background-color: '.get_post_meta($item->ID, 'menu-item-mymenucolor', true).';">';
$item_output .= '</a>';
I have tried almost every possibilities to get the value but none could work as of now. I know this will absolutely work like a charm, but I think i am missing something important here.
So, Is there any way this can be achieved?
Thanks!
You need to use $item->object_id instead of $item->ID and everything will work fine.
Maybe this is a late answer but since no one answered till now I thought it's a good idea to answer it, maybe it will help someone else too.
Related
i am using the following code to print a menu of a parent's child pages into a ul li menu. it works great. however i do not have ability to pipe in a classname just for the current page the user is on - same concept wordpress uses for the main menu code - i am curious what i would need to change in this code to pipe in a current menu item classname?
function nee_list_child_pages() {
global $post;
$string = '';
if ( is_page() && $post->post_parent )
$childpages = wp_list_pages( 'sort_column=menu_order&title_li=&child_of=' . $post->post_parent . '&echo=0' );
else
$childpages = wp_list_pages( 'sort_column=menu_order&title_li=&child_of=' . $post->ID . '&echo=0' );
if ( $childpages ) {
$string = '<div id="sidebar_menu"><ul>' . $childpages . '</ul></div>';
return $string;
}
}
add_shortcode('wpb_childpages', 'nee_list_child_pages');
You have to create a NavWalker for this.
Here a example implenentation:
https://gist.github.com/kosinix/5544535
and the office docs:
https://codex.wordpress.org/Class_Reference/Walker_Nav_Menu
i've read many Q/A's here during the last few days, but unfortunately none of them solved my issue.
I'm trying to fetch product attributes and display them on the frontend with a shortcode. I have managed to display ALL available attributes and display them in a list, but i need to select only one or two of them in different locations (thats why using shortcodes). For example like [shortcode_attribute name="brand"].
Any help is highly appreciated!
my code so far:
function tutsplus_list_attributes( $product ) {
global $product;
global $post;
$attributes = $product->get_attributes();
if ( ! $attributes ) {
return;
}
foreach ( $attributes as $attribute ) {
// Get the taxonomy.
$terms = wp_get_post_terms( $product->id, $attribute[ 'name' ], 'all' );
$taxonomy = $terms[ 0 ]->taxonomy;
// Get the taxonomy object.
$taxonomy_object = get_taxonomy( $taxonomy );
// Get the attribute label.
$attribute_label = $taxonomy_object->labels->name;
// Display the label followed by a clickable list of terms.
echo get_the_term_list( $post->ID, $attribute[ 'name' ] , '<div><li class="bullet-arrow">' . $attribute_label . ': ' , ', ', '</li></div>' );
}
}
add_action( 'woocommerce_product_meta_end', 'tutsplus_list_attributes' );
add_shortcode('display_attributes', 'tutsplus_list_attributes');
While I am still not 100% clear what you're after, what I'm going to create is a shortcode that creates a list of all terms in all attributes. To make it more flexible I'll add support for a shortcode parameter so you can create a list of specific attribute terms.
One major thing you need to alter from your code, is that shortcodes need to RETURN a string and not ECHO out html. Echoing out shortcodes can result in unexpected weirdness.
/**
* Attributes shortcode callback.
*/
function so_39394127_attributes_shortcode( $atts ) {
global $product;
if( ! is_object( $product ) || ! $product->has_attributes() ){
return;
}
// parse the shortcode attributes
$args = shortcode_atts( array(
'attributes' => array_keys( $product->get_attributes() ), // by default show all attributes
), $atts );
// is pass an attributes param, turn into array
if( is_string( $args['attributes'] ) ){
$args['attributes'] = array_map( 'trim', explode( '|' , $args['attributes'] ) );
}
// start with a null string because shortcodes need to return not echo a value
$html = '';
if( ! empty( $args['attributes'] ) ){
foreach ( $args['attributes'] as $attribute ) {
// get the WC-standard attribute taxonomy name
$taxonomy = strpos( $attribute, 'pa_' ) === false ? wc_attribute_taxonomy_name( $attribute ) : $attribute;
if( taxonomy_is_product_attribute( $taxonomy ) ){
// Get the attribute label.
$attribute_label = wc_attribute_label( $taxonomy );
// Build the html string with the label followed by a clickable list of terms.
// Updated for WC3.0 to use getters instead of directly accessing property.
$html .= get_the_term_list( $product->get_id(), $taxonomy, '<li class="bullet-arrow">' . $attribute_label . ': ' , ', ', '</li>' );
}
}
// if we have anything to display, wrap it in a <ul> for proper markup
// OR: delete these lines if you only wish to return the <li> elements
if( $html ){
$html = '<ul class="product-attributes">' . $html . '</ul>';
}
}
return $html;
}
add_shortcode( 'display_attributes', 'so_39394127_attributes_shortcode' );
Usage: This would be used in 1 of 2 ways.
First, to display specific attributes use:
[display_attributes attributes="color|material"]
Second, to display all attributes use:
[display_attributes]
Edit
Here's an always single display edition:
/**
* Attribute shortcode callback.
*/
function so_39394127_singular_attribute_shortcode( $atts ) {
global $product;
if( ! is_object( $product ) || ! $product->has_attributes() ){
return;
}
// parse the shortcode attributes
$args = shortcode_atts( array(
'attribute' => ''
), $atts );
// start with a null string because shortcodes need to return not echo a value
$html = '';
if( $args['attribute'] ){
// get the WC-standard attribute taxonomy name
$taxonomy = strpos( $args['attribute'], 'pa_' ) === false ? wc_attribute_taxonomy_name( $args['attribute'] ) : $args['attribute'];
if( taxonomy_is_product_attribute( $taxonomy ) ){
// Get the attribute label.
$attribute_label = wc_attribute_label( $taxonomy );
// Build the html string with the label followed by a clickable list of terms.
// Updated for WC3.0 to use getters instead of directly accessing property.
$html .= get_the_term_list( $product->get_id(), $taxonomy, $attribute_label . ': ' , ', ', '' );
}
}
return $html;
}
add_shortcode( 'display_attribute', 'so_39394127_singular_attribute_shortcode' );
This removes all HTML markup, and so you'd need to provide your own... which would happen if you are making a custom list.
<ul>
<li class="arrow">Static List Item</li>
<li class="arrow">[display_attribute attribute="color"]</li>
<ul>
EDIT: This code can be added to your theme's functions.php, but preferably either in a site-specific plugin or via the Code Snippets plugin.
If anyone else just wants to simply output the attribute value as text, replace the $html .= line with this:
$html .= strip_tags(get_the_term_list($product->get_id(), $taxonomy));
#helgatheviking There must be a more elegant way than this! :)
Needed this recently - found https://wordpress.org/plugins/wcpas-product-attributes-shortcode/ does the trick.
With this plugin you can set an "attribute" parameter to match one of the product attributes you have setup.
For example if you have a brand attribute you can use:
[wcpas_product_attributes attribute="brand"]
This will output a <ul> list containing all brands.
There are also a number of parameters you can use alongside the attribute including:
orderby (order the list by an orderby value)
order (order asc, desc, rand, etc)
hide_empty (hide empty terms)
show_counts (show the total number of products in the brand)
archive_links (use an archive based URL instead of a filter based URL)
min_price (When using archive_links = 0 you can have a price filter based URL off a minimum price)
max_price (When using archive_links = 0 you can have a price filter based URL off a maximum price)
I have a problem with no result page. I have a search box, if you find something then u can see "Search result : ", but if u didnt find then u can see "No Search result : ". I want to remove this NO before search result, but I dont find any solution for this. I tried already many different functions and css, but nothing .. I want to remove this NO, or whole this line (No search result : ).
This is what I have atm in functions.php
add_filter( 'tc_search_results_title' , 'my_search_results_title');
function my_search_results_title(){
$my_search_results_title = __('Search result :', 'customizr-child');
return $my_search_results_title;
}
add_filter('tc_breadcrumb_trail_items', 'search_results_breadcrumb');
function search_results_breadcrumb( $trail ){
if ( ! is_search() )
return $trail;
$_last = sizeof($trail) - 1;
$_search_string = __('Search result: ', 'customizr-child');
/* or you an use the function used for that other snippet to have the same title, in this case remove the comment of the line below */
// $_search_string = my_search_results_title();
if ( is_paged() )
$trail[$_last] = '' . sprintf( '%2$s "%1$s"' , esc_attr( get_search_query() ), $_search_string ) . '';
else
$trail[$_last] = sprintf( '%2$s "%1$s"', esc_attr( get_search_query() ), $_search_string );
return $trail;
}
add_filter( 'tc_no_result_content', 'my_no_result_content');
function my_no_result_content() {
return '<div class="tc-content span12"><h1>Didnt find anything, try again</h1></div>';
}
I use Customizr child theme in Wordpress
Maybe somebody can help me with this.
Thanks !
You're using the wrong filter. You'll have to use tc_search_results_header_content instead and replace the No string. You can also use to original part of code and remove the unwanted part, something like:
function my_search_results_header_content ($content) {
return sprintf( '<div class="row-fluid"><div class="%1$s"><h1 class="%2$s">%3$s%4$s %5$s </h1></div><div class="%6$s">%7$s</div></div>',
apply_filters( 'tc_search_result_header_title_class', 'span8' ),
apply_filters( 'tc_archive_icon', 'format-icon' ),
'', // Original uses: have_posts() ? '' : __( 'No' , 'customizr' ).' ' ,
apply_filters( 'tc_search_results_title' , __( 'Search Results for :' , 'customizr' ) ),
'<span>' . get_search_query() . '</span>',
apply_filters( 'tc_search_result_header_form_class', 'span4' ),
have_posts() ? get_search_form(false) : ''
);
}
add_filter('tc_search_results_header_content', 'my_search_results_header_content');
Hope it helps.
Entirely Revised Please Reread
Hello,
The theme I am using displays the page's title as opposed to it's menu label in the breadcrumbs. I am trying to get the breadcrumbs to instead display the associated menu label if it is available and if not then default to the page_title.
I have come up with some code that I think is close. Line 4/// $menu_items = wp_get_nav_menu_items( $slug ); returns null and it should return the nav item that contains $slug of the current post. Obviously, there is something I do not understand.
What I am attempting to do is get the slug of the current post, then using the slug get the nav item post. Then extract the title of the nav item and use that in place of the page title in the breadcrumbs. If the page was not in the nav system then it should default to the page title, as might be the case for a ppc campaign landing page.
if ( is_page() && !$post->post_parent ) {
$title = null;
$slug = mpactMEDIA_get_the_slug( get_the_ID() );
$menu_items = wp_get_nav_menu_items( $slug );
//var_dump((array)$menu_items);
foreach ( (array)$menu_items as $key => $menu_item ) {
$title = $menu_item->post_title;
}
if ( $title ) { echo $delimiter . ' ' . $before . $title . $after; }
else { echo $delimiter . ' ' . $before . get_the_title() . $after; }
}
I'm my functions.php file I have the following function
function mpactMEDIA_get_the_slug( $id=null ){
if( empty($id) ) global $post;
if( empty($post) ) return '';
$id = $post->ID;
endif;
$slug = basename( get_permalink($id) );
return $slug;
}
Thank you in advance,
Tim
I read the question a few times, I got here searching for an answer, ended up making my own.
function get_menu_label_by_post_id($post_id, $menu) {
$menu_title = '';
$nav = wp_get_nav_menu_items($menu);
foreach ( $nav as $item ) {
if ( $post_id == $item->object_id ) {
$menu_title = $item->post_title;
break;
}
}
return ($menu_title !== '') ? $menu_title : get_the_title($post_id);
}
Example usage:
echo get_menu_label_by_post_id($post->ID, 'Primary Nav');
This will return what the menu label is if it finds it, otherwise just the title of the post ID.
Check the documentation for wp_get_nav_menu_items. It doesn't take a page slug as a parameter at all.
If you want to list child pages of a given page, use wp_list_pages and pass a child_of parameter to it.
Also, as a side note, if you know the $post and want the slug, it's just $post->post_name
I have been searching for weeks and I still haven't found a proper solution to this problem.
I am writing a Wordpress Theme. I have a custom post type called Works. I would like to add my Works archive to my menu and have it as well as it's posts highlighted when I access them.
I can access my archive and posts on the following links
Works archive: /works/
Works single post: /works/postname/
My solution so fare have been to name my archive-works.php template file with a template name (Work archive). Then create an empty page using that template and adding the page to the menu. This highlights the archive in the menu but not the single posts.
I can easily solve this with a custom link and some javascript but there must be a better and cleaner way.
You can do a simple trick in your functions.php:
add_filter('nav_menu_css_class', 'current_type_nav_class', 10, 2);
function current_type_nav_class($classes, $item) {
// Get post_type for this post
$post_type = get_query_var('post_type');
// Go to Menus and add a menu class named: {custom-post-type}-menu-item
// This adds a 'current_page_parent' class to the parent menu item
if( in_array( $post_type.'-menu-item', $classes ) )
array_push($classes, 'current_page_parent');
return $classes;
}
In your case, you just have to add a class 'works-menu-item' with that archive menu item by the admin panel;
To add "custom posttype archive link" to menu, please look at the following guide
Open file functions.php , and enter code below
add_action('admin_head-nav-menus.php', 'wpclean_add_metabox_menu_posttype_archive');
function wpclean_add_metabox_menu_posttype_archive() {
add_meta_box('wpclean-metabox-nav-menu-posttype', __('Custom Post Type Archives'), 'wpclean_metabox_menu_posttype_archive', 'nav-menus', 'side', 'default');
}
function wpclean_metabox_menu_posttype_archive() {
$post_types = get_post_types(array('show_in_nav_menus' => true, 'has_archive' => true), 'object');
if ($post_types) :
$items = array();
$loop_index = 999999;
foreach ($post_types as $post_type) {
$item = new stdClass();
$loop_index++;
$item->object_id = $loop_index;
$item->db_id = 0;
$item->object = 'post_type_' . $post_type->query_var;
$item->menu_item_parent = 0;
$item->type = 'custom';
$item->title = $post_type->labels->name;
$item->url = get_post_type_archive_link($post_type->query_var);
$item->target = '';
$item->attr_title = '';
$item->classes = array();
$item->xfn = '';
$items[] = $item;
}
$walker = new Walker_Nav_Menu_Checklist(array());
echo '<div id="posttype-archive" class="posttypediv">';
echo '<div id="tabs-panel-posttype-archive" class="tabs-panel tabs-panel-active">';
echo '<ul id="posttype-archive-checklist" class="categorychecklist form-no-clear">';
echo walk_nav_menu_tree(array_map('wp_setup_nav_menu_item', $items), 0, (object) array('walker' => $walker));
echo '</ul>';
echo '</div>';
echo '</div>';
echo '<p class="button-controls">';
echo '<span class="add-to-menu">';
echo '<input type="submit"' . disabled(1, 0) . ' class="button-secondary submit-add-to-menu right" value="' . __('Add to Menu') . '" name="add-posttype-archive-menu-item" id="submit-posttype-archive" />';
echo '<span class="spinner"></span>';
echo '</span>';
echo '</p>';
endif;
}
Thanks
Thanks to rasmussvanejensen for her/his nice question and thethangtran for the answer, I am still confused why Wordpress has not yet added such a good feature to its code base by default.
By the way I think there is even a better solution than the one provided by thethangtran, as it may break on some situations.
Note 1
According to the Codex, using register_post_type, one can add extra post_types to the installation. There is chance, some one need to change the 'query_var' and thus the provided code will break.
Note 2
In addition, it may not handle the current-menu-item class, which will be used for css customization to show the menu item as active.
Note 3
As another note on the code, there is no need to define the loop_index, item and items variables. they are absolutely useless.
A better solution
So I suggest using this alternative, for those who want a more robust solution to on this:
function prefix_add_metabox_menu_posttype_archive(){
add_meta_box( 'prefix_metabox_menu_posttype_archive', __( 'Archives' ), 'prefix_metabox_menu_posttype_archive', 'nav-menus', 'side', 'default' );
}
add_action( 'admin_head-nav-menus.php', 'prefix_add_metabox_menu_posttype_archive' );
function prefix_metabox_menu_posttype_archive(){
$post_types = get_post_types( array( 'show_in_nav_menus' => true, 'has_archive' => true ), 'object' );
if( $post_types ){
foreach( $post_types as $post_type ){
$post_type->classes = array( $post_type->name );
$post_type->type = $post_type->name;
$post_type->object_id = $post_type->name;
$post_type->title = $post_type->labels->name;
$post_type->object = 'cpt_archive';
}
$walker = new Walker_Nav_Menu_Checklist( array() );?>
<div id="cpt-archive" class="posttypediv">
<div id="tabs-panel-cpt-archive" class="tabs-panel tabs-panel-active">
<ul id="ctp-archive-checklist" class="categorychecklist form-no-clear"><?php
echo walk_nav_menu_tree( array_map( 'wp_setup_nav_menu_item', $post_types ), 0, (object) array( 'walker' => $walker ) );?>
</ul>
</div>
</div>
<p class="button-controls">
<span class="add-to-menu">
<input type="submit"<?php disabled( $nav_menu_selected_id, 0 ); ?> class="button-secondary submit-add-to-menu" value="<?php esc_attr_e( 'Add to Menu' ); ?>" name="add-ctp-archive-menu-item" id="submit-cpt-archive" />
</span>
</p><?php
}
}
function prefix_cpt_archive_menu_filter( $items, $menu, $args ){
foreach( $items as &$item ){
if( $item->object != 'cpt_archive' ) continue;
$item->url = get_post_type_archive_link( $item->type );
if( get_query_var( 'post_type' ) == $item->type ){
$item->classes []= 'current-menu-item';
$item->current = true;
}
}
return $items;
}
add_filter( 'wp_get_nav_menu_items', 'prefix_cpt_archive_menu_filter', 10, 3 );
Navigate to Appearance > Menus;
Make sure you have the Works' custom post type selected at Screen Options;
Click on the name of your custom post type to expand it and then click on the ‘View all’ tab;
You will see an option for All Works. Check the box next to it and then click on the Add to Menu button;
Your custom post type archive will now appear as a menu item in the right column;
By default, the label will be "All Works". You can change this by writing something different at the Navigation Label;
Click on the Save Menu button to save your changes.
I found a solution on GitHub and it works out of the box by simply adding it to the functions.php without any walkers or extra classes.
This code solves it by comparing the slug of the current post type
with the navigation items, and adds a class accordingly.
Code on GitHub