Ajaxify Your Laravel Application Automatically

You have a “nice little” Laravel app. You return views from your controllers, you redirect with a flash message when you’re done with something or when something goes wrong.

But sometimes you need to send Ajax requests to the server. And this time those views and redirects don’t make sense. You need JSON response. You create two different methods in your controller, one returns JSON and one returns View, because you also want the page to be loaded from server when user(or search engines) hit the url directly. And now things start to get messy.

What if you can write an HTTP Middleware which detects Ajax requests and converts responses to Ajax compatible responses? For example turn the response of this:

$user = ['name' => 'Usman', 'email' => 'email@domain.com'];
return View::make('user.show', $user);

into this

{"name": "Usman", "email": "email@domain.com"}

or response of this

return Redirect::to('/')->with('error', 'User not found');

to this

{"error": "User not found"}
// with a 301 header

That’d be great, right? All this can be automated, I will show you how.

Let’s Code

We’ll create an HTTP middleware by running php artisan make:middleware Ajaxify
Or just create the class manually in (app/Http/Middleware/).

It’ll look like this:

<?php

namespace App\Http\Middleware;

use Closure;

class Ajaxify
{
    public function handle($request, Closure $next)
    {
        return $next($request);
    }
}

Handle Method

Here we basically use couple of helper methods, which we’ll create next.
We check if we really need to alter the original response or not. Then we check if the response is successful (it has 20* status code) or not. If yes, we get all the the data passed to our view and then return the data as JSON response. It’s the key thing here.

If the response is not successful, we get all the flash data. If we have them then we return the flash data as a JSON response.

public function handle($request, Closure $next, $guard = null)
{
    // Get the original response
    $response = $next($request);

    if (! $this->shouldAjaxify($request, $response)) {
        return $response;
    }

    // A 20* response
    if ($response->isSuccessful()) {
        $originalContent = $response->getOriginalContent();
        // Get the data we passed to our view
        $data = $originalContent->getData();
        // Return the data passed to view as JSON response
        return response()->json($data);
    }

    // We don't have a successful response,
    //  we rather have a redirect like response

    $flashData = $this->getFlashData($request);
    if (! count($flashData)) {
        return $response;
    }
    // Return all the flash data as JSON
    return response()->json($flashData, $response->getStatusCode());
}

Helper Methods

Now we’ll add couple of protected methods in the class. First one is to help us determine if we should convert the response to JSON response or not.

protected function shouldAjaxify($request, $response)
{
    // If we already have a JSON response we don't need to do anything
    if ($response instanceof JsonResponse) {
        return false;
    }

    // If there is a server (status 50*) error, we won't do anything
    if ($response->isServerError()) {
        return false;
    }

    // It's not a View response
    if ($response->isSuccessful() && ! method_exists($response->getOriginalContent(), 'getData')) {
        return false;
    }

    // Now if it's an Ajax request or the clients wants a JSON response or we've
    //  a query string param 'ajaxify' then we'll Ajaxify, else we won't.
    return $request->ajax() || $request->wantsJson() || $request->exists('ajaxify');
}

So this method basically determines if we need to alter the response or just ignore it.

Now another helper method to get all flash data from the session:

protected function getFlashData($request)
{
    // Get all session data and convert the array to a Collection object
    $sessionData = collect($request->session()->all());
    // Filter only flash data from session data
    $flashedKeys = $request->session()->get('flash.new');
    $flashData = $sessionData->only($flashedKeys);
  
    // Delete flash data, as we've already used them
    $request->session()->forget($flashedKeys);
    return $flashData;
}

Finally

Register our middleware as a global middleware, by going to this file app/Http/Kernel.php.
in the web section we will add this:

protected $middlewareGroups = [
        'web' => [
            ...
            \App\Http\Middleware\Ajaxify::class,
        ],
        ...
    ];

Now you can try sending an Ajax request to your existing route or just try this in your browser: http://yourapp.dev?ajaxify

Caution:
Do not pass sensitive data to your view or to flash session. It will expose all data. Be careful when passing full model, use $hidden and $visible properties in your models to secure sensitive database fields.