2

How do get the grandchildren to show up on the PARENT post, right now I can only get the children to show up on the parent post, and grandchildren on the child post.

I'd like to make a list this way:

Parent post (current page)
-Child 1
--grandchild 1
--grandchild 2
--grandchild 3
-Child 2
--grandchild 1
--grandchild 2
--grandchild 3

This is my code so far

<?php 
$args = array( 'post_parent' => get_the_ID(), 'post_type' => 'series', 'posts_per_page' => -1 );
$childs = new WP_Query( $args );
while ( $childs->have_posts() ) : $childs->the_post();
?>
<a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>

<?php endwhile;  wp_reset_postdata(); ?>

Any help is appreciated, thank you!

Update:

<ul>Child1 title
<li>grandchild</li>
<li>grandchild2</li>
<li>grandchild3</li>
</ul>
<ul>Child2 title
<li>grandchild</li>
<li>grandchild2</li>
<li>grandchild3</li>
</ul>
1
  • Ok, I've scrapped that used wp_list_pages, so far so good. I got the list I needed. How would I got about setting up a foreach query? I need to remove the URL from the Child posts and only keep the grandchildren linked, thank you very much for any help!
    – user156
    Dec 7, 2017 at 16:44

1 Answer 1

2

You can create walker for the wp_list_pages() this function use the Walker_Page you can extends him.

Walker fo example you put it in the functions.php I added comments with the class name No_Link_First_Child_Walker so you could see what I changed you can search for them.

class No_Link_First_Child_Walker extends Walker_Page {
    public function start_el( &$output, $page, $depth = 0, $args = array(), $current_page = 0 ) {
        if ( isset( $args['item_spacing'] ) && 'preserve' === $args['item_spacing'] ) {
            $t = "\t";
            $n = "\n";
        } else {
            $t = '';
            $n = '';
        }
        if ( $depth ) {
            $indent = str_repeat( $t, $depth );
        } else {
            $indent = '';
        }

        $css_class = array( 'page_item', 'page-item-' . $page->ID );

        if ( isset( $args['pages_with_children'][ $page->ID ] ) ) {
            $css_class[] = 'page_item_has_children';
        }

        if ( ! empty( $current_page ) ) {
            $_current_page = get_post( $current_page );
            if ( $_current_page && in_array( $page->ID, $_current_page->ancestors ) ) {
                $css_class[] = 'current_page_ancestor';
            }
            if ( $page->ID == $current_page ) {
                $css_class[] = 'current_page_item';
            } elseif ( $_current_page && $page->ID == $_current_page->post_parent ) {
                $css_class[] = 'current_page_parent';
            }
        } elseif ( $page->ID == get_option('page_for_posts') ) {
            $css_class[] = 'current_page_parent';
        }

        /**
         * Filters the list of CSS classes to include with each page item in the list.
         *
         * @since 2.8.0
         *
         * @see wp_list_pages()
         *
         * @param array   $css_class    An array of CSS classes to be applied
         *                              to each list item.
         * @param WP_Post $page         Page data object.
         * @param int     $depth        Depth of page, used for padding.
         * @param array   $args         An array of arguments.
         * @param int     $current_page ID of the current page.
         */
        $css_classes = implode( ' ', apply_filters( 'page_css_class', $css_class, $page, $depth, $args, $current_page ) );

        if ( '' === $page->post_title ) {
            /* translators: %d: ID of a post */
            $page->post_title = sprintf( __( '#%d (no title)' ), $page->ID );
        }

        $args['link_before'] = empty( $args['link_before'] ) ? '' : $args['link_before'];
        $args['link_after'] = empty( $args['link_after'] ) ? '' : $args['link_after'];

        $atts = array();
        $atts['href'] = get_permalink( $page->ID );

        /**
         * Filters the HTML attributes applied to a page menu item's anchor element.
         *
         * @since 4.8.0
         *
         * @param array $atts {
         *     The HTML attributes applied to the menu item's `<a>` element, empty strings are ignored.
         *
         *     @type string $href The href attribute.
         * }
         * @param WP_Post $page         Page data object.
         * @param int     $depth        Depth of page, used for padding.
         * @param array   $args         An array of arguments.
         * @param int     $current_page ID of the current page.
         */
        $atts = apply_filters( 'page_menu_link_attributes', $atts, $page, $depth, $args, $current_page );

        /**
         * No_Link_First_Child_Walker
         * Here I removed the the attribute for the href if the depth equal to 1
         */
        if($depth == 1) {
            unset($atts['href']);
        }

        $attributes = '';
        foreach ( $atts as $attr => $value ) {
            if ( ! empty( $value ) ) {
                $value = esc_attr( $value );
                $attributes .= ' ' . $attr . '="' . $value . '"';
            }
        }

        /**
         * No_Link_First_Child_Walker
         * Here I add a condition to check if depth is 1 so the tag will be span
         */
        $output .= $indent . sprintf(
            '<li class="%s">'.(($depth == 1) ? '<span%s>%s%s%s</span>' : '<a%s>%s%s%s</a>'),
            $css_classes,
            $attributes,
            $args['link_before'],
            /** This filter is documented in wp-includes/post-template.php */
            apply_filters( 'the_title', $page->post_title, $page->ID ),
            $args['link_after']
        );

        if ( ! empty( $args['show_date'] ) ) {
            if ( 'modified' == $args['show_date'] ) {
                $time = $page->post_modified;
            } else {
                $time = $page->post_date;
            }

            $date_format = empty( $args['date_format'] ) ? '' : $args['date_format'];
            $output .= " " . mysql2date( $date_format, $time );
        }
    }

}

