cybercog / laravel-love

Add Social Reactions to Laravel Eloquent Models. It lets people express how they feel about the content. Fully customizable Weighted Reaction System & Reaction Type System with Like, Dislike and any other custom emotion types. Do you react?

Home Page:https://komarev.com/sources/laravel-love

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Concept: Configurable behavior on event dispatching

antonkomarev opened this issue · comments

How could we override what will happen on Love event will be dispatched?

This question was raised many times before. Each time I've returned to it a new ideas come into my mind but all of them have their pros and cons. Finally I've got all pieces together and ready to share with my vision.

Thanks to Pavel Volkov @volkv for helping me out with better solution.

First, we should encapsulate increment & decrement logic in jobs. Because we will be able to run those jobs from any place in any time without dispatching an event (which may be listened by many listeners at moment). Listeners will become very simple and synchronous but they will dispatch queued jobs.

Second, we should have more control over the queue. Because developers should be able to choose if they want to use stock package behavior or they want to modify it to their application requirements.

I've designed 4 different implementations of the second part. Each implementation will be described in separate comment.

Spoiler: 4th solution will be implemented #145 (comment)

1. Love global jobs connection

Add love.jobs_connection config value which will affect on all jobs.

return [
    // ...other config fields

    'jobs_connection' => null,
];

Right now its fine because we have only 2 listeners, but later it might be an issue when only some jobs will require to work synchronously.

Pros:

  • Simple

Cons:

  • Not extendable

2. Queue options in config

Add love.jobs config array where we could define jobs queue options specific for each job.

return [
    // ...other config fields
    'jobs' => [
        Cog\Laravel\Love\Reactant\Jobs\IncrementAggregatesJob => [
            'connection' => 'custom_connection_name',
            'queue' => 'custom_queue_name',
            'timeout' => 60,
            'delay' => 90,
        ],
        Cog\Laravel\Love\Reactant\Jobs\DecrementAggregatesJob => [
            'connection' => 'custom_connection_name',
            'queue' => 'custom_queue_name',
            'timeout' => 60,
            'delay' => 90,
        ],
    ],
];

Pros:

  • Each job could be fine-tuned for the app requirements

Cons:

  • One more place where breaking changes may pop up in future versions
  • Hard to maintain
  • Not obvious where those options are coming from

3. Love jobs config registry

Add love.jobs config array which will work as service locator.

return  [
    // ...other config fields

    'jobs' => [
        Cog\Laravel\Love\Reactant\Jobs\IncrementAggregatesJob::class => App\Jobs\Love\ReactantIncrementAggregatesJob,
        Cog\Laravel\Love\Reactant\Jobs\DecrementAggregatesJob::class => App\Jobs\Love\ReactantDecrementAggregatesJob,
    ],
]

or

return  [
    // ...other config fields

    'jobs' => [
        'reactant_increment_aggregates' => Cog\Laravel\Love\Reactant\Jobs\IncrementAggregatesJob::class,
        'reactant_decrement_aggregates' => Cog\Laravel\Love\Reactant\Jobs\DecrementAggregatesJob::class,
    ],
]

or

return  [
    // ...other config fields

    'jobs' => [
        'reactant' => [
            'increment_aggregates' => Cog\Laravel\Love\Reactant\Jobs\IncrementAggregatesJob::class,
            'decrement_aggregates' => Cog\Laravel\Love\Reactant\Jobs\DecrementAggregatesJob::class,
        ],
    ],
]

Pros:

  • You can easily replace stock jobs with your own ones

Cons:

  • Not obvious where this rebinding is being done
  • Not obvious what arguments will be passed in job's __construct method
  • One more place where breaking changes may pop up in future versions

4. Love event service provider

Introduce LoveEventServiceProvider class which will be responsible for registering Event Listeners only.

final class LoveEventServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        Event::listen(ReactionHasBeenAdded::class, IncrementAggregates::class);
        Event::listen(ReactionHasBeenRemoved::class, DecrementAggregates::class);
    }
}

For example if you want to update reactant counters synchronously you could follow 3 easy steps.

  1. You will need to opt-out package discovery:
"extra": {
    "laravel": {
        "dont-discover": [
            "cybercog/laravel-love"
        ]
    }
},
  1. Register only core LoveServiceProvider provider in your application's AppServiceProvider:
$this->app->register(\Cog\Laravel\Love\LoveServiceProvider::class);
  1. Finally, register any custom event listeners in your EventServiceProvider. To not reinvent the wheel you could just make a copy of stock Love's listeners and dispatch jobs introduced in #146 on sync connection:
IncrementAggregatesJob::dispatch()->onConnection('sync');

Pros:

  • Easy to opt-out
  • Single responsibility

Cons:

  • ???

This concept has been implemented in these PRs: #146 #147