简体   繁体   中英

How to filter WordPress posts by Category first, then by Tags that belong to posts from the selected category?

It's my first post here as well as I'm a beginner so please bear with me.

What I'd like to accomplish is to create an AJAX function that will list all available categories in form of checkboxes that a user can select from. After selecting and hitting 'Apply' I'd like to show a list of all tags (also in form of checkboxes) from posts that are assigned to the selected category/categories. Then user can select from available tags and then after hitting final 'Apply' button the corresponding posts will be shown.

Similarly to TED.com 'What interests you?' but in AJAX. So first step would be Categories, second step would be Tags. Then posts that belong to selected category and that have selected tags assigned will be displayed.

So far, I figured how to list all available categories but when filtering by Tags it gives me a list of all tags not the tags for selected category only. Then the actual results (posts) are duplicated as well. How to filter by tags and remove duplication is beyond me unfortunately. I've put my function into shortcode as I'm using block builder so I can paste the shortcode into a Code module and place it anywhere on the page.

My function:

Edit: (now updated with Vitauts' answer + added code to remove duplicated results)

function filter_articles_shortcode() {

  $siteurl = site_url();
  $form = '<form id="filter2" action=" '. $siteurl .'/wp-admin/admin-ajax.php" method="POST">';
    if( $cats = get_terms(
      array(
        'taxonomy' => 'category',
        'orderby' => 'name'
        )
      )
    ) :
      $form .= '<div>';
      foreach ( $cats as $cat ) :
        $form .= '<input name="cFilter[]" type="checkbox" id="'. $cat->slug .'"  value="'. $cat->term_id .'">';
        $form .= '<label for="'. $cat->slug .'">'. $cat->name .'</label>';
        $form .= '<span>&#40;' . $cat->count . '&#41;</span>';
        $form .= '<br />';
      endforeach;
      $form .= '</div>';
    endif;
  $form .= '<button id="cat-btn">Apply</button>';
  $form .= '<input type="hidden" name="action" value="catFilter">';
  $form .= '</form>';
  $form .= '<div id="results"></div>';
  return $form;

}
add_shortcode('filter-articles', 'filter_articles_shortcode');

add_action('wp_ajax_catFilter', 'show_filter_results');
add_action('wp_ajax_nopriv_catFilter', 'show_filter_results');

//
////
//

function show_filter_results() {

  if( isset( $_POST['cFilter'] ) ) {
    $args['tax_query'] = array(
      array(
        'taxonomy' => 'category',
        'field' => 'id',
        'terms' => $_POST['cFilter']
      )
    );
  }

  $query = new WP_Query( $args );

  if( $query->have_posts() ) :
    while( $query->have_posts() ): $query->the_post();

    $w = the_title('<h3>', '</h3>', true);
    $w .= the_category(', ');
    $w .= the_tags('<div>',', ','</div>');
    $w .= '<hr />';
    echo $w;

  endwhile;
  wp_reset_postdata();
  else :
    echo 'nothing';
  endif;

  $siteurl = site_url();

  $o .= '<form id="filter-tags" action=" '. $siteurl .'/wp-admin/admin-ajax.php" method="POST">';

  $utags = array();

  if( $query->have_posts() ) :
    while( $query->have_posts() ): $query->the_post();

    $tags = get_the_tags();

    if ( !empty($tags) ) {
      foreach( $tags as $tag ) {

        $utag = $tag;

        if(! in_array($utag, $utags)) {

          $utags[] = $utag;

          $o .= '<input name="tFilter[]" type="checkbox" id="tag&#8722;'. $utag->slug .'" value="'. $utag->term_id .'" />';
          $o .= '<label for="tag&#8722;'. $utag->slug .'">'. $utag->name .'</label>';
          $o .= '<span>&#40;' . $utag->count . '&#41;</span>';
          $o .= '<br />';

        }
      }
    }

    endwhile;

  $o .= '<button id="tags-btn">Tags</button>';
  $o .= '<input type="hidden" name="action" value="tagFilter">';

  foreach ($_POST['cFilter'] as $cfilter) {
    $o .= '<input type="hidden" name="cFilter[]" value="' .  esc_attr($cfilter) . '">';
  }
  wp_reset_postdata();
  else :
    echo 'nothing';
  endif;

  $o .= '</form>';
  $o .= '<div id="tag-results"></div>';
  echo $o;

  echo "<script>
    jQuery(function($){
      $('#filter-tags').submit(function() {
        var tags = $('#filter-tags');
        $.ajax({
          url:tags.attr('action'),
          data:tags.serialize(),
          type:tags.attr('method'),
          beforeSend:function(xhr){
            tags.find('#tags-btn').text('Searching...');
          },
          success:function(data){
            tags.find('#tags-btn').text('Tag');
            $('#tag-results').html(data);
          }
        });
        return false;
      });

    });
    </script>";

  die();

}

add_action('wp_ajax_tagFilter', 'show_tags_results'); 
add_action('wp_ajax_nopriv_tagFilter', 'show_tags_results');

//
////
//

function show_tags_results() {

  if( isset( $_POST['tFilter'] ) ) {
    $args2['tax_query'] = array(
      'relation' => 'AND',
      array(
          'taxonomy' => 'category',
          'field' => 'id',
          'terms' => $_POST['cFilter'],
          'include_children' => false
      ),
      array(
          'taxonomy' => 'post_tag',
          'field' => 'id',
          'terms' => $_POST['tFilter'],
      )
    );
  }

  $query = new WP_Query( $args2 );

  if( $query->have_posts() ) :
    while( $query->have_posts() ): $query->the_post();

    $e = the_title('<h3>', '</h3>', true);
    $e .= the_category(', ');
    $e .= the_tags('<div>',', ','</div>');
    $e .= '<hr />';
    echo $e;

  endwhile;
  wp_reset_postdata();
  else :
    echo 'nothing';
  endif;

  die();

}

AJAX:

jQuery(document).ready(function($){
    $('#filter2').submit(function(){
        var filter2 = $('#filter2');
        $.ajax({
            url:filter2.attr('action'),
            data:filter2.serialize(), // form data
            type:filter2.attr('method'), // POST
            beforeSend:function(xhr){
                filter2.find('#cat-btn').text('Searching...');
            },
            success:function(data){
                filter2.find('#cat-btn').text('Apply'); // changing the button label back
                $('#results').html(data); // insert data
            }
        });
        return false;
    });

});

On a side note - ideally I'd like to remove the 'Apply' buttons and instead have data show automatically via AJAX. In other words: select a category(ries) and immediately show all tags then select a tag(s) and immediately show all posts.

Any help will be appreciated and thank you for your time.

You need some changes to your code. First, tags list form needs to resubmit cFilter value, so in show_filter_results() add this code before/after hidden action field.

foreach ($_POST['cFilter'] as $cfilter) {
    $o .= '<input type="hidden" name="cFilter[]" value="' .  esc_attr($cfilter) . '">';
}

Then in show_tags_results() change tax_query to this

$args2['tax_query'] = array(
    'relation' => 'AND',
    array(
        'taxonomy' => 'category',
        'field' => 'id',
        'terms' => $_POST['cFilter'],
        'include_children' => false
    ),
    array(
        'taxonomy' => 'post_tag',
        'field' => 'id',
        'terms' => $_POST['tFilter'],
    )
);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM