<?php
namespace App\Repositories;
use App\Enums\PlayableType;
use App\Models\Genre;
use App\Models\Song;
use App\Models\User;
use App\Values\GenreSummary;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
/** @extends Repository<Genre> */
class GenreRepository extends Repository
{
/** @return Collection<GenreSummary>|array<array-key, GenreSummary> */
public function getAllSummaries(?User $scopedUser = null): Collection
{
$genres = Genre::query()
->join('genre_song', 'genre_song.genre_id', '=', 'genres.id')
->join('songs', 'songs.id', '=', 'genre_song.song_id')
->accessibleBy($scopedUser ?? auth()->user())
->groupBy('genres.id', 'genres.name', 'genres.public_id')
->orderBy('genres.name')
->select(
'genres.public_id',
'genres.name',
DB::raw('COUNT(songs.id) AS song_count'),
DB::raw('SUM(songs.length) AS length')
)
->get()
->map(
static fn (object $genre) => GenreSummary::make(
publicId: $genre->public_id,
name: $genre->name,
songCount: $genre->song_count,
length: $genre->length
)
);
$summaryForNoGenre = $this->getSummaryForNoGenre($scopedUser);
// Only add the "No Genre" stats if there are indeed songs without a genre
if ($summaryForNoGenre->songCount > 0) {
$genres->unshift($summaryForNoGenre);
}
return $genres;
}
public function getSummaryForGenre(Genre $genre, ?User $scopedUser = null): GenreSummary
{
/** @var object $result */
$result = Song::query(type: PlayableType::SONG, user: $scopedUser ?? auth()->user())
->accessible()
->join('genre_song', 'songs.id', '=', 'genre_song.song_id')
->join('genres', 'genre_song.genre_id', '=', 'genres.id')
->where('genres.id', $genre->id)
->groupBy('genres.public_id', 'genres.name')
->select(
'genres.public_id',
'genres.name',
DB::raw('COUNT(songs.id) AS song_count'),
DB::raw('SUM(songs.length) AS length')
)
->firstOrFail();
return GenreSummary::make(
publicId: $result->public_id,
name: $result->name,
songCount: $result->song_count,
length: $result->length
);
}
public function getSummaryForNoGenre(?User $scopedUser = null): GenreSummary
{
/** @var object $result */
$result = Song::query(type: PlayableType::SONG, user: $scopedUser ?? auth()->user())
->accessible()
->leftJoin('genre_song', 'songs.id', '=', 'genre_song.song_id')
->whereNull('genre_song.genre_id')
->select(
DB::raw('COUNT(songs.id) AS song_count'),
DB::raw('SUM(songs.length) AS length')
)
->firstOrFail();
return GenreSummary::make(
publicId: Genre::NO_GENRE_PUBLIC_ID,
name: Genre::NO_GENRE_NAME,
songCount: (int) $result->song_count,
length: (float) $result->length
);
}
}