AJAX 5.7 style

5.7+
AJAX 5.7 style

Learn how to make AJAX requests in concrete5 5.7


Article by Ollie / / Comments / Difficulty 
AJAX 5.7 style

Some time ago now, I wrote this article, AJAX driven applications in concrete5. You may have read it - it seems to be one of the more popular articles on c5hub.

But it's irrelevant now. Like much of the older content on c5hub, you'll only find that article useful if you plan to stay with concrete5 5.6 long term. If you're using concrete5 5.7, the latest version, you'll need to approach this problem (and several others) in a different way.

So, let's pick up the topic of AJAX again, but in the brave new world of concrete5 5.7.

First of all, it's helpful to understand two major changes in 5.7 that mean we can (and to an extent, have to) tackle this problem in a different way.

Let's review them:-

Deprecation of 'tools' files

For as long as I've been around concrete5 we've had tools files. These were files that sat outside the MVC application structure in a tools folder. Tools provided a concrete5 runtime without the overhead of any frontend UI, making them ideal for operations that didn't need a UI. In our previous 5.6 article, tools files were used to provide end points that a typical Ajax request would need. These files ran, echoed their output which was subsequently returned to the Ajax request as a response.

But, tools files are no more. Along with several other file types these have been deprecated in 5.7 which means we need to do this in a different way.

One way is to use a Controller to return output in much the same way the tools file generated the output. Our AJAX call will route to a Controller and Method to return a View (or in this case just echo the output from the controller), rather than a use separate 'tools' powered URI end point.

Which brings me to a second major change.

Routing

In 5.6 the way you created routes for things like single pages and their partnering controllers was to physically represent the route with a directory path structure.

concrete5 5.7 introduces many new ideas and borrows from other frameworks where they represent better/best practice. And concrete5 5.7 borrows the routing component from Symfony2, a renowned PHP MVC framework which you've probably heard of.

Symfony2 provides its functionality through a suite of components: components which can easily be reused by other applications and frameworks.

What does Symfony2's routing component bring to concrete5 you're wondering?

Well, whilst route creation using physical single pages and controllers still works, in 5.7 you can create routes and map them independently of path structure too. You'll recognise the approach, first popularised in Ruby (I think), when we come to look at the code.

So that's the background on what has changed and why it is now necessary to approach something like making AJAX requests in a different way.

Making the request.

Just as in 5.6, there are still 3 pieces to the AJAX request.

  • The request itself, made from a block or single page view. And we're still going to be assuming that file is called 'view.php'.
  • The second piece is the server-side PHP script. But, you guessed it - it will not be a tools file but a fully fledged controller.
  • The third piece is, as in the 5.6 tutorial, a routine to handle the returned data and update the web page itself.

The third piece is unchanged, the first piece get's only a minor tweak to make the request to a different endpoint now we're not using tools files. It's the second piece of the request that's most different - this code will now run in a controller, how do we create that controller and how do we access it?

So on to the code, and as with the 5.6 tutorial we're going to use the simple best/favorite animal example.

First, we need to create a route, and map that to our controller. We're going to do it the Symfony2 way.

Open /application/bootstrap/app.php. At some point have a read of the comments whilst you are in this file, there's lots of things you can configure in application/bootstrap.php, but for now add this code to the bottom of the file:

Route::register(
    '/my/ajax/controller/animal/{favorite}',
    'Application\Controller\Animal::setFavorite'
);

We're using Route::register() to create a new route. Our endpoint for the ajax request will be (assuming pretty URLs are turned off) index.php/my/ajax/controller/animal/{favorite}, where {favorite} is a parameter and will provide the value of our favorite animal that we pass as part of the request.

The second parameter is the namespaced controller name we want to request and the method in that controller class to run. Neither exist yet, so we need to create them.

Create the controller in /application/controllers/ called animal.php
Add this code to the file - we'll walk through it in a moment:

namespace Application\Controller;
use Concrete\Core\Controller\Controller;
use \Concrete\Core\Http\Request;
use Core;

class Animal extends Controller {

    public function setFavorite() {
        $animal = Request::getInstance()->get('favorite');
        $th = Core::make('helper/text');
        $animal = $th->sanitize($animal);
        echo 'You told us your favorite animal is a "' . $animal .
             '", so we updated this page with that information using AJAX. Boom!';
    }

}

This file could use some explanation, aspects of it are different than what you'll be used to if coming from 5.6.

Concrete5 5.7 now uses PHP5 namespacing. This to prevent naming collisions whilst allowing developers to use simple class naming conventions and not long and convoluted names simply to avoid any collisions.

The use statements below it specify which other namespaces we need to use in our class, so Controller, Request and Core in this example. Where we use a short name i.e Core, we're referring to an alias of a Namespace, the longer backslash separated names are the full namespaces.

In the setFavorite method we're using Symfony2's request component to extract the value of the 'favorite' parameter we specified when we registered the route previously.

One other difference may jump out here. We're not using loader::helper to load the text validation class, that too has been deprecated. Instead we're using a more generic core::make method call to access the functionality, which is intended to make the process of overriding particular pieces of functionality much easier.

Once sanitized we output the favorite animal, along with some other text to screen.

You should now be able to access this URI in your browser, and if you put something in place of the {favorite} parameter, you should see it echo to screen.

We're done, we've created a custom route and a controller, which combined give us exactly the same functionality that the tools file did.

Lets create our view.php now. The only change to this file is that we are not accessing a tools endpoint, so we need a reliable way of accessing the custom route we defined above: reliable in so far as it will work even if we turn Pretty URLs on (thus removing index.php from the URL) or vice versa.

First the form fields. Note, I'm using the deprecated 'Loader::helper' syntax. It still works, it just wraps the more desirable Core::make method:-

$form = Loader::helper('form');
$opts = array('dog' => 'Dog', 'cat' => 'Cat');
echo $form->label('animal', 'Which animal is best?');
echo $form->select('animal', $opts, 'dog');
?>

Next a placeholder for our AJAX response:-

<div id="ajax-content"></div>

And finally, our javascript that initiates the AJAX request every time the select box is updated. Note the syntax used to safely represent the custom route to our Animal controller and the setFavourite method:-

<script type="text/javascript">
$(document).ready(function(){
    $('#animal').on('change', function(e){
        var animal = $(this).val();
        $.ajax({
            url: "<?php echo URL::to('/my/ajax/controller/animal');?>/" + escape(animal),
            success: function(response) {
                $('#ajax-content').html(response);
            }
        });
    });
});
</script>

If you've implemented all that, you're now doing AJAX concrete5 5.7 style!

Hope that helps. If I can improve it, let me know in the comments!

Join the conversation

comments powered by Disqus