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.16k stars 72 forks source link

what happened to the ability to toggle reaction on/off? #117

Closed vesper8 closed 4 years ago

vesper8 commented 5 years ago

I am uprading from v5 to v8 and previously I was using $user->toggleLike($article)

Which would like it if it wasn't already liked, and unlike otherwise

I notice that toggleLike is not mentioned in the upgrade guide, instead likeToggle is briefly mentioned in the v4->v5 upgrade but then not mentioned at all afterwards

It's my understanding that in order to like I must now do $user->reactTo($article, 'like') (assuming I've used the default reaction types when setting up, which I have

But is there no longer a way to easily toggle between on/off like I was doing before?

Or must I add a lot more code now to do something like (pseudo code) if not liked, then like, else, like ?

Thanks!

antonkomarev commented 5 years ago

Yeah, you are right, I dropped this method.

I suppose you've tried to write something like:

$userReactrer = $user->viaLoveReacter();
$userReactrer->toggleReactTo($article, 'Like');

But now you have to write:

$userReactrer = $user->viaLoveReacter();
if ($userReactrer->hasReactedTo($article, 'Like')) {
    $userReactrer->unreactTo($article, 'Like');
} else {
    $userReactrer->reactTo($article, 'Like');
}

Now we have reaction $rate as third parameter of reactTo method. So it becomes unclear what should I do if toggle method is called, but rate is different from persisted one.

$userReactrer = $user->viaLoveReacter();
$userReactrer->toggleReactTo($article, 'Like', 4);
$userReactrer->toggleReactTo($article, 'Like', 6);

Should I change rate of the reaction or remove reaction?

Initially I thought that this method will be very useful, but in fact I've never used it. Usually I have 2 endpoints POST /reactions & DELETE /reactions/{reaction} and toggle logic is on frontend side, because anyway you always need to check if like has been done or not to know what to display to user.

So it was some kind of duplication, maintenance of never used methods and controversial behavior. It's not that hard to implement on application side and to be honest not much to write, so I decided it will be better to remove it.

vesper8 commented 5 years ago

Ok.. I understand your logic. Initially everyone that was using laravel-likeable, and then laravel-love, were people who only had the standard like/dislike problem to solve. Now you've made the package much more versatile for complex situations, but you still have a lot of users, possibly the majority, that still only want to use a like/dislike with no application of rate (which seems like a pretty rare use-case probably). It's great that all of this is possible, but it would be also nice if to remember that many (or most) users won't ever use a 'rate'. I mean if you look at all the other big guys you mention (Github, Facebook, YouTube, Slack, Reddit, Medium), they all don't use any weight/rate, as far as I know.

Anyway I will write a custom toggle method and add it to my user model so that I can continue to call toggleLike.. I wouldn't mind if it was brought back in the future but it's no big deal

vesper8 commented 5 years ago

I also liked it more how you used to be able to just do $user->toggleLike (or $user->reactTo) instead of having to first get the reacter through the viaLoveReacter method. It's not clear to me why we have to jump through this extra hoop now, why can't the user react to the article directly, why the need to get this reacter first

I also don't understand why we need to go through all the extra logic of adding a distinct love_reactant_id / love_reacter_id if it was working fine before with a single table and polymorphic fields without having to add additional fields to user/article tables.

It seems all you've added since v5 is new types of reactions and weight/rate (I don't mean to downplay your efforts, I'm sure you've added a lot more functionality), but it's not clear to me why you couldn't keep doing that with a single "love_likes" table like before.

What I don't get is, why the need for the love_reactant_id / love_reacter_id fields, couldn't you have identified the reacter/reactants through a combination of their unique ID and their fully qualified class.. as you used to do before with likeable_id and likeable_type

antonkomarev commented 5 years ago

I mean if you look at all the other big guys you mention (Github, Facebook, YouTube, Slack, Reddit, Medium), they all don't use any weight/rate, as far as I know.

Love Reaction Rate feature is required to implement reactions like Medium Claps. You can react to the reactable model only once, but rate of your reaction could differ. So some of the listed companies using it :wink:

@vesper8, regarding your second comment I could say it's very reasonable questions and we've asked them to ourselves many times before switching to the new system. It's a long story with many reasons, but I'll describe only critical ones.

Imagine that your Article model has id column of type BIGINT, but your User has id column of type UUID and both of the models should be reactable. If you'll have love_reactions table with reactable_type + reactable_id columns - what type will have reactable_id column?

If it will be BIGINT - you wouldn't be able to store uuid there, if it will be CHAR (in MySQL) or UUID (in Postgres) - you wouldn't be able to store integer in it. Sure, you can make it VARCHAR, but then your database indices wouldn't work well and you'll have performance issues even on small database.

And here we've got one more additional layer:

Reacter & Reactant models are stored in love_reacters & love_reactants database tables and have single type of id column. Due to this you can have love_reacter_id & love_reactant_id columns of only one type in love_reactions table. In other words - you don't care about type of id columns of your application models, you don't even care about existence of users and articles table. In fact, package doesn't need them to manage reactions and compute counters. Your reaction system doesn't know anything about reactables & reacterable models. You can even wipe your users table, but your reactions subsystem wouldn't be corrupted.

And the main reason why User in new system can't do anything - is naming conflicts. In short - we've got about 100 Laravel private packages and some of them has same method names and if you are adding 2 traits which uses same method names - you've got a critical conflict and your app blows up. But if User will delegate all the reaction related jobs to Reacter - then package doesn't need to know about User model to do its job, it only need to know about Reacter model. All the logic will be hidden in Reacter model and user wouldn't have all those methods which could intersect with other packages.

Yes, it makes system more complicated and requires to change mindset to start using package this way, but in our use cases it was worth it. You know that user can react to the content - just say that he need to act as Reacter from Love package $user->viaLoveReacter() and do what you need to do.

antonkomarev commented 5 years ago

This system design is not a silver bullet and we already know some of the possible problems we'll face in the future, but I hope we'll find a way to solve them!