src/App/Form/Type/CategorizedSearchType.php line 59

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace App\Form\Type;
  3. use App\Doctrine\Cache\CacheDecider;
  4. use App\Entity\Deal;
  5. use App\Entity\Project;
  6. use App\Entity\ShopVideo;
  7. use App\Entity\Tag;
  8. use App\Model\TagCount;
  9. use App\Repository\DealRepository;
  10. use App\Repository\ProjectRepository;
  11. use App\Repository\ShopVideoRepository;
  12. use App\Repository\TagRepository;
  13. use InvalidArgumentException;
  14. use Symfony\Component\Form\AbstractType;
  15. use Symfony\Component\Form\ChoiceList\Loader\CallbackChoiceLoader;
  16. use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
  17. use Symfony\Component\Form\FormBuilderInterface;
  18. use Symfony\Component\OptionsResolver\OptionsResolver;
  19. use Symfony\Contracts\Service\ResetInterface;
  20. class CategorizedSearchType extends AbstractType implements ResetInterface
  21. {
  22.     private TagRepository $tagRepository;
  23.     private DealRepository $dealRepository;
  24.     private ProjectRepository $projectRepository;
  25.     private ShopVideoRepository $shopVideoRepository;
  26.     private CacheDecider $cacheDecider;
  27.     /**
  28.      * @var array<array<TagCount>>|null
  29.      */
  30.     private ?array $choices null;
  31.     public function __construct(TagRepository $tagRepositoryDealRepository $dealRepositoryProjectRepository $projectRepositoryShopVideoRepository $shopVideoRepositoryCacheDecider $cacheDecider)
  32.     {
  33.         $this->tagRepository $tagRepository;
  34.         $this->dealRepository $dealRepository;
  35.         $this->projectRepository $projectRepository;
  36.         $this->shopVideoRepository $shopVideoRepository;
  37.         $this->cacheDecider $cacheDecider;
  38.     }
  39.     /**
  40.      * @param array<mixed> $options
  41.      */
  42.     public function buildForm(FormBuilderInterface $builder, array $options): void
  43.     {
  44.         $builder->add('category'ChoiceType::class, [
  45.             'label' => 'search.category',
  46.             'choice_value' => 'id',
  47.             'placeholder' => 'search.choose_value_category',
  48.             'choice_label' => function (): string {
  49.                 return 'search.choice';
  50.             },
  51.             'choice_loader' => new CallbackChoiceLoader(function () use ($options): array {
  52.                 return $this->getChoices($options)[Tag::TYPE_CATEGORY];
  53.             }),
  54.             'choice_translation_parameters' => function (TagCount $choice): array {
  55.                 return [
  56.                     '{ choice }' => $choice->getName(),
  57.                     '{ count }' => $choice->getCount(),
  58.                 ];
  59.             },
  60.             'attr' => [
  61.                 'class' => 'js-search-category',
  62.                 'data-allow-clear' => 'true',
  63.                 'autocomplete' => 'off',
  64.             ],
  65.             'required' => false,
  66.         ])->add('dress_code'ChoiceType::class, [
  67.             'label' => 'search.dress_code',
  68.             'choice_value' => 'id',
  69.             'placeholder' => 'search.choose_value_dress_code',
  70.             'choice_label' => function (): string {
  71.                 return 'search.choice';
  72.             },
  73.             'choice_loader' => new CallbackChoiceLoader(function () use ($options): array {
  74.                 return $this->getChoices($options)[Tag::TYPE_DRESS_CODE];
  75.             }),
  76.             'choice_translation_parameters' => function (TagCount $choice): array {
  77.                 return [
  78.                     '{ choice }' => $choice->getName(),
  79.                     '{ count }' => $choice->getCount(),
  80.                 ];
  81.             },
  82.             'attr' => [
  83.                 'class' => 'js-search-dress-code',
  84.                 'data-allow-clear' => 'true',
  85.                 'autocomplete' => 'off',
  86.             ],
  87.             'required' => false,
  88.         ])->add('character'ChoiceType::class, [
  89.             'label' => 'search.character',
  90.             'choice_value' => 'id',
  91.             'placeholder' => 'search.choose_value_character',
  92.             'choice_label' => function (): string {
  93.                 return 'search.choice';
  94.             },
  95.             'choice_loader' => new CallbackChoiceLoader(function () use ($options): array {
  96.                 return $this->getChoices($options)[Tag::TYPE_CHARACTER];
  97.             }),
  98.             'attr' => [
  99.                 'class' => 'js-search-character',
  100.                 'data-allow-clear' => 'true',
  101.                 'autocomplete' => 'off',
  102.             ],
  103.             'choice_translation_parameters' => function (TagCount $choice): array {
  104.                 return [
  105.                     '{ choice }' => $choice->getName(),
  106.                     '{ count }' => $choice->getCount(),
  107.                 ];
  108.             },
  109.             'required' => false,
  110.         ]);
  111.     }
  112.     public function configureOptions(OptionsResolver $resolver): void
  113.     {
  114.         $resolver->setDefaults([
  115.             'translation_domain' => 'frontend',
  116.             'requested_data' => [
  117.                 'category' => null,
  118.                 'dress_code' => null,
  119.                 'character' => null,
  120.             ],
  121.         ]);
  122.         $resolver->setRequired('target_entity');
  123.         $resolver->setAllowedTypes('target_entity', ['string']);
  124.         $resolver->setAllowedTypes('requested_data', ['array']);
  125.         $resolver->setDefault('method''GET');
  126.     }
  127.     /**
  128.      * @param array<mixed> $options
  129.      * @return array<array<TagCount>>
  130.      */
  131.     private function getChoices(array $options): array
  132.     {
  133.         if (null !== $this->choices) {
  134.             return $this->choices;
  135.         }
  136.         if (Deal::class === $options['target_entity']) {
  137.             $queryBuilder $this->dealRepository->filter($this->dealRepository->applyPublicFilters([]));
  138.         } elseif (Project::class === $options['target_entity']) {
  139.             $queryBuilder $this->projectRepository->filter($this->projectRepository->applyPublicFilters([]));
  140.         } elseif (ShopVideo::class === $options['target_entity']) {
  141.             $queryBuilder $this->shopVideoRepository->filter($this->shopVideoRepository->applyPublicFilters([]));
  142.         } else {
  143.             throw new InvalidArgumentException('Unknown target_entity.');
  144.         }
  145.         $requestedCategory $requestedDressCode $requestedCharacter null;
  146.         if (is_array($options['requested_data'])) {
  147.             if (is_numeric($options['requested_data']['category'])) {
  148.                 $requestedCategory = (int) $options['requested_data']['category'];
  149.             }
  150.             if (is_numeric($options['requested_data']['dress_code'])) {
  151.                 $requestedDressCode = (int) $options['requested_data']['dress_code'];
  152.             }
  153.             if (is_numeric($options['requested_data']['character'])) {
  154.                 $requestedCharacter = (int) $options['requested_data']['character'];
  155.             }
  156.         }
  157.         return $this->choices $this->tagRepository->getCounts(
  158.             $options['target_entity'],
  159.             $requestedCategory,
  160.             $requestedDressCode,
  161.             $requestedCharacter,
  162.             $queryBuilder,
  163.             $this->cacheDecider
  164.         );
  165.     }
  166.     public function reset(): void
  167.     {
  168.         $this->choices null;
  169.     }
  170. }