Laravel中如何关联两张表并按主表ID分组?
解决酒店查询重复显示的问题
你遇到的核心问题是LEFT JOIN会把每个关联的人员和酒店拼成单独的记录,导致酒店随着关联人员的数量重复出现。这里给你两种实用的解决思路,优先推荐第一种,更贴合Laravel的最佳实践:
方法一:使用Eloquent关联(推荐)
这种方式更优雅,能直接把每个酒店对应的人员集合作为对象属性获取,还能避免N+1查询的性能问题。
1. 定义模型关联
先在Hotel模型里添加和People模型的关联关系:
// app/Models/Hotel.php namespace App\Models; use Illuminate\Database\Eloquent\Model; class Hotel extends Model { protected $table = 'hotels'; // 声明一个酒店对应多个人员的关联 public function people() { return $this->hasMany(People::class, 'hotel_id'); } }
2. 预加载关联数据
在控制器里用with()方法预加载人员数据,一次性完成酒店和对应人员的查询:
use App\Models\Hotel; // ... $hotels = Hotel::with('people')->get(); return view('hotels', ['hotels' => $hotels]);
3. 视图中嵌套遍历展示
现在每个$hotel对象都自带people属性,是该酒店下的所有人员集合,直接嵌套遍历就能避免酒店重复:
@foreach($hotels as $hotel) <div class="hotel-card"> <h3>{{ $hotel->name }}</h3> <div class="staff-list"> @if($hotel->people->isNotEmpty()) <ul> @foreach($hotel->people as $person) <li>{{ $person->name }}</li> @endforeach </ul> @else <p>该酒店暂无人员</p> @endif </div> </div> @endforeach
方法二:使用原生查询分组(适合无模型场景)
如果你不想用Eloquent模型,也可以通过GROUP BY结合聚合函数把人员名字拼接起来,直接消除酒店重复:
修正并调整查询代码
注意:你原代码里的hotel.id是笔误,应该是hotels.id,先修正这个错误。然后用MySQL的GROUP_CONCAT函数把人员名字拼接成字符串:
$hotels = DB::table('hotels') ->select( 'hotels.*', DB::raw('GROUP_CONCAT(people.name SEPARATOR ", ") as people_names') ) ->leftjoin('people', 'people.hotel_id', '=', 'hotels.id') ->groupBy('hotels.id') // 提示:如果MySQL开启了严格模式,需要把hotels的所有字段都加入GROUP BY,或者调整sql_mode关闭严格分组检查 ->get(); return view('hotels', ['hotels' => $hotels]);
视图展示
直接输出拼接好的人员名字即可:
@foreach($hotels as $h) <div> <strong>{{ $h->name }}</strong>: {{ $h->people_names ?? '无人员' }} </div> @endforeach
补充:如果用的是PostgreSQL,需要把
GROUP_CONCAT换成STRING_AGG(people.name, ', '),不同数据库的字符串聚合函数略有差异。
内容的提问来源于stack exchange,提问作者Studio DnB
相关产品推荐
相关产品推荐

