<?php
declare(strict_types=1);
namespace App\Controller\V1;
use App\Entity\Local\MovieFilter\Filters;
use App\Entity\Netural\Review;
use App\Entity\Netural\Reviews;
use App\Entity\Vista\ScheduledFilm;
use App\Helper\SessionHelper;
use App\Repository\LocationRepository;
use App\Repository\MovieFiltersRepository;
use App\Repository\MovieRepositoryInterface as MovieRepository;
use App\Repository\MovieArchiveRepository;
use App\Repository\RestrictionsRepository;
use App\Repository\ScheduledMovieRepository;
use Doctrine\Common\Persistence\ManagerRegistry;
use FOS\RestBundle\Controller\Annotations as Rest;
use FOS\RestBundle\Controller\Annotations\Route;
use FOS\RestBundle\Controller\Annotations\View;
use FOS\RestBundle\Controller\FOSRestController;
use Nelmio\ApiDocBundle\Annotation\Model;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Swagger\Annotations as SWG;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* @Route("/movies")
* @SWG\Tag(name="Movie_v1")
*/
class MovieController extends FOSRestController
{
use ResponseTrait;
/** @var ScheduledMovieRepository|\Doctrine\Common\Persistence\ObjectRepository */
protected $scheduledMovieRepository;
/** @var SessionHelper */
protected $sessionHelper;
/** @var LocationRepository */
protected $locationRepository;
/** @var MovieFiltersRepository */
protected $movieFiltersRepository;
/** @var MovieRepository */
protected $movieRepository;
/** @var RestrictionsRepository */
protected $restrictionsRepository;
/** @var MovieArchiveRepository */
protected $movieArchiveRepository;
public function __construct(
ManagerRegistry $registry,
SessionHelper $sessionHelper,
LocationRepository $locationRepository,
MovieFiltersRepository $movieFiltersRepository,
MovieRepository $movieRepository,
MovieArchiveRepository $movieArchiveRepository,
RestrictionsRepository $restrictionsRepository
) {
$this->scheduledMovieRepository = $registry->getRepository(ScheduledFilm::class);
$this->sessionHelper = $sessionHelper;
$this->locationRepository = $locationRepository;
$this->movieFiltersRepository = $movieFiltersRepository;
$this->movieRepository = $movieRepository;
$this->movieArchiveRepository = $movieArchiveRepository;
$this->restrictionsRepository = $restrictionsRepository;
}
/**
* @param array|null $filter
* @param array|null $order
* @param int|null $limit
* @param int|null $offset
* @param int|null $location
* @param int|null $cinema
* @param \DateTimeInterface|null $date
* @param bool $setDefaultDate
*
* @return array
*/
protected function prepareInput(
$filter = null,
$order = null,
$limit = null,
$offset = null,
$location = null,
$cinema = null,
$date = null,
$setDefaultDate = false
) {
if (!$filter) {
$filter = [];
}
if (!$order) {
$order = null;
}
if (!$limit) {
$limit = null;
}
if (!$offset) {
$offset = null;
}
if (is_numeric($location)) {
$filter['cinemaId'] = $cinemaId = $this->locationRepository->findById($location)->getItems();
} else {
$filter['cinemaId'] = $cinemaId = $this->locationRepository->findLocationIds();
}
if ($cinema) {
$filter['cinemaId'] = $cinemaId = [$cinema];
}
if ($date) {
$filter['date'] = $date;
} elseif ($setDefaultDate) {
$defaultDate = $this->sessionHelper->getDefaultDate($cinemaId);
$defaultDate and $filter['date'] = $defaultDate;
}
if (isset($filter['technology']) && is_array($filter['technology'])) {
$decoded = [];
foreach ($filter['technology'] as $item) {
$decoded[] = urldecode($item);
}
$filters = $this->movieFiltersRepository->getMovieFiltersRoot()->getFilters();
$filter['technology'] = $filters->getTechnologyFilter($decoded);
if (empty($filter['technology'])) {
unset($filter['technology']);
}
}
if (is_array($filter['version'] ?? null)) {
$item = '';
assert(is_array($filter['version']));
foreach ($filter['version'] as &$item) {
$item = urldecode($item);
}
$filters = $this->movieFiltersRepository->getMovieFiltersRoot()->getFilters();
$filter['version'] = $filters->getVersionMatching($item);
if (empty($filter['version'])) {
unset($filter['version']);
}
}
$resolver = new OptionsResolver();
$resolver->setDefaults([
'date' => null,
'rating' => null,
'technology' => null,
'version' => null,
'title' => null,
'genreId' => null,
'cinemaId' => $this->locationRepository->findLocationIds(),
'shortURL' => null
]);
try {
$filter = array_filter($resolver->resolve($filter));
} catch (UndefinedOptionsException $exception) {
throw new \InvalidArgumentException('Invalid filter parameters');
}
return [$filter, $order, $limit, $offset];
}
/**
* @Route("", methods={"GET"})
* @Rest\QueryParam(name="filter", allowBlank=true)
* @Rest\QueryParam(name="order", allowBlank=true)
* @Rest\QueryParam(name="limit", requirements="\d+", allowBlank=true)
* @Rest\QueryParam(name="offset", requirements="\d+", allowBlank=true)
* @Rest\QueryParam(name="location", allowBlank=true)
* @Rest\QueryParam(name="cinema", allowBlank=true)
* @Rest\QueryParam(name="date", allowBlank=true)
* @View(serializerGroups={"Default", "movie_list"})
*
* @SWG\Parameter(name="filter", type="string", in="query")
* @SWG\Parameter(name="order", type="string", in="query")
*
* @SWG\Response(
* response="200",
* description="All movies",
* @SWG\Schema(type="array", items=@SWG\Items(ref=@Model(type=\App\Entity\Local\MoviesComposed::class))))
*
* @param $filter
* @param $order
* @param $limit
* @param $offset
* @param $location
* @param $cinema
* @param \DateTime|null $date
* @return array
* @throws \Exception
*/
public function getAllMoviesAction($filter, $order, $limit, $offset, $location, $cinema, ?\DateTime $date)
{
[
$filter,
$order,
$limit,
$offset,
] = $this->prepareInput(
$filter,
$order,
$limit,
$offset,
$location,
$cinema,
$date,
true
);
return $this->composeByCorporateId($this->movieRepository->search($filter, $order, $limit, $offset));
}
/**
* @Route("/schoolcinema", methods={"GET"})
* @Rest\QueryParam(name="filter", allowBlank=true)
* @Rest\QueryParam(name="order", allowBlank=true)
* @Rest\QueryParam(name="limit", requirements="\d+", allowBlank=true)
* @Rest\QueryParam(name="offset", requirements="\d+", allowBlank=true)
* @Rest\QueryParam(name="location", allowBlank=true)
* @Rest\QueryParam(name="cinema", allowBlank=true)
* @Rest\QueryParam(name="date", allowBlank=true)
* @Rest\QueryParam(name="filmtipp", allowBlank=true)
* @View(serializerGroups={"Default", "movie_list"})
*
* @SWG\Parameter(name="filter", type="string", in="query")
* @SWG\Parameter(name="order", type="string", in="query")
*
* @SWG\Response(
* response="200",
* description="All School Cinema movies",
* @SWG\Schema(type="array", items=@SWG\Items(ref=@Model(type=\App\Entity\Local\MoviesComposed::class))))
*
* @param $filter
* @param $order
* @param $limit
* @param $offset
* @param $location
* @param $cinema
* @param \DateTime|null $date
* @param $filmtipp
* @return array
* @throws \Exception
*/
public function getSchoolCinemaAction($filter, $order, $limit, $offset, $location, $cinema, ?\DateTime $date, $filmtipp)
{
[
$filter,
$order,
$limit,
$offset,
] = $this->prepareInput(
$filter,
$order,
$limit,
$offset,
$location,
$cinema,
$date,
false
);
if ($filmtipp && filter_var($filmtipp, FILTER_VALIDATE_BOOLEAN)) {
$filter['filmTipp'] = true;
}
$filter['schoolMovie'] = true;
return $this->composeByCorporateId($this->movieRepository->searchWithSession($filter, $order, $limit, $offset));
}
/**
* @Route("/schoolcinema/coming-soon", methods={"GET"})
* @Rest\QueryParam(name="filter", allowBlank=true)
* @Rest\QueryParam(name="order", allowBlank=true)
* @Rest\QueryParam(name="limit", requirements="\d+", allowBlank=true)
* @Rest\QueryParam(name="offset", requirements="\d+", allowBlank=true)
* @Rest\QueryParam(name="location", allowBlank=true)
* @Rest\QueryParam(name="cinema", allowBlank=true)
* @Rest\QueryParam(name="date", allowBlank=true)
* @Rest\QueryParam(name="filmtipp", allowBlank=true)
* @View(serializerGroups={"Default", "movie_list"})
*
* @SWG\Parameter(name="filter", type="string", in="query")
* @SWG\Parameter(name="order", type="string", in="query")
* @SWG\Response(
* response=200,
* description="Coming soon School Cinema movies",
* @SWG\Schema(
* type="array",
* items=@SWG\Items(ref=@Model(type=\App\Entity\Local\MoviesComposed::class))))
*
* @param $filter
* @param $order
* @param $limit
* @param $offset
* @param $location
* @param $cinema
* @param \DateTimeImmutable|null $date
* @param $filmtipp
* @return array
* @throws \Exception
*/
public function getComingSoonSchoolCinemaAction($filter, $order, $limit, $offset, $location, $cinema, ?\DateTimeImmutable $date, $filmtipp)
{
[$filter, $order, $limit, $offset] = $this->prepareInput($filter, $order, $limit, $offset, $location, $cinema, $date);
unset($filter['cinemaId']);
$filter['>=openingDate'] = new \DateTimeImmutable('now');
if ($date) {
unset($filter['date']);
$filter['>=openingDate'] = $date;
$filter['<openingDate'] = $date->modify('+1 month');
}
if ($filmtipp && filter_var($filmtipp, FILTER_VALIDATE_BOOLEAN)) {
$filter['filmTipp'] = true;
}
$filter['schoolMovie'] = true;
return $this->composeByCorporateId($this->movieRepository->search(
$filter ?: [],
$order ?: null,
$limit ?: null,
$offset ?: null,
['coming-soon']
));
}
/**
* @Route("/search/advanced", methods={"POST"})
* @ParamConverter("filter", converter="fos_rest.request_body", class="array")
* @Rest\QueryParam(name="order", allowBlank=true)
* @Rest\QueryParam(name="limit", requirements="\d+", allowBlank=true)
* @Rest\QueryParam(name="offset", requirements="\d+", allowBlank=true)
* @Rest\QueryParam(name="location", allowBlank=true)
* @Rest\QueryParam(name="cinema", allowBlank=true)
* @Rest\QueryParam(name="date", allowBlank=true)
* @View(serializerGroups={"Default", "movie_list"})
*
* @SWG\Parameter(
* name="body",
* in="body",
* @SWG\Schema(type="object",
* @SWG\Property(property="title", type="string")))
*
* @SWG\Response(
* response=200,
* description="Success",
* @SWG\Schema(type="array", items=@SWG\Items(ref=@Model(type=\App\Entity\Vista\Film::class))))
*
* @param array $filter
* @param array $order
* @param int $limit
* @param int $offset
* @param int $location
* @param int $cinema
* @param \DateTime|null $date
* @return array
*/
public function advancedSearchAction(
$filter = null,
$order = null,
$limit = null,
$offset = null,
$location = null,
$cinema = null,
?\DateTime $date = null
) {
[$filter, $order, $limit, $offset] = $this->prepareInput($filter, $order, $limit, $offset, $location, $cinema, $date);
$flags = ['advanced-search'];
is_numeric($location) and $flags[] = 'exclude-coming-soon';
return $this->movieRepository->search($filter, $order, $limit, $offset, $flags);
}
/**
* @Route("/coming-soon", methods={"GET"})
* @Rest\QueryParam(name="filter", allowBlank=true)
* @Rest\QueryParam(name="order", allowBlank=true)
* @Rest\QueryParam(name="limit", requirements="\d+", allowBlank=true)
* @Rest\QueryParam(name="offset", requirements="\d+", allowBlank=true)
* @Rest\QueryParam(name="location", allowBlank=true)
* @Rest\QueryParam(name="cinema", allowBlank=true)
* @Rest\QueryParam(name="date", allowBlank=true)
* @View(serializerGroups={"Default", "movie_list"})
*
* @SWG\Parameter(name="filter", type="string", in="query")
* @SWG\Parameter(name="order", type="string", in="query")
* @SWG\Response(
* response=200,
* description="Coming soon movies",
* @SWG\Schema(
* type="array",
* items=@SWG\Items(ref=@Model(type=\App\Entity\Local\MoviesComposed::class))))
*
* @param $filter
* @param $order
* @param $limit
* @param $offset
* @param $location
* @param $cinema
* @param \DateTimeImmutable|null $date
* @return array
* @throws \Exception
*/
public function getComingSoonMoviesAction($filter, $order, $limit, $offset, $location, $cinema, ?\DateTimeImmutable $date)
{
[$filter, $order, $limit, $offset] = $this->prepareInput($filter, $order, $limit, $offset, $location, $cinema, $date);
unset($filter['cinemaId']);
$filter['>=openingDate'] = new \DateTimeImmutable('now');
if ($date) {
unset($filter['date']);
$filter['>=openingDate'] = $date;
$filter['<openingDate'] = $date->modify('+1 month');
}
return $this->composeByCorporateId($this->movieRepository->search(
$filter ?: [],
$order ?: null,
$limit ?: null,
$offset ?: null,
['coming-soon']
));
}
/**
* @Route("/top", methods={"GET"})
* @Rest\QueryParam(name="filter", allowBlank=true)
* @Rest\QueryParam(name="order", allowBlank=true)
* @Rest\QueryParam(name="limit", requirements="\d+", allowBlank=true)
* @Rest\QueryParam(name="offset", requirements="\d+", allowBlank=true)
* @Rest\QueryParam(name="location", allowBlank=true)
* @Rest\QueryParam(name="cinema", allowBlank=true)
* @Rest\QueryParam(name="date", allowBlank=true)
* @View(serializerGroups={"Default", "movie_list"})
*
* @SWG\Parameter(name="filter", type="string", in="query")
* @SWG\Parameter(name="order", type="string", in="query")
* @SWG\Response(
* response=200,
* description="Top movies",
* @SWG\Schema(
* type="array",
* items=@SWG\Items(ref=@Model(type=\App\Entity\Local\MoviesComposed::class))))
*
* @param $filter
* @param $order
* @param $limit
* @param $offset
* @param $location
* @param $cinema
* @param \DateTime|null $date
* @return array
* @throws \Exception
*/
public function getTopMoviesAction($filter, $order, $limit, $offset, $location, $cinema, ?\DateTime $date)
{
[$filter, $order, $limit, $offset] = $this->prepareInput($filter, $order, $limit, $offset, $location, $cinema, $date, true);
$filter['top'] = true;
return $this->composeByCorporateId($this->movieRepository->search(
$filter ?: [],
$order ?: null,
$limit ?: null,
$offset ?: null
));
}
/**
* @Route("/grouped-by-corporate-id/{corporateId}/sessions", methods={"GET"})
* @Rest\QueryParam(name="filter", allowBlank=true)
* @Rest\QueryParam(name="order", allowBlank=true)
* @Rest\QueryParam(name="limit", requirements="\d+", allowBlank=true)
* @Rest\QueryParam(name="offset", requirements="\d+", allowBlank=true)
* @Rest\QueryParam(name="location", allowBlank=true)
* @Rest\QueryParam(name="cinema", allowBlank=true)
* @Rest\QueryParam(name="date", allowBlank=true)
* @Rest\View(serializerGroups={"Default", "session_list"})
*
* @SWG\Parameter(name="filter", type="string", in="query")
* @SWG\Parameter(name="order", type="string", in="query")
* @SWG\Response(
* response="200",
* description="Get list of sessions by movie id",
* @SWG\Schema(
* type="array",
* items=@SWG\Items(ref=@Model(type=\App\Entity\Vista\DTO\SessionsByDateDTO::class))))
*
* @param Request $request
* @param string $corporateId
* @param array $filter
* @param array $order
* @param int $limit
* @param int $offset
* @param null $location
* @param null $cinema
* @param \DateTime|null $date
*
* @return array
* @throws \Exception
*/
public function sessionsAction(
Request $request,
$corporateId,
$filter = [],
$order = [],
$limit = 0,
$offset = 0,
$location = null,
$cinema = null,
?\DateTime $date = null
) {
$filter or $filter = [];
[$filter, $order, $limit, $offset] = $this->prepareInput($filter, $order, $limit, $offset, $location, $cinema, null);
$movies = $this->scheduledMovieRepository->fetchMovieByCorporateIdOrHOFilmCode($corporateId);
$filter['scheduledFilm'] = $movies;
if (isset($filter['date'])) {
unset($filter['date']);
}
if ('mobile' === $request->attributes->get('_platform')) {
if ($date) {
$filter['>=showtime'] = $date->setTime(0, 0, 0);
$filter['<=showtime'] = (clone $date)->add(new \DateInterval('P1D'));
} else {
$restrictions = $this->restrictionsRepository->findOrCreate();
$expiryDate = (new \DateTimeImmutable())->sub(new \DateInterval(sprintf(
'PT%sS',
$restrictions->getGreySessionTTL()
)));
$filter['>=showtime'] = $expiryDate;
}
} else {
$date or $date = $this->sessionHelper->getDefaultDate();
if ($date) {
$filter['>=showtime'] = $date;
$filter['<=showtime'] = (clone $date)
->setTime(0, 0, 0)
->add(new \DateInterval('P1D'));
}
}
return $this->prepareSessionsResponse($this->sessionHelper->findBy(
$filter ?: [],
$order ?: null,
$limit ?: null,
$offset ?: null
));
}
/**
* @Route("/{id}", methods={"GET"})
* @View(serializerGroups={"Default", "movie_details"})
*
* @SWG\Response(
* response=200,
* description="A movie",
* @Model(type=\App\Entity\Vista\Film::class))
*
* @param $id
* @return array|\Symfony\Component\HttpKernel\Exception\NotFoundHttpException
* @throws \Exception
*/
public function getMovieByIdAction($id)
{
$film = $this->movieRepository->fetchMovieByIdHOFilmCode($id);
if (null === $film) {
$film = $this->movieArchiveRepository->fetchMovieByIdHOFilmCode($id);
if (null === $film) {
return $this->createNotFoundException();
}
return $this->prepareFilmArchive($film);
}
return $this->prepareFilm($film);
}
/**
* @Route("/{id}/comments", methods={"GET"})
* @Rest\QueryParam(name="skip", allowBlank=true)
* @Rest\QueryParam(name="take", allowBlank=true)
* @View()
*
* @SWG\Response(
* response=200,
* description="A movie comments",
* @SWG\Schema(type="array", items=@SWG\Items(ref=@Model(type=\App\Entity\Netural\Review::class))))
*
* @param string $id
* @param int $skip
* @param int $take
* @return array
*/
public function getMovieCommentsAction($id, $skip = null, $take = null)
{
(!is_numeric($skip) || $skip < 0) and $skip = 0;
(!is_numeric($take) || $take < 0) and $take = 0;
$skip = intval($skip);
$take = intval($take);
$film = $this->movieRepository->fetchMovieByIdHOFilmCode($id);
if (null === $film) {
$film = $this->movieArchiveRepository->fetchMovieByIdHOFilmCode($id);
if (null === $film) {
throw new \InvalidArgumentException(sprintf('Invalid movie id: %s', $id));
}
}
/** @var Reviews|null $reviews */
$reviews = $film->getReviews();
if (!$reviews) {
return [];
}
$featured = $reviews->getFeaturedReview();
$indexedReviews = [];
/** @var Review $item */
foreach ($reviews->getEntries() ?? [] as $item) {
$indexedReviews[$item->getReviewId()] = $item;
}
if ($featured) {
$featured->setIsFeatured(true);
unset($indexedReviews[$featured->getReviewId()]);
}
usort($indexedReviews, function ($a, $b) {
/**
* @var Review $a
*/
/**
* @var Review $b
*/
if ($a->getTime() < $b->getTime()) {
return -1;
} elseif ($a->getTime() > $b->getTime()) {
return 1;
}
return 0;
});
$indexedReviews = array_reverse($indexedReviews);
$preResult = array_filter(array_merge([$featured], array_values($indexedReviews)));
$highIndex = count($preResult) ? count($preResult) - 1 : -1;
$startIndex = $skip;
if (($startIndex > $highIndex) || !$take) {
return [];
}
return array_slice($preResult, $startIndex, $take);
}
/**
* @Route("/filters/list", methods={"GET"})
* @View()
*
* @SWG\Response(
* response=200,
* description="Filters",
* @Model(type=\App\Entity\Local\MovieFilter\Filters::class))
*
* @return Filters|null
*/
public function filtersListAction()
{
return $this->movieFiltersRepository->getMovieFiltersRoot()->getFilters();
}
/**
* @Route("/filters/months/list", methods={"GET"})
* @Rest\QueryParam(name="corporateFilmId", allowBlank=true, strict=true, nullable=true)
* @Rest\QueryParam(name="comingSoon", allowBlank=true, strict=true, nullable=true)
* @View()
*
* @SWG\Response(
* response="200",
* description="Success",
* @SWG\Schema(type="array", items=@SWG\Items(ref=@Model(type=\App\Entity\Vista\Film::class))))
*
* @param string $corporateFilmId
* @param bool $comingSoon
* @return array
*/
public function filtersMonthListAction(string $corporateFilmId = null, bool $comingSoon = null)
{
$filter = $corporateFilmId ? ['corporateFilmId' => $corporateFilmId] : [];
$flags = null !== $comingSoon ? ['coming-soon'] : [];
return $this->movieRepository->findMonthsBy($filter, null, null, null, $flags);
}
}