Laravel基于模型与数据透视表实现周历的方案咨询
Hey Charlotte, let's break this down step by step to build that weekly calendar interface you're aiming for, with clean, maintainable code.
For filtering logic (week, channel, date), encapsulating this in model scopes is the way to go rather than cluttering your controller. This keeps your business logic tied to the model where it belongs, makes queries reusable, and keeps your controller focused on handling requests and passing data to the view.
Add Scopes to Your Episode Model
// app/Models/Episode.php use Carbon\Carbon; public function scopeForWeek($query, Carbon $startOfWeek) { $endOfWeek = $startOfWeek->copy()->endOfWeek(); return $query->whereHas('programmation', function($subQuery) use ($startOfWeek, $endOfWeek) { $subQuery->whereBetween('date', [$startOfWeek, $endOfWeek]); }); } public function scopeForChannel($query, $channelId) { return $query->whereHas('channels', function($subQuery) use ($channelId) { $subQuery->where('channel_id', $channelId); }); }
Your controller will take care of parsing user input (selected week, channel), calling the model scopes, and passing formatted data to the view. This way, you avoid messy Carbon calculations directly in your blade files.
// app/Http/Controllers/ProgrammationsController.php use Carbon\Carbon; use Illuminate\Http\Request; use App\Models\Episode; use App\Models\Channel; public function index(Request $request) { // Parse or set the selected week start date $weekStart = $request->has('week') ? Carbon::createFromFormat('j/m/Y', $request->week)->startOfWeek() : Carbon::now()->startOfWeek(); $weekEnd = $weekStart->copy()->endOfWeek(); // Build base query with eager loading $episodesQuery = Episode::with(['channels', 'programmation']); // Apply channel filter if present if ($request->has('channel')) { $episodesQuery->forChannel($request->channel); } // Filter episodes for the selected week $episodes = $episodesQuery->forWeek($weekStart)->get(); // Fetch all channels for the filter dropdown $channels = Channel::all(); return view('front.programmations.calendrier', compact( 'episodes', 'weekStart', 'weekEnd', 'channels' )); }
Move all date logic to the controller so your view only handles rendering. Add a channel filter and clean up week navigation links that preserve the selected filter state.
<div class="row"> <div class="col-md-12"> <!-- Channel Filter Dropdown --> <form method="GET" action="{{ route('programmations.index') }}" class="mb-4"> <input type="hidden" name="week" value="{{ $weekStart->format('j/m/Y') }}"> <div class="form-group"> <label for="channel">Filtrer par chaîne:</label> <select id="channel" name="channel" class="form-control" onchange="this.form.submit()"> <option value="">Toutes les chaînes</option> @foreach($channels as $channel) <option value="{{ $channel->id }}" {{ request('channel') == $channel->id ? 'selected' : '' }}> {{ $channel->name }} </option> @endforeach </select> </div> </form> <!-- Week Navigation Pagination --> <ul class="pagination"> <a href="{{ route('programmations.index', [ 'week' => $weekStart->copy()->subWeek()->format('j/m/Y'), 'channel' => request('channel') ]) }}"> <li class="left-arrow">← Semaine précédente</li> </a> <li> <p>Diffusion séries US : <br /> <span class="calendar-week">Semaine du {{ $weekStart->format('j/m/Y')}} au {{ $weekEnd->format('j/m/Y') }}</span> </p> </li> <a href="{{ route('programmations.index', [ 'week' => $weekStart->copy()->addWeek()->format('j/m/Y'), 'channel' => request('channel') ]) }}"> <li class="right-arrow">Semaine suivante →</li> </a> </ul> </div> </div> <!-- Weekly Calendar Grid --> <div class="row calendar-grid"> @foreach($weekStart->toPeriod($weekEnd)->days as $day) <div class="col-md-2 day-column"> <h3 class="day-title">{{ $day->format('l j/m') }}</h3> @foreach($channels as $channel) @php // Filter episodes for the current day and channel $dailyEpisodes = $episodes->filter(function($episode) use ($day, $channel) { return $episode->programmation->date->isSameDay($day) && $episode->channels->contains($channel); }); @endphp @if($dailyEpisodes->isNotEmpty()) <div class="channel-episodes"> <h4>{{ $channel->name }}</h4> <ul> @foreach($dailyEpisodes as $episode) <li> {{ $episode->name }}<br> <small>{{ $episode->programmation->date->format('H:i') }}</small> </li> @endforeach </ul> </div> @endif @endforeach </div> @endforeach </div>
- Separation of Concerns: Models handle data logic, controllers handle request flow, views handle rendering.
- Reusability: The
forWeekandforChannelscopes can be reused elsewhere in your app (e.g., admin dashboards). - Cleaner Views: No Carbon calculations or query logic cluttering your blade files.
- Maintainability: Easy to update filters or add new ones (like date range) without rewriting core code.
内容的提问来源于stack exchange,提问作者Charlotte

