laravel / ideas

Issues board used for Laravel internals discussions.
938 stars 28 forks source link

[Proposal] Add middlewarable trait #1763

Open imanghafoori1 opened 5 years ago

imanghafoori1 commented 5 years ago

We can implement a simple trait to put on any class and then be able to decorate it's methods with laravel middlewares. Why limit the use of middlewares only to Http section ?

class UserRepository
{
    use Middlewarable;      //     <----- we use the trait.

    public function find ($id) 
    {
        return User::find($id);   //    <----  we wanna cache it, right ?
    }
    . . .
}

In the controller :

public function show($id, UserRepository $repo)
{
    $cachedUser = $repo->middleware('cacher:fooKey,60')->find($id);
    // or 
   $cachedUser = UserRepoFacade::middleware('cacher:fooKey,60')->find($id);
}

or even directly on eloquent models :

public function show($id)
{
    User::middleware('cacher:fooKey,60')->find($id);
}

there are a lot of other use cases for middlewares. (like sanitization of inputs and outputs, logging, firing events,...)

The important part is that:

I have implemented the trait as a POC package and it works. The is just a simple wrapper around the Pipeline class which laravel uses for Routing stuff.

https://github.com/imanghafoori1/laravel-middlewarize

martinbean commented 5 years ago

Things like repositories should be using an interface, which can then be decorated in the container binding itself:

use Illuminate\Contracts\Cache\Repository as CacheRepository;

$this->app->singleton(UserRepositoryContract::class, function ($app) {
    // No need to modify underlying repository classes to add trait
    return new CachingRepository(
        $app->make(EloquentUserRepository::class);
        $app->make(CacheRepository::class)
    );
});
class UserController extends Controller
{
    public function __construct(UserRepositoryContract $users)
    {
        // No need to litter client client code with middleware to use in repository
        $this->users = $users;
    }
}
imanghafoori1 commented 5 years ago
  1. How to share decorators in packages?? that decorator you mentioned is tightly coupled to a specific repo class.

  2. If you decorate the repo interface at boot phase how do you decide to skip decorators in case you don't what them in a specific controller?

  3. What is harmful about using a trait? Is creating interfaces and all the binding easier?

Before eagerly down vote, think it a second time.

martinbean commented 5 years ago

Before eagerly down vote, think it a second time.

@imanghafoori1 Stop getting so defensive. I read your proposal, I disagreed. There was no rushing.

  1. What’s this got to do with packages? You’re proposing it for a framework, not a package. Package authors can distribute decorators for repositories and other classes if they so wish. But this requires package maintainers to know about a particular user’s implementation, which is difficult unless package maintainers are clairvoyant.
  2. Why would a particular controller dictate what decorators a repository (or any other class) has?
  3. As someone who’s so concerned with academic definitions of programming patterns and principles, I’d hope you’d see that having to open classes to add a trait is a violation of the ‘O’ in SOLID. You’re having to open and modify every class you want to add this functionality to, instead of just simply decorating those classes with the desired behaviour.