Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add advanced search option to watchlist #481

Merged
merged 4 commits into from
Aug 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions public/js/component/modal-advanced-search-person.html.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ function search() {
}

function resetSearchOptions() {
searchSortBySelect.value = 'name'
searchSortOrderSelect.value = 'asc'
searchSortBySelect.value = 'uniqueAppearances'
searchSortOrderSelect.value = 'desc'
searchGenderSelect.value = ''
searchPerPageSelect.value = '24'
}
55 changes: 55 additions & 0 deletions public/js/component/modal-advanced-search-watchlist.html.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
document.getElementById('searchTermInput').addEventListener('keypress', function (event) {
if (event.key === "Enter" || event.keyCode === 13) {
event.preventDefault();
document.getElementById('directSearchButton').click();
}
});

const searchSortBySelect = document.getElementById('searchSortBySelect');
const searchSortOrderSelect = document.getElementById('searchSortOrderSelect');
const searchGenreSelect = document.getElementById('searchGenreSelect');
const searchLanguageSelect = document.getElementById('searchLanguageSelect');
const searchReleaseYearSelect = document.getElementById('searchReleaseYearSelect');
const searchPerPageSelect = document.getElementById('searchPerPageSelect');

function search() {
let sortBy = searchSortBySelect.value
let sortOrder = searchSortOrderSelect.value
let searchGenre = searchGenreSelect.value
let searchLanguage = searchLanguageSelect.value
let searchReleaseYear = searchReleaseYearSelect.value
let searchPerPage = searchPerPageSelect.value
let searchTerm = document.getElementById('searchTermInput').value

let getParameters = '?'

getParameters += 'sb=' + sortBy
getParameters += '&so=' + sortOrder
getParameters += '&pp=' + searchPerPage

if (searchGenre != '') {
getParameters += '&ge=' + searchGenre
}
if (searchLanguage != '') {
getParameters += '&la=' + searchLanguage
}
if (searchReleaseYear != '') {
getParameters += '&ry=' + searchReleaseYear
}
if (searchTerm != '') {
getParameters += '&s=' + searchTerm
}

const urlWithoutGetParameters = window.location.href.split('?')[0];

window.location.href = urlWithoutGetParameters + getParameters;
}

function resetSearchOptions() {
searchSortBySelect.value = 'addedAt'
searchSortOrderSelect.value = 'desc'
searchGenreSelect.value = ''
searchLanguageSelect.value = ''
searchReleaseYearSelect.value = ''
searchPerPageSelect.value = '24'
}
1 change: 1 addition & 0 deletions public/js/watchlist.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const optionsModal = new bootstrap.Modal('#optionsModal');
optionsModal.hide()

async function removeFromWatchList() {
let movieId = document.getElementById('optionsModal').dataset.movieid;
Expand Down
64 changes: 60 additions & 4 deletions src/Domain/Movie/Watchlist/MovieWatchlistApi.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@

namespace Movary\Domain\Movie\Watchlist;

use Movary\Api\Tmdb\TmdbApi;
use Movary\Domain\User\UserApi;
use Movary\Service\UrlGenerator;
use Movary\ValueObject\DateTime;
use Movary\ValueObject\SortOrder;
use Movary\ValueObject\Year;
use Psr\Log\LoggerInterface;

class MovieWatchlistApi
Expand All @@ -13,6 +16,7 @@ public function __construct(
private readonly MovieWatchlistRepository $repository,
private readonly UrlGenerator $urlGenerator,
private readonly UserApi $userApi,
private readonly TmdbApi $tmdbApi,
private readonly LoggerInterface $logger,
) {
}
Expand All @@ -35,14 +39,66 @@ public function fetchAllWatchlistItems(int $userId) : ?array
return $this->repository->fetchAllWatchlistItems($userId);
}

public function fetchWatchlistCount(int $userId, ?string $searchTerm = null) : int
public function fetchUniqueMovieGenres(int $userId) : array
{
return $this->repository->fetchWatchlistCount($userId, $searchTerm);
return $this->repository->fetchUniqueMovieGenres($userId);
}

