0

I have a select tag (in filter.php) with different training prices ($80, $120, $500, etc). The values are stored in the array $options (thanks to Tom) :

  1. I'd like to display, when visitor clicks in $80, all pages with this value inside (prix_1) and so on...
  2. ...then display results in current page (below the select tag).

What I tried to do :

  1. I have tried array_values(); $options->price_1, a href in options and different combinations in order to display the related pages but errors. I think javascript is necessary.

  2. It seems that to make results appear in current page I have to insert the_permalink in the action form tag and create a custom loop below the filter to display those resulsts, right ?

  3. Last thing, this code is inside filter.php, which is included () in a 4 category pages, is that a problem to display the results ?

Thank you, if you someone can give me some tracks to follow, will be helpfull. I am not developper but I spend time to challenge myself.

<form action="<?php the_permalink(); ?>">

<?php
    $args = array(
        'post_type'      => 'page',
        'posts_per_page' => '-1',
        'meta_key'       => 'prix_1',
    );

    $price_query = new WP_Query( $args );

ALL PRICE CUSTOM VALUES IN A ARRAY (to avoid duplication of values)

 $options = array();

 if ( $price_query->have_posts() ) {   
    while ( $price_query->have_posts() ) {
        $price_query->the_post();
        $field = get_field( 'prix_1' );
        if ( !in_array( $field, $options ) ) {
            $options[] = $field; 
        }
    }
}

?>

PRICE CUSTOM VALUES IN SELECT TAG : How can I display the pages of each value selectd ?

<div class="col-lg-3">
    <select name="page-dropdown">

        <?php if ( !empty( $options ) ) {

            foreach ( $options as $option ) { ?>

                <option value=""><a href="<?php the_permalink(); ?>"><?php echo $option->'price_1'; ?></a></option>

            <?php } //end foreach

        } //endif ?>
    </select>
</div>

DISPLAY THE RESULT OF PAGES : Custom loop to display the selected tag results in current page, but not working at this point. May I need to transfer values.

if ( $price_query->have_posts() ) :
    while ( $price_query->have_posts() ) : ?>

     <div class="row">
        <div class="col-lg-12">

            My own pages layout here             

        </div>
    </div>

    <?php endwhile; ?>
<?php endif; ?>

1 Answer 1

1

@Nick83,

While I'm not trying to make the answer overly complicated, in order to do what you are attempting to do correctly I believe you'll need to call WordPress via Ajax.

My recommendation is the following:

  1. Create two ajax calls. The first gets the prices (without duplicates). The second takes whichever price the user chose and queries the database for a page or post that has the meta_value of that price.

  2. Create an HTML form that is empty upon page load. Your Javascript will get the price values and insert them into the select field. Once the user has chosen a price, the script will again react by grabbing that price and calling an ajax function inside your functions.php, providing the price, and outputting the resulting posts.

I went ahead and built a local copy of a stock WordPress site to create an example. Here is the code you'll need:

functions.php

/*
 * Adding scripts to the theme. Note the wp_localize_script: This is how we provide ajax URL and security to the custom.js file.
 *
 * Sources and further reading:
 *  https://developer.wordpress.org/reference/functions/wp_enqueue_script/
 *  https://codex.wordpress.org/Function_Reference/wp_localize_script
 *  https://www.barrykooij.com/check-nonces-wordpress-ajax-calls/
 *  https://codex.wordpress.org/Function_Reference/wp_verify_nonce
 */
add_action( 'wp_enqueue_scripts', 'nick83_enqueue' );
function nick83_enqueue() {
    wp_enqueue_script( 'customjs', get_stylesheet_directory_uri() . '/custom.js', array( 'jquery' ), '1.0', true );
    //nklocal
    wp_localize_script( 'customjs', 'nklocal', array(
        'ajaxurl'  => admin_url( 'admin-ajax.php' ),
        'security' => wp_create_nonce( 'nick83_security_key' ),
    ) );
}

/*
 * This is your first ajax call. I have two actions because WordPress allows non-logged-in users to call ajax via "wp_ajax_nopriv"
 * We are also checking a nonce value (provided by wp_localize_script and custom.js)
 *
 * Sources:
 *  https://codex.wordpress.org/AJAX_in_Plugins
 *  https://codex.wordpress.org/Plugin_API/Action_Reference/wp_ajax_(action)
 *  https://codex.wordpress.org/Plugin_API/Action_Reference/wp_ajax_nopriv_(action)
 */
