Software as a Service Consulting

Corey McMahon

10 years of creating software for the web – and he keeps coming back for more. Corey is a software consultant with a wide range of experience, having worked at agencies, start-ups, and enterprise consulting firms.

Now he spends his time launching and marketing software products using best-in-class frameworks, tools and techniques; and helping businesses to do the same.

A Pattern for Reusable Resource Controllers in Laravel 4.2

In most web apps, we encounter situations where we need to implement repetitive CRUD-type use cases across multiple entities. In our recent projects at Slashnode, we’ve been experimenting with different patterns to try to make it easier and quicker to build out these CRUD-type use cases.

The repetition stems from the fact that most CRUD actions for (basic) resources follow exactly the same sequence of operations. Consider the following methods of a typical resource controller:

Resource Controller Methods

index()

Route is GET (resource)/. Used to display a list of the resources.

Sequence of Operations:

Paginate all the records and inject into a view

edit($id)

Route is GET (resource)/{id}. Used to display a pre-existing resource for editing.

Sequence of Operations:

Fetch the record

Generate values required by the form

Inject the record and values into a form

create()

Route is GET (resource)/create. Used to show an empty form, allowing the user to create a new resource.

Sequence of Operations:

Instantiate a new record

Generate values required by the form

Inject the record and values into a form

store()

Route is POST (resource)/. Used to create a new resource from the values entered via a create() form.

Sequence of Operations:

Validate the Input values

If fails, redirect back with error messages

If passes, create a new record and redirect with a success message

update($id)

Route is PUT (resource)/{id}, Used to update an existing resource based on new values provided via a edit() form.

Opportunities for Reuse

For the purposes of illustration, consider the pseudo code controller below:

This provides a typical example of the different methods we might implement in a resource controller.

Based on this information, we can start to see opportunities for reusing parts of the code. We should be able to reuse the same controller for different resources (products, categories, orders, etc) by defining the repeated behaviour in an abstract parent class and overriding the following in the concrete implementations:

The model class.

The repository class.

The validation class(es).

The resource name.

A Pattern for Reuse

For the purposes of illustration, we’ll consider building a resource controller for an e-commerce application.

Note: we’ll also split routes and functionality based on user-types (admin or user). This means we will have separate controllers, routes, etc. for administrators and normal users.

The resource will be a “product,” so our routes will look like this:

OK, nothing too controversial so far.

Let’s do things in reverse and implement the concrete version of the class first. We know what we need to override on a per-resource basis, so we’ll add methods to return the implementation-specific values:

We can see a few important things in here:

The constructor: we inject the product-specific implementations for the model, repository and validator. This means when the abstract class tries to call “find()”, “update()”, etc. we’ll be calling these methods on the product version of the model / repostiory / validator.

function userRole() and function resourceName(): these are used to construct view and route paths.

function formData(): this is used to pass values into the create and edit forms. This will make more sense when you see the abstract parent class.

Now we can see the abstract parent class.

The abstract parent class is quite dense, but it’s quite simple once you break it down. Here are the important points:

View names: when ever we refer to a route, we need to dynamically generate it using the concrete implementation. This is because the view name will depend on the resource type.

Route names: likewise, we also need to dynamically generate the routes for redirects. We use the same naming convention for views and routes, so we can use the same method to generate them.

formData(): form data injects anything we need to display the create / edit form. We merge this with the $item, which is also required in all create / edit actions.

data(): when ever we need to fetch input data from the user, we defer to the data() function which is actually implemented on the BaseController (not BaseResourceController). This simply pulls a list of the model attributes from the validator. See this reproduced below:

View and Route Naming Structure

As you can see from the code above, the naming convention for routes and views is a critical part of the pattern. We need to use a similar naming convention for both so that we can refer to them using the same methods in the controllers. So with that said, our routes and views will look like this (route – view):

admin.products.index – views/admin/products/index.blade.php

admin.products.create – views/admin/products.create.blade.php

admin.products.edit – views/admin/products.edit.blade.php

You can see and example of one of these views reproduced below:

Conclusion

And that’s it!

We can use the same pattern to create a new resource simply by adding the routes, creating the supporting classes (repository, model, validator), building the concrete implementation of the abstract resource controller and adding the views.

Do you use any methods to make resource controller creation a breeze? We’ve love to hear them! Add your suggestions in the comments below.