Coder Perfect

MVC (Laravel) is a framework for adding logic.

Problem

Let’s imagine I want to do something else everytime I perform a CRUD transaction or edit a relationship in a specific way. For instance, anytime someone publishes a blog article, I’d like to save something to a table for analytics purposes. Although this isn’t the finest example, there is a lot of “grouped” functionality in general.

This form of logic is usually found in controllers. That’s nice and wonderful until you wish to duplicate the functionality in many locations. When you start working with partials, developing an API, and generating dummy content, keeping things DRY becomes a problem.

Events, repositories, libraries, and adding to models are some of the techniques I’ve seen to manage this. Here’s how I see each of them:

Most folks would probably place this code in the services section. My primary concern with services is that it can be difficult to locate certain functionality, and I believe they are overlooked when people are focused on using Eloquent. When I could just perform $post->is published = 1, how would I know I needed to call a method publishPost() in a library?

The only circumstance in which I see this working is if you solely use services (and ideally make Eloquent inaccessible somehow from controllers all together).

If your queries generally match your model structure, this appears to basically add a load of extra unneeded files.

Repositories: This appears to be a service, however there is an interface that allows you to swap between ORMs, which I don’t require.

Events: In some respects, I think this is the most elegant solution because you know your model events will always be called on Eloquent methods, allowing you to build your controllers regularly. However, I can see this getting messy, and if anyone has any instances of huge projects employing events for important coupling, I’d appreciate seeing them.

Models: In the past, I’d have classes that handled both CRUD and crucial coupling. This really made things easier because you knew all of the CRUD functionality and that whatever you needed to do with it was available.

Simple, however I don’t see this done too often with MVC architecture. In some ways, I prefer this to services because it’s easier to locate and there are fewer files to keep care of. It can, however, become disorderly. I’d want to hear about the drawbacks of this strategy and why so few people tend to use it.

What are the benefits and drawbacks of each method? Is there something I’m overlooking?

Asked by Sabrina Leggett

Solution #1

I believe that all of the patterns / architectures you present are quite valuable as long as the SOLID principles are followed.

Where should logic be added? I believe it is critical to mention the Single Responsibility Principle. In addition, my response assumes you’re working on a medium or large project. If you’re working on a one-page project, ignore this answer and put everything into controllers or models.

The short answer is: wherever you think it makes sense (with services).

The long answer:

Controllers’ Responsibilities: What are the responsibilities of controllers? Sure, you can put all of your logic in a controller, but is that really the controller’s job? No, I don’t believe so.

The controller, in my opinion, should receive a request and return data; it is not the place to put validations, call database procedures, and so on.

Is this a suitable area to add logic, such as sending a welcome email to new users or updating a post’s vote count? What happens if you need to send the same email from a different part of your code? Do you create a static method? What if that emails needs information from another model?

The model, in my opinion, should reflect an entity. I solely use the model class in Laravel to add things like fillable, guarded, table, and relations (this is because I use the Repository Pattern, otherwise the model would also have the save, update, find, etc methods).

Repositories (Pattern of Repositories): At first, I was very perplexed by this. And, like you, I figured, “Well, I use MySQL, so that’s it.”

However, I’ve weighed the benefits and drawbacks of utilizing the Repository Pattern and currently utilize it. I believe that for the time being, I will only need to use MySQL. However, if I need to switch to something like MongoDB in three years, the most of the work is already done. All of this comes at the cost of one more interface and a $app->bind(«interface», «repository»).

Events (Observer Pattern): Events are handy for throwing stuff at any class at any moment. Consider the case of sending a user a notification. You can use the event to send a notification to any class in your application whenever you need it. Then you can create a class called UserNotificationEvents to manage all of your user notification events.

Previously, you had the option of adding logic to controllers or models. Adding the reasoning to Services makes perfect sense to me. Let’s face it, Services is just another way of saying “classes.” You can also have as many classes as you like within your application.

Consider the following scenario: I created something similar to Google Forms not long ago. I began with a CustomFormService and ended up with CustomFormService, CustomFormRender, CustomFieldService, CustomFieldRender, CustomAnswerService, CustomAnswerRender, CustomAnswerService, and CustomAnswerRender. Why? Because that was logical to me. If you work in a group, you should organize your logic so that it makes clear to everyone.

The benefit of using Services rather than Controllers or Models is that you are not limited to a particular Controller or Model. Based on the architecture and needs of your application, you can create as many services as you need. Add to that the benefit of being able to invoke a Service from any class in your application.

This is a long paragraph, but I’d want to show you how I organised my application:

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

Each folder has a distinct purpose for me. The Validators directory, for example, contains a BaseValidator class that handles validation depending on the $rules and $messages of specific validators (usually one for each model). I could place this code in a Service, but it seems more logical to me to have a separate folder for it, even if it will only be used within the service (for now).