add_action( 'wp_ajax_nopriv_nick83_get_posts_with_meta', 'nick83_get_posts_with_meta' );
add_action( 'wp_ajax_nick83_get_posts_with_meta', 'nick83_get_posts_with_meta' );
function nick83_get_posts_with_meta() {
    $data = esc_sql( $_POST );
    if ( ! wp_verify_nonce( $data['security'], 'nick83_security_key' ) ) {
        wp_die( 'Security check' );
    }

    $options = nick83_query_meta_posts();

    if ( ! empty( $options ) ) {
        echo json_encode( array( 'msg' => 'success', 'data' => $options ) );
    }

    wp_die();
}

/*
 * For a clean functions.php, I have broken the AJAX function and the query into two separate functions. This function contains the query and method we used previously to remove duplicates. Once complete, it returns the non-duplicate array of prices.
 */
function nick83_query_meta_posts() {
    $args = array(
        'post_type'      => 'post', //define the post type you want
        'posts_per_page' => '-1',
        'meta_key'       => 'prix_1',
    );

    $price_query = new WP_Query( $args );

    $options = array();

    if ( $price_query->have_posts() ) {
        while ( $price_query->have_posts() ) {
            $price_query->the_post();
            $field = get_field( 'prix_1' );

            //Here we make sure there are no duplicates
            if ( ! in_array( $field, $options ) ) {
                $options[] = $field;
            }
        }
    }

    return $options;
}


/*
 *  This is the second ajax call. In custom.js, we grab the price the user has chosen and include it in the $_POST (which is turned into $data). If $data['price'] exists, we can give it to the function 'nick83_query_meta_posts_with_price' and hopefully have posts returned.
 */
add_action( 'wp_ajax_nopriv_nick83_render_list_of_posts', 'nick83_render_list_of_posts' );
add_action( 'wp_ajax_nick83_render_list_of_posts', 'nick83_render_list_of_posts' );
function nick83_render_list_of_posts() {
    $data = esc_sql( $_POST );
    if ( ! wp_verify_nonce( $data['security'], 'nick83_security_key' ) ) {
        wp_die( 'Security check' );
    }

    if ( ! isset( $data['price'] ) || empty( $data['price'] ) ) {
        wp_die( 'No Price' );
    }


    $options = nick83_query_meta_posts_with_price( $data['price'] );
    if ( ! empty( $options ) ) {
        echo json_encode( array( 'msg' => 'success', 'data' => $options ) );
    }

    wp_die();
}

/*
 * This is the function that takes a price ($data['price'] from the ajax function) and uses it in a WordPress query to see if we can find a meta_key of 'prix_1' and a meta value of the price chosen.
 *
 * Note here that I am creating an empty array, $links, and then adding the title of the post as the array key and the URL of the post as the array value.
 */
function nick83_query_meta_posts_with_price( $price = false ) {
    if ( $price ) {
        $args = array(
            'post_type'      => 'post', //define the post type you want
            'posts_per_page' => '-1',
            'meta_key'       => 'prix_1',
            'meta_value'     => $price,
        );

        $links = array();
        $meta_query = new WP_Query( $args );
        if ( $meta_query->have_posts() ) {
            while ( $meta_query->have_posts() ) {
                $meta_query->the_post();

                //Array[key] = value;
                $links[ get_the_title() ] = get_permalink();
            }
        }

        if ( ! empty( $links ) ) {
            return $links;
        }
    }

    return false;
}

/*
 * *** WARNING ***
 * I am only using this piece of code because I do not have ACF installed.
 * If you have ACF you will not need this.
 */
if ( ! function_exists( 'get_field' ) ) {
    function get_field( $key ) {
        global $post;

        return get_post_meta( $post->ID, $key, true );
    }
}

custom.js

/*
 This is a clean way to write javascript since it is all encapsulated in an 'iife'

 Sources:
 https://developer.mozilla.org/en-US/docs/Glossary/IIFE
 */
