ajax - Apply filters to main query instead of creating new one?

admin2025-06-02  1

I've been working with this tutorial that adds taxonomy filters to archive.php and it works, but the output is a new WP query that echoes post titles. How can modify the function so instead of creating a new query it would apply to the main query? Is there a filter for this? I know about pre_get_posts but since this uses ajax i'm not sure how to put it together.

Html form on archive.php

<form action="<?php echo site_url() ?>/wp-admin/admin-ajax.php" method="POST" id="filter">

    <?php
        if( $terms = get_terms( array( 'taxonomy' => 'genre', 'orderby' => 'name' ) ) ) : 

            echo '<select name="genrefilter"><option value="">Select category...</option>';
            foreach ( $terms as $term ) :
                echo '<option value="' . $term->term_id . '">' . $term->name . '</option>'; // ID of the category as the value of an option
            endforeach;
            echo '</select>';
        endif;
    ?>
    <?php
        if( $terms = get_terms( array( 'taxonomy' => 'language', 'orderby' => 'name' ) ) ) : 

            echo '<select name="languagefilter"><option value="">Select category...</option>';
            foreach ( $terms as $term ) :
                echo '<option value="' . $term->term_id . '">' . $term->name . '</option>'; // ID of the category as the value of an option
            endforeach;
            echo '</select>';
        endif;
    ?>
    <input type="text" name="price_min" placeholder="Min price" />
    <input type="text" name="price_max" placeholder="Max price" />
    <label>
        <input type="radio" name="date" value="ASC" checked="checked" /> Date: Ascending
    </label>
    <label>
        <input type="radio" name="date" value="DESC" selected="selected" /> Date: Descending
    </label>
    <label>
        <input type="checkbox" name="featured_image" /> Only posts with featured images
    </label>
    <button>Apply filter</button>
    <input type="hidden" name="action" value="myfilter">
    <input type="hidden" name="post_type" value="<?php echo get_post_type();?>">
</form>
<div id="response"></div>

<script>
jQuery(function($){
    $('#filter').submit(function(){
        var filter = $('#filter');
        $.ajax({
            url:filter.attr('action'),
            data:filter.serialize(), // form data
            type:filter.attr('method'), // POST
            beforeSend:function(xhr){
                filter.find('button').text('Processing...'); // changing the button label
            },
            success:function(data){
                filter.find('button').text('Apply filter'); // changing the button label back
                $('#response').html(data); // insert data
            }
        });
        return false;
    });
});
</script>

functions.php

add_action('wp_ajax_myfilter', 'misha_filter_function'); // wp_ajax_{ACTION HERE} 
add_action('wp_ajax_nopriv_myfilter', 'misha_filter_function');

function misha_filter_function(){
    $args = array(
        'post_type' => $_POST['post_type'],
        'orderby' => 'date', // we will sort posts by date
        'order' => $_POST['date'] // ASC or DESC
    );

    // for taxonomies / categories
if( isset( $_POST['genrefilter'] ) && isset( $_POST['languagefilter'] ) ) {
        $args['tax_query'] = array(
            'relation' => 'AND',
            array(
                'taxonomy' => 'genre',
                'field' => 'id',
                'terms' => $_POST['genrefilter']
            ),
            array(
                'taxonomy' => 'language',
                'field' => 'id',
                'terms' => $_POST['languagefilter']
            ),
        );

    }   


    // create $args['meta_query'] array if one of the following fields is filled
    if( isset( $_POST['price_min'] ) && $_POST['price_min'] || isset( $_POST['price_max'] ) && $_POST['price_max'] || isset( $_POST['featured_image'] ) && $_POST['featured_image'] == 'on' )
        $args['meta_query'] = array( 'relation'=>'AND' ); // AND means that all conditions of meta_query should be true

    // if both minimum price and maximum price are specified we will use BETWEEN comparison
    if( isset( $_POST['price_min'] ) && $_POST['price_min'] && isset( $_POST['price_max'] ) && $_POST['price_max'] ) {
        $args['meta_query'][] = array(
            'key' => '_price',
            'value' => array( $_POST['price_min'], $_POST['price_max'] ),
            'type' => 'numeric',
            'compare' => 'between'
        );
    } else {
        // if only min price is set
        if( isset( $_POST['price_min'] ) && $_POST['price_min'] )
            $args['meta_query'][] = array(
                'key' => '_price',
                'value' => $_POST['price_min'],
                'type' => 'numeric',
                'compare' => '>'
            );

        // if only max price is set
        if( isset( $_POST['price_max'] ) && $_POST['price_max'] )
            $args['meta_query'][] = array(
                'key' => '_price',
                'value' => $_POST['price_max'],
                'type' => 'numeric',
                'compare' => '<'
            );
    }


    // if post thumbnail is set
    if( isset( $_POST['featured_image'] ) && $_POST['featured_image'] == 'on' )
        $args['meta_query'][] = array(
            'key' => '_thumbnail_id',
            'compare' => 'EXISTS'
        );
    // if you want to use multiple checkboxed, just duplicate the above 5 lines for each checkbox

    $query = new WP_Query( $args );

    if( $query->have_posts() ) :
        while( $query->have_posts() ): $query->the_post();
            echo '<h2>' . $query->post->post_title . '</h2>';
        endwhile;
        wp_reset_postdata();
    else :
        echo 'No posts found';
    endif;

    die();
}

