How Do You Work in Laravel?

Shawn Mayzes
5 min readSep 11, 2017

--

I’m curious how other artisans work with the Laravel framework. I’ve seen talks from Adam Wathan, about resource controller coding and how simple/clean it looks.

I would like to put it it out to the community to share how they work in Laravel. I’d love to learn something new and see what I can improve upon with my design patterns.

In my code right now I am using the following:

Controller -> Service -> Repository -> Model

Where possible, I try to follow SOLID principles as a general guideline

So without further ado, the code..

Routes

I like to use laravel Resource Controllers. As an example, lets create a pizza listing page(index). I’ve also added two examples to show a nested order page within the pizza purview. (a page to create and then finally a page to store the order)

Route::resource('/pizzas', 'PizzaController', ['only' => [
'index',
]]);

Route::group(['prefix' => 'pizzas'], function() {
Route::resource('/orders', 'Pizza\OrderController', ['only' => [
'create', 'store',
]]);
});

Routes Outcome:

GET     
/pizzas
App\Http\Controllers\PizzaController@index
GET
/pizzas/orders/create
App\Http\Controllers\Pizza\OrderController@create
POST
/pizzas
App\Http\Controllers\Pizza\OrderController@store

You will notice that I nest my namespaces based on the URL. The reason I do this is so that I find it easy to know where to debug, in case of failure. Plus in my opinion it’s a separation of concerns and allows me to keep my controllers using only the 7 actions handled by a Resource Controller.

  • Pizza\OrderController— Has the responsibility of only handling orders for pizzas
  • PizzaController — Has the responsibility of handling details for pizzas

Controllers

As mentions I try to only use the 7 action methods as suggested in their documentation for Resource Controllers.

  • index()
  • create()
  • store()
  • show()
  • edit()
  • update()
  • destroy()

I will admit there are times when I may throw in a one-off action where I feel it makes more sense to be contained inside the specific controller class, but I do keep it minimal.

My controller action methods will use Automatic Injection to load the Service class. So for our pizza listing page, we want to use the PizzaService to get all the pizza’s from the database.

public function index(PizzaService $pizzaService)
{
return view('pizza.index', [
'pizzas' => $pizzaService->all(),
]);
}

Note: the views also follow the same folder pattern as the namespaces.

Services

I like to use Services to handle the logic in my apps. A Service to me can be a Domain Driven concept or 1-to-1 with a Model (database table). I have an abstract class that handles the common methods that I use a lot in my Services. (Note: Comments/docblocks removed in code examples)

<?php

namespace App\Services;

abstract class BaseService
{
public $repo;

public function all()
{
return $this->repo->all();
}

public function paginated()
{
return $this->repo->paginated(config('paginate'));
}
public function create(array $input)
{
return $this->repo->create($input);
}
public function find($id)
{
return $this->repo->find($id);
}

public function update($id, array $input)
{
return $this->repo->update($id, $input);
}

public function destroy($id)
{
return $this->repo->destroy($id);
}
}

So my Domain/Model based Service looks like:

<?php

namespace App\Services;

use App\Repositories\PizzaRepository;

class PizzaService extends BaseService
{
private $pizzaRepository;

public function __construct(PizzaRepository $pizzaRepository)
{
$this->pizzaRepository = $pizzaRepository;
}
}

On this PizzaService I can add my own custom methods specific to the logic I’m attempting to handle. In continuation of the pizza listing page, $pizzaService->all() calls the all() method in the BaseRepository as we are not overwriting it.

Repositories

Repositories in my codebase are basically methods that use Eloquent to get or set data in the database. Only a Service can call a Repository Layer. (I’ve gone back and forth in my head about doing this, and I always tend to fall on being explicit with this rule).

<?phpnamespace App\Repositories;

use Illuminate\Database\Eloquent\Model;

