MVC (Laravel) where to add logic MVC (Laravel) where to add logic php php

MVC (Laravel) where to add logic


I think all patterns / architectures that you present are very useful as long as you follow the SOLID principles.

For the where to add logic I think that it's important to refer to the Single Responsibility Principle. Also, my answer considers that you are working on a medium / large project. If it's a throw-something-on-a-page project, forget this answer and add it all to controllers or models.

The short answer is: Where it makes sense to you (with services).

The long answer:

Controllers: What is the responsibility of Controllers? Sure, you can put all your logic in a controller, but is that the controller's responsibility? I don't think so.

For me, the controller must receive a request and return data and this is not the place to put validations, call db methods, etc..

Models: Is this a good place to add logic like sending an welcome email when a user registers or update the vote count of a post? What if you need to send the same email from another place in your code? Do you create a static method? What if that emails needs information from another model?

I think the model should represent an entity. With Laravel, I only use the model class to add things like fillable, guarded, table and the relations (this is because I use the Repository Pattern, otherwise the model would also have the save, update, find, etc methods).

Repositories (Repository Pattern): At the beginning I was very confused by this. And, like you, I thought "well, I use MySQL and thats that.".

However, I have balanced the pros vs cons of using the Repository Pattern and now I use it. I think that now, at this very moment, I will only need to use MySQL. But, if three years from now I need to change to something like MongoDB most of the work is done. All at the expense of one extra interface and a $app->bind(«interface», «repository»).

Events (Observer Pattern): Events are useful for things that can be thrown at any class any given time. Think, for instance, of sending notifications to a user. When you need, you fire the event to send a notification at any class of your application. Then, you can have a class like UserNotificationEvents that handles all of your fired events for user notifications.

Services: Until now, you have the choice to add logic to controllers or models. For me, it makes all sense to add the logic within Services. Let's face it, Services is a fancy name for classes. And you can have as many classes as it makes sense to you within your aplication.

Take this example: A short while ago, I developed something like the Google Forms. I started with a CustomFormService and ended up with CustomFormService, CustomFormRender, CustomFieldService, CustomFieldRender, CustomAnswerService and CustomAnswerRender. Why? Because it made sense to me. If you work with a team, you should put your logic where it makes sense to the team.

The advantage of using Services vs Controllers / Models is that you are not constrained by a single Controller or a single Model. You can create as many services as needed based on the design and needs of your application. Add to that the advantage of calling a Service within any class of your application.

This goes long, but I would like to show you how I have structured my application:

app/    controllers/    MyCompany/        Composers/        Exceptions/        Models/        Observers/        Sanitizers/        ServiceProviders/        Services/        Validators/    views    (...)

I use each folder for a specific function. For example the Validators directory contains a BaseValidator class responsible for processing the validation, based on the $rules and $messages of specific validators (usually one for each model). I could as easily put this code within a Service, but it makes sense to me to have a specific folder for this even if it is only used within the service (for now).

I recommend you to read the following articles, as they might explain things a little better to you:

Breaking the Mold by Dayle Rees (author of CodeBright): This is where I put it all together, even though I changed a few things to fit my needs.

Decoupling your code in Laravel using Repositories and Services by Chris Goosey: This post explains well what is a Service and the Repository Pattern and how they fit together.

Laracasts also have the Repositories Simplified and Single Responsibility which are good resources with practical examples (even though you have to pay).


I wanted to post a response to my own question. I could talk about this for days, but I'm going to try to get this posted fast to make sure I get it up.

I ended up utilizing the existing structure that Laravel provides, meaning that I kept my files primarily as Model, View, and Controller. I also have a Libraries folder for reusable components that aren't really models.

I DID NOT WRAP MY MODELS IN SERVICES/LIBRARIES. All of the reasons provided didn't 100% convince me of the benefit of using services. While I may be wrong, as far as I can see they just result in tons of extra nearly empty files I need to create and switch between when working with models and also really reduce the benefit of using eloquent (especially when it comes to RETRIEVING models, e.g., using pagination, scopes, etc).

