1

How can I get the post date and/or the last modified date of the target page of a menu link? I want to include this value in a data attribute.

  • For links to posts and pages, it should be the last modified date of the post/page.
  • For links to post archives, it should be the date of the most recent post in that archive.
  • For links to taxonomy terms, it should be the date of the most recent post using that term.
  • For custom links, it doesn't need to have a date.

I tried using $atts['data-modified'] = $item->post_modified; in a custom menu walker, but that just got the last modified date for the menu item itself.

1

The nav_menu_link_attributes filter can be used to accomplish this, so a walker is not necessary. Here's an example with comments along the way to explain what's happening.

add_filter( 'nav_menu_link_attributes', 'wpse_nav_menu_link_attributes', 10, 4 );
/**
 * Filters the HTML attributes applied to a menu item's anchor element.
 *
 * Adds data-modified attribute to links.
 * - For single post types, the data-modified attribute's value stores the
 *   modified date for that post.
 *
 * - For post type archives, the data-modified attribute's value stores the
 *   modified date for the most recently published post in the archive.
 *
 * - For taxonomies, the data-modified attribute's value stores the
 *   modified date for the most recently published post assigned to the taxonomy term.
 *
 * ----
 *
 * Docs below via WordPress core.
 *
 * @param array $atts {
 *     The HTML attributes applied to the menu item's `<a>` element, empty strings are ignored.
 *
 *     @type string $title  Title attribute.
 *     @type string $target Target attribute.
 *     @type string $rel    The rel attribute.
 *     @type string $href   The href attribute.
 * }
 * @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.
 */
function wpse_nav_menu_link_attributes( $atts, $item, $args, $depth ) {

    // Get all post meta for this menu item. Post, Page, and other info is
    // stored in the menu item post's meta data.
    $item_meta = get_post_meta( $item->ID );

    // Modify date format to suit your preferneces.
    // http://php.net/manual/en/function.date.php
    $date_format = 'l F j, Y';

    // Bail if there is no meta.
    if ( ! $item_meta ) {
        return $atts;
    }

    $menu_item_type = false;
    // Bail if _menu_item_type is unavailable.
    if ( ! isset( $item_meta['_menu_item_type'][0] ) ) {
        return $atts;
    } else {
        $menu_item_type = $item_meta['_menu_item_type'][0]; // For readability.
    }

    // Handle post types and post type archives accordingly.
    switch ( $menu_item_type ) {

        /**
         * Handle single post type menu item type. E.g.: posts/pages/etc.
         */
        case 'post_type' :

            // Bail if _menu_item_object_id is unavailable.
            if ( ! isset( $item_meta['_menu_item_object_id'][0] ) ) {               
                return $atts;
            } else {
                $menu_item_object_id = $item_meta['_menu_item_object_id'][0]; // For readability.
            }

            // Handle special case when this is the page for posts.
            $show_on_front  = get_option( 'show_on_front' ); // 'page' for static front page, 'posts' for latest posts.
            $page_for_posts = get_option( 'page_for_posts' ); 
            if ( 'page' === $show_on_front && $page_for_posts === $menu_item_object_id ) {
                // Get the most recent post of the post post type. ...Buffalo buffalo Buffalo buffalo buffalo buffalo Buffalo buffalo.
                $recent_post = get_posts( [
                    'posts_per_page' => 1,
                    'post_type'      => 'post',
                    'orderby'        => 'date',
                    'order'          => 'DESC',                 
                    'post_status'    => 'publish',
                ] );

                // Add the data-modified attribute and set the date via the most recent post.
                if ( is_array( $recent_post ) ) {
                    $atts['data-modified'] = esc_attr( get_the_modified_date(
                            $date_format,
                            $recent_post[0]
                    ) );
                }

            // Handle typical case for single post/page/custom post type.
            } else { 
                // Add the data-modified attribute and set the value to post's modified date.
                $atts['data-modified'] = esc_attr( get_the_modified_date(
                        $date_format, 
                        get_post( $menu_item_object_id )
                ) );
            }
        break;

        /**
         *  Handle post type archive menu item type.
         */
        case 'post_type_archive' :

            // Bail if _menu_item_object is unavailable.
            if ( ! isset( $item_meta['_menu_item_object'][0] ) ) {              
                return $atts;
            } else {
                $menu_item_object = $item_meta['_menu_item_object'][0]; // For readability.
            }

            // Get the most recent post of this archive's type.
            $recent_post = get_posts( [
                'posts_per_page' => 1,
                'post_type'      => $menu_item_object,
                'orderby'        => 'date',
                'order'          => 'DESC',                 
                'post_status'    => 'publish',
            ] );

            // Add the data-modified attribute and set the date via the most recent post.
            if ( is_array( $recent_post ) ) {
                $atts['data-modified'] = esc_attr( get_the_modified_date(
                        $date_format,
                        $recent_post[0]
                ) );
            }
        break;

        /**
         *  Handle taxonomy menu item type.
         */
        case 'taxonomy' :

            // Bail if _menu_item_object is unavailable.
            if ( ! isset( $item_meta['_menu_item_object'][0] ) ) {              
                return $atts;
            } else {
                $menu_item_object = $item_meta['_menu_item_object'][0];
            }

            // Bail if _menu_item_object_id is unavailable.
            if ( ! isset( $item_meta['_menu_item_object_id'][0] ) ) {               
                return $atts;
            } else {
                $menu_item_object_id = $item_meta['_menu_item_object_id'][0];
            }

            // Get the most recent post using this taxonomy and term.
            $recent_post = get_posts( [
                'posts_per_page' => 1,
                'post_type'      => 'any',
                'orderby'        => 'date',
                'order'          => 'DESC',
                'post_status'    => 'publish',
                'tax_query' => array (
                    [
                        'taxonomy' => $menu_item_object,
                        'terms'    => $menu_item_object_id,
                        'field'    => 'id',
                    ],
                )       
            ] );            

            // Add the data-modified attribute and set the date via the most recent post using this taxonomy and term.
            if ( is_array( $recent_post ) ) {
                $atts['data-modified'] = esc_attr( get_the_modified_date(
                        $date_format,
                        $recent_post[0]
                ) );
            }
        break;

    } // end switch

    return $atts;
}

Note: Each link in a menu is stored as a custom post type named nav_menu_item. The additional information (such as the post ID that the menu item post points to) is stored in post meta. The post_id field of the meta will point back to the nav_menu_item's post id.

| improve this answer | |
  • This is close to what I need, but some menu items get no/the wrong date. Is there a way to differentiate the Posts Page from normal pages? What I thought were archives have a _menu_item_type of taxonomy, is the procedure to get the date the same? I stuck these modifications in a custom menu walker because I am already using it to modify the HTML for this menu – Malik Drako May 24 '17 at 20:25
  • Yes, it's possible to get the page for posts. $page_for_posts = get_option( 'page_for_posts' ); This code will only affect menu items of type post_type and post_type_archive by design. Additional menu item types could be added to the switch statement (e.g. taxonomy (I didn't tackle those because they weren't part of the original question). For the page for posts, additional logic might be necessary. I'll check that out. FWIW, I'd stick to using a filter for this particular functionality, even if you have a walker. The filter is made for customizing attributes. – Dave Romsey May 24 '17 at 20:31
  • Whew! Okay, I've updated the code so that it now handles both static front page and the standard post listing as well as the taxonomy menu type. – Dave Romsey May 24 '17 at 21:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.