I've been working with this tutorial that adds taxonomy filters to archive.php and it works, but the output is a new WP query that echoes post titles. How can modify the function so instead of creating a new query it would apply to the main query? Is there a filter for this? I know about pre_get_posts but since this uses ajax i'm not sure how to put it together.

Html form on archive.php

<form action="<?php echo site_url() ?>/wp-admin/admin-ajax.php" method="POST" id="filter">

    <?php
        if( $terms = get_terms( array( 'taxonomy' => 'genre', 'orderby' => 'name' ) ) ) : 

            echo '<select name="genrefilter"><option value="">Select category...</option>';
            foreach ( $terms as $term ) :
                echo '<option value="' . $term->term_id . '">' . $term->name . '</option>'; // ID of the category as the value of an option
            endforeach;
            echo '</select>';
        endif;
    ?>
    <?php
        if( $terms = get_terms( array( 'taxonomy' => 'language', 'orderby' => 'name' ) ) ) : 

            echo '<select name="languagefilter"><option value="">Select category...</option>';
            foreach ( $terms as $term ) :
                echo '<option value="' . $term->term_id . '">' . $term->name . '</option>'; // ID of the category as the value of an option
            endforeach;
            echo '</select>';
        endif;
    ?>
    <input type="text" name="price_min" placeholder="Min price" />
    <input type="text" name="price_max" placeholder="Max price" />
    <label>
        <input type="radio" name="date" value="ASC" checked="checked" /> Date: Ascending
    </label>
    <label>
        <input type="radio" name="date" value="DESC" selected="selected" /> Date: Descending
    </label>
    <label>
        <input type="checkbox" name="featured_image" /> Only posts with featured images
    </label>
    <button>Apply filter</button>
    <input type="hidden" name="action" value="myfilter">
    <input type="hidden" name="post_type" value="<?php echo get_post_type();?>">
</form>
<div id="response"></div>

<script>
jQuery(function($){
    $('#filter').submit(function(){
        var filter = $('#filter');
        $.ajax({
            url:filter.attr('action'),
            data:filter.serialize(), // form data
            type:filter.attr('method'), // POST
            beforeSend:function(xhr){
                filter.find('button').text('Processing...'); // changing the button label
            },
            success:function(data){
                filter.find('button').text('Apply filter'); // changing the button label back
                $('#response').html(data); // insert data
            }
        });
        return false;
    });
});
</script>

functions.php

add_action('wp_ajax_myfilter', 'misha_filter_function'); // wp_ajax_{ACTION HERE} 
add_action('wp_ajax_nopriv_myfilter', 'misha_filter_function');

