Laravel中如何仅用一个foreach循环从三元数据透视表获取并展示对应数据
Hey there, let's sort out that duplicate data problem and get your timesheet table showing the correct paired data without messy nested loops. The core issue here is how we're accessing the ternary pivot data—let's break this down step by step.
Step 1: Fix Model Relationship Definitions
First, there are a couple of small mistakes in your model relationships that are causing confusion when accessing pivot data:
Pattern Model Correction
Your shifts method in the Pattern model is incorrectly including shift_id in the pivot (that's already the foreign key for the Shift relationship). Instead, we need to include the relevant cross-reference pivot fields:
class Pattern extends Model { public function shifts() { // Include user_id from pivot to link the full ternary set return $this->belongsToMany('App\Shift', 'pattern_shift_user')->withPivot('user_id'); } public function users() { // Include shift_id to get the associated shift for each user-pattern pair return $this->belongsToMany('App\User', 'pattern_shift_user')->withPivot('shift_id'); } }
Shift Model Correction
Also, your user method in the Shift model should be plural (users) since it's a many-to-many relationship:
class Shift extends Model { public function patterns() { return $this->belongsToMany('App\Pattern', 'pattern_shift_user')->withPivot('user_id'); } public function users() { // Changed from user() to users() return $this->belongsToMany('App\User', 'pattern_shift_user')->withPivot('pattern_id'); } }
Step 2: Optimize Controller Data Fetching
Instead of passing all users/shifts/patterns to the view, we can eager load the authenticated user's related patterns with their pivot shift data. This keeps the data lean and avoids unnecessary queries:
public function index() { // Eager load the user's patterns along with the pivot shift_id $user = Auth::user()->load('patterns'); // Optional: Preload all shifts to avoid N+1 queries when accessing shift details Shift::all(); // Caches shifts in memory for quick access return view('layouts/timesheet/index', compact('user')); }
Step 3: Rewrite the View with a Single Foreach Loop
Now, we can loop directly through the user's patterns, and use the pivot shift_id to fetch the corresponding Shift record. This eliminates the nested loop and redundant data:
<table class="table table-sm table-success"> <tr> <th scope="col">DATE/DAY</th> <th scope="col">SHIFT</th> <th scope="col">STATION</th> </tr> <tbody class="table-light"> @foreach ($user->patterns as $pattern) {{-- Get the shift linked to this user-pattern pair via pivot --}} @php $shift = \App\Shift::find($pattern->pivot->shift_id) @endphp @if($shift) <tr> <td>{{ $pattern->day_of_week }}</td> <td>{{ $shift->Name }}</td> <td>{{-- Add your station data here once ready --}}</td> </tr> @endif @endforeach </tbody> </table>
Optional: Even Better - Eager Load Shifts with Pivot Data
To avoid the extra find() query in the view, adjust your User model's patterns method to eager load the linked shift directly:
public function patterns() { return $this->belongsToMany('App\Pattern', 'pattern_shift_user') ->withPivot('shift_id') ->with(['shifts' => function($query) { $query->where('id', DB::raw('pattern_shift_user.shift_id')); }]); }
Then in the view, you can access the shift directly without the find():
<td>{{ $pattern->shifts->first()->Name }}</td>
This way, you'll get clean, non-redundant rows showing each pattern's day paired with its corresponding shift for the authenticated user.
内容的提问来源于stack exchange,提问作者LEG

