From 1cf1ba1c6784fd5816790276507e01c0fda8187d Mon Sep 17 00:00:00 2001 From: AmirmahdiNourkazemi Date: Tue, 16 Jun 2026 13:09:03 +0330 Subject: [PATCH] fix: duration fix --- app/Http/Controllers/MediaController.php | 74 ++++++++++++++++-------- 1 file changed, 50 insertions(+), 24 deletions(-) diff --git a/app/Http/Controllers/MediaController.php b/app/Http/Controllers/MediaController.php index 3f3b8f0..00b463d 100644 --- a/app/Http/Controllers/MediaController.php +++ b/app/Http/Controllers/MediaController.php @@ -125,20 +125,27 @@ private function applyMediaFilters($query, Request $request) }); } - // Duration ranges (duration stored in minutes). + // Duration ranges (duration stored in minutes). Keys come from durationRanges(). if ($request->filled('durations')) { - $ranges = explode(',', $request->durations); - $query->where(function ($q) use ($ranges) { - foreach ($ranges as $range) { - if ($range === '1-2') $q->orWhereBetween('duration', [1, 2]); - if ($range === '2-5') $q->orWhereBetween('duration', [3, 5]); - if ($range === '5-10') $q->orWhereBetween('duration', [6, 10]); - if ($range === '10-30') $q->orWhereBetween('duration', [10, 30]); - if ($range === '30-60') $q->orWhereBetween('duration', [30, 60]); - if ($range === '60-120') $q->orWhereBetween('duration', [60, 120]); - if ($range === 'other') $q->orWhere('duration', '>', 120); - } - }); + $defs = $this->durationRanges(); + // Keep only known range keys so an unknown value can't produce an empty + // (match-everything) WHERE group. + $ranges = array_filter( + explode(',', $request->durations), + fn ($r) => $r === '120-' || isset($defs[$r]) + ); + + if (!empty($ranges)) { + $query->where(function ($q) use ($ranges, $defs) { + foreach ($ranges as $range) { + if ($range === '120-') { + $q->orWhere('duration', '>', $this->durationOtherMin()); + } else { + $q->orWhereBetween('duration', $defs[$range]); + } + } + }); + } } if ($request->filled('tags')) { @@ -168,6 +175,26 @@ private function applyMediaFilters($query, Request $request) return $query; } +// Single source of truth for duration buckets (minutes), used by both the +// filter facet (/media/filters) and the search filter. Non-overlapping. +// Anything above durationOtherMin() falls into the 'other' bucket. +private function durationRanges(): array +{ + return [ + '1-2' => [1, 2], + '2-5' => [3, 5], + '5-10' => [6, 10], + '10-30' => [11, 30], + '30-60' => [31, 60], + '60-120' => [61, 120], + ]; +} + +private function durationOtherMin(): int +{ + return 120; +} + // NEWEST media, paginated (?per_page=, ?page=). Same visibility scope as index. public function newest(Request $request) { @@ -304,18 +331,16 @@ public function filters(Request $request) |-------------------------------------------------------------------------- */ + // Build the CASE from the same range definitions the search filter uses, + // so the facet keys (e.g. "10-30") always match what /media/search accepts. + $caseSql = 'CASE '; + foreach ($this->durationRanges() as $key => [$min, $max]) { + $caseSql .= "WHEN duration BETWEEN {$min} AND {$max} THEN '{$key}' "; + } + $caseSql .= "WHEN duration > {$this->durationOtherMin()} THEN '120-' END"; + $durations = Media::select( - DB::raw(" - CASE - WHEN duration BETWEEN 1 AND 2 THEN '1-2' - WHEN duration BETWEEN 3 AND 5 THEN '2-5' - WHEN duration BETWEEN 5 AND 10 THEN '5-10' - WHEN duration BETWEEN 10 AND 20 THEN '10-20' - WHEN duration BETWEEN 20 AND 30 THEN '20-30' - WHEN duration BETWEEN 60 AND 120 THEN '60-120' - ELSE '120-' - END as duration_range - "), + DB::raw("{$caseSql} as duration_range"), DB::raw('COUNT(*) as total') ) ->whereNotNull('duration') @@ -324,6 +349,7 @@ public function filters(Request $request) ->orWhere('user_id', auth()->id()); }) ->groupBy('duration_range') + ->havingRaw('duration_range IS NOT NULL') ->get(); return response()->json([