cybercog/laravel-love
Add reactions, likes, votes, and claps to any Eloquent model. Laravel Love provides a flexible, enterprise-ready system for GitHub/Facebook-style reactions with migrations and upgrade guidance, letting users express feelings about your content in minutes.
Install the package:
composer require cybercog/laravel-love
php artisan migrate
Make a model reactable (e.g., Post):
use Cog\Laravel\Love\Traits\Reactable;
class Post extends Model
{
use Reactable;
}
Make a model reacterable (e.g., User):
use Cog\Laravel\Love\Traits\Reacterable;
class User extends Model
{
use Reacterable;
}
First reaction (e.g., a "like"):
$user = User::first();
$post = Post::first();
$user->reactTo($post, 'like'); // Default reaction type
Display reaction counts on a post:
$post = Post::withReactionCounts()->find(1);
echo $post->reaction_counts['like']; // Outputs: 42
Reaction System Setup
php artisan love:setup-reactable and php artisan love:setup-reacterable to generate migrations for existing models.config/love.php:
'reaction_types' => [
'like' => ['mass' => 1, 'weight' => 1],
'dislike' => ['mass' => -1, 'weight' => 1],
'love' => ['mass' => 2, 'weight' => 2],
],
Weighted Reactions
$user->reactTo($post, 'like', 5.0); // Rate between Reaction::RATE_MIN (1) and Reaction::RATE_MAX (10)
$post->reaction_totals['like']; // Returns weighted sum (e.g., 5.0 * 42)
Querying Reactions
Post::whereReactedToBy(User::first(), 'like')->get();
Post::whereNotReactedToBy(User::first())->get();
Post::joinReactionCounterOfType('like')->get();
Aggregation & Caching
php artisan love:recount --queue-connection=redis
$post->reaction_counts; // ['like' => 42, 'dislike' => 5]
$post->reaction_totals; // ['like' => 210.0, 'dislike' => -25.0]
Custom Reaction Types
$reactionType = \Cog\Laravel\Love\ReactionType::create('clap', ['mass' => 1, 'weight' => 1]);
$user->reactTo($post, $reactionType);
Reacter::hasReactedTo() to check reactions in auth middleware:
if (auth()->user()->hasReactedTo($post, 'like')) {
return response()->json(['already_liked' => true]);
}
Post::with(['reaction_counts', 'reaction_totals'])->get();
php artisan love:recount to manually update aggregates after bulk operations.Rate Validation
Reaction::RATE_MIN (1) and Reaction::RATE_MAX (10) throw RateOutOfRange or RateInvalid. Validate client-side to avoid API errors.Queue Dependencies
reaction_counts, reaction_totals) are updated asynchronously. Avoid relying on them immediately after reactions (e.g., in real-time APIs). Use Reaction::count() for live counts:
$post->reactions()->where('type', 'like')->count();
Migration Conflicts
love_reactant_id or love_reacter_id columns, ensure they match the config (love.tables.reactants, love.tables.reacters). Use --not-nullable in setup commands for required fields.Observer Overrides
ReactionObserver in v10+. If extending, register custom observers in LoveEventServiceProvider:
public function boot()
{
$this->registerReactantListeners();
$this->registerReacterListeners();
}
Caching Quirks
php artisan cache:clear
--queue-connection with love:recount to avoid timeouts.Reaction Not Showing?
Check if the reacter_id (user) and reactant_id (post) are correctly linked. Run:
php artisan tinker
>> \Cog\Laravel\Love\Reaction::where('reacter_id', 1)->where('reactant_id', 1)->get();
Aggregates Out of Sync? Force recount:
php artisan love:recount --force
Performance Issues?
Disable aggregate caching in config/love.php:
'cache_aggregates' => false,
Custom Reaction Models
Override the default Reaction model by binding your class in LoveServiceProvider:
$this->app->bind(\Cog\Contracts\Love\Reaction::class, CustomReaction::class);
Event Listeners Extend reaction logic by listening to events:
\Cog\Laravel\Love\Events\ReactionCreated::class => [
\App\Listeners\LogReaction::class,
],
Dynamic Reaction Types Fetch all types dynamically:
$types = \Cog\Laravel\Love\ReactionType::all();
Multi-Tenant Support
Scope reactions by tenant in Reacterable/Reactable traits by overriding:
protected static function getReacterTable()
{
return 'tenants_' . tenant()->id . '.users';
}
Reacter::reactToMany() for batch operations:
$user->reactToMany($posts, 'like');
reaction_totals with reaction_counts to calculate averages:
$post->average_rating = $post->reaction_totals['like'] / $post->reaction_counts['like'];
Reacter/Reactant models use SoftDeletes if reactions should persist after deletion. Override getQualifiedReacterKeyName()/getQualifiedReactantKeyName() to handle soft-deleted keys.How can I help you explore Laravel packages today?