I put the business logic IN THE MODELS and access eloquent directly from my controllers. I use a number of approaches to make sure that the business logic doesn't get bypassed:

  • Accessors and mutators: Laravel has great accessors and mutators. If I want to perform an action whenever a post is moved from draft to published I can call this by creating function setIsPublishedAttribute and including the logic in there
  • Overriding Create/Update etc: You can always override Eloquent methods in your models to include custom functionality. That way you can call functionality on any CRUD operation. Edit: I think there's a bug with overriding create in newer Laravel versions (so I use events now registered in boot)
  • Validation: I hook my validation in the same way, e.g., I'll run validation by overriding CRUD functions and also accessors/mutators if needed. See Esensi or dwightwatson/validating for more information.
  • Magic Methods: I use the __get and __set methods of my models to hook into functionality where appropriate
  • Extending Eloquent: If there's an action you'd like to take on all update/create you can even extend eloquent and apply it to multiple models.
  • Events: This is a straight forward and generally agreed upon place to do this as well. Biggest drawback with events I think is that exceptions are hard to trace (might not be the new case with Laravel's new events system). I also like to group my events by what they do instead of when they are called...e.g., have a MailSender subscriber which listens for events that send mail.
  • Adding Pivot/BelongsToMany Events: One of the things I struggled with the longest was how to attach behavior to the modification of belongsToMany relationships. E.g., performing an action whenever a user joins a group. I'm almost done polishing up a custom library for this. I haven't published it yet but it is functional! Will try to post a link soon. EDIT I ended up making all my pivots into normal models and my life has been so much easier...

Addressing people's concerns with using models:

  • Organization: Yes if you include more logic in models, they can be longer, but in general I've found 75% of my models are still pretty small. If I chose to organize the larger ones I can do it using traits (e.g., create a folder for the model with some more files like PostScopes, PostAccessors, PostValidation, etc as needed). I know this is not necessarily what traits are for but this system works without issue.

Additional Note: I feel like wrapping your models in services is like having a swiss army knife, with lots of tools, and building another knife around it that basically does the same thing? Yeah, sometimes you might want to tape a blade off or make sure two blades are used together...but there are typically other ways to do it...

WHEN TO USE SERVICES: This article articulates very well GREAT examples for when to use services (hint: it's not very often). He says basically when your object uses multiple models or models at strange parts of their lifecycle it makes sense. http://www.justinweiss.com/articles/where-do-you-put-your-code/


What I use to do to create the logic between controllers and models is to create a service layer. Basically, this is my flow for any action within my app:

  1. Controller get user's requested action and sent parameters and delegates everything to a service class.
  2. Service class do all the logic related to the operation: input validation, event logging, database operations, etc...
  3. Model holds information of fields, data transformation, and definitions of attributes validations.

This is how I do it:

This the method of a controller to create something:

public function processCreateCongregation(){    // Get input data.    $congregation                 = new Congregation;    $congregation->name           = Input::get('name');    $congregation->address        = Input::get('address');    $congregation->pm_day_of_week = Input::get('pm_day_of_week');    $pmHours                      = Input::get('pm_datetime_hours');    $pmMinutes                    = Input::get('pm_datetime_minutes');    $congregation->pm_datetime    = Carbon::createFromTime($pmHours, $pmMinutes, 0);    // Delegates actual operation to service.    try    {        CongregationService::createCongregation($congregation);        $this->success(trans('messages.congregationCreated'));        return Redirect::route('congregations.list');    }    catch (ValidationException $e)    {        // Catch validation errors thrown by service operation.        return Redirect::route('congregations.create')            ->withInput(Input::all())            ->withErrors($e->getValidator());    }    catch (Exception $e)    {        // Catch any unexpected exception.        return $this->unexpected($e);    }}

This is the service class that does the logic related to the operation:

public static function createCongregation(Congregation $congregation){    // Log the operation.    Log::info('Create congregation.', compact('congregation'));    // Validate data.    $validator = $congregation->getValidator();    if ($validator->fails())    {        throw new ValidationException($validator);    }    // Save to the database.    $congregation->created_by = Auth::user()->id;    $congregation->updated_by = Auth::user()->id;    $congregation->save();}

And this is my model:

class Congregation extends Eloquent{    protected $table = 'congregations';    public function getValidator()    {        $data = array(            'name' => $this->name,            'address' => $this->address,            'pm_day_of_week' => $this->pm_day_of_week,            'pm_datetime' => $this->pm_datetime,        );        $rules = array(            'name' => ['required', 'unique:congregations'],            'address' => ['required'],            'pm_day_of_week' => ['required', 'integer', 'between:0,6'],            'pm_datetime' => ['required', 'regex:/([01]?[0-9]|2[0-3]):[0-5]?[0-9]:[0-5][0-9]/'],        );        return Validator::make($data, $rules);    }    public function getDates()    {        return array_merge_recursive(parent::getDates(), array(            'pm_datetime',            'cbs_datetime',        ));    }}

For more information about this way I use to organize my code for a Laravel app: https://github.com/rmariuzzo/Pitimi