How to Add Reactions to Laravel Using reactify
I added a reaction system to my blog posts, and it doesn't have to be complicated. With Reactify, you can easily make any Eloquent model capable of receiving reactions.
In this guide, I'll walk you through the step-by-step process of setting up Reactify. Let's get started!
Step 1: Install the Package
Start by installing the package via Composer:
composer require phpdominicana/reactify
Step 2: Publish the Config and Migrations
Publish the config and migration files:
php artisan vendor:publish --provider="PHPDominicana\Reactify\ReactifyServiceProvider"
Then run the migrations:
php artisan migrate
Step 3: Make Your Model Reactionable
You can make any model (like Post, Product, etc.) reactionable by using the HasReaction trait:
use PHPDominicana\Reactify\Traits\HasReaction; class Post extends Model { use HasReaction; }
Now your model can receive reactions!
Step 4: Add Reaction Functionality in Controller
Let’s assume you want to let users react on a post. Here's a simple controller method:
public function toggle(Request $request) { $request->validate([ 'reactable_id' => 'required|integer', 'reactable_type' => 'required|string', 'type' => ['required', new Enum(Reaction::class)], ]); $reactableType = $request->input('reactable_type'); $reactableId = $request->input('reactable_id'); $type = Reaction::from($request->input('type')); // Get the reactable model $reactable = $reactableType::findOrFail($reactableId); // Toggle the reaction $reactable->react(Auth::user(), $type); // Return the updated reaction counts return response()->json([ 'success' => true, 'reactions' => [ 'like' => $reactable->reactions(Reaction::LIKE), 'love' => $reactable->reactions(Reaction::LOVE), 'laugh' => $reactable->reactions(Reaction::LAUGH), 'wow' => $reactable->reactions(Reaction::WOW), 'sad' => $reactable->reactions(Reaction::SAD), 'angry' => $reactable->reactions(Reaction::ANGRY), ], 'user_reactions' => $reactable->getUserReactions(Auth::user()), ]); }
Step 5: Display Reactions in Blade
In your post.blade.php (or wherever you're showing the post):
@props(['model']) <div x-data="{ reactions: {}, userReactions: [], loading: true, error: null, init() { @auth this.fetchReactions(); @else this.loading = false; @endauth }, fetchReactions() { this.loading = true; fetch('/api/reactions', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': '{{ csrf_token() }}' }, body: JSON.stringify({ reactable_id: {{ $model->id }}, reactable_type: 'App\\Models\\{{ class_basename($model) }}' }) }) .then(response => response.json()) .then(data => { this.reactions = data.reactions; this.userReactions = data.user_reactions; this.loading = false; }) .catch(error => { this.error = 'Failed to load reactions'; this.loading = false; console.error('Error:', error); }); }, toggleReaction(type) { fetch('/api/reactions/toggle', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': '{{ csrf_token() }}' }, body: JSON.stringify({ reactable_id: {{ $model->id }}, reactable_type: 'App\\Models\\{{ class_basename($model) }}', type: type }) }) .then(response => response.json()) .then(data => { if (data.success) { this.reactions = data.reactions; this.userReactions = data.user_reactions; } }) .catch(error => { console.error('Error:', error); }); }, isReacted(type) { return this.userReactions.includes(type); } }" class="mt-8 mb-8"> <h3 class="text-lg font-semibold mb-2">Reactions</h3> <div x-show="loading" class="text-gray-500">Loading reactions...</div> <div x-show="error" x-text="error" class="text-red-500"></div> @auth <div x-show="!loading && !error" class="flex flex-wrap gap-2"> <!-- Like --> <button @click="toggleReaction('like')" :class="{ 'bg-blue-100 border-blue-300': isReacted('like'), 'hover:bg-gray-100': !isReacted('like') }" class="flex items-center space-x-1 px-3 py-1 rounded-full border transition-colors"> <span class="text-xl">👍</span> <span x-text="reactions['like'] || 0" class="text-sm font-medium"></span> </button> <!-- Love --> <button @click="toggleReaction('love')" :class="{ 'bg-red-100 border-red-300': isReacted('love'), 'hover:bg-gray-100': !isReacted('love') }" class="flex items-center space-x-1 px-3 py-1 rounded-full border transition-colors"> <span class="text-xl">❤️</span> <span x-text="reactions['love'] || 0" class="text-sm font-medium"></span> </button> <!-- Laugh --> <button @click="toggleReaction('laugh')" :class="{ 'bg-yellow-100 border-yellow-300': isReacted('laugh'), 'hover:bg-gray-100': !isReacted('laugh') }" class="flex items-center space-x-1 px-3 py-1 rounded-full border transition-colors"> <span class="text-xl">😄</span> <span x-text="reactions['laugh'] || 0" class="text-sm font-medium"></span> </button> <!-- Wow --> <button @click="toggleReaction('wow')" :class="{ 'bg-purple-100 border-purple-300': isReacted('wow'), 'hover:bg-gray-100': !isReacted('wow') }" class="flex items-center space-x-1 px-3 py-1 rounded-full border transition-colors"> <span class="text-xl">😮</span> <span x-text="reactions['wow'] || 0" class="text-sm font-medium"></span> </button> <!-- Sad --> <button @click="toggleReaction('sad')" :class="{ 'bg-gray-100 border-gray-300': isReacted('sad'), 'hover:bg-gray-100': !isReacted('sad') }" class="flex items-center space-x-1 px-3 py-1 rounded-full border transition-colors"> <span class="text-xl">😢</span> <span x-text="reactions['sad'] || 0" class="text-sm font-medium"></span> </button> <!-- Angry --> <button @click="toggleReaction('angry')" :class="{ 'bg-orange-100 border-orange-300': isReacted('angry'), 'hover:bg-gray-100': !isReacted('angry') }" class="flex items-center space-x-1 px-3 py-1 rounded-full border transition-colors"> <span class="text-xl">😠</span> <span x-text="reactions['angry'] || 0" class="text-sm font-medium"></span> </button> </div> @else <div x-show="!loading && !error" class="text-gray-500 mt-2"> <a href="{{ route('login') }}" class="text-blue-500 hover:underline">Log in</a> to react to this post. </div> @endauth @auth <div x-show="!loading && Object.keys(reactions).length === 0" class="text-gray-500 mt-2"> Be the first to react to this post! </div> @endauth </div>
Step 6: Add AlpineJS to allow reaction without reloading the page
<!-- In the head of the pag --> <script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js"></script>
Step 7: Add the getReactions method to the reaction controller
public function getReactions(Request $request) { $request->validate([ 'reactable_id' => 'required|integer', 'reactable_type' => 'required|string', ]); $reactableType = $request->input('reactable_type'); $reactableId = $request->input('reactable_id'); // Get the reactable model $reactable = $reactableType::findOrFail($reactableId); return response()->json([ 'reactions' => [ 'like' => $reactable->reactions(Reaction::LIKE), 'love' => $reactable->reactions(Reaction::LOVE), 'laugh' => $reactable->reactions(Reaction::LAUGH), 'wow' => $reactable->reactions(Reaction::WOW), 'sad' => $reactable->reactions(Reaction::SAD), 'angry' => $reactable->reactions(Reaction::ANGRY), ], 'user_reactions' => $reactable->getUserReactions(Auth::user()), ]); }
Conclusion
Reactify package makes it super easy to add reaction features to your Laravel application. With just a few steps, you’ve got a fully working reaction system!
Please login to leave a comment.