Now you call the function wp_list_pages() with this walker too like this.

wp_list_pages(array('post_type'=>'series', 'walker'=>new No_Link_First_Child_Walker()));

Or you could make another while loop inside the second while for the grandchildren.

Try this

class No_Link_First_Child_Walker extends Walker_Page {
    /**
     * Outputs the beginning of the current level in the tree before elements are output.
     *
     * @since 2.1.0
     *
     * @see Walker::start_lvl()
     *
     * @param string $output Used to append additional content (passed by reference).
     * @param int    $depth  Optional. Depth of page. Used for padding. Default 0.
     * @param array  $args   Optional. Arguments for outputting the next level.
     *                       Default empty array.
     */
    public function start_lvl( &$output, $depth = 0, $args = array() ) {
        if ( isset( $args['item_spacing'] ) && 'preserve' === $args['item_spacing'] ) {
            $t = "\t";
            $n = "\n";
        } else {
            $t = '';
            $n = '';
        }
        $indent = str_repeat( $t, $depth );
        // No_Link_First_Child_Walker check depth and remove <ul> for depth 0
        if($depth !== 0) {
            $output .= "{$n}{$indent}<ul class='children'>{$n}";
        }
    }

    /**
     * Outputs the end of the current level in the tree after elements are output.
     *
     * @since 2.1.0
     *
     * @see Walker::end_lvl()
     *
     * @param string $output Used to append additional content (passed by reference).
     * @param int    $depth  Optional. Depth of page. Used for padding. Default 0.
     * @param array  $args   Optional. Arguments for outputting the end of the current level.
     *                       Default empty array.
     */
    public function end_lvl( &$output, $depth = 0, $args = array() ) {
        if ( isset( $args['item_spacing'] ) && 'preserve' === $args['item_spacing'] ) {
            $t = "\t";
            $n = "\n";
        } else {
            $t = '';
            $n = '';
        }
        $indent = str_repeat( $t, $depth );
        // No_Link_First_Child_Walker check depth and remove </ul> for depth 0
        if($depth !== 0) {
            $output .= "{$indent}</ul>{$n}";
        }

    }

