<?php declare(strict_types=1);
namespace App\Form\Type;
use App\Doctrine\Cache\CacheDecider;
use App\Entity\Deal;
use App\Entity\Project;
use App\Entity\ShopVideo;
use App\Entity\Tag;
use App\Model\TagCount;
use App\Repository\DealRepository;
use App\Repository\ProjectRepository;
use App\Repository\ShopVideoRepository;
use App\Repository\TagRepository;
use InvalidArgumentException;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\ChoiceList\Loader\CallbackChoiceLoader;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Contracts\Service\ResetInterface;
class CategorizedSearchType extends AbstractType implements ResetInterface
{
private TagRepository $tagRepository;
private DealRepository $dealRepository;
private ProjectRepository $projectRepository;
private ShopVideoRepository $shopVideoRepository;
private CacheDecider $cacheDecider;
/**
* @var array<array<TagCount>>|null
*/
private ?array $choices = null;
public function __construct(TagRepository $tagRepository, DealRepository $dealRepository, ProjectRepository $projectRepository, ShopVideoRepository $shopVideoRepository, CacheDecider $cacheDecider)
{
$this->tagRepository = $tagRepository;
$this->dealRepository = $dealRepository;
$this->projectRepository = $projectRepository;
$this->shopVideoRepository = $shopVideoRepository;
$this->cacheDecider = $cacheDecider;
}
/**
* @param array<mixed> $options
*/
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder->add('category', ChoiceType::class, [
'label' => 'search.category',
'choice_value' => 'id',
'placeholder' => 'search.choose_value_category',
'choice_label' => function (): string {
return 'search.choice';
},
'choice_loader' => new CallbackChoiceLoader(function () use ($options): array {
return $this->getChoices($options)[Tag::TYPE_CATEGORY];
}),
'choice_translation_parameters' => function (TagCount $choice): array {
return [
'{ choice }' => $choice->getName(),
'{ count }' => $choice->getCount(),
];
},
'attr' => [
'class' => 'js-search-category',
'data-allow-clear' => 'true',
'autocomplete' => 'off',
],
'required' => false,
])->add('dress_code', ChoiceType::class, [
'label' => 'search.dress_code',
'choice_value' => 'id',
'placeholder' => 'search.choose_value_dress_code',
'choice_label' => function (): string {
return 'search.choice';
},
'choice_loader' => new CallbackChoiceLoader(function () use ($options): array {
return $this->getChoices($options)[Tag::TYPE_DRESS_CODE];
}),
'choice_translation_parameters' => function (TagCount $choice): array {
return [
'{ choice }' => $choice->getName(),
'{ count }' => $choice->getCount(),
];
},
'attr' => [
'class' => 'js-search-dress-code',
'data-allow-clear' => 'true',
'autocomplete' => 'off',
],
'required' => false,
])->add('character', ChoiceType::class, [
'label' => 'search.character',
'choice_value' => 'id',
'placeholder' => 'search.choose_value_character',
'choice_label' => function (): string {
return 'search.choice';
},
'choice_loader' => new CallbackChoiceLoader(function () use ($options): array {
return $this->getChoices($options)[Tag::TYPE_CHARACTER];
}),
'attr' => [
'class' => 'js-search-character',
'data-allow-clear' => 'true',
'autocomplete' => 'off',
],
'choice_translation_parameters' => function (TagCount $choice): array {
return [
'{ choice }' => $choice->getName(),
'{ count }' => $choice->getCount(),
];
},
'required' => false,
]);
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'translation_domain' => 'frontend',
'requested_data' => [
'category' => null,
'dress_code' => null,
'character' => null,
],
]);
$resolver->setRequired('target_entity');
$resolver->setAllowedTypes('target_entity', ['string']);
$resolver->setAllowedTypes('requested_data', ['array']);
$resolver->setDefault('method', 'GET');
}
/**
* @param array<mixed> $options
* @return array<array<TagCount>>
*/
private function getChoices(array $options): array
{
if (null !== $this->choices) {
return $this->choices;
}
if (Deal::class === $options['target_entity']) {
$queryBuilder = $this->dealRepository->filter($this->dealRepository->applyPublicFilters([]));
} elseif (Project::class === $options['target_entity']) {
$queryBuilder = $this->projectRepository->filter($this->projectRepository->applyPublicFilters([]));
} elseif (ShopVideo::class === $options['target_entity']) {
$queryBuilder = $this->shopVideoRepository->filter($this->shopVideoRepository->applyPublicFilters([]));
} else {
throw new InvalidArgumentException('Unknown target_entity.');
}
$requestedCategory = $requestedDressCode = $requestedCharacter = null;
if (is_array($options['requested_data'])) {
if (is_numeric($options['requested_data']['category'])) {
$requestedCategory = (int) $options['requested_data']['category'];
}
if (is_numeric($options['requested_data']['dress_code'])) {
$requestedDressCode = (int) $options['requested_data']['dress_code'];
}
if (is_numeric($options['requested_data']['character'])) {
$requestedCharacter = (int) $options['requested_data']['character'];
}
}
return $this->choices = $this->tagRepository->getCounts(
$options['target_entity'],
$requestedCategory,
$requestedDressCode,
$requestedCharacter,
$queryBuilder,
$this->cacheDecider
);
}
public function reset(): void
{
$this->choices = null;
}
}