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 protected]'];
return View::make('user.show', $user);

into this
{"name": "Usman", "email": "[email protected]"}

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.

Don’t Fear Exceptions

If you’re not one of the few, you surely get scared once you see one of these:

Fatal error: Uncaught exception ‘Exception’ with message ‘Not found’ in /home/usman/www/test.php on line 27

Fatal error: Uncaught exception ‘UnexpectedValueException’ in /home/usman/www/test.php on line 27

When you see Red or Orange screen with Exception you fear like you’ve committed a sin and just go back to code to repent. Congrats, you’re the majority.

What if I told you that Exceptions are no sin, rather blessings of object oriented programming?
What if I told you that an Exception doesn’t just mean that your code is wrong?

Let me show you how.

For example code, I am using Laravel PHP framework. But the knowledge is not Laravel specific. This article assumes that you know basics of Exception and how to catch it.

An Example

I believe in practical example, so rather than preaching, I am showing you a real life example.

It is really common, when a you search for an entry in database, you check if this entry exists. If not then you show an error.

$person = Person::find($id);

if (! $person) {
    // Show message: Person not found
}

// Person found
return View::make('person.show', compact('person'));

You handle this problem with an if everywhere, why? Why don’t you just throw an exception when something doesn’t exist? Do just this:

$person = Person::findOrFail($id); // Will throw an exception if not found
return View::make('person.show', compact('person'));

In just one place catch this error. For Laravel, put this in your app/start/global.php:

App::error(function(\Illuminate\Database\Eloquent\ModelNotFoundException $e)
{
    return 'Entry not found'; // You can show 404, or do whatever you want
});

If you’re not using Laravel then catch the exception using try-catch when you boot your application.

Advantages of Using Exceptions

An Exception is just what is not expected. Be fearless to throw an Exception whenever you think something is not expected and the function/method/application should stop executing further.

One of the big advantages of using Exception over traditional redirect, die/exit is that you separate your business logic from your transport layer(ex, HTTP).

For example, you use a direct HTTP redirect when a form validation fails. Now what if the end user is not using a browser at all, he is using your mobile app which uses the same business logic with an API? What if you’re unit testing? What if you’re firing a background job with CLI? No HTTP at all, now how?

You’re right, Exception. Create a class ValidationException which extends \Exception. And whenever a validation fails:

    throw new ValidationException('Hey kid, you forgot your name?');

Now catch this Exception:
In case of usual HTTP use, do a redirect.
In case of API, set a status code with JSON message perhaps.
In case of CLI, just dump the error message.

In the Example section above, you’ve seen another advantage. How we made our code simple and DRY(Don’t Repeat Yourself).
There are many other advantages, I’m not going over everything, please check this article if you’re not convinced.

In the end

I ask you one thing, please do yourself a favor, make use of object oriented best practices(like utilizing Exception) where it fits, you won’t regret. Our PHP community has been late in this regard, but the revolution is coming.

Please feel free to share your thoughts.

If you’re a Laravel developer then you may also like my model self-validating package with Exception – DRYVal.

Strana – Smart Pagination Library for PHP

A framework agnostic, smart pagination library for PHP. Just a few lines of code and fully functional pagination is ready.

Paginate your records with Strana. Strana will slice(limit and offset) these records, generate pagination links for you and reads page number from them, all automatically.

Features:

  • Built-in adapters for DoctrineEloquent (Laravel)Pixie, PHP Array and you can do it manually.
  • Readable syntax
  • Add Infinite Scroll with one line
  • It automatically detects which DBAL you are using.
  • Styles automatically with Twitter Bootstrap, Zurb Foundation and most of other CSS frameworks.

Check out the installation instructions and documentation on GitHub.

Install or View on GitHub

Screenshot:

Screenshot

Example

Basically Strana makes it very easy, like the code below:

$paginator = $strana->perPage(10)->make($records);

That’s basically it.

Full Usage Example

// Make sure you have Composer's autoload file included
require 'vendor/autoload.php';

