0

I am developping a theme for wordpress which uses woocommerce. Currently I have developed a new page where I show the products. Initially I did the page and the filters for categories without using ajax and everything worked perfectly. However, the designer did not like that the page reloads when a filter was applied, then I developed everything again using ajax from wordpress because the pure ajax did not work. Now the page runs okay, the first load and the filters works with ajax. But the issue is the following: When the page calls to admin-ajax, it delays 3-5s. However, when I tested in localhost the duration was instantaneous.

This is the function in functions.php:

add_action('wp_ajax_nopriv_filter_category_action','educaforma_filter_ajax');
add_action('wp_ajax_filter_category_action','educaforma_filter_ajax');

function educaforma_filter_ajax(){
$category_filter = "";
if(isset($_POST['category'])){
    if($_POST['category'] == "todos"){
        $category_filter = "";
    }
    else{
        $category_filter = $_POST['category'];
    }
}
$output = "";
$params = array(
    'product_cat' => $category_filter,
    'posts_per_page' => 15,
    'post_type' => 'product',
);
$wc_query = new WP_Query($params);
$count = $wc_query->post_count;
$cont_post = 0; 
    if ($wc_query->have_posts()) : 
    while ($wc_query->have_posts()) :
            $wc_query->the_post(); 
            global $product;
            if($cont_post == 0): 

                $output .= "<div class = 'row'>"; 

            elseif($cont_post%3 == 0):

                $output .= "</div>"; 
                $output .= "<div class = 'row'>"; 

            endif;

    $output .= "<div class ='col-md-4 col-sm-4'>";
        // <!-- <div class="container-course"> -->
        $output .= "<a class='list-course' href='".$product->get_permalink()."'>";

            // $output .= the_post_thumbnail();
            // $output .= $product->get_image(artiest,1,1); 
            $attachment_id = get_post_thumbnail_id( $product->id );
            $attachment = wp_get_attachment_image_src($attachment_id, 'full' );
            $output .= "<div class='image-product' style='position: relative;
                                    background-repeat: no-repeat;
                                    background-position: 50% 50%;
                                    background-size: cover;
                                    background-clip: border-box;
                                    box-sizing: border-box;
                                    overflow: hidden;
                                    height: 180px;
                                    width: 100%;
                                    background-image: url(".$attachment[0].") !important;
                                    '>
                                    <div class='darken-image'></div>
                        </div>";
            $output .= "<div class='separator'></div>";
            $output .= "<div class='container-course'>";
                $output .= "<h3 class='title-course'>";
                // $output .= the_title(); 
                $output .= $product->get_title();

                $output .= "</h3>";
             //the_excerpt(); 
             $output .= "<p class='title-attribute'>Curso:  <span class='value-attribute'>".$product->get_attribute('Curso')." </span></p>";
             $output .= "<p class='title-attribute'>Inicio:  <span class='value-attribute'>". $product->get_attribute('Inicio')." </span></p>";
             $output .= "<p class='title-attribute'>Finalización:  <span class='value-attribute'>". $product->get_attribute('Finalizacion')." </span></p>";
             $output .= "<p class='title-attribute'>Precio:  <span class='value-attribute'>".$product->get_attribute('Precio')." </span></p>";
             $output .= "<p class='title-attribute'>Avalado por: <span class='value-attribute'> ".$product->get_attribute('Avalado')." </span></p>";
            $output .= "</div>";
        $output .= "</a>";
    $output .= "</div>";

     $cont_post++; 
  endwhile; 
  wp_reset_postdata(); 
  else:  
    $ouput.= "<li>";
    $ouput .= "No hay cursos";
    $output.="</li>";
  endif; 
echo $output;
}

This is a part of the javascript ajax:

