Changing select options based on previous select with PHP & AJAX

The first step you’ll need to take is to reorganize your code a little. WordPress allows you to register Ajax calls via an action, wp_ajax. This is a safer and easier way to create an ajax call that you can use with javascript.

Below I’ve created everything you’ll need. The first two functions will be placed inside your themes functions.php and the last piece will need to be placed inside a custom.js file inside your themes root directory.

The first function calls the custom.js and queues it for loading on your theme. This is WordPress’ way of sending the script up the loading chain to load inside wp_head or wp_footer, depending on your preference. We also use wp_localize_script to set up a few variables inside of javascript so we can use ajax. These are stored inside clocal, and look like this: clocal.ajaxurl and clocal.nonce. The “nonce” are security keys we use to double check a bad guy isn’t attempting to attack your site via this ajax call, among other things.

/*
 * File: functions.php
 * This function will enqueue (insert) the script into the WordPress header or footer the proper way, instead of adding it directly to the header.php
 * We use wp_localize_script to inject a javascript object, clocal, into our custom.js to gain access to the variables ajaxurl and nonce.
 *
 * Source:
 * https://developer.wordpress.org/reference/functions/wp_enqueue_script/
 * https://codex.wordpress.org/Function_Reference/wp_localize_script
 * https://codex.wordpress.org/Function_Reference/wp_create_nonce
 */

add_action( 'wp_enqueue_scripts', 'cortez_enqueue_script' );
function cortez_enqueue_script() {
    wp_enqueue_script( 'cortez_custom_js', get_stylesheet_directory_uri() . '/custom.js', array( 'jQuery' ), '1.0', true );
    wp_localize_script( 'cortez_custom_js', 'clocal', array(
        'ajaxurl' => admin_url( 'admin-ajax.php' ),
        'nonce'   => wp_create_nonce( 'cortez_nonce_security_key' ),
    ) );
}

The next function is your ajax call. We use two actions, wp_ajax and wp_ajax_nopriv to enable the ajax action “cortez_get_terms”. Notice the action is right inside the WordPress action: wp_ajax_cortez_get_terms. With this defined, we can request on the javascript side the action “cortez_get_terms”.

At the beginning of this function you’ll notice wp_verify_nonce. This is checking that security key we created in the previous function to make sure it is a valid request.

/*
 * File: functions.php
 * Add this to your functions.php to enable the ajax call to be called from custom.js. The two actions are required if you want logged in and non-logged in users to be able to use the ajax function.
 *
 * Source:
 * 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)
 * https://codex.wordpress.org/Function_Reference/wp_verify_nonce
 */
add_action( 'wp_ajax_cortez_get_terms', 'cortez_get_terms' );
add_action( 'wp_ajax_nopriv_cortez_get_terms', 'cortez_get_terms' );
function cortez_get_terms() {
    $data = esc_sql( $_POST );
    if ( ! wp_verify_nonce( $data['nonce'], 'cortez_nonce_security_key' ) ) {
        wp_die( 'Security check' );
    }
    if ( ! isset( $data['term_chosen'] ) || empty( $data['term_chosen'] ) ) {
        wp_die( 'No Term Chosen' );
    }

    $tipos_bicicletas="tipos_bicicletas";
    $modelos_bicicletas="modelos_bicicletas";
    $marcas_bicicletas="marcas_bicicletas";
    $tax_tipos_bicicletas   = get_terms( $tipos_bicicletas, array( 'hide_empty' => false ) );
    $tax_modelos_bicicletas = get_terms( $modelos_bicicletas, array( 'hide_empty' => false ) );
    $tax_marcas_bicicletas  = get_terms( $marcas_bicicletas, array( 'hide_empty' => false ) );

    $json = json_encode( $tax_tipos_bicicletas );
    if ( $data['term_chosen'] == 'bicicleta' ) {
        echo $json;
    }


    wp_die(); //stop function once you've echoed (returned) what you need.
}

Finally, we tie everything together via custom.js. This is a file you’ll need to create inside your theme (wp-content/themes/YOUR_THEME/custom.js). Once created, load the following code into it. This is the same as your ajax call, only it is using the variables we set up in wp_localize_script as the url and it is a POST request, not a GET request.

Notice I am also passing a “data” attribute. This is a set of data I am going to post to the WordPress ajax handler (admin-ajax.php). This must include an “action” node that is your ajax call, which in our case is cortez_get_terms. Without this WordPress does not know what to do.

You’ll see I am passing term_chosen in this data attribute as well. This is the chosen value of whichever dropdown the user chooses. We send this to the ajax script (in the previous function inside functions.php) and if you look we can use that variable to double check the users choice and output the correct data.

/*
 * Filename: custom.js
 *
 */

jQuery(function ($) {
    $("#opt-categorias").change(function () {
        var opt_categorias = $("#opt-categorias").val();
        $.ajax({
            type: "POST",
            url: clocal.ajaxurl,
            contentType: "application/json; charset=utf-8",
            dataType: "json",
            data: {
                'action': 'cortez_get_terms',
                'nonce': clocal.nonce,
                'term_chosen': opt_categorias,
            },
            success: function (data) {
                $("#opt_tipo").empty();
                $("#opt_tipo").append("<option value=""> Tipo de produto</option>");
                $.each(data, function (i, item) {
                    $("#opt_tipo").append('<option value="' + data[i].slug + '">' + data[i].name + '</option>');

                });
            },
            error: function(error){
            },
            complete: function () {
            }
        });
    });
});

This is a quick and dirty example and there is definitely more to suggest but I think this should get you started. Be sure to take a look at the “source” links I put in the comments of the functions and let me know if you have any questions.