Easily decorate your method calls with laravel-decorator package
Branch: master
Clone or download
Latest commit dbcd667 Jan 25, 2019
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
src Apply fixes from StyleCI Jan 25, 2019
tests make facades decoratable with using decoratable trait. Jan 25, 2019
.gitignore add some files Jan 9, 2019
.scrutinizer.yml add some files Jan 9, 2019
.travis.yml Update .travis.yml Jan 13, 2019
LICENSE Initial commit Jan 8, 2019
README.md Update README.md Jan 17, 2019
composer.json Added composer suggestions Jan 17, 2019
composer.lock add some files Jan 9, 2019
phpunit.xml add some files Jan 9, 2019
phpunit.xml.dist add some files Jan 9, 2019

README.md

🎄 Laravel Decorator

Build Status Quality Score StyleCI Software License Latest Stable Version Code Coverage

Made with ❤️ for smart clean coders

A try to port "decorator" feature from python language to laravel framework.

python-and-prey

You might say, why such a horrible header 😱 is chosen for a laravel package ?! You will shortly see why...

🚚 Installation :

composer require imanghafoori/laravel-decorator

What is a "Decorator"

A decorator is callable which wraps around the original decorated callable, in order to form a new callable composed of the previous two.

Like a python snake swallowing a deer whole and wraps around it's body !

After that the snake becomes capable to eat and digest grasses 🌿 but only if the deer is still alive.

Technically, A "Decorator" :

1 - Is a "callable" (python is an animal)

2 - It takes a "callable" as it's only argument (like a python swallows an other animal)

3 - It returns a newly born "callable" that calls that callable (and turns into a bloated animal surrounding it.)

What ?!??! ?!!? ?!?!? ???!

What is a "callable", man ?!

Long story short, A callable (here in laravel) is anything that can be called (invoked) with \App::call();

Look at the below picture :

Here you can see the bare anatomy of a decorator.

image

Why it is a decorator ? well because :

1 - A public method is a "callable" in php.

2 - The method has taken a "callable" as it's argument.

3 - The whole thing surrounded by the black line is a closure, which is "returned" from the method.

A Use Case:

Cache Like a Pro:

Imagine that you have a MadControllerwhich calls a MadRepository to get some $mad items.

Then after a while you decide to put a cache layer between does two for obvious reasons, but they both are so mad, that they do not allow you to touch a single line of their code.

You can not, (or maybe you CAN but shouldn't) put the cache logic in your controller or your repository.

You want to add a new feature (caching in this case) without modifing the existing code.

It smells like Open-closed Principle Yeah ?! 👃

And you want to keep the responsibilities seperate. In this case caching should not be in a repository or controller but in it's own class.

It smells like Single Responsibility Principle yeah ?! 👃

(Or maybe both MadRepository and MadController are imprisoned in the vendor folder and are part of a laravel package, so you can not touch them)


class MadController extends Controller
{
    public function index () {
    
        // we don't want to put any cache logic here...
        
        $madUser = MadRepositoryFacade::geMadUser($madId);
        ...
    }
}

So, what now ?!

With the help of laravel-decorator built-in cache decorator, you can go to AppServiceProvider.php (without any mad person realizing it.) 😁

<?php

use Imanghafoori\Decorator\Decorators\DecoratorFactory;

class AppServiceProvider extends ServiceProvider {

    public function boot( ) {

        $keyMaker = function ($madId) {
            return 'mad_user_key_' . $madId;
        };
        
        MadRepositoryFacade::decorateMethod('geMadUser', DecoratorFactory::cache($keyMaker, 10));
    }
}

Just that.

You will get cached results from your Facade calls, in your entire app without changing a single line of code !!

// you get cached results then ! Nice ?!
$madUser = MadRepositoryFacade::geMadUser($madId);

Here we return a callable that calls the original callable and casts it's result into string.

So Let's apply this to decorate on the baz method on the Bar class :".

image

Alternatively, you can use the \Decorator facade to do so

It's time to enjoy having a decorated call :"

image

Note: finally a closure is returned here (not the actual value)

Sample decorators:

For good working examples please take a look at the tests folder.

Naming Your Decorators:


public function boot () {
    \Decorator::define('myDecoratorName1', 'SomeClass@someMethod');
    
// or

    \Decorator::define('myDecoratorName2', function ($callable) {
        return function (...) use ($callable){ ... } 
    });
}

Then you can use this name (myDecoratorName) to decorate methods.

How to apply a decorator on a method ?


// You may set multiple decorators on a single method...

\Decorator::decorate('class@method, 'someClass@someOtherDecorator');

// or reference the decorator by it's name :

\Decorator::decorate('class@method, 'myDecoratorName');

How to call a method with it's decorator ?

image

Decorate Facades :

Decorating Facade Methods

First, you should extend the Imanghafoori\Decorator\DecoratableFacade class (instead of the laravel base Facade).

image

Now You Can Apply Decorators in your ServiceProvider's boot method:

image

then if you call your facade as normal you get decorated results.

image

⚠️ Warning :

With great power, comes great responsibilities.

Remember not to violate the Liskoves Substitution Principle when you decorate something.

For example a method call which returns int|null should not unexpectedly return a string after being decorated.

$result = app('decorate')->call(...

Since the users of the method should be ready for type of value they get back.

But if you return only int and your decorator causes the null value to be filtered out. that's ok.

❗️ Security

If you discover any security related issues, please email imanghafoori1@gmail.com instead of using the issue tracker.

⭐️ Your Stars Make Us Do More ⭐️

As always if you found this package useful and you want to encourage us to maintain and work on it, Please press the star button to declare your willing.

More packages from the author:

💎 A minimal yet powerful package to give you opportunity to refactor your controllers.


💎 A minimal yet powerful package to give a better structure and caching opportunity for your laravel apps.


💎 It allows you login with any password in local environment only.


💎 Authorization and ACL is now very easy with hey-man package !!!