function($){
$(document).on('click','#negocios',function(e){
    e.preventDefault();

    $.ajax({
        url: filter_categories_vars.ajaxurl,
        type: 'POST',
        data: { 
            action: 'filter_category_action', // The name of the WP action
            category: 'negocios-internacionales',
        },
        // dataType: 'json',
        complete : function ( ) {   // optionally to develop in any case: success or error
                }, 
        success: function ( response ) {     // to develop in case of success
                $('#cursos_catalogo').fadeOut(600)
                setTimeout(() => {

                    $('#cursos_catalogo').html(response.substring(0,(response.length - 1)));  
                    $('#cursos_catalogo').fadeIn(600)
                }, 600);
                document.getElementById("negocios").classList.add("filtered");
                document.getElementById("todos").classList.remove("filtered");
                document.getElementById("turismo").classList.remove("filtered");
                document.getElementById("habilidades").classList.remove("filtered");

        },
        error: function ( errorThrown ) {   // to develop in case of error
                    console.log("ERROR")
                    console.log( errorThrown ); 
                }, 
    });

});

duration loading

I would like to know two things:

  1. Why I can not use a normal ajax?

  2. Why the admin-ajax is so slow and what can I do for reduce this delay?

Thanks

1

Possible solution

I think a part of your issue is the \WP_Query call - querying the database isn't "cheap" with regards to making fast, efficient AJAX calls, but you also can't really avoid it in your situation.

One approach you could take would be caching the results of the query for some amount of time, at least in that case you would have one initially slow response for a category, and then rapid responses for subsequent queries.

function educaforma_filter_ajax(){
    $category_filter = "";

    if(isset($_POST['category'])){
        if($_POST['category'] == "todos"){
            $category_filter = "";
        }
        else{
            $category_filter = $_POST['category'];
        }
    }

    $output = "";
    $params = array(
        'product_cat'    => $category_filter,
        'posts_per_page' => 15,
        'post_type'      => 'product',
    );

    // Create a cache key based on the parameters and try to get a transient.
    $cache_key      = "educaforma_filter_results_" . implode( '_', $params );
    $cached_results = get_transient( $cache_key);

    if ( ! empty( $cached_results ) ) {
        echo $cached_results;
        exit;
    }

    $wc_query = new WP_Query($params);
    $count = $wc_query->post_count;
    $cont_post = 0; 
    if ($wc_query->have_posts()) : 
        while ($wc_query->have_posts()) :
            $wc_query->the_post(); 
    global $product;
    if($cont_post == 0): 

        $output .= "<div class = 'row'>"; 

    elseif($cont_post%3 == 0):

        $output .= "</div>"; 
    $output .= "<div class = 'row'>"; 

    endif;

    $output .= "<div class ='col-md-4 col-sm-4'>";
    // <!-- <div class="container-course"> -->
    $output .= "<a class='list-course' href='".$product->get_permalink()."'>";

    // $output .= the_post_thumbnail();
    // $output .= $product->get_image(artiest,1,1); 
    $attachment_id = get_post_thumbnail_id( $product->id );
    $attachment = wp_get_attachment_image_src($attachment_id, 'full' );
    $output .= "<div class='image-product' style='position: relative;
    background-repeat: no-repeat;
    background-position: 50% 50%;
    background-size: cover;
    background-clip: border-box;
    box-sizing: border-box;
overflow: hidden;
height: 180px;
width: 100%;
       background-image: url(".$attachment[0].") !important;
       '>
           <div class='darken-image'></div>
           </div>";
       $output .= "<div class='separator'></div>";
       $output .= "<div class='container-course'>";
       $output .= "<h3 class='title-course'>";
       // $output .= the_title(); 
       $output .= $product->get_title();

       $output .= "</h3>";
       //the_excerpt(); 
       $output .= "<p class='title-attribute'>Curso:  <span class='value-attribute'>".$product->get_attribute('Curso')." </span></p>";
       $output .= "<p class='title-attribute'>Inicio:  <span class='value-attribute'>". $product->get_attribute('Inicio')." </span></p>";
       $output .= "<p class='title-attribute'>Finalización:  <span class='value-attribute'>". $product->get_attribute('Finalizacion')." </span></p>";
       $output .= "<p class='title-attribute'>Precio:  <span class='value-attribute'>".$product->get_attribute('Precio')." </span></p>";
       $output .= "<p class='title-attribute'>Avalado por: <span class='value-attribute'> ".$product->get_attribute('Avalado')." </span></p>";
       $output .= "</div>";
       $output .= "</a>";
       $output .= "</div>";

       $cont_post++; 
       endwhile; 
       wp_reset_postdata(); 
else:  
       $ouput.= "<li>";
       $ouput .= "No hay cursos";
       $output.="</li>";
       endif; 

    // Set the transient here!
    set_transient( $cache_key, $output, DAY_IN_SECONDS );
    echo $output;

}

In the above snippet, I added the following lines:

    // Create a cache key based on the parameters and try to get a transient.
    $cache_key      = "educaforma_filter_results_" . implode( '_', $params );
    $cached_results = get_transient( $cache_key);

    if ( ! empty( $cached_results ) ) {
        echo $cached_results;
        exit;
    }

This will create a "key" based on the parameters and attempt to find a matching transient with that name. Further below:

    // Set the transient here!
    set_transient( $cache_key, $output, DAY_IN_SECONDS );
    echo $output;

This sets the transient if we didn't stop earlier when we found it. Now, the next time someone queries for the same category, the transient will be set and the results should be returned very quickly.

Notice also that the expiration is set for 24 hours (DAY_IN_SECONDS). You may want it shorter or longer, and depending on how frequently you update content, you may want some other means of invalidating the transients.

On query slowness

One reason your query may be slow is that it's using your custom parameter, product_cat, which I'm assuming you're parsing in some filter on the query somewhere and that probably relates to a term relationship. This means that WordPress is JOIN'ing several tables, and depending on your database, this can be a little slow.

Also, you're delaying your response by 600 milliseconds with setTimeout - I know it's not huge, but consider that you're adding more than half a second to an already slow response.

I'm not sure what you can do to serve the content quicker aside from caching, at least without really digging into the internals of your site. Adding some form of visual feedback - like a loading spinner - could at least let users know that something is happening and will at least help the perception of your site's load time (even if it doesn't actually get any faster).

  • 1
    thanks for your answer. I think that your possible solution can be usefull for someone, but in my case, the delay did not decrease. Another note is that anything that the function return, has the same delay, then the wordpress query is not causing this long time. I have found 2 alternatives solutions with new code structures for this problem that I will write when I have time. Anyway, thank you for your answer. – Javi Torre Sustaeta Apr 5 at 12:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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