Ad

Why Should I Use Both Traits And Services In Laravel?

I have a simple question I am struggling to answer and I hope guys you can help me. So I am developing application on laravel. I wanted to make Controllers clear so I have complex queries inside Model classes itself (I don't want to use repositories). If I want some functionality that will be used for many models I put this functionality inside traits and use it whenever needed. So here is my question.. Do I need Service classes at all? Isn't Traits enough?

Ad

Answer

Let's say you have a method in a trait which requires a Cat model and a Dog model. Each time you use that trait, you need to pass in an instance of Cat and an instance of Dog for it to do its work.

Eventually that's going to get tiring. And what happens if you eventually add a feature and the trait now requires a Bird model? Now you have a lot of code to modify just to start passing the Bird to the method in the trait each time you want to use it.

This is where a service class becomes very handy. In the service class' constructor, you can inject the User, Permission, and Role model like so...

public function __construct(Cat $cat, Dog $dog, Bird $bird)
{
    $this->cat = $cat;
    $this->dog = $dog;
    $this->bird = $bird;
}

Then you can add a service provider to your app which instructs Laravel how to create this service class by passing in the appropriate models. From there, you can have this service automatically injected into your controllers as you see fit where currently you are just using a trait for this.

What this means is if you ever needed to add a dependency, you only need to update one, possibly two files. The service class itself to add additional items to the constructor and possibly the service provider. All the controllers that are using this don't need to be updated to pass in those additional items to the constructor since that's handled by Laravel (more appropriately the IoC container).

This is also extremely beneficial for testing. When injecting dependencies into a class, it's easier to just set up your testing environment to mock those dependencies rather than trying to build mocks on the fly to pass into the trait methods. It's also more difficult to test a trait since something needs to use that trait in order for the trait's methods to be tested.

// we can use a service provider to tell laravel how to create this service (https://laravel.com/docs/5.7/providers)
class PetService
{
    protected $cat;
    protected $dog;
    protected $bird;

    public function __construct(Cat $cat, Dog $dog, Bird $bird)
    {
        $this->cat = $cat;
        $this->dog = $dog;
        $this->bird = $bird;
    }

    public function doStuff()
    {
        //  does things with $this->cat, $this->dog, $this->bird
    }
}


class PetController
{
    // use PetTrait;

    protected $petService;

    // laravel will automatically create the PetService for us and pass it into the constructor when building this controller (https://laravel.com/docs/5.7/container)
    public function __construct(PetService $petService) 
    {
        $this->petService = $petService;
    }

    public function someControllerMethod()
    {
        // $this->doStuff(new Cat, new Dog, new Bird); // In order to modify this method, we need to update each time it's used.
        $this->petService->doStuff(); // We don't need to worry about passing in arguments since it's handled by the service container when constructing the pet service and can make changes without updating each controller that uses it.
    }
}

And to go further into detail, what happens if you decide Bird now needs an instance of Chicken, Turkey, Dodo in order for it to function correctly. Now you need to again go through any traits that use Bird and update each to pass in an instance of each of its dependencies.

This is why it's advantageous to use the service container and providers to plan out and build your dependencies of your service classes. It greatly increases the maintainability of your app. It would be by far most beneficial in a very large and complicated app.

However, if your app is fairly simple, it may not be as useful and maybe even add unnecessary complexity and traits may in fact be better to use at that point if they make more sense to you. It's not good to have your app be flexible using services when it doesn't need to be.

Ad
source: stackoverflow.com
Ad