drew.d.lenhart

programming, software, technology, anything on my mind...

Auto-refreshing Dashboard in Slim/Twig

2016/08/20

In my last post I showed off an experiment, a Job/Task scheduler in PHP. As part of the experiment I needed a dashboard to see the status of jobs/tasks in the queue. To do this I wanted a view that was up to date as possible without completely refreshing the whole window. In a much older post I showed how to do this. But this time around I created it differently since I created the dashboard portion using Slim 3, Eloquent ORM, and the Twig template engine.




Slim Routes

I created three routes. The first is the home/root route which is self explanatory ( its rendering the main page).

Take a look at this screen shot with the red boxes:


The content inside the red boxes is what I needed to refresh at a regular interval. I set the refresh interval to 5 seconds ( see next section where this is done ). To have something refresh, I created TWO routes that can be accessed via url:


//QUEUE - queries queue table.
$app->get('/boxes/queue','stphpschedule\Controller\HomepageController:queue');
//JOBS - queries job table.
$app->get('/boxes/jobs','stphpschedule\Controller\HomepageController:job');

Here are the controller methods for each route:


    public function queue(Request $request, Response $response, $args)
    {
        $que = New Queue;
        $que = Queue::all();
        $body = $this->view->fetch('home_queue.html', array('qItems' => $que));
        return $response->write($body);
    }

    public function job(Request $request, Response $response, $args)
    {
        $job = New Jobs;
        $job = Jobs::all();
        $body = $this->view->fetch('home_job.html', array('jItems' => $job));
        return $response->write($body);
    }

If you aren't familiar with Slim, model, views, or controllers this may be a bit confusing. Basically I created a route and called a controller method to perform an action. Take the queue method for example, here a new Queue object is created, ( this is where I am refrencing the Queue model - this is the magic of using Eloquent! ), Queue::all() is an Eloquent method for querying the database for all items. In other words:


$sql = "SELECT * FROM table";

After the query, the results are stored in an array and passed into a Twig template ( home_queue.html ).

Heres the Twig template for home_queue.html


{% block content %}
<div class="box_c"><div class="box_t">Queue Status:</div><div class="st_boxes">
    <table class="table"><tr><td>JOB NUM</td><td>JOB_ID</td><td>PATH</td><td>IN QUE TIME</td><td>STATUS</td><td>Actions</td></tr>
    {% for item in qItems %}
    <tr>
        <td>{{ item.id }}</td>
        <td>{{ item.job_id }}</td>
        <td>{{ item.path }}</td>
        <td>{{ item.in_que_time|date("m/d/Y H:i:s") }}</td>
        {% if item.hold == 0 %}
            <td>Held</td>
        {% else %}
            <td>Ready</td>
        {% endif %}
        <td>
        {% if item.hold == 0 %}
            <a class="btn btn-default btn-xs" alt="Run job." href="#" role="button" onclick="holdJob('{{ item.id }}', 1)"><span id="runSpin{{ item.id }}" class="glyphicon glyphicon-refresh spinning"></span> Run</a>
        {% else %}
            <a class="btn btn-danger btn-xs" alt="Put job on hold." href="#" role="button" onclick="holdJob('{{ item.id }}', 0)"><span id="runSpin{{ item.id }}" class="glyphicon glyphicon-refresh spinning"></span> Hold</a>
        {% endif %}
        <a class="btn btn-danger btn-xs" alt="Remove job from queue" href="#" role="button" onclick="deleteJobq('{{ item.id }}')"><span class="glyphicon glyphicon-remove" aria-hidden="true"> <span id="delSpin{{ item.id }}" class="glyphicon glyphicon-refresh spinning"></span> </span></a>
        </td>
    </tr>
    {% else %}  
        <p style='color: red'>There are currently no items in the queue.</p>
    {% endfor %}
    </table></div>
</div>
{% endblock %}

You should be able to hit each route and see data:

http://whateverdomain/boxes/queue
http://whateverdomain/boxes/jobs

We'll need these url paths for the javascript code.


Javascript

This is the code that does all the magic:


(function($){
    $(document).ready(function()
    {
        $.ajaxSetup(
        {
            cache: false,
            beforeSend: function() {
                $('#loading2').show();
                $('#loading').show();
            },
            complete: function() {
                $('#jobs').show().fadeIn("slow");
                $('#queue').show().fadeIn("slow");
            },
            success: function() {
                $('#loading').hide();
                $('#loading2').hide();
                $('#jobs').show().fadeIn("slow");
                $('#queue').show().fadeIn("slow");
            }
        });
        var $container = $("#jobs");
        var $container2 = $("#queue");

        $container.load("/boxes/jobs").fadeIn("slow");
        $container2.load("/boxes/queue").fadeIn("slow");
        var refreshId = setInterval(function()
        {
            $container.load('/boxes/jobs').fadeIn("slow");
            $container2.load('/boxes/queue').fadeIn("slow");
        }, 5000);  //refresh container(s) every 5 seconds
    });
})(jQuery);

There really isn't anything too fancy here. Im making use of JQuery to .show() and .hide() some text boxes as the data is being pulled. The real meat is the .load('some url can go here'). The two routes created are called here. setInterval() is doing the actual work of refreshing at 5000 milliseconds ( 5 seconds ).


Feel free to browse the source of the project and experiment on your own!

Thanks for reading! --Drew

View code on Github!