Skip to main content

Laravel Project Architecture: The Missing Guide

At my job, we've been doing a lot of learning and development using Taylor Otwell's Laravel 4 PHP framework.  As we've become more familiar with it, we've had to come up with better ways to structure our projects outside of what the documentation indicates and the default distribution that is provided.

If you've been working with Laravel 4 for any amount of time or come with experience from another framework and are just getting started, you've probably noticed that there are a lot of different ways to cut up your projects.

Choice is nice, but sometimes it can be paralysing or misleading.

Concrete Advice

This post is done in such a way that you can just skim the headings, but if you want a detailed explanation in each section, feel free to read in where necessary.

While I can't say the entirety of my advice is in practice throughout the community, I can say that we are starting to use it, and to very good effect at my job.  Especially considering that our apps tend to need both web and API front ends, good architecture is a very high priority.

Use Namespaces

...properly!  I've seen a lot of packages out there that classmap a file with a namespace declaration in it and yes while that's technically viable and nothing will blow up, it's cluttered.  The whole idea behind PSR namespace loading is that the files are easy to find and cohesive.  Resorting to a "just find it all here under this esoteric name I've given it" approach makes your project more difficult for others to get up to speed with.

How do you namespace properly?  At the root of your project (as in: adjacent to your .git directory), have a directory called "src".  Inside of there you might have: "YourOrganization/YourProject", followed by all the necessary namespaces for the guts of your application.  Some of which I will be clearly mentioning later in this post.

This is merely laying the groundwork for what's to come, but before we move on, there are two more suggestions I have which some people may disagree with:
  • Never repeat namespace names in classes.  So nothing like "YourOrganization\YourProject\Controller\PostController".  If you need a different name for the class, alias it in the file where you're using it, where the meaning is really implied.
  • Never pluralize your namespaces.  It's not necessary and will make sense once you see a complete class name.

Don't use Facades in Namespaced Code

It may seem tempting, but every time you do it, you're turning down an opportunity to make use of the IoC and reap the benefits of a clearer architecture.  Instead, define a constructor on your class and list the dependencies there.

Namespace Your Models

It's just going to be more organized that way and you'll be less inclined to abuse them.

Only Extend Model

I mean this in two ways:

First, don't extend "Eloquent", extend the fully namespaced eloquent class "Model" itself.

Second, if you see a package claim along the lines of "oh yes, we will give you slugs, just extend our sluggable base class and away you go!", immediately put on the brakes.  Go to that project's github page and open an issue titled: "Use Traits Instead" with the body text "Please see title.", click submit.  The response will usually be "oh most people don't have PHP 5.4", at which point you can remind them what year it is and that PHP 5.3 is EoL.

Write Thin Models

