
本文详解如何在 Laravel 中正确配置模型以支持 slug 路由绑定与查询,解决因混用 DB 查询与 Eloquent 特性导致的 Attempt to read property "id" on null 错误。
本文详解如何在 laravel 中正确配置模型以支持 slug 路由绑定与查询,解决因混用 db 查询与 eloquent 特性导致的 `attempt to read property "id" on null` 错误。
在 Laravel 中,getRouteKeyName() 方法仅对 隐式/显式路由模型绑定(Route Model Binding) 生效,它不会影响 DB::table() 查询或 Model::find() 的行为。你当前的代码存在三个关键问题:
- 绕过 Eloquent 模型:控制器中使用 DB::table('headers')->find($id) 直接操作查询构造器,完全忽略 Header 模型中定义的 getRouteKeyName() 和任何模型逻辑;
- 主键语义错配:DB::table()->find($id) 默认按 id 字段查找,但你的业务希望按 slug 查找 —— 这需要显式指定字段或改用 Eloquent;
- 路由未启用模型绑定:路由 /{id} 声明的是普通参数,而非绑定语法 /{header},因此 getRouteKeyName() 不会被触发。
✅ 正确做法是 统一使用 Eloquent 模型,并明确配置主键字段:
// app/Models/Header.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Header extends Model
{
use \Illuminate\Database\Eloquent\Factories\HasFactory;
// 关键:将主键设为 'slug',使 Header::find($slug) 按 slug 字段查询
protected $primaryKey = 'slug';
// 可选:仅当启用路由模型绑定时生效(如 Route::get('/headers/{header}'))
public function getRouteKeyName()
{
return 'slug';
}
// 推荐:定义关联关系,替代手动 DB 查询
public function blocks()
{
return $this->hasMany(Block::class, 'header_id');
}
}
对应控制器应改为:
// app/Http/Controllers/MainController.php
public function show($slug)
{
// ✅ 使用 Eloquent find() —— 自动按 $primaryKey ('slug') 查询
$header = Header::find($slug);
if (!$header) {
abort(404, 'Header not found');
}
// ✅ 利用已定义的关系自动加载关联数据(N+1 问题可通过 with() 优化)
$blocks = $header->blocks;
return view('test', compact('header', 'blocks'));
}
同时,更新路由以匹配语义(推荐):
// routes/web.php
Route::get('/{slug}', [MainController::class, 'show'])->name('show');
// 或更清晰的命名路由(强烈建议):
// Route::get('/headers/{slug}', [MainController::class, 'show'])->name('header.show');
⚠️ 注意事项:
- 若仍需保留 /id 形式路由但按 slug 查询,请改用 where('slug', $id)->first(),但语义上不推荐;
- protected $primaryKey = 'slug' 意味着所有 Eloquent 操作(find, findOrFail, delete, 关系约束等)均基于 slug 字段,确保数据库中 slug 唯一且非空;
- 关系方法名(如 blocks())应与关联表外键逻辑一致(此处 blocks.header_id 关联 headers.id),若实际外键是 header_slug,则需调整 hasMany() 第二个参数;
- 开发阶段可添加日志或 dd(Header::getQuery()->toSql()) 验证生成的 SQL 是否符合预期。
通过以上配置,你既能实现基于 slug 的 URL 友好访问(如 /about-us),又能享受 Eloquent 的关系管理、自动类型转换与生命周期钩子等全部特性。