public function fetchWatchlistPaginated(int $userId, int $limit, int $page, ?string $searchTerm = null) : array
public function fetchUniqueMovieLanguages(int $userId) : array
{
$watchlistEntries = $this->repository->fetchWatchlistPaginated($userId, $limit, $page, $searchTerm);
$uniqueLanguages = [];

foreach ($this->repository->fetchUniqueMovieLanguages($userId) as $index => $item) {
if (empty($item) === true) {
continue;
}

$uniqueLanguages[$index]['name'] = $this->tmdbApi->getLanguageByCode($item);
$uniqueLanguages[$index]['code'] = $item;
}

$languageNames = array_column($uniqueLanguages, 'name');
array_multisort($languageNames, SORT_ASC, $uniqueLanguages);

return $uniqueLanguages;
}

public function fetchUniqueMovieReleaseYears(int $userId) : array
{
return $this->repository->fetchUniqueMovieReleaseYears($userId);
}

public function fetchWatchlistCount(int $userId, ?string $searchTerm = null, ?Year $releaseYear = null, ?string $language = null, ?string $genre = null) : int
{
return $this->repository->fetchWatchlistCount($userId, $searchTerm, $releaseYear, $language, $genre);
}

public function fetchWatchlistPaginated(
int $userId,
int $limit,
int $page,
?string $searchTerm = null,
string $sortBy = 'addedAt',
?SortOrder $sortOrder = null,
?Year $releaseYear = null,
?string $language = null,
?string $genre = null,
) : array {
if ($sortOrder === null) {
$sortOrder = SortOrder::createDesc();
}

$watchlistEntries = $this->repository->fetchWatchlistPaginated(
$userId,
$limit,
$page,
$searchTerm,
$sortBy,
$sortOrder,
$releaseYear,
$language,
$genre,
);

return $this->urlGenerator->replacePosterPathWithImageSrcUrl($watchlistEntries);
}
Expand Down
148 changes: 131 additions & 17 deletions src/Domain/Movie/Watchlist/MovieWatchlistRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
namespace Movary\Domain\Movie\Watchlist;

use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Platforms\SqlitePlatform;
use Movary\ValueObject\DateTime;
use Movary\ValueObject\SortOrder;
use Movary\ValueObject\Year;

class MovieWatchlistRepository
{
Expand All @@ -30,36 +33,144 @@ public function fetchAllWatchlistItems(int $userId) : ?array
);
}

public function fetchWatchlistCount(int $userId, ?string $searchTerm) : int
public function fetchUniqueMovieGenres(int $userId) : array
{
if ($searchTerm !== null) {
return $this->dbConnection->fetchFirstColumn(
<<<SQL
SELECT DISTINCT g.name
FROM watchlist w
JOIN movie m on w.movie_id = m.id
JOIN movie_genre mg on m.id = mg.movie_id
JOIN genre g on mg.genre_id = g.id
WHERE user_id = ?
ORDER BY g.name
SQL,
[$userId],
);
}

public function fetchUniqueMovieLanguages(int $userId) : array
{
return $this->dbConnection->fetchFirstColumn(
<<<SQL
SELECT DISTINCT m.original_language
FROM watchlist mh
JOIN movie m on mh.movie_id = m.id
WHERE user_id = ?
ORDER BY original_language DESC
SQL,
[$userId],
);
}

public function fetchUniqueMovieReleaseYears(int $userId) : array
{
if ($this->dbConnection->getDatabasePlatform() instanceof SqlitePlatform) {
return $this->dbConnection->fetchFirstColumn(
<<<SQL
SELECT COUNT(*)
FROM watchlist
JOIN movie m on movie_id = m.id
WHERE m.title LIKE ? AND user_id = ?
SELECT DISTINCT strftime('%Y',release_date)
FROM watchlist w
JOIN movie m on w.movie_id = m.id
WHERE user_id = ?
ORDER BY strftime('%Y',release_date) DESC
SQL,
["%$searchTerm%", $userId],
)[0];
[$userId],
);
}

return $this->dbConnection->fetchFirstColumn(
'SELECT COUNT(*) FROM watchlist JOIN movie m on movie_id = m.id WHERE user_id = ?',
<<<SQL
SELECT DISTINCT YEAR(m.release_date)
FROM watchlist w
JOIN movie m on w.movie_id = m.id
WHERE user_id = ?
ORDER BY YEAR(m.release_date) DESC
SQL,
[$userId],
)[0];
);
}