...with no external dependencies.  That means nothing like:

    public function getUrlAttribute() {
        return $this->urlGeneratorService->generateUrl($this->attributes['name']);

If you've been paying attention, you'll realize doing this in your model would force it to depend on an external service - and that implies a context, which is undesirable.  If you write methods like this in your models, you will have architecture problems down the line.

If there's some kind of transformation you need to perform on model data, it's entirely likely that it is specific to a certain circumstance that model may not always be finding itself in.  That circumstance should never be coupled to the model itself, ever.

What can you write in your models?  Basically anything that leverages the model itself, its relations, internal PHP methods, scopes or something from the "Illuminate/Database/Eloquent" namespace.

Create Repositories

Repository classes represent the different graphs you'll be requesting from the database.  A repository might look as simple as an Eloquent model getter (and seem redundant), but creating this layer will allow you to keep your classes from becoming a dizzy mess of queries.

Everything else is a Service

Where something isn't a repository to fetch models from the database, you can create a service.  Whether it's a utility to build static CDN URLs, or a social library that you've created.  Most everything else in Laravel can be defined and injected as a service to be called.  These can either call each other or be mixed and matched at the right level as needed.

Namespace your Controllers

Controllers will depend on services and/or repositories, and services are injected. Best to get them in the structure.  Here's where I'd hope to find a controller in your project:

  • your-project/
    • app/
    • src/
      • YourOrganization
        • YourProjectName
          • Controller
            • Api
              • User
              • Post
              • Social
            • Web
              • User
              • Post
              • Admin
          • Model
          • Repository
          • Service
    • public/
    • vendor/
Notice that I don't repeat names anywhere and everything is split up nicely based on roles.  I have clean names to use in my routes and nobody has to go digging to find specific functionality.  The separation of concerns in my project is clear based on this namespace structure alone.

Non-Namespaced Files

Migrations, views, routes, and configs are all the kinds of configuration-like framework files that don't need to be namespaced.  This is where the facades really come in handy to grant your static code access to the dependency injection container.

On Migrations

Don't use the enum type in Laravel 4 migrations currently as you'll lose the ability to alter the table later on down the line.  Also, when authoring migrations for a package, write them to exist in the package and not to be copied into the main project by a command.  This was something I got tricked into doing and it ended up being unoptimal when the time came for updates.

The More Theoretical

These next few are more to do with how your efforts on a general whole can be improved when working with Laravel 4.  I just think they're a little more abstract than "do this with this here and not there".

Don't Subclass Packages

If you see a particular package in the wild and think to yourself "hm, I just need to change this one method on this class...", I urge you to reconsider.  Either think about it from the angle of wrapping it with a service, or fork the project and turn the original package into an upstream source.  The amount of confusion that can arise just from having multiple service providers attaching to the same realm of functionality as well as any potential model issues will mire you.

Write Packages!

...and then open source them!  Release them to the community and help the community grow.  I have a co-worker who firmly believes that nothing is too small to be a package.  With the right designs in mind, I'd say he's right!

Here's how I like my packages to look:

  • your-package/
    • config/
    • migrations/
    • src/
      • YourOrganization/
        • YourPackageName/
          • {any namespaces needed}/
          • ServiceProvider.php
    • composer.json
I'm a bit on the fence about putting migrations and config in "src" as I think it should always be the root of a namespaced loader.  If you have any thoughts on that, feel free to send me a comment!

Don't Fight It!

I was given some good advice very early on when I started working with Symfony 1.4 which was to never try and fight the framework.  Always work with the grain, go with the flow.  Given the title of my blog, you can correctly conclude that I tend to challenge convention wherever possible.  But that doesn't mean I'm immune to well thought explanations.  In this, frameworks shine.

Generally speaking, if a framework is popular and has a strong community, it stands to reason that many eyes have gone over the tools and given them a general "ok" in a broader scale design analysis.  I think the irresistable force paradox merits mention here.

Use the Local Development Server

Don't let the vagaries of any environment make you think that Laravel should be expected to behave differently in production.  See my previous point.  I think you'll find some parallels between this practice and writing tests for your code as well.  If you can't download your project, install dependencies and then run artisan, you have a problem.

Be Pragmatic

If you haven't read the Pragmatic Programmer and are not familiar with one of Symfony (1.x or 2.x) or Ruby on Rails, do yourself a favour and read the book.  I think a lot of the advice in that book urges us to think more about our work with a longer term focus than is normally seen.  What's important is not making decisions out of convenience and being aware of impatient duplication, orthogonality and the broken windows theory.  All of these topics are explored to good effect in the book and help us learn a lot about ourselves as developers.

Be Still

I have one of my own to add which is to always try and write as little code as possible.  Code that conducts excessive checks or produces more data structures than is necessary always has to be balanced somewhere else down the line.  All of my advice here in a lot of ways is based on the desire to keep things DRY and simple.

Alright, that's it for now!  New guidelines can come along in another post if I come up with a decent enough list.
I've been tinkering away at this post for a bit and I think what's in here is good enough to share.  It's definitely the kind of resource I had hoped for when getting started with Laravel.  Please feel free to post a comment with your thoughts or improvements.

Keep in mind that what I've listed here is actually a combination of things I've discovered both on my own as well as through the community.

Popular posts from this blog

Amazon in Winnipeg?

Late last week, Amazon put word out that they're starting to look for a city to locate a second headquarters.  The fuel this announcement provides has the hype train in Winnipeg going so fast, it's missing stops.

I'm curious though.  As you've been digesting this exciting news, who have you spoken or listened to?
Was it this guy and his party's historic theft of public infrastructure (pdf) and incompetence with digital strategy? Or how about this guy, who maintains that Winnipeg doesn't need a proper rapid transit option?  You know, the kind that might actually catch the eye of handsome Amazon who now has Winnipeg trying to be the belle of the ball.

Runner up articles are Winnipeg's inability to join the 21st centry on active transport and the compounding of the aforementioned digital strategy mistakes.
Stop Listening to These Guys They are not technology experts. At worst, they're unabashed self promoters. At best they're conduits for very bad pol…

The Cons, Winnipeg's New Splash Pads

I never had the chance to be ungrateful, the new splash pad at Kildare and Wabasha isn't a splash pad at all.  It was taken away from us and replaced with an "aquatic park", a little bit of wordsmithing designed to gloss over the fact that an open piece of our community has been replaced with yet another closed gate.

As I write this post now, I can hear it already: "Taken away, what?! It's a new water park, you're so..."
Sure, some might reach for that tired recrimination, which is why I started this blog post by dismissing such a premise.  To be fair however, I offer the response: Don't spoil this discussion with nonsense.
You see, I was grateful before the renovations happened.  The communal service on offer was adequate and I never complained about it or saw it as flawed.  Don't believe me? Here, this is a cute google-generated animation of my son enjoying the splash pad in 2014.

Today we took the kids to see if we could spend some time at th…

Winnipeg Devs! Let's get WPL to carry O'Reilly Safari!

Short and sweet. I discovered the other day that the Toronto Public Library actually gives members access to O'Reilly Safari -- I was instantly jealous! You probably already know that this is a pretty good service chock-a-block full of great resources.

The good news is that we have a way to request new content from the Winnipeg Public Library.  This includes digital content, so I've done exactly that!
If you're interested in adding your voice to mine, I've put together a letter that you need to copy/paste and enter into their simple form. Here's a copy that you can read as part of this post:

This is a request for ongoing and full access to the complete O'Reilly Safari catalogue via a Winnipeg Public Library card. I am filing this under "Magazine Purchase" as it's not a single book, but an ongoing service. Following is a list of the most prominent benefits of adding this service to the collection:
O'Reilly produces extremely high quality conten…