diff --git a/assets/styles/app.css b/assets/styles/app.css index dd6181a..6230eed 100644 --- a/assets/styles/app.css +++ b/assets/styles/app.css @@ -1,3 +1,9 @@ body { background-color: skyblue; + font-family: 'Noto Sans', sans-serif; +} + +#actors td { + width: 16px; + height: 16px; } diff --git a/config/packages/doctrine.yaml b/config/packages/doctrine.yaml index 0bfa16d..fbc30d7 100644 --- a/config/packages/doctrine.yaml +++ b/config/packages/doctrine.yaml @@ -29,6 +29,9 @@ doctrine: alias: App controller_resolver: auto_mapping: false + dql: + numeric_functions: + Random: App\Doctrine\Extension\Random when@prod: doctrine: diff --git a/src/Context/TMDB/MovieCreditsContext.php b/src/Context/TMDB/MovieCreditsContext.php index 38b3c8d..74b0f25 100644 --- a/src/Context/TMDB/MovieCreditsContext.php +++ b/src/Context/TMDB/MovieCreditsContext.php @@ -2,12 +2,12 @@ namespace App\Context\TMDB; -use App\Model\TMDB\TMDBActor; +use App\Model\TMDB\TMDBMovieCredit; class MovieCreditsContext { public function __construct( - /** @var TMDBActor[] */ + /** @var TMDBMovieCredit[] */ public array $cast { get => $this->cast; }, ) {} } diff --git a/src/Controller/HomepageController.php b/src/Controller/HomepageController.php index 8291137..c4040d8 100644 --- a/src/Controller/HomepageController.php +++ b/src/Controller/HomepageController.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace App\Controller; use App\Gateway\TMDBGateway; +use App\Repository\ActorRepository; use App\Repository\MovieRepository; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; @@ -14,24 +15,60 @@ use Symfony\Component\Serializer\SerializerInterface; class HomepageController extends AbstractController { public function __construct( - private readonly MovieRepository $movieRepository, - private readonly TMDBGateway $TMDBGateway, + private readonly ActorRepository $actorRepository ) {} #[Route('/')] public function index(SerializerInterface $serializer): Response { - $movie = $this->movieRepository->findOneBy([]); - $creditsContext = $this->TMDBGateway->getMovieCredits($movie->getTmdbId()); - $cast = $creditsContext->cast; - $actors = []; - foreach ($cast as $actor) { - if (2 <= $actor->popularity) { - $actors[] = $actor; - } - } - dd($actors); + // Final actor to be guessed + $mainActor = $this->actorRepository->findOneRandom(4); - return $this->render('homepage/index.html.twig'); + // Actors for the grid + $actors = []; + $leftSize = 0; + $rightSize = 0; + foreach (str_split(strtolower($mainActor->getName())) as $char) { + if (!preg_match('/[a-z]/', $char)) { + continue; + } + + $tryFindActor = 0; + do { + $actor = $this->actorRepository->findOneRandom(4, $char); + ++$tryFindActor; + } while ( + $actor === $mainActor + || in_array($actor, array_map(fn ($actorMap) => $actorMap['actor'], $actors)) + || $tryFindActor < 5 + ); + + $actorData = [ + 'actor' => $actor, + 'pos' => strpos($actor->getName(), $char), + ]; + + if ($leftSize < $actorData['pos']) { + $leftSize = $actorData['pos']; + } + + $rightSizeActor = strlen($actor->getName()) - $actorData['pos'] - 1; + if ($rightSize < $rightSizeActor) { + $rightSize = $rightSizeActor; + } + + $actors[] = $actorData; + } + + // Predict grid size + $width = $rightSize + $leftSize + 1; + $middle = $leftSize; + + return $this->render('homepage/index.html.twig', [ + 'mainActor' => $mainActor, + 'actors' => $actors, + 'width' => $width, + 'middle' => $middle, + ]); } } diff --git a/src/Doctrine/Extension/Random.php b/src/Doctrine/Extension/Random.php new file mode 100644 index 0000000..3f6b258 --- /dev/null +++ b/src/Doctrine/Extension/Random.php @@ -0,0 +1,27 @@ +match(TokenType::T_IDENTIFIER); + $parser->match(TokenType::T_OPEN_PARENTHESIS); + $parser->match(TokenType::T_CLOSE_PARENTHESIS); + } + + public function getSql(SqlWalker $sqlWalker): string + { + return 'RANDOM()'; + } +} diff --git a/src/Gateway/TMDBGateway.php b/src/Gateway/TMDBGateway.php index 28bf041..ef506e6 100644 --- a/src/Gateway/TMDBGateway.php +++ b/src/Gateway/TMDBGateway.php @@ -2,6 +2,7 @@ namespace App\Gateway; +use App\Context\TMDB\ActorCreditsContext; use App\Context\TMDB\MovieCreditsContext; use App\Context\TMDB\MovieSearchContext; use App\Exception\GatewayException; @@ -44,7 +45,7 @@ readonly class TMDBGateway */ public function getMovieCredits(int $movieId): ?MovieCreditsContext { - $url = str_replace('{id}', $movieId, $this->host.self::MOVIE_CREDITS_URI); + $url = $this->host.str_replace('{id}', $movieId, self::MOVIE_CREDITS_URI); return $this->fetchSerialized('GET', $url, MovieCreditsContext::class); } diff --git a/src/Model/TMDB/TMDBActor.php b/src/Model/TMDB/TMDBMovieCredit.php similarity index 93% rename from src/Model/TMDB/TMDBActor.php rename to src/Model/TMDB/TMDBMovieCredit.php index e1522c7..ce7b86b 100644 --- a/src/Model/TMDB/TMDBActor.php +++ b/src/Model/TMDB/TMDBMovieCredit.php @@ -2,7 +2,7 @@ namespace App\Model\TMDB; -class TMDBActor +class TMDBMovieCredit { public function __construct( public int $id { get => $this->id; }, diff --git a/src/Repository/ActorRepository.php b/src/Repository/ActorRepository.php index d4de24b..672b4ed 100644 --- a/src/Repository/ActorRepository.php +++ b/src/Repository/ActorRepository.php @@ -16,28 +16,26 @@ class ActorRepository extends ServiceEntityRepository parent::__construct($registry, Actor::class); } - // /** - // * @return Actor[] Returns an array of Actor objects - // */ - // public function findByExampleField($value): array - // { - // return $this->createQueryBuilder('a') - // ->andWhere('a.exampleField = :val') - // ->setParameter('val', $value) - // ->orderBy('a.id', 'ASC') - // ->setMaxResults(10) - // ->getQuery() - // ->getResult() - // ; - // } + public function findOneRandom(?float $popularity = null, ?string $char = null): Actor + { + $qb = $this->createQueryBuilder('o'); + $expr = $qb->expr(); - // public function findOneBySomeField($value): ?Actor - // { - // return $this->createQueryBuilder('a') - // ->andWhere('a.exampleField = :val') - // ->setParameter('val', $value) - // ->getQuery() - // ->getOneOrNullResult() - // ; - // } + if (!empty($popularity)) { + $qb->andWhere($expr->gte('o.popularity', ':popularity')) + ->setParameter('popularity', $popularity); + } + + if (!empty($char)) { + $qb->andWhere($expr->like('o.name', ':name')) + ->setParameter('name', '%'.$char.'%'); + } + + return $qb + ->orderBy('RANDOM()') + ->setMaxResults(1) + ->getQuery() + ->getOneOrNullResult() + ; + } } diff --git a/src/Twig/AppExtension.php b/src/Twig/AppExtension.php new file mode 100644 index 0000000..a081bab --- /dev/null +++ b/src/Twig/AppExtension.php @@ -0,0 +1,21 @@ + + {% set iActor = 0 %} + {% for mainChar in mainActor.name|split('') %} + {% if not mainChar|match('/[a-zA-Z]/') %} + + {% else %} + {% set actor = actors[iActor] %} + + {% set i = 0 %} + {% set start = middle - actor.pos %} + {% for c in range(0, width) %} + {% if c >= start and c - start < actor.actor.name|length %} + {{ actor.actor.name|slice(c - start, 1)|upper }} + {% else %} + + {% endif %} + {% endfor %} + + {% set iActor = iActor + 1 %} + {% endif %} + {% endfor %} + +{% endblock %}