admin管理员组

文章数量:1291037

I have an old site that has sooo many child pages that I'd like to limit the return of child pages on a current page to just a specific year so that I can pull each year into an accordion.

I am currently using the following and it returns the pages I want listed, I just want to limit the returns to specific years.

  global $id;
  wp_list_pages("title_li=&child_of=$id&show_date=modified&date_format=$date_format"); 

What can I add to this statement to return only pages published in a specific year?

I have an old site that has sooo many child pages that I'd like to limit the return of child pages on a current page to just a specific year so that I can pull each year into an accordion.

I am currently using the following and it returns the pages I want listed, I just want to limit the returns to specific years.

  global $id;
  wp_list_pages("title_li=&child_of=$id&show_date=modified&date_format=$date_format"); 

What can I add to this statement to return only pages published in a specific year?

Share Improve this question asked Jun 14, 2021 at 16:30 AndreaAndrea 235 bronze badges 8
  • Due to wp_list_pages() depending on get_pages() and how the latter implements it's own esoteric database query logic, I don't believe you can achieve the desired outcome by just adding something to the query. I think this would be a good use-case for a custom Walker_Page class. In any scenario, can you elaborate on the specific behavior a little more - if a child was published three years ago, and it has grandchildren published two years ago as well as this year, does the child appear in just 2018, or all of 2018, 2019, and 2021? – bosco Commented Jun 14, 2021 at 17:10
  • Okay, totally understandable, other than PHP is not my bag so I'm unable to really write what I need. I'm not familiar with the Walker_Page class. – Andrea Commented Jun 14, 2021 at 17:38
  • I have pages that have about 20 child pages entered per year, (15 years). Which creates a giant list of pages in an ul li format. The list is so long it makes for a terrible visual & user experience. I was hoping to create an accordion where each year would be collapsible making the page much shorter and easier to navigate years. I want to pull in each year via shortcode into sections of an accordion. It would allow me to put a year anywhere I want on the page, so to pull in child pages of current page and only return one year is what I'm looking to do. Do you have any suggestions? – Andrea Commented Jun 14, 2021 at 17:46
  • Oh, and I'm guess Walker is the menu system? Is there a way to write a collapsible child page menu by year? would be like 2021 Documents then click and it shows ul li of children of current page published in 2021, then 2020 and so on? – Andrea Commented Jun 14, 2021 at 17:54
  • 1 @bosco can you write that up as an answer rather than a comment? – Tom J Nowell Commented Jun 14, 2021 at 19:27
 |  Show 3 more comments

1 Answer 1

Reset to default 0

Due to wp_list_pages() leveraging get_pages() to actually retrieve the Page posts, and because get_pages() implements it's own query logic independent of WP_Query's, it's not possible to use arbitrary query variables which are not directly supported by get_pages(), including year, mothnum, day, date_query, etc.

In order to continue using wp_list_pages() to this end, I think the most elegant solution would be to implement a custom Walker_Page class which you might pass to wp_list_pages() as an argument in order to customize the generated HTML.

As an added benefit, this solution continues to use a single get_pages() query instead of one query per year, and can fluidly handle grand-children pages and continue to scale as more years are added.

Tailored Walker_Page

In WordPress "Walkers" are classes which are designed to take lists representing hierarchical or tree-like data like that of a like of hierarchical post-types, taxonomies, or navigation menu items and turn them into markup for display.

wp_list_pages() provides a parameter to pass a custom Walker to it in place of the Walker_Page class it uses by default, allowing you to customize the markup the function produces.

// mytheme/lib/class-year-accordion-walker-page.php
// (or functions.php, or some other theme library code location)

/**
 * A Walker_Page customized to additionally group first-level pages into lists based
 * on their publication year.
 **/
class Year_Accordion_Walker_Page extends Walker_Page {
  /**
   * Creates the opening markup for a year's accordion group.
   *
   * @param string  &$output The string to append output to.
   * @param int     $year The year which to build group markup for.
   * @param boolean $is_active If this year matches the publication date for the current page, as indicated by "child_of".
   * @param array   $args Any additional arguments passed to `walk()`.
   **/
  public function start_accordion_group( &$output, $year, $is_active = false, $args = [] ) {
    // Setup HTML classes to be added to various parts of the markup.
    $container_classes = [];
    $header_classes    = [ 'ui-accordion-header' ];
    $content_classes   = [ 'ui-accordion-content' ];

    if( $is_active ) {
      $container_classes[] = 'current_page_ancestor';
      $header_classes[]    = 'ui-accordion-header-active';
      $content_classes[]   = 'ui-accordion-content-active';
    }

    // Add this accordion group's opening markup to the output, joining each line with the configured indentation.
    $output .= implode(
      "\n\t",
      [
        '<li class="' . implode( ' ', $container_classes ) . '">',
        '<h3 class="' . implode( ' ', $header_classes ) . '">' . $year . '</h3>',
        '<ul class="' . implode( ' ', $content_classes ) . '">',
      ]
    );
  }

