Installation:
composer require webotvorba/laravel-reactions
Publish the migration and config:
php artisan vendor:publish --provider="Webotvorba\LaravelReactions\LaravelReactionsServiceProvider" --tag="migrations"
php artisan vendor:publish --provider="Webotvorba\LaravelReactions\LaravelReactionsServiceProvider" --tag="config"
Run migrations:
php artisan migrate
Configure Models:
Add the HasReactions trait to your Eloquent model (e.g., Post.php):
use Webotvorba\LaravelReactions\HasReactions;
class Post extends Model
{
use HasReactions;
}
First Use Case: Add a reaction to a model instance:
$post = Post::find(1);
$post->react('👍'); // Add a reaction
Fetch reactions for a model:
$reactions = $post->reactions; // Collection of reactions
$count = $post->reactionCount('👍'); // Count for a specific emoji
Adding/Removing Reactions:
// Add a reaction (creates or increments)
$model->react('😂');
// Remove a reaction (decrements or deletes)
$model->unreact('👍');
// Toggle reaction (adds if missing, removes if exists)
$model->toggleReaction('❤️');
Querying Reactions:
// Get all reactions for a model
$model->reactions;
// Get reaction count for a specific emoji
$model->reactionCount('👍');
// Check if a user reacted
$model->hasReacted('😂', auth()->id());
Custom Reaction Types:
Extend the Reaction model to add custom fields (e.g., created_at for timestamps):
class CustomReaction extends \Webotvorba\LaravelReactions\Models\Reaction
{
protected $table = 'custom_reactions';
}
Update the config to use your custom model:
'model' => \App\Models\CustomReaction::class,
Frontend Integration: Use Blade directives to render reactions:
@reactions($post)
<button data-reaction="{{ $reaction }}">
{{ $reaction }} ({{ $post->reactionCount($reaction) }})
</button>
@endreactions
JavaScript (e.g., Alpine.js) to handle toggling:
document.querySelectorAll('[data-reaction]').forEach(button => {
button.addEventListener('click', () => {
const reaction = button.dataset.reaction;
axios.post(`/posts/${postId}/react`, { reaction });
});
});
API Endpoints: Create a controller method to handle reactions:
public function react(Request $request, Post $post)
{
$post->toggleReaction($request->reaction);
return response()->json(['success' => true]);
}
Add a route:
Route::post('/posts/{post}/react', [PostReactionController::class, 'react']);
Migration Conflicts:
reactions table already exists, manually adjust the migration or drop the table before re-running migrations.reactionable_id and reactionable_type columns are correctly set up for polymorphic relations.Caching Reactions:
$count = Cache::remember("reactions_{$model->id}_{$emoji}", now()->addHours(1), function() use ($model, $emoji) {
return $model->reactionCount($emoji);
});
Duplicate Reactions:
react method in your model:
public function react($emoji, $userId = null)
{
$userId = $userId ?: auth()->id();
$this->reactions()->updateOrCreate(
['user_id' => $userId, 'emoji' => $emoji],
['emoji' => $emoji]
);
}
Performance with Large Datasets:
withReactions only when necessary:
$posts = Post::withReactions()->get(); // Only if you need reactions for all posts
Custom Validation:
public function rules()
{
return [
'reaction' => ['required', 'string', Rule::in(['👍', '👎', '😂', '❤️'])]
];
}
Check Database:
reactions table has the expected columns (user_id, emoji, reactionable_id, reactionable_type).php artisan tinker to test reactions directly:
$post = App\Models\Post::find(1);
$post->react('👍');
$post->reactions()->get();
Log Queries: Enable Laravel's query logging to debug issues:
DB::enableQueryLog();
$post->react('👍');
dd(DB::getQueryLog());
Event Listeners:
public function boot()
{
\Webotvorba\LaravelReactions\Events\ReactionAdded::class => function ($event) {
Notification::send($event->model->user, new ReactionNotification($event->reaction));
};
}
Custom Reaction Models:
Reaction model to add metadata (e.g., ip_address, device):
class Reaction extends \Webotvorba\LaravelReactions\Models\Reaction
{
protected $fillable = ['ip_address', 'device'];
}
Reaction Scopes:
public function scopeWithReaction($query, $emoji)
{
return $query->whereHas('reactions', function($q) use ($emoji) {
$q->where('emoji', $emoji);
});
}
Usage:
$posts = Post::withReaction('👍')->get();
Reaction Aggregates:
public function getTopReactionsAttribute()
{
return $this->reactions()->groupBy('emoji')
->select('emoji', DB::raw('COUNT(*) as count'))
->orderBy('count', 'desc')
->get();
}
Reaction Policies:
public function react(Post $post)
{
$this->authorize('react', $post);
$post->react($request->reaction);
}
How can I help you explore Laravel packages today?