function misha_filter_function(){
    $args = array(
        'post_type' => $_POST['post_type'],
        'orderby' => 'date', // we will sort posts by date
        'order' => $_POST['date'] // ASC or DESC
    );

    // for taxonomies / categories
if( isset( $_POST['genrefilter'] ) && isset( $_POST['languagefilter'] ) ) {
        $args['tax_query'] = array(
            'relation' => 'AND',
            array(
                'taxonomy' => 'genre',
                'field' => 'id',
                'terms' => $_POST['genrefilter']
            ),
            array(
                'taxonomy' => 'language',
                'field' => 'id',
                'terms' => $_POST['languagefilter']
            ),
        );

    }   


    // create $args['meta_query'] array if one of the following fields is filled
    if( isset( $_POST['price_min'] ) && $_POST['price_min'] || isset( $_POST['price_max'] ) && $_POST['price_max'] || isset( $_POST['featured_image'] ) && $_POST['featured_image'] == 'on' )
        $args['meta_query'] = array( 'relation'=>'AND' ); // AND means that all conditions of meta_query should be true

    // if both minimum price and maximum price are specified we will use BETWEEN comparison
    if( isset( $_POST['price_min'] ) && $_POST['price_min'] && isset( $_POST['price_max'] ) && $_POST['price_max'] ) {
        $args['meta_query'][] = array(
            'key' => '_price',
            'value' => array( $_POST['price_min'], $_POST['price_max'] ),
            'type' => 'numeric',
            'compare' => 'between'
        );
    } else {
        // if only min price is set
        if( isset( $_POST['price_min'] ) && $_POST['price_min'] )
            $args['meta_query'][] = array(
                'key' => '_price',
                'value' => $_POST['price_min'],
                'type' => 'numeric',
                'compare' => '>'
            );

        // if only max price is set
        if( isset( $_POST['price_max'] ) && $_POST['price_max'] )
            $args['meta_query'][] = array(
                'key' => '_price',
                'value' => $_POST['price_max'],
                'type' => 'numeric',
                'compare' => '<'
            );
    }


    // if post thumbnail is set
    if( isset( $_POST['featured_image'] ) && $_POST['featured_image'] == 'on' )
        $args['meta_query'][] = array(
            'key' => '_thumbnail_id',
            'compare' => 'EXISTS'
        );
    // if you want to use multiple checkboxed, just duplicate the above 5 lines for each checkbox

    $query = new WP_Query( $args );

    if( $query->have_posts() ) :
        while( $query->have_posts() ): $query->the_post();
            echo '<h2>' . $query->post->post_title . '</h2>';
        endwhile;
        wp_reset_postdata();
    else :
        echo 'No posts found';
    endif;

    die();
}
Share Improve this question asked Mar 7, 2019 at 18:29 Michael RogersMichael Rogers 5498 silver badges37 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 1

You can't modify main query if you're doing AJAX, because during AJAX requests there is no main query created at all.

On the other hand, doing this search form using AJAX isn't the best idea - you can't get URL for filtered list for example.

So I would remove the AJAX part and do it like so:

<form action="" method="GET" id="filter">

    <?php
        if( $terms = get_terms( array( 'taxonomy' => 'genre', 'orderby' => 'name' ) ) ) : 

            echo '<select name="genrefilter"><option value="">Select category...</option>';
            foreach ( $terms as $term ) :
                echo '<option value="' . $term->term_id . '">' . $term->name . '</option>'; // ID of the category as the value of an option
            endforeach;
            echo '</select>';
        endif;
    ?>
    <?php
        if( $terms = get_terms( array( 'taxonomy' => 'language', 'orderby' => 'name' ) ) ) : 

            echo '<select name="languagefilter"><option value="">Select category...</option>';
            foreach ( $terms as $term ) :
                echo '<option value="' . $term->term_id . '">' . $term->name . '</option>'; // ID of the category as the value of an option
            endforeach;
            echo '</select>';
        endif;
    ?>
    <input type="text" name="price_min" placeholder="Min price" />
    <input type="text" name="price_max" placeholder="Max price" />
    <label>
        <input type="radio" name="date" value="ASC" checked="checked" /> Date: Ascending
    </label>
    <label>
        <input type="radio" name="date" value="DESC" selected="selected" /> Date: Descending
    </label>
    <label>
        <input type="checkbox" name="featured_image" /> Only posts with featured images
    </label>
    <button>Apply filter</button>
    <input type="hidden" name="action" value="myfilter">
    <input type="hidden" name="post_type" value="<?php echo get_post_type();?>">
</form>

<div id="response">
<?php 
    if ( have_posts() ) :
        while( have_posts() ): the_post();
            echo '<h2>' . get_the_title() . '</h2>';
        endwhile;
    else :
        echo 'No posts found';
    endif;
?>
</div>

And in your functions.php

add_action( 'pre_get_posts', function ( $query ) {
    if ( ! is_admin() && is_archive() && $query->is_main_query() ) {
        // modify the $query according to your needs
        if ( isset( $_GET['genrefilter'] ) && isset( $_GET['languagefilter'] ) ) {
            $query->set( 'tax_query', array(
                'relation' => 'AND',
                array(
                    'taxonomy' => 'genre',
                    'field' => 'term_id',
                    'terms' => $_GET['genrefilter']
                ),
                array(
                    'taxonomy' => 'language',
                    'field' => 'term_id',
                    'terms' => $_GET['languagefilter']
                ),
            ) );
        }   
    }

    // put your other filters here 
} );

PS. You shouldn't use <?php echo site_url() ?>/wp-admin/admin-ajax.php. It should be <?php echo esc_attr( admin_url( 'admin-ajax.php' ) ); ?>

转载请注明原文地址:http://conceptsofalgorithm.com/Algorithm/1748826574a314045.html

最新回复(0)