    public function start_el( &$output, $page, $depth = 0, $args = array(), $current_page = 0 ) {

        if ( isset( $args['item_spacing'] ) && 'preserve' === $args['item_spacing'] ) {
            $t = "\t";
            $n = "\n";
        } else {
            $t = '';
            $n = '';
        }
        if ( $depth ) {
            $indent = str_repeat( $t, $depth );
        } else {
            $indent = '';
        }

        $css_class = array( 'page_item', 'page-item-' . $page->ID );

        if ( isset( $args['pages_with_children'][ $page->ID ] ) ) {
            $css_class[] = 'page_item_has_children';
        }

        if ( ! empty( $current_page ) ) {
            $_current_page = get_post( $current_page );
            if ( $_current_page && in_array( $page->ID, $_current_page->ancestors ) ) {
                $css_class[] = 'current_page_ancestor';
            }
            if ( $page->ID == $current_page ) {
                $css_class[] = 'current_page_item';
            } elseif ( $_current_page && $page->ID == $_current_page->post_parent ) {
                $css_class[] = 'current_page_parent';
            }
        } elseif ( $page->ID == get_option('page_for_posts') ) {
            $css_class[] = 'current_page_parent';
        }

        /**
         * Filters the list of CSS classes to include with each page item in the list.
         *
         * @since 2.8.0
         *
         * @see wp_list_pages()
         *
         * @param array   $css_class    An array of CSS classes to be applied
         *                              to each list item.
         * @param WP_Post $page         Page data object.
         * @param int     $depth        Depth of page, used for padding.
         * @param array   $args         An array of arguments.
         * @param int     $current_page ID of the current page.
         */
        $css_classes = implode( ' ', apply_filters( 'page_css_class', $css_class, $page, $depth, $args, $current_page ) );

        if ( '' === $page->post_title ) {
            /* translators: %d: ID of a post */
            $page->post_title = sprintf( __( '#%d (no title)' ), $page->ID );
        }

        $args['link_before'] = empty( $args['link_before'] ) ? '' : $args['link_before'];
        $args['link_after'] = empty( $args['link_after'] ) ? '' : $args['link_after'];

        $atts = array();
        $atts['href'] = get_permalink( $page->ID );

        /**
         * Filters the HTML attributes applied to a page menu item's anchor element.
         *
         * @since 4.8.0
         *
         * @param array $atts {
         *     The HTML attributes applied to the menu item's `<a>` element, empty strings are ignored.
         *
         *     @type string $href The href attribute.
         * }
         * @param WP_Post $page         Page data object.
         * @param int     $depth        Depth of page, used for padding.
         * @param array   $args         An array of arguments.
         * @param int     $current_page ID of the current page.
         */
        $atts = apply_filters( 'page_menu_link_attributes', $atts, $page, $depth, $args, $current_page );

        /**
         * No_Link_First_Child_Walker
         * Here I removed the the attribute for the href if the depth equal to 1
         */
        if($depth == 1) {
            unset($atts['href']);
        }

        $attributes = '';
        foreach ( $atts as $attr => $value ) {
            if ( ! empty( $value ) ) {
                $value = esc_attr( $value );
                $attributes .= ' ' . $attr . '="' . $value . '"';
            }
        }

        /**
         * No_Link_First_Child_Walker
         * Here I add a condition to check if depth is 1 so the tag will be h3
         * remove the <li> too for this depth
         */
        $output .= $indent . sprintf(
            (($depth == 1) ? '<h3 class="%s">%s%s%s</h3>' : '<li class="%s"><a%s>%s%s%s</a>'),
            $css_classes,
            $attributes,
            $args['link_before'],
            /** This filter is documented in wp-includes/post-template.php */
            apply_filters( 'the_title', $page->post_title, $page->ID ),
            $args['link_after']
        );

        if ( ! empty( $args['show_date'] ) ) {
            if ( 'modified' == $args['show_date'] ) {
                $time = $page->post_modified;
            } else {
                $time = $page->post_date;
            }

            $date_format = empty( $args['date_format'] ) ? '' : $args['date_format'];
            $output .= " " . mysql2date( $date_format, $time );
        }
    }

    public function end_el( &$output, $page, $depth = 0, $args = array() ) {
        if ( isset( $args['item_spacing'] ) && 'preserve' === $args['item_spacing'] ) {
            $t = "\t";
            $n = "\n";
        } else {
            $t = '';
            $n = '';
        }
        // No_Link_First_Child_Walker check depth and remove closing </li> for depth 1
        if($depth !== 1) {
            $output .= "</li>{$n}";
        }
    }

}
5
  • Thank you so much, that worked great. One more thing if it's not much trouble, I'd like to wrap each child in its own <ul></ul> tags, how would I go about doing that? <code><ul> <span>Child title</span> <li>grandchild</li> </ul> <ul>Child 2 <span>child 2 title</span> <li>grandchild</li> <li>grandchild</li> </ul>
    – user156
    Dec 8, 2017 at 5:03
  • You need <li> childs for <ul> and <ol> if not your HTML is invalid.
    – Shibi
    Dec 8, 2017 at 11:35
  • I know, and that is the markup I'm going for. <code><ul>Child <li>gchild</li> </ul></code> Then the second child on it's own <ul> and gchildren. I hope I'm being clear enough. Thank you for the help, I've been stuck at this for 2 days now.
    – user156
    Dec 8, 2017 at 19:49
  • I added a new code at the bottom of my answer check it.
    – Shibi
    Dec 9, 2017 at 12:40
  • Thank you for all the help, couldn't have done it without it! Appreciated.
    – user156
    Dec 15, 2017 at 8:11

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.