$strana = new \Strana\Paginator();
$records = array(1, 2, 3, .... 100);
$paginator = $strana->perPage(10)->make($records);

// Loop paginated items
foreach ($paginator as $item) {
    echo $item['field_name'] . '<br>';
}

// Print pagination links
echo '<br><br>;' . $paginator;

There are some advanced options which are documented below. Sold? Lets install.

Pixie – A Database Query Builder for PHP

It took me few weeks to build Pixie, a lightweight, expressive, framework agnostic query builder for PHP, it can also be referred as a Database Abstraction Layer. Pixie supports MySQL, SQLite and PostgreSQL and it takes care of query sanitization, table prefixing and many other things with a unified API. At least PHP 5.3 is required.

It has some advanced features like:

  • Query Events
  • Nested Criteria
  • Sub Queries
  • Nested Queries
  • Multiple Database Connections.

Check out the installation instructions and documentation on GitHub.

Install or View on GitHub

Example

// Make sure you have Composer's autoload file included
require 'vendor/autoload.php';

// Create a connection, once only.
$config = array(
            'driver'    => 'mysql', // Db driver
            'host'      => 'localhost',
            'database'  => 'your-database',
            'username'  => 'root',
            'password'  => 'your-password',
            'charset'   => 'utf8', // Optional
            'collation' => 'utf8_unicode_ci', // Optional
            'prefix'    => 'cb_', // Table prefix, optional
        );

new \Pixie\Connection('mysql', $config, 'QB');

Simple Query:

The query below returns the row where id = 3, null if no rows.

$row = QB::table('my_table')->find(3);

Full Queries:

$query = QB::table('my_table')->where('name', '=', 'Sana');

// Get result
$query->get();

Query Events:

After the code below, every time a select query occurs on users table, it will add this where criteria, so banned users don’t get access.

QB::registerEvent('before-select', 'users', function($qb)
{
    $qb->where('status', '!=', 'banned');
});

There are many advanced features which are documented on GitHub project page. Sold? Lets install.

PHP Router in 140 Characters

This is a dimple(damn simple) URL routing script for PHP which is written in just 140 characters of code, so it fits within a tweet.

Its NOT a good router at all, don’t use it in a real application, its not exception handled either. I did it just for fun and to demonstrate the simplicity

So here’s the router code, and that’s all (seriously).

class R{
function a($r,callable $c){$this->r[$r]=$c;}
function e(){$s=$_SERVER;$i='PATH_INFO';$p=isset($s[$i])?$s[$i]:'/';$this->r[$p]();}
}

Fork on GitHub.

Usage

Create a file index.php in your server root and try this code

<?php
// The router code
class R{
function a($r,callable $c){$this->r[$r]=$c;}
function e(){$s=$_SERVER;$i='PATH_INFO';$p=isset($s[$i])?$s[$i]:'/';$this->r[$p]();}
}
//


// Create 
$router = new R();

// Add a route with a callback function
$router->a('/a', 'callbackFunction');

// Add a route with a closure
$router->a('/b', function(){
    echo 'Hello B';
});

// Add homepage route
$router->a('/', function(){
	echo 'Hello World';
});

// Add route with class method
$router->a('/c', [new Foo, 'bar']);
// Add multiple slashed route with class method
$router->a('/c/d', [new Foo, 'bar']);

// Execute the route
$router->e();

// Callback handlers
function callbackFunction(){
	echo 'Hello A';
}

class Foo{
	function bar(){
		echo 'Hello Bar';
	}
}

Now visit your server root http://localhost/index.php, http://localhost/index.php/a, http://localhost/index.php/b, http://localhost/index.php/c and http://localhost/index.php/c/d.

Or you may run built-in PHP server from the command line (in the same dir)

[code]
php -S localhost:8081
[/code]

and visit http://localhost:8081/index.php.

Runs on 5.4+. 140 chars idea is inspired by Fabien Potencier’s Twittee.