  /**
   * Creates the closing markup for a year's accordion group.
   *
   * @param string  &$output The string to append output to.
   * @param int     $year The year which to build group markup for.
   * @param boolean $is_active If this year matches the publication date for the current page, as indicated by "child_of".
   * @param array   $args Any additional arguments passed to `walk()`.
   **/
  public function end_accordion_group( &$output, $year, $is_active = false, $args = [] ) {
    $output .= implode(
      "\n",
      [
        "\t</ul>", // Close the accordion group's page list.
        '</li>', // Close the accordion group's container li.
      ]
    );
  }

  /**
   * Display array of Pages, grouped by the topmost pages' publication year.
   *
   * $max_depth = 0 means display all levels.
   * $max_depth > 0 specifies the number of display levels.
   *
   * @param array $pages An array of WP_Post items.
   * @param int   $max_depth The maximum hierarchical depth.
   * @param mixed ...$args Optional additional arguments.
   * @return string Markup representing $pages grouped by publication year.
   **/
  public function walk( $pages, $max_depth = 0, ...$args ) {
    $output = '';
    
    // Invalid parameter or nothing to walk.
    if ( $max_depth < 0 || empty( $pages ) )
      return $output;

    $options                 = $args[0]; // When used with `walk_page_tree()` this is an assoc array of any additional arguments.
    $current_page_id         = $args[1]; // When used with `walk_page_tree()` this is the current post ID.
    $active_page_year        = null; // Keep track of which year the current_page_id Page was published in for custom classes, etc.
    $parent_field            = $this->db_fields['parent']; // Inherited from `Walker_Page` - will always be `ID`.
    $parent_id               = isset( $options['child_of'] ) ? $options['child_of'] : 0; // The ID of the top-level common parent, if any.
    $top_level_pages_by_year = []; // A mapping of year => top-level pages (parent is $parent_id, or no parent if $parent_id is not set).
    $child_pages_by_parent   = []; // A mapping of parent_id => pages, collecting all pages whose parent is not $parent_id (or who have a parent, if $parent_id is not set)

    // Split $pages into $child_pages_by_parent and $top_level_pages_by_year.
    foreach( $pages as $page ) {
      $year = date( 'Y', strtotime( $page->post_date ) );

      if( $page->ID === $current_page_id )
        $active_page_year = $year; // If this is the current page, record the year in which it was published.

      // If this is a grandchild page, we don't care about it's publish date - just add it to the map of parent_id => child pages.
      if( ( empty( $parent_id ) && ! empty( $page->$parent_field ) ) || ( ! empty( $parent_id ) && $page->$parent_field !== $parent_id ) ) {
        if( ! isset( $child_pages_by_parent[ $page->$parent_field ] ) )
          $child_pages_by_parent[ $page->$parent_field ] = [];

        $child_pages_by_parent[ $page->$parent_field ][] = $page;
        continue;
      }

      if( ! isset( $top_level_pages_by_year[ $year ] ) )
        $top_level_pages_by_year[ $year ] = [];
      
      $top_level_pages_by_year[ $year ][] = $page; // Add the top level page to it's respective year list.
    }

    // Build the markup, one year at a time.
    foreach( $top_level_pages_by_year as $year => $pages ) {
      // Build the opening markup for this year's accordion group.
      $this->start_accordion_group( $output, $year, $year == $active_page_year, $args );

      // Add each top-level page in the group's child tree markup as per usual.
      foreach( $pages as $page )
        $this->display_element( $page, $child_pages_by_parent, $max_depth, 1, $args, $output );

      // Close this year's accordion group.
      $this->end_accordion_group( $output, $year, $year == $active_page_year, $args );
    }

    return $output;
  }
}

With the above class available, wp_list_pages() can be called to utilize it as such:

// Template file
wp_list_pages(
  [
    'title_li'    => '&',
    'child_of'    => $id,
    'show_date'   => 'modified',
    'date_format' => $date_format,
    'walker'      => new Year_Accordion_Walker_Page,
  ]
); 

Finishing Touches

For the icing on the cake, you might even create a "template tag" function to load and leverage this class when necessary:

// functions.php
// (or other library file loaded by functions.php)

/**
 * Display pages in an accordion, grouped by year. Loads the customized
 * Walker_Page if it's not already available.
 *
 * @param array $args Array of wp_list_pages() arguments.
 **/
function wpse390524_page_year_accordion( $args = [] ) {
  if( ! class_exists( 'Year_Accordion_Walker_Page' ) )
    require_once get_stylesheet_directory() . '/lib/class-year-accordion-walker-page.php';

  $args['walker'] = new Year_Accordion_Walker_Page;

  wp_list_pages( $args );
}
// Template file
wpse390524_page_year_accordion(
  [
    'title_li'    => '&',
    'child_of'    => $id,
    'show_date'   => 'modified',
    'date_format' => $date_format,
  ]
);

本文标签: List child pages of current page but limit to specific year