How to Add Reactions to Laravel Using reactify

Fermin Perdomo Fermin Perdomo
schedule 6 min read

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!

Reactions

lock You need to be logged in to react.
Log In

Newsletter

Get new posts delivered straight to your inbox.

mail

Great Tools for Developers

Git Tower

Git Tower

A powerful Git client for Mac and Windows that simplifies version control.

Visit arrow_forward
Mailcoach

Mailcoach

Self-hosted email marketing platform for sending newsletters and automated emails.

Visit arrow_forward
Uptimia

Uptimia

Website monitoring and performance testing tool to ensure your site is always up and running.

Visit arrow_forward
Cloudways

Cloudways

Managed cloud hosting platform that simplifies server management for developers.

Visit arrow_forward

Comments

No comments yet. Be the first to share your thoughts.

chat_bubble Join the conversation — log in to leave a comment.
Log In