window.NICK83APP = (function (window, document, $, undefined) {
    'use strict';
    var app = {};

    /*
     We can build functions into our mini-javascript application by defining them as app.name = function(){}
     */
    app.init = function () {

        //This is our select field that the user will find has pricing inside and will choose a price from.
        app.select = jQuery('#page_price_options');

        //This is the empty <ul> on your page. When the user chooses a price, the links will appear here.
        app.page_list = jQuery('.nick83_page_list');

        if (app.select.length > 0) {
            app.get_price_options();

            /*
             Uses jQuery "on" function.
             Sources: http://api.jquery.com/on/
             */
            app.select.on('change', app.render_pages);
        }
    };

    /*
     This function is called if the select exists on the page.
     We disable the select, then set up our ajax call (using the data from wp_localize_script inside functions.php
     If our ajax call is successful, we expect a JSON string to come back with pricing data inside. If we receive that, we create select <option> elements and place them inside our select.
     */
    app.get_price_options = function () {

        app.select.attr('disabled', 'disabled'); //Disable the select since there is no data inside it.

        var data = {
            'security': nklocal.security, //here is the nonce security
            'action': 'nick83_get_posts_with_meta' //here is our ajax call
        };

        app.ajax_call(data, function (response) { //This is a custom function but it is simply a jQuery $.ajax call.
            if (response.msg === 'success') {

                app.select.empty().append(jQuery('<option>').text("Please choose...").attr('value', '-1'));
                jQuery.each(response.data, function (i, price) {
                    app.select.append(jQuery('<option>').text(price).attr('value', price));
                });

                app.select.attr('disabled', false);

            } else {
                console.log(response);
                alert('Could not find posts with meta...');
            }
        }, function (error) {
            console.log(error);
            alert('Could not find posts with meta...');
        });
    };

    /*
     This function is fired every time the user changes the select field.
     When the script detects a change, the script executes an ajax call (nick83_render_list_of_posts) and passes the selected price to the ajax script inside functions.php (look for $data['price']).
     */
    app.render_pages = function () {
        var selected = app.select.val();
        if (selected !== '-1') {
            var data = {
                'security': nklocal.security,
                'action': 'nick83_render_list_of_posts',
                'price': selected
            };

            app.ajax_call(data, function (response) {
                if (response.msg === 'success') {
                    app.page_list.empty();
                    jQuery.each(response.data, function (title, link) {
                        var html = '<li><a href="' + link + '">' + title + '</a></li>';
                        app.page_list.append(html);
                    });
                } else {
                    console.log(response);
                    alert('Could not find posts with price...');
                }
            }, function (error) {
                console.log(error);
                alert('Could not find posts with price...');
            });
        }
    };

    /*
     Just a wrapper for $.ajax.
     Source: http://api.jquery.com/jquery.ajax/
     */
    app.ajax_call = function (data, success, fail) {
        var $validationApiCall = $.ajax({
            url: nklocal.ajaxurl,
            type: "POST",
            data: data,
            dataType: 'JSON'
        });

        $.when($validationApiCall).then(
            function (response) {
                success(response);
            },
            function (error) {
                fail(error);
            });
        return false;
    };

    $(document).ready(function () {
        //This only loads the script if the form exists with a class meta_search. If you don't have this form nothing will work!
        if ($('form.meta_search').length > 0) {
            app.init();
        }
    });
    return app;

})(window, document, jQuery);

Required HTML for the script to work

<!-- This is our form with the required "meta_search" class. We are also required to have select with ID "page_price_options" and the <ul> with nick83_page_list-->
<form action="" class="meta_search">
    <select name="page_price_options" id="page_price_options">
        <option value="">Please choose...</option>
    </select>
</form>

<div class="page-container">
    <h2>Pages will appear here...</h2>
    <ul class="nick83_page_list"></ul>
</div>

Here's a GIST if that's a cleaner way to read the code and comments: https://gist.github.com/tmort/f80658c3f02ae63e5636aa562f312bd6

Here's a quick video walkthrough since it's a bit hard to understand via text: https://youtu.be/-WyScMmjENo

2
  • Great Job Tom !! The code works again, I was very far from this, even searching for hours would not give an answer like yours...I also found a plugin that can help for advanced filtering (even for beginners) : FacetWP. Thanks again for helping me :)
    – Nick
    Commented Oct 24, 2017 at 19:51
  • @Nick83 No problem. FacetWP is a fantastic plugin as well, enjoy using that!
    – Tom
    Commented Oct 24, 2017 at 20:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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