You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel 5.4点赞系统关联问题:点击按钮重复创建点赞记录

Hey there, let's fix that duplicate like issue you're running into—super common pitfall when building like systems in Laravel, so don't worry, we'll get it sorted!

First, let's start with the core problem: right now, your code is just creating a new Like record every time the button is clicked, without checking if the user already liked that memo. Let's break down the fixes step by step, starting with your models and controller.

1. Double-Check Your Model Relationships

First, let's make sure your associations are set up correctly. Depending on whether you're using a direct many-to-many relationship or a dedicated Like model (for extra fields like timestamps), here's how they should look:

If using a dedicated Like model:

User.php

public function likes()
{
    return $this->hasMany(Like::class);
}

Memo.php

public function likes()
{
    return $this->hasMany(Like::class);
}

Like.php

public function user()
{
    return $this->belongsTo(User::class);
}

public function memo()
{
    return $this->belongsTo(Memo::class);
}

If using a many-to-many pivot table (no dedicated Like model):

You can skip the Like model entirely and use Laravel's built-in pivot functionality. Just make sure your likes table has user_id and memo_id columns:

User.php

public function likedMemos()
{
    return $this->belongsToMany(Memo::class, 'likes')->withTimestamps();
}

Memo.php

public function likedByUsers()
{
    return $this->belongsToMany(User::class, 'likes')->withTimestamps();
}

2. Fix the Controller Logic to Prevent Duplicates

This is where the magic happens—we need to check if the user already liked the memo before creating a new record, and toggle the like state instead.

Option 1: Using a dedicated Like model

Update your AJAX handler in MemoController.php:

public function toggleLike(Request $request)
{
    // Validate the request first
    $request->validate([
        'memo_id' => 'required|exists:memos,id'
    ]);

    $userId = auth()->id();
    $memoId = $request->memo_id;

    // Check if the like already exists
    $existingLike = Like::where('user_id', $userId)
                        ->where('memo_id', $memoId)
                        ->first();

    if ($existingLike) {
        // User already liked it—delete the like
        $existingLike->delete();
        return response()->json(['status' => 'unliked', 'message' => 'Like removed']);
    } else {
        // User hasn't liked it—create the like
        Like::create([
            'user_id' => $userId,
            'memo_id' => $memoId
        ]);
        return response()->json(['status' => 'liked', 'message' => 'Like added']);
    }
}

Option 2: Using many-to-many relationships

If you went the pivot table route, use attach() and detach() with an existence check:

public function toggleLike(Request $request)
{
    $request->validate([
        'memo_id' => 'required|exists:memos,id'
    ]);

    $memo = Memo::findOrFail($request->memo_id);
    $user = auth()->user();

    if ($user->likedMemos()->where('memo_id', $memo->id)->exists()) {
        $user->likedMemos()->detach($memo->id);
        return response()->json(['status' => 'unliked']);
    } else {
        $user->likedMemos()->attach($memo->id);
        return response()->json(['status' => 'liked']);
    }
}

3. Add a Database Constraint (Critical!)

Even with perfect code, race conditions (like a user clicking twice super fast) can create duplicates. Let's add a composite unique index to your likes table to block duplicates at the database level.

In your create_likes_table migration file:

public function up()
{
    Schema::create('likes', function (Blueprint $table) {
        $table->id();
        $table->foreignId('user_id')->constrained()->onDelete('cascade');
        $table->foreignId('memo_id')->constrained()->onDelete('cascade');
        $table->timestamps();

        // Add this line to enforce unique user-memo pairs
        $table->unique(['user_id', 'memo_id']);
    });
}

Run php artisan migrate:refresh (or migrate:rollback then migrate) to apply this change.

4. Prevent Duplicate AJAX Requests

Finally, update your frontend code to disable the like button while the request is processing—this stops users from spamming clicks:

$('.like-button').on('click', function(e) {
    e.preventDefault();
    const button = $(this);
    const memoId = button.data('memo-id');

    // Disable the button to prevent double clicks
    button.prop('disabled', true);

    $.ajax({
        url: '/memos/toggle-like', // Make sure this matches your route
        method: 'POST',
        data: {
            memo_id: memoId,
            _token: '{{ csrf_token() }}'
        },
        success: function(response) {
            // Update the button UI (e.g., change icon/text)
            if (response.status === 'liked') {
                button.html('<i class="fas fa-heart text-red-500"></i> Liked');
            } else {
                button.html('<i class="far fa-heart"></i> Like');
            }
            button.prop('disabled', false);
        },
        error: function() {
            button.prop('disabled', false);
            alert('Oops, something went wrong!');
        }
    });
});

That should do it! With these changes, users won't be able to create duplicate likes, and the like button will toggle between liked/unliked states properly.

内容的提问来源于stack exchange,提问作者Maz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 09:58:32