
本文详解如何在 laravel 8 中借助 ajax 实现前端按钮点击触发用户模型中的收藏/取消收藏逻辑,涵盖路由配置、控制器处理、前端交互及 csrf 安全防护。
本文详解如何在 laravel 8 中借助 ajax 实现前端按钮点击触发用户模型中的收藏/取消收藏逻辑,涵盖路由配置、控制器处理、前端交互及 csrf 安全防护。
在 Laravel 中,框架本身不提供类似 onclick 的原生 PHP 指令——所有 DOM 事件(如按钮点击)均由前端 JavaScript 处理。因此,实现“点击收藏按钮调用 User::bookmark()”的关键在于:从前端发起异步请求(AJAX),由 Laravel 控制器接收并调用模型方法,而非直接在 Blade 模板中执行 PHP 函数。
✅ 正确实现步骤
1. 定义 API 路由(routes/web.php)
// 使用中间件确保仅认证用户可操作
Route::post('/bookmarks/toggle', [BookmarkController::class, 'toggle'])->name('bookmarks.toggle');2. 创建控制器处理逻辑(app/Http/Controllers/BookmarkController.php)
<?php
namespace App\Http\Controllers;
use App\Models\Post;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class BookmarkController extends Controller
{
public function toggle(Request $request)
{
$request->validate(['post_id' => 'required|exists:posts,id']);
$user = Auth::user();
$post = Post::findOrFail($request->post_id);
// 复用 User 模型中的 bookmark() 方法
$result = $user->bookmark($post);
return response()->json([
'success' => true,
'action' => $result ? 'added' : 'removed',
'count' => $user->bookmarks()->count()
]);
}
}? 注意:确保 User 模型的 bookmark() 方法已正确关联 bookmarks() 关系(如 HasMany 或 BelongsToMany),且 $this->isBookmarked($object) 已实现(通常基于 bookmarks()->where('post_id', $object->id)->exists())。
3. 前端按钮与 AJAX 调用(bookmarks.blade.php)
<!-- 确保页面已加载 jQuery(或改用 Fetch API) -->
<button
type="button"
class="bookmark-btn"
data-post-id="{{ $post->id }}"
aria-label="{{ $user->isBookmarked($post) ? '取消收藏' : '收藏文章' }}"
>
<i class="bi bi-bookmark-heart-fill text-2xl ml-4 {{ $user->isBookmarked($post) ? 'text-red-500' : 'text-gray-500' }}"></i>
</button>
@push('scripts')
<script>
function toggleBookmark(postId) {
const button = document.querySelector(`[data-post-id="${postId}"]`);
const icon = button.querySelector('i');
$.ajax({
url: "{{ route('bookmarks.toggle') }}",
type: 'POST',
data: {
_token: "{{ csrf_token() }}", // 必须包含 CSRF Token
post_id: postId
},
success: function(response) {
if (response.action === 'added') {
icon.classList.remove('text-gray-500');
icon.classList.add('text-red-500');
button.setAttribute('aria-label', '取消收藏');
} else {
icon.classList.remove('text-red-500');
icon.classList.add('text-gray-500');
button.setAttribute('aria-label', '收藏文章');
}
console.log('收藏状态已更新,当前总数:', response.count);
},
error: function(xhr) {
alert('操作失败:' + (xhr.responseJSON?.message || '请重试'));
}
});
}
// 绑定点击事件(推荐使用事件委托,避免重复绑定)
document.addEventListener('click', function(e) {
if (e.target.closest('.bookmark-btn')) {
const postId = e.target.closest('.bookmark-btn').dataset.postId;
toggleBookmark(postId);
}
});
</script>
@endpush⚠️ 关键注意事项
- CSRF 防护:Laravel 强制要求 POST 请求携带 _token,务必通过 {{ csrf_token() }} 注入;
- 权限控制:路由应受 auth 中间件保护,控制器中需校验用户登录状态(Auth::user());
- 数据验证:始终验证 post_id 是否真实存在,防止越权操作;
- 用户体验:建议添加加载状态(如禁用按钮、旋转图标)和错误提示;
-
替代方案:若项目未使用 jQuery,可用原生 fetch() 替代:
fetch("{{ route('bookmarks.toggle') }}", { method: 'POST', headers: { 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content') }, body: new URLSearchParams({ post_id: postId }) })
通过以上结构化实现,你既能复用模型层业务逻辑,又能保证前后端职责清晰、安全可靠——这才是 Laravel 应用中处理交互行为的专业实践。

















