summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDeveloper2026-06-27 03:27:06 +0200
committerDeveloper2026-06-27 03:27:06 +0200
commit8f43d48714a7346d0730145a8ab209c0ff8bc8fe (patch)
treeab5fcf5598e88a4ef7883dc09e11fb1def6fc9e1
parentdf321aa19b9734981b81847389176021a14f44d4 (diff)
downloadcurious-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.php65
-rw-r--r--resources/views/cinema_show.blade.php2
-rw-r--r--resources/views/genre_movies.blade.php156
-rw-r--r--resources/views/genres.blade.php42
-rw-r--r--resources/views/layouts/app.blade.php3
-rw-r--r--routes/web.php2
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">&#127916;</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">&#11088; {{ 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');