
本文教你如何用 Eloquent 关系替代原始 SQL 查询,在 Blade 中直接访问外键关联的字段(如通过 city_id 显示城市名称),避免 N+1 查询,提升性能与可维护性。
本文教你如何用 eloquent 关系替代原始 sql 查询,在 blade 中直接访问外键关联的字段(如通过 `city_id` 显示城市名称),避免 n+1 查询,提升性能与可维护性。
在 Laravel 开发中,直接使用 DB::select() 获取原始数据虽可行,但会失去 Eloquent 提供的关系加载、自动类型转换和查询优化等核心优势。你当前的 Villa 模型已正确定义了 City、Seller 和 Payment 关系,但控制器中却绕过了 Eloquent,导致无法在 Blade 中便捷访问关联数据(例如 city_id 对应的 City::name)。
✅ 正确做法:使用 Eloquent + 预加载(Eager Loading)
首先,修正控制器逻辑,改用 Villa::with() 预加载关联模型,避免 N+1 查询问题:
// App\Http\Controllers\WebsiteController.php
public function index()
{
// 使用 with() 一次性预加载所有关联,高效且语义清晰
$villas = Villa::with(['City', 'Seller', 'Payment'])->get();
return view('website', ['villas' => $villas]);
}
⚠️ 注意:
with(['City', 'Seller', 'Payment'])会生成 4 条 SQL(1 条主查询 + 3 条 JOIN 或 IN 查询),远优于循环中逐条查询(N+1)。
✅ 修正 Blade 模板:安全访问关联属性
在 website.blade.php 中,可直接通过关系方法访问关联模型的字段。但需注意——必须确保关联存在(如某 villa 的 city_id 为 null),否则会抛出 Trying to get property 'name' of non-object 错误。推荐使用空合并运算符 ?? 或 optional() 辅助函数:
@foreach ($villas as $villa)
<div class="villa-block">
<a href="single-villa.html" id="villaname">{{ old('name', $villa->title) }}</a>
<!-- 安全获取城市名称:若 City 不存在则显示 '未知城市' -->
<a href="#" id="villaLocation">
{{ $villa->City?->name ?? '未知城市' }}
</a>
<!-- 同理获取卖家姓名与支付方式 -->
<span>卖家:{{ $villa->Seller?->name ?? '未指定' }}</span>
<span>付款方式:{{ $villa->Payment?->method ?? '未设置' }}</span>
</div>
@endforeach
? 小技巧:
$villa->City?->name是 PHP 8.0+ 的空安全操作符(Nullsafe Operator),比optional($villa->City)->name更简洁;若项目使用旧版 PHP,可用optional($villa->City)->name替代。
? 补充建议:优化模型关系定义
你当前的 hasOne 关系定义存在逻辑偏差。hasOne 表示“一个别墅对应一个城市”,但实际是 多对一(Many-to-One) 关系(多个 villa 共享同一个 city)。应改为 belongsTo,并统一命名规范(小驼峰):
// App\Models\Villa.php
public function city() // 推荐小驼峰命名
{
return $this->belongsTo(City::class, 'city_id'); // belongsTo 自动识别外键
}
public function seller()
{
return $this->belongsTo(Seller::class, 'seller_id');
}
public function payment()
{
return $this->belongsTo(Payment::class, 'payment_id');
}
对应地,控制器和 Blade 中调用也更新为小驼峰:
$villas = Villa::with(['city', 'seller', 'payment'])->get();
// Blade 中:{{ $villa->city?->name }}
? 总结
- ❌ 避免
DB::select()+ 手动关联:丧失 ORM 优势,易出错且难维护; - ✅ 用
Model::with()预加载关系:高效、简洁、支持链式调用; - ✅ 在 Blade 中用
?->或optional()安全访问嵌套属性; - ✅ 根据真实业务关系选择正确关联类型(此处应为
belongsTo而非hasOne); - ✅ 统一使用小驼峰命名关系方法,符合 Laravel 社区惯例。
这样改造后,你的前端不仅能正确显示 city.name,还能获得更好的性能、更少的数据库查询和更强的代码可读性。