I propose that you read the following articles, as they may help you understand things better:

Dayle Rees (author of CodeBright) has written a book called Breaking the Mold. Even though I adjusted a few things to suit my needs, this is where I put it all together.

Chris Goosey explains how to decouple your code in Laravel using repositories and services: This article discusses what a Service and the Repository Pattern are, as well as how they work together.

Laracasts also has Repositories Simplified and Single Responsibility, both of which are useful tools with examples (even though you have to pay).

Answered by Luís Cruz

Solution #2

I intended to respond to my own inquiry by posting a comment. 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 using Laravel’s existing structure, which meant I maintained my files basically organized as Model, View, and Controller. I also save reusable components that aren’t models in a Libraries folder.

MY MODELS WERE NOT WRAPPED IN SERVICES/LIBRARIES. All of the explanations given did not completely persuade me of the advantages of employing services. While I could be wrong, they seem to result in a lot of extra essentially empty files that I have to generate and switch between when working with models, as well as significantly reducing the value of utilizing 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:

Taking care of people’s concerns about using models:

Additional Note: Do you think wrapping your models with services is like taking a swiss army knife and creating another knife around it that essentially performs the same thing? Yes, there are situations when you may want to tape a blade or ensure that two blades are used together…but there are usually better options…

WHEN TO USE SERVICES: This article provides some excellent instances of when to use services (hint: it’s not often). It makes sense, he argues, when your object employs many models or models at odd points in its lifecycle. http://www.justinweiss.com/articles/where-do-you-put-your-code/

Answered by Sabrina Leggett

Solution #3

A service layer is what I use to develop the logic that connects controllers and models. In general, this is how I handle any activity in my app:

Here’s how I go about it:

This is a controller’s way for creating 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 handles the operation’s logic:

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 here’s what I’ve come up with as a 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 detail on how I organize my code for Laravel apps, see: https://github.com/rmariuzzo/Pitimi

Answered by Rubens Mariuzzo

Solution #4

Laravel, in my perspective, already has a lot of choices for storing your business logic.

Short answer:

Long(er) answer:

Use Repositories Only When Necessary: Repositories are prone to become overly bloated, and are frequently employed as a model accessor. I believe they have some utility, but unless you’re building a large application that necessitates such flexibility that you’d be able to abandon Laravel totally, avoid repositories. You’ll be glad you did that afterwards, and your code will be much easier to understand.

Consider whether you’ll be switching PHP frameworks or using a database type that Laravel doesn’t support.

If your answer is “Almost certainly not,” don’t use the repository pattern.

Also, please don’t plaster a pattern on top of a fantastic ORM like Eloquent. You’re just introducing complexity where it isn’t needed, and it won’t help you in any way.

Service classes are basically a location to hold business logic to accomplish a certain operation with its supplied dependencies, in my opinion. These are called ‘Jobs’ in Laravel, and they provide a lot more versatility than a custom Service class.

Laravel, in my opinion, provides a well-rounded answer to the MVC logic challenge. It’s simply a question of organizing.

Example:

Request:

namespace App\Http\Requests;

use App\Post;
use App\Jobs\PostNotifier;
use App\Events\PostWasCreated;
use App\Http\Requests\Request;

class PostRequest extends Request
{
    public function rules()
    {
        return [
            'title'       => 'required',
            'description' => 'required'
        ];
    }

    public function persist(Post $post)
    {
        if (! $post->exists) {
            // If the post doesn't exist, we'll assign the
            // post as created by the current user.
            $post->user_id = auth()->id();
        }

        $post->title = $this->title;
        $post->description = $this->description;

        $post->save();

        // Maybe we'll fire an event here that we can catch somewhere 
        // else that needs to know when a post was created.
        event(new PostWasCreated($post));

        // Maybe we'll notify some users of the new post as well.
        dispatch(new PostNotifier($post));

        return $post;
    }
}

Controller:

namespace App\Http\Controllers;

use App\Post;
use App\Http\Requests\PostRequest;

class PostController extends Controller
{
    public function store(PostRequest $request)
    {
        $request->persist(new Post());

        flash()->success('Successfully created new post!');

        return redirect()->back();
    }

    public function update(PostRequest $request, Post $post)
    {
        $request->persist($post);

        flash()->success('Successfully updated post!');

        return redirect()->back();
    }
}

The request input is automatically checked in the example above, so all we have to do now is execute the persist method and pass in a new Post. Readability and maintainability, in my opinion, should always take precedence over complex and unnecessary design patterns.

You can then utilize the exact same persist method for updating posts as well, since we can check whether or not the post already exists and perform alternating logic when needed.

Answered by Steve Bauman

Post is based on https://stackoverflow.com/questions/23595036/mvc-laravel-where-to-add-logic