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?
https://komarev.com/sources/laravel-love
MIT License
1.17k stars 71 forks source link

How to delete all reactions made by the User (unreactAll)? #131

Closed wimil closed 4 years ago

wimil commented 4 years ago

There is some method to erase all the reactions that a reacter has reacted to.

example something like unreactTo but all would be unreactAll

antonkomarev commented 4 years ago

Could you provide a use-case for this feature, please?

Reacter's unreactTo method just trying to find reaction from the Reacter related to Reactant model and then deletes it. So you can do it manually in a pretty trivial way:

$reactions = $user->viaLoveReacter()->getReactions();
foreach ($reactions as $reaction) {
    $reaction->delete();
}

If you will delete reactions directly from the database using plain SQL query - then Laravel Eloquent model events wouldn't be fired and reaction counters will be un-synced.

wimil commented 4 years ago

I am using Facebook reactions, a user can react with a "pisses me off", but he can change his reaction to a "like". How could I do that? If the only way is to eliminate the reaction, and save the new reaction, now the unreactTo method only eliminates by type of reaction, then I would have to bring the reaction to get the type, and then eliminate. what occurred to me was to eliminate all the reactions and then add the new reaction, so I created a method called unreactAll, but it does not update the total and count of the reactions

public function unreactAll(ReactantContract $reactant): void
{
    if ($reactant->isNull()) {
        throw ReactantInvalid::notExists();
    }

    $this->reactions()
        ->where('reactant_id', $reactant->getId())->delete();
}
antonkomarev commented 4 years ago

Ah, now I see your requirement.

That happens because you are calling delete method of the Query Builder instead of the Eloquent Model. Fetch all reactions first and then delete each.

$reactions = $this->reactions()->where('reactant_id', $reactant->getId())->get();
foreach ($reactions as $reaction) {
    $reaction->delete();
}

Or use some Laravel magic if you like:

$reactions = $this->reactions()->where('reactant_id', $reactant->getId())->get();
$reactions->each->delete();

Sadly, there is no way to listen Query Builder requests by event listeners so we cannot execute recounting.

wimil commented 4 years ago

Thanks for the help, I created a method called "unreactFirst" that brings the first result and eliminates it, so use the eloquent and activate the events

public function unreactFirst(ReactantContract $reactant): void
    {
        if ($reactant->isNull()) {
            throw ReactantInvalid::notExists();
        }
        $this
            ->reactions()
            ->where('reactant_id', $reactant->getId())
            ->first()
            ->delete();
    }

With this I have solved the problem and now the user can react and change his reaction.

antonkomarev commented 4 years ago

If user is allowed to create only one reaction, that should work.

If your method located in application's User model, I'd recommend you to rely on Cog\Contracts\Love\Reactable\Models\Reactable contract instead of Cog\Contracts\Love\Reactant\Models\Reactant. Then your application logic will be less coupled with Love package implementation.

use Cog\Contracts\Love\Reactable\Models\Reactable as ReactableContract;

public function unreactFirst(ReactableContract $reactable): void
{
    $reactant = $reactable->getLoveReactant();

    if ($reactant->isNull()) {
        throw ReactantInvalid::notExists();
    }

    $this
        ->getLoveReacter()
        ->reactions()
        ->where('reactant_id', $reactant->getId())
        ->first()
        ->delete();
}

Instead of:

$user->unreactFirst($comment->getLoveReactant());

You will write:

$user->unreactFirst($comment);

You can see how better your code looks like by using Reacter Facade & Reactant Facade.

wimil commented 4 years ago

thank you very much for the advice and help