abstract class BaseRepository
{
public $sortBy = 'created_at';
public $sortOrder = 'asc'; public function all()
{
return $this->model
->orderBy($this->sortBy, $this->sortOrder)
->get();
}

public function paginated($paginate)
{
return $this
->model
->orderBy($this->sortBy, $this->sortOrder)
->paginate($paginate);
}

public function create($input)
{
$model = $this->model;
$model->fill($input);
$model->save();

return $model;
}

public function find($id)
{
return $this->model->where('id', $id)->first();
}

public function destroy($id)
{
return $this->find($id)->delete();
}

public function update($id, array $input)
{
$model = $this->find($id);
$model->fill($input);
$model->save();

return $model;
}
}

So a PizzaRepository that is loaded by PizzaService and looks like this:

<?php

namespace App\Repositories;

use App\Models\Pizza;

class PizzaRepository extends BaseRepository
{
protected $model;

public function __construct(Pizza $pizza)
{
$this->model = $pizza;
}
}

I should also note that in my Services and Repositories I can overwrite the default methods to use my own implementation should I need.

You recall earlier in our pizza listing example, the BaseService layer was calling the all() method on the Repository. Now since the PizzaRepository is not overwriting the BaseRepository it uses the all() method within the BaseRepository to return all the listings of pizzas from the database.

As an example in overwriting the BaseRepository method one of my codebases uses Stored Procedures for inserting data, so I could overwrite the create method from the BaseRepository like so.

<?php

namespace App\Repositories;

use App\Models\Pizza;

class PizzaRepository extends BaseRepository
{
protected $model;

public function __construct(Pizza $pizza)
{
$this->model = $pizza;
}
public function create(array $input)
{
return $this->model->hydrate(
DB::select(
'CALL create_pizza(?, ?)',
[
$name,
$hasCheese,
]
)
);
}
}

That is a simple example, but now I return a hydrated result from my stored procedure.

Traits

I just introduced the concept of Traits in my codebase. This came about as I found that some of my Repository layers needed the ability to customize the sorting (you will notice that my BaseRepository has two properties sortBy and sortOrder. So I created a trait called Sortable. Now I can sort my pizza listing page by these properties.

<?php

namespace App\Repositories\Traits;

trait Sortable
{
public $sortBy = 'created_at';

public $sortOrder = 'asc';

public function setSortBy($sortBy = 'created_at')
{
$this->sortBy = $sortBy;
}

public function setSortOrder($sortOrder = 'desc')
{
$this->sortOrder = $sortOrder;
}
}

So now in my Service, where I applicable I can set the Sorting. (The example below sets the order in the constructor, but you can run that method in your custom Service methods as well.)

<?php

namespace App\Services;

use App\Repositories\PizzaRepository;

class PizzaService extends BaseService
{
private $pizzaRepository;

public function __construct(PizzaRepository $pizzaRepository)
{
$this->pizzaRepository = $pizzaRepository;

$this->pizzaRepository->setSortBy('sort_order');
}
}

I was also having some difficulties with trying to figure out how to handle eager loading, I didn’t like the idea of returning the data to my Controller and then using lazy eager loading. That wasn’t too great for optimizing my database queries. Once I made the Sortable Trait, I then decided to make one called Relationable.

<?php

namespace App\Repositories\Traits;

trait Relationable
{
public $relations = [];

public function setRelations($relations = null)
{
$this->relations = $relations;
}
}

Then I added the with() method to my BaseRepository methods. For example:

public function all()
{
return $this->model
->with($this->relations)
->orderBy($this->sortBy, $this->sortOrder)
->get();
}

Via my service I can add the following code to any method (orders is a relation method on the model)

$this->repo->setRelations([‘orders’]);

I do worry it can be easy to complicate my app with too many Traits over time, but right now it’s working quite well.

All in all, I would really like to see what other kinds of patterns other PHP/Laravel developers use

Looking forward to seeing your comments, blog posts or Medium.com articles!

--

--

Shawn Mayzes

The more I think about what to put here, the more I procrastinate. Founder: @larachatslack