JaggyGauran

Freelance developer, and designer

RESTful CakePHP

Ever since I've tried Ruby on Rails and Laravel, I fell in love with the concept of REST and named routes. Now, CakePHP2 currently does not support named routes and RESTful controllers are a tad bit tedious to setup.

Now, I made a CakePHP Rest Plug-in to give the ease of making a RESTful app.

On our controllers, we always end up merging our create and store behaviours and have this function:

/**
 + GET  /users/add
 + POST /users/add
 */
function add()
{
    if ($this->request->is('post'))
    {
        // save and redirect to index
    }
    
    // send some data to the view if we're not saving
}

While if he have a more RESTful approach:

/**
 + GET /users/create
 + 
 */
function create()
{
    // send data to the view
}

/**
 + POST /users
 *
 */
function store()
{
    // save and redirect to index
}

Now I'm using other framework's approach or using index, show, create, store, edit, update and delete as the actions for a controller.

Usually, to implement this with Cake, you'll have to do something like this inside your Config/routes.php

<?php

Router::connect('/users/create', array('controller' => 'users', 'action' => 'create', '[method]' => 'GET'));

Router::connect('/', array('controller' => 'users', 'action' => 'store', '[method]' => 'POST'));

This does work but you'll end up with a routes file with hundreds of lines of code and you might lose track of a few URLs in the long run.

CakePHP REST

This plug-in makes using REST a piece of cake.

Usage

In your Config/routes.php

Route::get('/users/create', [
    'as'    => 'users.create',
    'uses'  => 'Users@create 
]);

Route::post('/users', [
    'as'    => 'users.store',
    'uses'  => 'Users@store 
]);

// URLs with parameters
Route::get('/users/:username', [
    'as'    => 'users.profile',
    'uses'  => 'Users@profile'
], [
    'pass'     => ['username'],
    'username' => '[a-zA-Z0-9]+'
]);

Also you can generate all the actions (index, show, create, store, edit, update and delete) in a single line.

Route::resource('users');

// you can also use `except` and `only` like so
Route::resource('users', [
    'except' => [ 'delete', 'show' ]
]);

Named Routes

You can list down your routes through the CLI

$ cake rest.routes

Which displays something like this.

RouteShell in Action

With these named routes, you can use them to redirect to other pages like so

$this->redirect(Route::url('users.index'));
$this->redirect(Route::url('users.show', $id));
$this->redirect(Route::url('users.profile', $username));

Form Helper

Also, I've included a Form helper to use the named routes with the forms.

$this->Rest->open(['route' => ['users.store']]);

Drawbacks

Since Cake depends on the SessionComponent to store the data when validation fails, using a redirect from store to create messed things up a bit, luckily, I've found a simple workaround for this issue.

Within your AppController.php

public function beforeFilter()
{
    parent::beforeFilter();

    if($this->Session->read('Data.old')) 
        $this->request->data = $this->Session->read('Data.old'); 
        
    $this->Session->delete('Data.old');
}


protected function invalidate($message = 'Something went wrong')
{
    $this->Session->setFlash(__($message));
    $this->Session->write('Data.old', $this->request->data);

    $this->redirect($this->referer());
}

The invalidate() function is then inherited by all your Controllers.

What this does is whenever a controller is invalidated, the data you tried to insert is placed within the session, returned to the form and deletes the session.

And in your controller.

public function store()
{
    $user = $this->User->save($this->request->data);

    if($user)
        // redirect to profile 

    $this->invalidate('Could not save user for some reason');
}

Fork me on Github