2

What I am trying to do is is use jQuery/Ajax to make a request to a PHP script and return a status update each time the foreach loop completes. I want to use the output to make a progress bar.

The problem I am having is the jQuery will only update the page once the whole script has completed. Is there a way to force either the jQuery or PHP to output on every loop and not just dump all of it on success function.

        jQuery(document).ready(function($) {            
            $(document).on('click','#fetch-snowfall-data a',function () {
              $(this).text('Fetching Snowfall Data...');
                $.ajax({
                    type: "GET",
                    async: true,
                    url: "url.php",  
                    data:{},                
                    dataType: "html", 
                    success: function(response){
                        $("#response_container").append(response);
                        $('#fetch-snowfall-data a').text('Fetch Snowfall Data');
                    }
                }); 
            return false;
            });
        });

PHP

foreach ( $results['resorts'] as $resort ) {

    //Do all the things here

    $count++;
    echo $count .'/'. $results['total_rows'];

}
user1711576
  • 412
  • 10
  • 25
  • Not really, you'd probably need SSE, Sockets or do polling for that, you generally can't return partial content (but it is possible with flushing) – adeneo Jan 05 '16 at 18:47
  • An easy solution would be add the `beforeSend:` attribute and put a function that will fill your container with a spinner gif or something so at least people will see it's doing something. You would need a more sophisticated jquery: http://stackoverflow.com/questions/19139613/update-progress-bar-using-ajax-request-seconds for actual progress. – Rasclatt Jan 05 '16 at 18:48
  • @Rasclatt I tried that earlier but I couldn't get any output from it. It doesn't detail any of the server side code, so I was not sure how to structure output – user1711576 Jan 05 '16 at 18:57
  • @adeneo I tried using flushing to no avail. I have been looking at SSE but have yet found a relevant example or tutorial. If you know of any please share – user1711576 Jan 05 '16 at 18:58
  • So the `console.log(percentComplete)` listed in the example didn't produce anything in your console? – Rasclatt Jan 05 '16 at 18:59
  • Just to put it out here, you can also do recursive on ajax upon success. Then have progress bar on each ajax call – stanley1943 Jan 05 '16 at 19:00
  • @Rasclatt in the `console.log(percentComplete)` output all I get is `1` on completion of the php script – user1711576 Jan 05 '16 at 19:07
  • @stanley1943 Do you have any examples/tutorials of how to do this? – user1711576 Jan 05 '16 at 19:08
  • I suggest to store result in an array and than use json encode .. and use a loop in ajax success – devpro Jan 05 '16 at 19:46

2 Answers2

4

Thanks everyone for all your help. I eventually managed to get this all working properly and will share exactly how I did it for anyone else's future benefit.

There are 3 files that are required - The page you are loading from, the processing script and the listening script.

You will need to know how many rows you are going to process for everything to work. I load mine from php variable $results['total_rows']; in loading.php

Loading.php

       jQuery(document).ready(function($) {
            setTimeout(getProgress,1000);
            $(document).on('click','#fetch-snowfall-data a',function () {
              $(this).text('Fetching Snowfall Data...');
                $.ajax({
                    url: 'process.php',
                    success: function(data) {
                        $("#response_container2").append(data);
                    }
                });
                setTimeout(getProgress,3000);
            return false;
            });
            function getProgress(){
                $.ajax({
                    url: 'listen.php',
                    success: function(data) {
                        if(data<=<?php echo $results['total_rows']; ?> && data>=1){
                            console.log(data);
                            $('#response_container').html('<div id="progress"><div class="progress-bar" role="progressbar" style="width:'+ (data / <?php echo $results["total_rows"] ?>)*100 +'%">'+ data + '/' +<?php echo $results["total_rows"] ?> +'</div></div>');
                            setTimeout(getProgress,1000);
                            console.log('Repeat');
                        } else {
                            $('#fetch-snowfall-data a').text('Fetch Snowfall Data');
                            console.log('End');
                        }
                    }
                });
            }
        });

Process.php

foreach ( $results['resorts'] as $resort ) { 

    //Do all the things here you want to.

    $count++;
    session_start();
    $_SESSION['progress'] = $count;
    $_SESSION['total'] = $results['total_rows'];
    session_write_close();
    sleep(1);
}

unset($_SESSION['progress']);

Listen.php

session_start();
echo (!empty($_SESSION['progress']) ? $_SESSION['progress'] : '');

if (!empty($_SESSION['progress']) && $_SESSION['progress'] >= $_SESSION['total']) {
    unset($_SESSION['progress']);
}

Some CSS for the progress bar

#progress {
    height: 20px;
    margin-bottom: 20px;
    /* overflow: hidden; */
    background-color: #f5f5f5;
    border-radius: 4px;
    -webkit-box-shadow: inset 0 1px 2px rgba(0,0,0,.1);
    box-shadow: inset 0 1px 2px rgba(0,0,0,.1);
}
.progress-bar {
    float: left;
    width: 0;
    height: 100%;
    font-size: 12px;
    line-height: 20px;
    color: #fff;
    text-align: center;
    background-color: #337ab7;
    -webkit-box-shadow: inset 0 -1px 0 rgba(0,0,0,.15);
    box-shadow: inset 0 -1px 0 rgba(0,0,0,.15);
    -webkit-transition: width .6s ease;
    -o-transition: width .6s ease;
    transition: width .6s ease;
}
user1711576
  • 412
  • 10
  • 25
1

As suggested by adeneo, I dont think you can return partial content. I tried to research on this before with no avail. However, if anyone can correct me, i'll be very appreciative.

The end result for me is to do recursive ajax.

javascript

var data = '';
$(document).on('click','#fetch-snowfall-data a',function () {
  ajaxCall(0);
});

function ajaxCall(num) {
  $.ajax({
    type: "POST",
    async: true,
    url: "url.php",
    data: num,
    dataType: "json",
    xhr:function(){
        //progress bar information here.
    },
    success: function(response) {
      $("#response_container").append(response['data']);
      if (response['check'] === 'next') {
        var nextNum = num +1;
        data += response['data'];
        ajaxCall(nextNum); //call itself for the next iteration
      }else if (response['check'] === 'done'){
        data = response['data'];
        //do stuff with the data here.
      }else if (response['check'] === 'error') {
        return response['data'];
      }
    },
    error:function(xhr, status,error){
        //error information here
    }
  });
}

For PHP:

Just make sure you output stuff in json with two information. Json.check: to see if you want to move into the next iteration, finish and output data, or report error. Json.data to output whatever data it needs. What you need to do is output each report at a time without foreach loop

--edit--

I found some topic on streaming for php

  1. http://www.sitepoint.com/php-streaming-output-buffering-explained/
  2. Best approach for (cross-platform) real-time data streaming in PHP?
Community
  • 1
  • 1
stanley1943
  • 865
  • 1
  • 7
  • 15
  • It is possible in coldfusion, but i'm not a php dev so no idea if it can be done with php. Look into data streaming and/or flushing the buffer. – Kevin B Jan 05 '16 at 21:14
  • @KevinB Thanks, i'll definitely look into it. It seems like running nodejs would be better than php? – stanley1943 Jan 05 '16 at 21:38
  • the same can be accomplished in both, so at that point it's up to personal preference. I prefer node.js – Kevin B Jan 05 '16 at 21:46