Using traits for service layer: is it bad practice?
Asked Answered
B

2

5

I have noticed that it is common for laravel programmers to use traits to implement some kind of dependency injection in controllers and also laravel itself uses lots of traits to implement something that it seems to me, to be services.

I come from symfony where traits are not widely used from the framework itself and I find it a little bit odd as I find using trait for such a reason, not so clear design. Shouldn't services be defined in their own classes? Is it acceptable to use trait for services?

Bohner answered 16/1, 2018 at 11:57 Comment(4)
It really depends on what exactly you want to do.Humphreys
using traits its often referred as putting the sweat under the bed! However it can be usefull when you do not want to overload models !Ipswich
I agree with leo's answer. It depends more on how you use traits.Karbala
Interesting question but as previously pointed out, you might need to provide some more details. As a historical note, Symfony 2 was originally developed using php 5.3. Traits were not introduced until 5.4. Hence, you still don't see many traits in the core software. But they are in there. There is for example a ControllerTrait in the framework that implements the various controller helper functionality.Pym
L
7

I noticed the previous answer was not yet accepted so I thought giving my own 2 cents.

Coming as well from a Symfony 2 environment, being involved with Laravel currently and preparing for a Symfony 3 environment I was reading up on the subject as well, as I used to read that traits are evil. The following link has an accepted answer that is in my opinion a not so badly subjective one, it makes some fair assumptions and it seems well constructed: https://codereview.stackexchange.com/questions/74077/trait-accessing-variables-of-classes-using-it

However, I would think the biggest difference for you might be that the default Laravel is using ActiveRecord, in contrast with having the Service/Repository layers that are used by Symfony. I personally prefer the latter, it's less heavy, keeps it easier to be SOLID, logic and data are more often already decoupled, making the layers more easily swappable. Anyway, this is a not-so-on-topic and very personal note.

After working in Laravel now for a couple of months (in comparison, I have worked for years in sf2 (and sf1)) I'm hardly an expert in how Laravel works. Personally I still dislike traits because they feel too magical to me, if you do not interface them at least. I often think it is better handled through other design patterns.

TL;DR: (gist of the link basically)

  • A good use case for a trait is horizontal scaling, supporting actions that stand by itself, without the overhead of having to implement it each time the interface is added. (usually this is fairly simple logic)
  • A bad one is a trait that uses information out of its own scope (for instance properties of the 'main' object, or simply global state), or enforces/breaks contracts.
Longinus answered 2/5, 2018 at 16:27 Comment(0)
I
2

A trait is similar to an abstract class which cannot be instantiated on its own.

Traits is a mechanism for code reuse in single inheritance languages in this case PHP.

Now whenever you find yourself that your model's for instance are getting too bloated and you are using the same functionallity over and over in different models then you might think as a tell tail sign of using a trait.

Now you might think since we have same functionallity why not using a template design pattern instead?! However since all the models in Laravel they do extend a base model you really should be careful with interitance use.

Laravel by default uses a lot of traits example if you want to use softDeletes() then simply you just pull a trait to bring in functionallity to that model.

Here is a practical use of a trait. One of my apps it happens that I want to keep track of all updates that are happening on my database and store them in a different table. So if I want to track a model activity then I simply use this trait:

<?php

namespace App;

use App\Activity;

trait RecordsActivity
{

    protected static function bootRecordsActivity()
    {

        static::created(function ($thread) {
            $thread->recordActivity('created');
        });
    }

    protected function recordActivity($event)
    {
        Activity::create([

            'user_id'      => auth()->id(),
            'type'         => $event . '-' . strtolower((new \ReflectionClass($this))->getShortName()),
            'subject_id'   => $this->id,
            'subject_type' => get_class($this),
        ]);
    }
}

So whenever I want to track a model activity I simply pull in this trait,

use RecordsActivity; 

instead of bloating the model with extra code and even worst reapating code in different models, or making it more spageti using class inheritance.

Or in some build in traits in Laravel when you need them like softDeletes you just have to pull them in, instead of having them ready in the base class even when you dont need them.

Ipswich answered 16/1, 2018 at 12:38 Comment(3)
Still that doesn't answer if it is ok to use traits to implement services. You just provided an obvious case in which traits suit well and in fact it is a case that it's not ambiguous if traits should be used.Bohner
@Bohner to be honest I explained queit well on when its okay to use traits and when not. Its up to you to decide. At least I am the only one that provided you an answer.Ipswich
I appreciate that, still can't see how you answered when not to use traitsBohner

© 2022 - 2024 — McMap. All rights reserved.