
本文详解 laravel 8(适配 php 8)中 fullcalendar 数据不显示的常见原因及解决方案,涵盖事件数据格式、csrf 配置、js 初始化顺序、时间字段规范等关键点,并提供可直接运行的完整代码示例。
本文详解 laravel 8(适配 php 8)中 fullcalendar 数据不显示的常见原因及解决方案,涵盖事件数据格式、csrf 配置、js 初始化顺序、时间字段规范等关键点,并提供可直接运行的完整代码示例。
在 Laravel 8 升级至 PHP 8 后,许多开发者发现原本在 Laravel 7/PHP 7 下正常工作的 FullCalendar(v3.x)突然无法渲染数据库中的日程事件——页面空白、控制台无报错、events: [...] 数组看似已注入,但日历始终空载。这并非兼容性“崩溃”,而是由数据结构不合规、时区/格式隐式变更、脚本加载冲突及 Laravel 资源路由误用共同导致。以下为系统性解决方案:
✅ 核心问题定位与修复
1. 事件数据必须严格遵循 FullCalendar v3 要求
FullCalendar v3 要求每个事件对象至少包含 id、title、start 字段,且 start/end 必须为 ISO 8601 格式字符串(如 "2024-05-10T09:00:00"),不能是 MySQL DATETIME 原始值(如 "2024-05-10 09:00:00")。PHP 8 对字符串处理更严格,未格式化的日期会被解析失败。
✅ 正确写法(Blade 中):
events: [
@foreach ($data as $event)
{
id: '{{ $event->id }}',
title: '{{ addslashes($event->judul) }}', // 防止 JSON 引号冲突
start: '{{ $event->dari->toISOString() }}', // 使用 Carbon 实例调用 isISOString()
end: '{{ $event->sampai ? $event->sampai->toISOString() : null }}',
className: 'bg-primary'
}@if(!$loop->last),@endif
@endforeach
],⚠️ 注意:确保 $event->dari 和 $event->sampai 是 Carbon 实例(Eloquent 模型中已自动转换),否则需手动 Carbon::parse($event->dari)。
2. 避免 events 属性重复定义
原代码中同时设置了 events: "{{ route('kkalender.index') }}" 和 events: [ ... ] —— 后者会完全覆盖前者,且 AJAX 路由未定义,导致服务端请求 404。Laravel 8 应禁用 AJAX 加载,改用内联静态数组(简单场景)或统一使用 AJAX 接口(推荐)。
✅ 推荐方案:移除冗余 events 行,仅保留内联数组:
// ❌ 删除这一行
// events: "{{ route('kkalender.index') }}",
// ✅ 仅保留
events: [
// ... 如上所示
],3. CSRF Token 与脚本加载顺序
PHP 8 下 $.ajaxSetup() 若在 FullCalendar 初始化前未生效,会导致 POST 请求被拒绝。同时,混用 CDN 和本地资源易引发版本冲突(如 moment.js 与 fullcalendar.js 版本不匹配)。
✅ 规范做法:
- 所有 JS/CSS 通过 asset() 加载本地文件,避免 CDN 不稳定性;
- 将 meta[name="csrf-token"] 放在 <head> 顶部;
- $.ajaxSetup() 必须在 $('#calendar').fullCalendar({...}) 之前执行。
4. 路由与控制器必须匹配资源路由规范
原 Route::get('/kkalender', [...]) 仅支持展示,但删除操作需要 POST 接口。应使用 Route::resource() 并补充 AJAX 处理方法:
// routes/web.php
Route::resource('kkalender', K_JadwalController::class)->except(['create', 'edit']);
Route::post('kkalender/ajax', [K_JadwalController::class, 'ajax'])->name('kkalender.ajax');// app/Http/Controllers/K_JadwalController.php
public function ajax(Request $request)
{
if ($request->type === 'delete') {
K_Jadwal::findOrFail($request->id)->delete();
return response()->json(['success' => true]);
}
abort(400);
}5. 前端事件点击删除逻辑修正
原代码中 calendar.fullCalendar('removeEvents', event.id) 在 v3 中已弃用,应使用 removeEvents 方法传入函数筛选:
eventClick: function(event) {
if (confirm("确定删除该日程?")) {
$.post("{{ route('kkalender.ajax') }}", {
_token: $('meta[name="csrf-token"]').attr('content'),
id: event.id,
type: 'delete'
}, function(res) {
calendar.fullCalendar('removeEvents', function(ev) {
return ev.id == event.id;
});
toastr.success('日程已删除');
});
}
}? 完整可运行示例(精简版)
resources/views/kkalender/index.blade.php 关键片段:
<head>
<meta name="csrf-token" content="{{ csrf_token() }}">
<!-- 全部 CSS 通过 asset() 加载 -->
<link rel="stylesheet" href="{{ asset('plugins/fullcalendar/css/fullcalendar.min.css') }}">
</head>
<body>
<div id="calendar"></div>
<script src="{{ asset('plugins/moment/moment.min.js') }}"></script>
<script src="{{ asset('plugins/fullcalendar/js/fullcalendar.min.js') }}"></script>
<script>
$(document).ready(function() {
$.ajaxSetup({
headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') }
});
$('#calendar').fullCalendar({
header: { left: 'prev,next today', center: 'title', right: 'month,agendaWeek,agendaDay' },
editable: true,
events: [
@foreach($data as $e)
{
id: {{ $e->id }},
title: "{{ addslashes($e->judul) }}",
start: "{{ $e->dari->toISOString() }}",
end: "{{ $e->sampai ? $e->sampai->toISOString() : '' }}",
className: 'bg-info'
}@if(!$loop->last),@endif
@endforeach
],
eventClick: function(event) {
if(confirm('删除?')) {
$.post("{{ route('kkalender.ajax') }}", {
id: event.id,
type: 'delete'
}, () => {
$('#calendar').fullCalendar('removeEvents', ev => ev.id == event.id);
toastr.success('已删除');
});
}
}
});
});
</script>
</body>⚠️ 重要注意事项
- 不要混用 FullCalendar v3 与 v5+ API:fullCalendar() 方法名、事件参数、renderEvent 等均不同;
- PHP 8 严格模式下,Blade 中 {{ $var }} 若 $var 为 null 会报错,务必使用空合并 {{ $var ?? '' }} 或 Carbon 的 ?->toISOString();
- 时间字段必须含时区信息:MySQL 存储建议用 TIMESTAMP 类型,Laravel 默认以 UTC 存储,前端显示时 FullCalendar 会自动按浏览器时区转换;
- 生产环境务必启用缓存:php artisan config:cache,避免因配置未生效导致 CSRF 失效。
遵循以上步骤,你的 Laravel 8 + PHP 8 日历将稳定渲染所有数据库事件,告别“数据存在却不见”的困扰。


















