Package Data | |
---|---|
Maintainer Username: | antonkomarev |
Maintainer Contact: | anton@komarev.com (Anton Komarev) |
Package Create Date: | 2018-01-02 |
Package Last Update: | 2024-03-09 |
Home Page: | https://komarev.com/sources/laravel-love |
Language: | PHP |
License: | MIT |
Last Refreshed: | 2024-11-29 15:18:43 |
Package Statistics | |
---|---|
Total Downloads: | 231,503 |
Monthly Downloads: | 2,697 |
Daily Downloads: | 94 |
Total Stars: | 1,168 |
Total Watchers: | 23 |
Total Forks: | 71 |
Total Open Issues: | 11 |
Laravel Love is emotional part of the application. It let people express how they feel about the content. Make any model reactable in a minutes!
There are many different implementations in modern applications:
This package developed in mind that it should cover all the possible use cases and be viable in enterprise applications.
It is a successor of the very simple abandoned package: Laravel Likeable.
id
column types.love:recount {model?} {type?}
to re-fetch reactions stats.Reaction
— the response that reveals Reacter's feelings or attitude.ReactionType
— type of the emotional response (Like, Dislike, Love, Hate, etc).Reacterable
— User, Person, Organization or any other model which can act as Reacter.Reacter
— one who reacts.Reactable
— Article, Comment, User or any other model which can act as Reactant.Reactant
— subject which could receive Reactions.ReactionCounter
— aggregated statistical values of ReactionTypes related to Reactant.ReactionTotal
— aggregated statistical values of total reactions count & their weight related to Reactant.Laravel Love has a few requirements you should be aware of before installing:
Pull in the package through Composer.
$ composer require cybercog/laravel-love
Run database migrations.
$ php artisan migrate
If you want to make changes in migrations, publish them to your application first.
$ php artisan vendor:publish --tag=love-migrations
To start using package you need to have:
Reacterable
model, which will act as Reacter
and will react to the content.Reactable
model, which will act as Reactant
and will receive reactions.Each model which can act as Reacter
and will react to content must implement Reacterable
contract.
Declare that model implements Cog\Contracts\Love\Reacterable\Models\Reacterable
contract
and use Cog\Laravel\Love\Reacterable\Models\Traits\Reacterable
trait.
use Cog\Contracts\Love\Reacterable\Models\Reacterable as ReacterableContract;
use Cog\Laravel\Love\Reacterable\Models\Traits\Reacterable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable implements ReacterableContract
{
use Reacterable;
}
After that create & run migrations which will add unsigned big integer column love_reacter_id
to each database table where reacterable models are stored.
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->unsignedBigInteger('love_reacter_id');
});
}
Each model which can act as Reactant
and will receive reactions must implement Reactable
contract.
Declare that model implements Cog\Contracts\Love\Reactable\Models\Reactable
contract
and use Cog\Laravel\Love\Reactable\Models\Traits\Reactable
trait.
use Cog\Contracts\Love\Reactable\Models\Reactable as ReactableContract;
use Cog\Laravel\Love\Reactable\Models\Traits\Reactable;
use Illuminate\Database\Eloquent\Model;
class Article extends Model implements ReactableContract
{
use Reactable;
}
After that create & run migrations which will add unsigned big integer column love_reactant_id
to each database table where reactable models are stored.
public function up(): void
{
Schema::table('articles', function (Blueprint $table) {
$table->unsignedBigInteger('love_reactant_id');
});
}
ReactionType
model describes how Reacter
reacted to Reactant
.
By default there are 2 types of reactions Like
and Dislike
.
Each Like
adds +1
to reactant's total weight while Dislike
type subtract -1
from it.
$reactionType = ReactionType::fromName('Like');
$typeName = $reactionType->getName(); // 'Like'
$typeWeight = $reactionType->getWeight(); // 1
$likeType = ReactionType::fromName('Like');
$dislikeType = ReactionType::fromName('Dislike');
$isEqual = $likeType->isEqualTo($likeType); // true
$isEqual = $likeType->isEqualTo($dislikeType); // false
$isNotEqual = $likeType->isNotEqualTo($likeType); // false
$isNotEqual = $likeType->isNotEqualTo($dislikeType); // true
To let User
react to the content it need to be registered as Reacter
.
By default it will be done automatically on successful Reacterable
creation,
but if this behavior was changed you still can do it manually.
$user->registerAsLoveReacter();
Creation of the Reacter
could be done only once for each Reacterable
model.
If you will try to register Reacterable
as Reacter
one more time then
Cog\Contracts\Love\Reacterable\Exceptions\AlreadyRegisteredAsLoveReacter
exception will be thrown.
If you want to skip auto-creation of related
Reacter
model just add boolean methodshouldRegisterAsLoveReacterOnCreate
toReacterable
model which will returnfalse
.
If you want to verify if Reacterable
is registered as Reacter
or not you can use boolean methods.
$isRegistered = $user->isRegisteredAsLoveReacter(); // true
$isNotRegistered = $user->isNotRegisteredAsLoveReacter(); // false
Only Reacter
model can react to content. Get Reacter
model from your Reacterable
model.
$reacter = $user->getLoveReacter();
If
Reacterable
model is not registered asReacter
you will receiveNullReacter
model instead (NullObject design pattern). All it's methods will be callable, but will throw exceptions or returnfalse
.
$reacterable = $reacter->getReacterable();
$reacter->reactTo($reactant, $reactionType);
$reacter->unreactTo($reactant, $reactionType);
Determine if Reacter
reacted to Reactant
with any type of reaction.
$isReacted = $reacter->isReactedTo($reactant);
$isNotReacted = $reacter->isNotReactedTo($reactant);
Determine if Reacter
reacted to Reactant
with exact type of reaction.
$reactionType = ReactionType::fromName('Like');
$isReacted = $reacter
->isReactedToWithType($reactant, $reactionType);
$isNotReacted = $reacter
->isNotReactedToWithType($reactant, $reactionType);
$reactions = $reacter->getReactions();
TODO: Need to add pagination
To let Article
to receive reactions from users it need to be registered as Reactant
.
By default it will be done automatically on successful Reactable
creation,
but if this behavior was changed you still can do it manually.
$user->registerAsLoveReactant();
Creation of the Reactant
could be done only once for each Reactable
model.
If you will try to register Reactable
as Reactant
one more time then
Cog\Contracts\Love\Reactable\Exceptions\AlreadyRegisteredAsLoveReactant
exception will be thrown.
If you want to skip auto-creation of related
Reactant
model just add boolean methodshouldRegisterAsLoveReactantOnCreate
toReactable
model which will returnfalse
.
If you want to verify if Reactable
is registered as Reactant
or not you can use boolean methods.
$isRegistered = $user->isRegisteredAsLoveReactant(); // true
$isNotRegistered = $user->isNotRegisteredAsLoveReactant(); // false
Only Reacter
model can react to content. Get Reacter
model from your Reactable
model.
$reactant = $user->getLoveReactant();
If
Reactable
model is not registered asReactant
you will receiveNullReactant
model instead (NullObject design pattern). All it's methods will be callable, but will throw exceptions or returnfalse
.
$reactable = $reactant->getReactable();
Determine if Reacter
reacted to Reactant
with any type of reaction.
$isReacted = $reactant->isReactedBy($reacter);
$isNotReacted = $reactant->isNotReactedBy($reacter);
Determine if Reacter
reacted to Reactant
with exact type of reaction.
$reactionType = ReactionType::fromName('Like');
$isReacted = $reactant
->isReactedByWithType($reacter, $reactionType);
$isNotReacted = $reactant
->isNotReactedByWithType($reacter, $reactionType);
$reactions = $reactant->getReactions();
TODO: Need to add pagination
Each Reactant
has many counters (one for each reaction type) with aggregated data.
$reactionCounters = $reactant->getReactionCounters();
Or get only counter of exact type.
$reactionType = ReactionType::fromName('Like');
$reactionCounter = $reactant->getReactionCounterOfType($reactionType);
When you need to determine count of reactions of this type you can get count.
$totalWeight = $reactionCounter->getCount();
When you need to determine weight which all reactions of this type gives you can get weight.
$totalWeight = $reactionCounter->getWeight();
Each Reactant
has one total with aggregated data. Total is sum of counters of all reaction types.
$reactionTotal = $reactant->getReactionTotal();
When you need to determine total reactions count you can get count.
$totalWeight = $reactionTotal->getCount();
When you need to determine total weight of reactions you can get weight.
$totalWeight = $reactionTotal->getWeight();
If each
Like
has weight+1
andDislike
has weight-1
then 3 likes and 5 dislikes will produce-2
total weight.
$reacter = $user->getLoveReacter();
Article::query()
->whereReactedBy($reacter)
->get();
$reacter = $user->getLoveReacter();
$reactionType = ReactionType::fromName('Like');
$articles = Article::query()
->whereReactedByWithType($reacter, $reactionType)
->get();
$reactionType = ReactionType::fromName('Like');
$articles = Article::query()
->joinReactionCounterOfType($reactionType)
->get();
Each Reactable model will contain reactions_count
& reactions_weight
virtual attributes.
After adding counter aggregate models could be ordered by this value.
$articles = Article::query()
->joinReactionCounterOfType($reactionType)
->orderBy('reactions_count', 'desc')
->get();
$articles = Article::query()
->joinReactionTotal()
->get();
Each Reactable model will contain reactions_total_count
& reactions_total_weight
virtual attributes.
After adding total aggregate models could be ordered by this values.
Order by reactions_total_count
:
$articles = Article::query()
->joinReactionTotal()
->orderBy('reactions_total_count', 'desc')
->get();
Order by reactions_total_weight
:
$articles = Article::query()
->joinReactionTotal()
->orderBy('reactions_total_weight', 'desc')
->get();
Laravel Love ships with Love
facade and allows to execute actions as Reacterable
model
instead of acting as Reacter
and affect on Reactable
models instead of Reactant
.
Note: Love facade is experimental feature which will be refactored in next releases. Try to avoid it's usage if possible.
$isOfType = Love::isReactionOfTypeName($reaction, 'Like');
$isNotOfType = Love::isReactionNotOfTypeName($reaction, 'Like');
Same functionality without facade:
$reactionType = ReactionType::fromName('Like');
$isOfType = $reaction->isOfType($reactionType);
$isNotOfType = $reaction->isNotOfType($reactionType);
$isReacted = Love::isReacterableReactedTo($user, $article);
$isNotReacted = Love::isReacterableNotReactedTo($user, $article);
Same functionality without facade:
$reactant = $article->getLoveReactant();
$isReacted = $reacterable
->getLoveReacter()
->isReactedTo($reactant);
$isNotReacted = $reacterable
->getLoveReacter()
->isNotReactedTo($reactant);
$isReacted = Love::isReacterableReactedToWithTypeName($user, $article, 'Like');
$isReacted = Love::isReacterableNotReactedToWithTypeName($user, $article, 'Like');
Same functionality without facade:
$reactant = $article->getLoveReactant();
$reactionType = ReactionType::fromName('Like');
$isReacted = $reacterable
->getLoveReacter()
->isReactedToWithType($reactant, $reactionType);
$isNotReacted = $reacterable
->getLoveReacter()
->isNotReactedToWithType($reactant, $reactionType);
$isReacted = Love::isReactableReactedBy($article, $user);
$isReacted = Love::isReactableNotReactedBy($article, $user);
Same functionality without facade:
$reacter = $user->getLoveReacter();
$isReacted = $reactable
->getLoveReactant()
->isReactedBy($reacter);
$isNotReacted = $reactable
->getLoveReactant()
->isNotReactedBy($reacter);
$isReacted = Love::isReactableReactedByWithTypeName($article, $user, 'Like');
$isReacted = Love::isReactableNotReactedByWithTypeName($article, $user, 'Like');
Same functionality without facade:
$reacter = $user->getLoveReacter();
$reactionType = ReactionType::fromName('Like');
$isReacted = $reactable
->getLoveReactant()
->isReactedByWithType($reacter, $reactionType);
$isNotReacted = $reactable
->getLoveReactant()
->isNotReactedByWithType($reacter, $reactionType);
$likesCount = Love::getReactableReactionsCountForTypeName($article, 'Like');
Same functionality without facade:
$reactionType = ReactionType::fromName('Like');
$likesCount = $reactable
->getLoveReactant()
->getReactionCounterOfType($reactionType)
->getCount();
$likesWeight = Love::getReactableReactionsWeightForTypeName($article, 'Like');
Same functionality without facade:
$reactionType = ReactionType::fromName('Like');
$likesWeight = $reactable
->getLoveReactant()
->getReactionCounterOfType($reactionType)
->getWeight();
$reactionsTotalCount = Love::getReactableReactionsTotalCount($article);
Same functionality without facade:
$reactionsTotalCount = $reactable
->getLoveReactant()
->getReactionTotal()
->getCount();
$reactionsTotalWeight = Love::getReactableReactionsTotalWeight($article);
Same functionality without facade:
$reactionsTotalWeight = $reactable
->getLoveReactant()
->getReactionTotal()
->getWeight();
When accessing Eloquent relationships as properties, the relationship data is "lazy loaded". This means the relationship data is not actually loaded until you first access the property. However, Eloquent can "eager load" relationships at the time you query the parent model. Eager loading alleviates the N + 1 query problem. More details read in official Laravel documentation.
List of the most common eager loaded relations:
loveReactant.reactions.type
loveReactant.reactions.reacter.reacterable
loveReactant.reactionCounters
loveReactant.reactionTotal
$articles = Article::query()
->with([
'loveReactant.reactions.reacter.reacterable',
'loveReactant.reactions.type',
'loveReactant.reactionCounters',
'loveReactant.reactionTotal',
])
->get();
On each added reaction Cog\Laravel\Love\Reaction\Events\ReactionHasBeenAdded
event is fired.
On each removed reaction Cog\Laravel\Love\Reaction\Events\ReactionHasBeenRemoved
event is fired.
$ love:recount
$ love:recount --model="article"
$ love:recount --model="App\Models\Article"
$ love:recount --type="Like"
$ love:recount --model="article" --type="Like"
$ love:recount --model="App\Models\Article" --type="Like"
$ love:recount --type="Dislike"
$ love:recount --model="article" --type="Dislike"
$ love:recount --model="App\Models\Article" --type="Dislike"
Please see CHANGELOG for more information on what has changed recently.
Please see UPGRADING for detailed upgrade instructions.
Please see CONTRIBUTING for details.
Run the tests with:
$ vendor/bin/phpunit
If you discover any security related issues, please email open@cybercog.su instead of using the issue tracker.
| Anton Komarev | Squigg | Kevin Olson | Ranie Santos |
| :---: | :---: | :---: | :---: |
Laravel Love contributors list
Feel free to add more alternatives as Pull Request.
Laravel Love
package is open-sourced software licensed under the MIT license by Anton Komarev.Devil
image licensed under Creative Commons 3.0 by YuguDesign.CyberCog is a Social Unity of enthusiasts. Research best solutions in product & software development is our passion.