* } */ public function stats(): array { // PHPStan cannot equate the identical array shapes; see favoriteDishes(). /** @phpstan-ignore return.type */ return [ 'dish_count' => $this->dishCount(), 'user_count' => $this->userCount(), 'meals_this_month' => $this->mealsThisMonth(), 'favorite_dishes' => $this->favoriteDishes(), ]; } public function dishCount(): int { return Dish::where('planner_id', $this->planner->id)->count(); } public function userCount(): int { return User::where('planner_id', $this->planner->id)->count(); } public function mealsThisMonth(): int { $start = Carbon::now()->startOfMonth()->toDateString(); $end = Carbon::now()->endOfMonth()->toDateString(); return ScheduledUserDish::query() ->where('is_skipped', false) ->whereNotNull('user_dish_id') ->whereHas('schedule', fn ($query) => $query ->where('planner_id', $this->planner->id) ->where('is_skipped', false) ->whereBetween('date', [$start, $end])) ->count(); } /** * @return Collection */ public function favoriteDishes(): Collection { $users = User::where('planner_id', $this->planner->id) ->orderBy('name') ->get(); $favorites = ScheduledUserDish::query() ->with(['user', 'userDish.dish']) ->whereNotNull('user_dish_id') ->whereHas('schedule', fn ($query) => $query->where('planner_id', $this->planner->id)) ->get() ->groupBy('user_id') ->map(fn (Collection $items) => $this->favoriteForItems($items)); // Eloquent map() returns a union type PHPStan cannot narrow to the declared shape. /** @phpstan-ignore return.type */ return $users->map(fn (User $user) => $favorites->get($user->id) ?? [ 'user' => $user, 'dish' => null, 'count' => 0, ])->toBase()->values(); } /** * @param Collection $items * @return array{user: User, dish: Dish|null, count: int} */ private function favoriteForItems(Collection $items): array { $dishCounts = $items ->groupBy(fn (ScheduledUserDish $item) => $item->userDish->dish_id) ->map(fn (Collection $dishItems) => [ 'dish' => $dishItems->firstOrFail()->userDish->dish, 'count' => $dishItems->count(), ]); $top = $dishCounts->sort(function (array $a, array $b) { if ($a['count'] === $b['count']) { return ($a['dish']->name ?? '') <=> ($b['dish']->name ?? ''); } return $b['count'] <=> $a['count']; })->first(); return [ 'user' => $items->firstOrFail()->user, 'dish' => $top['dish'] ?? null, 'count' => $top['count'] ?? 0, ]; } }