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!
Comments
Great Tools for Developers
Git tower
A powerful Git client for Mac and Windows that simplifies version control.
Mailcoach's
Self-hosted email marketing platform for sending newsletters and automated emails.
Uptimia
Website monitoring and performance testing tool to ensure your site is always up and running.
Please login to leave a comment.