Cleaning Up Your Laravel Codebase
I wrote a blog post some time ago about how i tried over-architecting a Laravel app, what i learnt in the process of doing that (TLDR; maintainance hell). With that it seems kind of nice to write a follow up as to what have changed since that and how i write Laravel apps this days.
Most of what i am going to be talking about are by no more means new processes/methods but it’s something i still find a lot of people viciously against - including myself until recently.
- Route Model Binding
When injecting a model ID to a route or controller action, you will often query to retrieve the model that corresponds to that ID. Laravel route model binding provides a convenient way to automatically inject the model instances directly into your routes. For example, instead of injecting a user’s ID, you can inject the entire User model instance that matches the given ID.
Chances are you’d most likely have ~7 controllers doing something like shown above. Is this really bad ? It isn’t. But we have just succeeded in repeating ourselves over and over again. And we as programmers are supposed to be DRY.
This is not novel by any standard. It has been flying around since i joined the Laravel community in
5.3 but it puts us a step ahead into a thinner controller.
- Prevent duplication of Eloquent queries by adding behavior to models
PPS => Eloquent models are an ActiveRecord implementation and already violate SRP, this step sounds like taking the violation one notch higher but i feel rules aren’t set in stone.
This looks good but we are kind of running into the same problem we had when we refactored to make use of route model binding. So take a look at how this can be cleaned up :
With this,we can get rid of all that multiple call to
Another example where this makes sense is this ; consider our web app allows users post adverts of anything but before they can do that, they have to confirm their email address (account activation). For user experience purposes, we don’t want to force that on them at the point of account registration.
We first define an
isAccountActivated method on our
User model which returns a
bool depending on a certain field in the database.
We then (optionally) create a
CreateItemRequest form request which helps us abstract a little bit of the validation process. Remember we want to force account activation only on item creation.
Form requests in Laravel provide a convenient
authorize method to determine if the validation should take place or not. Seems like a nice place to stop the user. We can manually call
$user->is_email_validated === 200 but chances are we might duplicate that call in some other part of the codebase.
- Write more Middleware
This also has the benefit of reducing the amount of clutter in the codebase. For example, given a route like
app.live/@moniker/edit. Rather than check if a user can edit a post in the
edit method of the
UserController, why not write a middleware that prevents access to that page itself.
While this works and isn’t bad enough, this type of checks are best left to a middleware, as personally, all i want to do in a controller is just serve the request/ do some plumbing. We can refactor that into a
All we have to do is register this middleware and add it to the
app.live/@moniker/edit route. With this we can get rid of all of that check and just allow the user edit since we are really sure (s)he is the one making the request.
As an aside, policies can be a little different. Say you have something like Twitter’s profile where you can edit your profile on the same page, i wouldn’t feel bad writing a check in the controller as that seems to be the only reasonable way you can decide if you want to display the
I feel this is also applicable to Policies. Write more of them.
- Dependency Injection is your friend
It seems most codebase only seem to inject the
Request object into each method of their controller but just use the helper methods to access other things from the service container. I am not against helper functions, i make use of them fairly regularly but in most cases, i prefer typehinting my dependencies against my controller’s method.
Below is trivial example
I could have resorted to making use of the
event helper but i leveraged DI in other to fire an event. There are no differences between both methods except that the dependency injection version gives a vivid description of what our handler (controller method) depends on.
- But make use of the Facade
This is kind of an opposite to the previous point as Laravel facades hide a lot from us.
From Laravel’s docs ;
However, some care must be taken when using facades. The primary danger of facades is class scope creep. Since facades are so easy to use and do not require injection, it can be easy to let your classes continue to grow and use many facades in a single class. Using dependency injection, this potential is mitigated by the visual feedback a large constructor gives you that your class is growing too large. So, when using facades, pay special attention to the size of your class so that its scope of responsibility stays narrow.
This is a personal decision as to when (not) to use them but having a couple of them in the codebase doesn’t mean coupling, testability issues and tons of other concerns.
- Have a testsuite
While this isn’t related to cleaning up the codebase, tests are a MUST.
I kind of feel lucky to have discovered testing early in my programming journey. I read Modern PHP two/three months after i started coding and in the same year, i also read Pragramtic programmers and Clean code (both books i actually plan to revisit once in a while). I wrote a blog post on some awesome technique i learnt from clean code
The codebase indirectly benefits from a well written testsuite in numerous ways since it forces to code in a certain way. Just ask Uncle Bob. I am no authority on topics like this.
Like i said early on, you probably are already doing this but i wrote this as a follow up to the previous post. I hope this helps somebody.