diff options
| author | Developer | 2026-06-27 03:27:06 +0200 |
|---|---|---|
| committer | Developer | 2026-06-27 03:27:06 +0200 |
| commit | 8f43d48714a7346d0730145a8ab209c0ff8bc8fe (patch) | |
| tree | ab5fcf5598e88a4ef7883dc09e11fb1def6fc9e1 | |
| parent | df321aa19b9734981b81847389176021a14f44d4 (diff) | |
| download | curious-8f43d48714a7346d0730145a8ab209c0ff8bc8fe.tar.gz | |
feat: add /genres page listing all genres with movie counts
- Add CinemaController::genres() and ::genre($name) methods
- /genres shows all genres with movie counts (like /popular/topics)
- /genres/{genre} shows movies filtered by that genre with pagination
- Genre badges on cinema_show page now link to /genres/{genre}
- Add Genres link to navbar next to Cinema
| -rw-r--r-- | app/Http/Controllers/CinemaController.php | 65 | ||||
| -rw-r--r-- | resources/views/cinema_show.blade.php | 2 | ||||
| -rw-r--r-- | resources/views/genre_movies.blade.php | 156 | ||||
| -rw-r--r-- | resources/views/genres.blade.php | 42 | ||||
| -rw-r--r-- | resources/views/layouts/app.blade.php | 3 | ||||
| -rw-r--r-- | routes/web.php | 2 |
6 files changed, 269 insertions, 1 deletions
diff --git a/app/Http/Controllers/CinemaController.php b/app/Http/Controllers/CinemaController.php index c433eea..a526830 100644 --- a/app/Http/Controllers/CinemaController.php +++ b/app/Http/Controllers/CinemaController.php @@ -80,6 +80,71 @@ class CinemaController extends Controller ]); } + public function genres() + { + $genres = DB::connection('cinema') + ->table('genre') + ->select('genre.name', DB::raw('COUNT(imdb_genre.imdb_id) as count')) + ->join('imdb_genre', 'imdb_genre.genre_id', '=', 'genre.id') + ->join('imdb', 'imdb.id', '=', 'imdb_genre.imdb_id') + ->whereNotNull('imdb.primary_title') + ->whereNotNull('imdb.wiki_article') + ->where('imdb.title_type', 'movie') + ->groupBy('genre.name') + ->orderBy('count', 'desc') + ->get(); + + return view('genres', ['genres' => $genres]); + } + + public function genre($name) + { + $genre = DB::connection('cinema') + ->table('genre') + ->where('name', $name) + ->first(); + + if (! $genre) { + abort(404); + } + + $movies = DB::connection('cinema') + ->table('imdb') + ->select( + 'imdb.id', + 'imdb.imdb_id', + 'imdb.primary_title', + 'imdb.original_title', + 'imdb.start_year', + 'imdb.average_rating', + 'imdb.num_votes', + 'imdb.synopsis', + 'imdb.poster_url', + 'imdb.title_type', + 'imdb.runtime_minutes' + ) + ->join('imdb_genre', 'imdb_genre.imdb_id', '=', 'imdb.id') + ->join('genre', 'genre.id', '=', 'imdb_genre.genre_id') + ->where('genre.name', $name) + ->whereNotNull('imdb.primary_title') + ->whereNotNull('imdb.wiki_article') + ->where('imdb.title_type', 'movie') + ->orderBy('imdb.average_rating', 'desc') + ->simplePaginate(12); + + $count = DB::connection('cinema') + ->table('imdb') + ->join('imdb_genre', 'imdb_genre.imdb_id', '=', 'imdb.id') + ->join('genre', 'genre.id', '=', 'imdb_genre.genre_id') + ->where('genre.name', $name) + ->whereNotNull('imdb.primary_title') + ->whereNotNull('imdb.wiki_article') + ->where('imdb.title_type', 'movie') + ->count(); + + return view('genre_movies', ['movies' => $movies, 'count' => $count, 'genre_name' => $name]); + } + public function search(Request $request) { $query = $request->input('q', ''); diff --git a/resources/views/cinema_show.blade.php b/resources/views/cinema_show.blade.php index 5f849e3..8379a59 100644 --- a/resources/views/cinema_show.blade.php +++ b/resources/views/cinema_show.blade.php @@ -95,7 +95,7 @@ @if ( ! empty($genres) ) <div class="mt-2"> @foreach( $genres as $genre ) - <span class="badge badge-pill badge-primary">{{ $genre }}</span> + <a href="{{ route('cinema.genre', $genre) }}" class="badge badge-pill badge-primary" style="color: #fff;">{{ $genre }}</a> @endforeach </div> @endif diff --git a/resources/views/genre_movies.blade.php b/resources/views/genre_movies.blade.php new file mode 100644 index 0000000..05f8ca3 --- /dev/null +++ b/resources/views/genre_movies.blade.php @@ -0,0 +1,156 @@ +@extends('layouts.app') + +@section('styles') +@include ('layouts.footer-style') + +html { + height: inherit; +} + +.movie-grid { + display: flex; + flex-wrap: wrap; + gap: 20px; + margin-top: 20px; +} + +.movie-card { + flex: 1 1 280px; + min-width: 250px; + max-width: 320px; +} + +.movie-card .poster { + width: 100%; + aspect-ratio: 2 / 3; + object-fit: cover; + background-color: #333; + border-radius: 4px 4px 0 0; +} + +.movie-card .year { + color: #868e96; + font-size: 0.85rem; +} + +.movie-card .rating { + font-size: 0.9rem; + color: #868e96; +} + +.movie-card .synopsis { + font-size: 0.875rem; + color: #6c757d; + display: -webkit-box; + -webkit-line-clamp: 4; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.movie-card h1 { + font-size: 1rem; + padding: 10px 12px 4px; + margin: 0; +} + +.movie-card h1 a { + color: inherit; + text-decoration: none; +} + +.movie-card h1 a:hover { + text-decoration: underline; +} + +.movie-card .card-body { + padding: 0 12px 12px; +} + +.no-poster { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + aspect-ratio: 2 / 3; + background-color: #495057; + color: #adb5bd; + font-size: 3rem; + border-radius: 4px 4px 0 0; +} + +@endsection + +@section('content') +<div class="container"> + <div style="margin-top: 20px"></div> + + <div class="card"> + <h1 class="card-header"> + Genre: <strong>{{ $genre_name }}</strong> + @if ( "1" != $movies->currentPage() ) + (Page {{ $movies->currentPage() }}) + @endif + </h1> + <div class="card-body"> + <p class="card-text"> + Browse {{ number_format($count) }} movies in the <strong>{{ $genre_name }}</strong> genre. + </p> + <a href="{{ route('cinema.genres') }}" class="btn btn-sm btn-outline-primary">All Genres</a> + </div> + </div> + + @if ( 0 != $movies->count() ) + <div class="movie-grid"> + @foreach( $movies as $movie) + <div class="card movie-card"> + <a href="{{ route('cinema.show', $movie->imdb_id) }}"> + @if ( ! is_null($movie->poster_url) ) + <img class="poster" src="{{ $movie->poster_url }}" alt="{{ $movie->primary_title }} poster" loading="lazy"> + @else + <div class="no-poster">🎬</div> + @endif + </a> + <h1> + <a href="{{ route('cinema.show', $movie->imdb_id) }}"> + {{ $movie->primary_title }} + </a> + </h1> + <div class="card-body"> + <div> + @if ( ! is_null($movie->start_year) ) + <span class="year">{{ $movie->start_year }}</span> + @endif + @if ( ! is_null($movie->runtime_minutes) ) + @if ( ! is_null($movie->start_year) ) + <span class="year"> · </span> + @endif + <span class="year">{{ $movie->runtime_minutes }} min</span> + @endif + </div> + <div class="mt-1"> + @if ( ! is_null($movie->average_rating) ) + <span class="rating">⭐ {{ number_format($movie->average_rating, 1) }}</span> + @if ( ! is_null($movie->num_votes) ) + <span class="year"> ({{ number_format($movie->num_votes) }} votes)</span> + @endif + @endif + </div> + @if ( ! empty($movie->synopsis) ) + <p class="card-text synopsis mt-2"> + {{ strip_tags($movie->synopsis) }} + </p> + @endif + </div> + </div> + @endforeach + </div> + + <div style="margin-bottom: 20px"></div> + {{ $movies->appends(Request::input())->links() }} + @endif +</div> +@endsection + +@section('footer') + @include ('layouts.footer') +@endsection diff --git a/resources/views/genres.blade.php b/resources/views/genres.blade.php new file mode 100644 index 0000000..1fff8d2 --- /dev/null +++ b/resources/views/genres.blade.php @@ -0,0 +1,42 @@ +@extends('layouts.app') + +@section('styles') +@include ('layouts.footer-style') +html { + height: inherit; +} +@endsection + +@section('content') + +<div class="container"> + + <div style="margin-top: 20px"></div> + <div class="card"> + <h1 class="card-header"> + Genres + </h1> + <div class="card-body"> + <p class="card-text"> + Browse movies by genre. Each genre shows all movies that have been categorized. + </p> + </div> + </div> + <div style="margin-bottom: 20px"></div> + + <div class="text-center card"> + <div class="card-body"> + @foreach ($genres as $genre) + @if ( "" != $genre->name ) + <a href="{{ route('cinema.genre', $genre->name) }}" class="btn btn-lg btn-primary mx-1 my-1">{{ $genre->name }} ({{ $genre->count }} Movies)</a> + @endif + @endforeach + </div> + </div> + +</div> +@endsection + +@section('footer') + @include ('layouts.footer') +@endsection diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php index 35c57b4..9b22bac 100644 --- a/resources/views/layouts/app.blade.php +++ b/resources/views/layouts/app.blade.php @@ -141,6 +141,9 @@ <li class="nav-item"> <a class="nav-link" href="{{ route('cinema.index') }}">{{ __('Cinema') }}</a> </li> + <li class="nav-item"> + <a class="nav-link" href="{{ route('cinema.genres') }}">{{ __('Genres') }}</a> + </li> @else <li class="nav-item dropdown"> <a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre> diff --git a/routes/web.php b/routes/web.php index 95ac094..a2132c1 100644 --- a/routes/web.php +++ b/routes/web.php @@ -35,4 +35,6 @@ Route::get('/feed/search', 'FeedController@search')->name('feed_search'); Route::get('/cinema', 'CinemaController@cinema')->name('cinema.index'); Route::get('/cinema/movie/{imdb_id}', 'CinemaController@show')->name('cinema.show'); Route::get('/cinema/search', 'CinemaController@search')->name('cinema.search'); +Route::get('/genres', 'CinemaController@genres')->name('cinema.genres'); +Route::get('/genres/{genre}', 'CinemaController@genre')->name('cinema.genre')->where('genre', '(.+)'); Route::get('/about', 'IndexController@about')->name('about'); |