public function fetchWatchlistPaginated(int $userId, int $limit, int $page, ?string $searchTerm) : array
public function fetchWatchlistCount(int $userId, ?string $searchTerm, ?Year $releaseYear, ?string $language, ?string $genre) : int
{
$payload = [$userId, $userId];
$payload = [$userId, "%$searchTerm%"];

$whereQuery = 'WHERE m.title LIKE ? ';

if (empty($releaseYear) === false) {
if ($this->dbConnection->getDatabasePlatform() instanceof SqlitePlatform) {
$whereQuery .= 'AND strftime(\'%Y\', m.release_date) = ? ';
} else {
$whereQuery .= 'AND YEAR(m.release_date) = ? ';
}
$payload[] = (string)$releaseYear;
}

if (empty($language) === false) {
$whereQuery .= 'AND m.original_language = ? ';
$payload[] = $language;
}

if (empty($genre) === false) {
$whereQuery .= 'AND g.name = ? ';
$payload[] = $genre;
}

return $this->dbConnection->fetchFirstColumn(
<<<SQL
SELECT COUNT(DISTINCT m.id)
FROM movie m
JOIN watchlist w on w.movie_id = m.id and w.user_id = ?
LEFT JOIN movie_genre mg on m.id = mg.movie_id
LEFT JOIN genre g on mg.genre_id = g.id
$whereQuery
SQL,
$payload,
)[0];
}

public function fetchWatchlistPaginated(
int $userId,
int $limit,
int $page,
?string $searchTerm,
string $sortBy,
SortOrder $sortOrder,
?Year $releaseYear,
?string $language,
?string $genre,
) : array {
$payload = [$userId, $userId, "%$searchTerm%"];

$offset = ($limit * $page) - $limit;

$whereQuery = '';
if ($searchTerm !== null) {
$payload[] = "%$searchTerm%";
$whereQuery .= 'WHERE m.title LIKE ?';
$sortBySanitized = match ($sortBy) {
'rating' => 'rating',
'releaseDate' => 'release_date',
'addedAt' => 'added_at',
'runtime' => 'runtime',
default => 'title'
};

$whereQuery = 'WHERE m.title LIKE ? ';

if (empty($releaseYear) === false) {
if ($this->dbConnection->getDatabasePlatform() instanceof SqlitePlatform) {
$whereQuery .= 'AND strftime("%Y",m.release_date) = ? ';
} else {
$whereQuery .= 'AND YEAR(m.release_date) = ? ';
}

$payload[] = (string)$releaseYear;
}

if (empty($language) === false) {
$whereQuery .= 'AND original_language = ? ';
$payload[] = $language;
}

if (empty($genre) === false) {
$whereQuery .= 'AND g.name = ? ';
$payload[] = $genre;
}

return $this->dbConnection->fetchAllAssociative(
Expand All @@ -68,8 +179,11 @@ public function fetchWatchlistPaginated(int $userId, int $limit, int $page, ?str
FROM movie m
JOIN watchlist wl on wl.movie_id = m.id and wl.user_id = ?
LEFT JOIN movie_user_rating mur ON wl.movie_id = mur.movie_id and mur.user_id = ?
LEFT JOIN movie_genre mg on m.id = mg.movie_id
LEFT JOIN genre g on mg.genre_id = g.id
$whereQuery
ORDER BY added_at DESC
GROUP BY m.id, title, release_date, added_at, rating
ORDER BY $sortBySanitized $sortOrder, title asc
LIMIT $offset, $limit
SQL,
$payload,
Expand Down
Loading