src/Form/SearchProfileForm.php line 272

Open in your IDE?
  1. <?php
  2. /**
  3.  * Created by simpson <simpsonwork@gmail.com>
  4.  * Date: 2019-04-05
  5.  * Time: 19:52
  6.  */
  7. namespace App\Form;
  8. use App\Entity\Location\City;
  9. use App\Entity\Location\Station;
  10. use App\Entity\Profile\HairColors;
  11. use App\Entity\Profile\Nationalities;
  12. use App\Entity\Profile\PrivateHaircuts;
  13. use App\Entity\Service;
  14. use App\Entity\TakeOutLocations;
  15. use App\Form\DataTransformer\FormToSpecTransformer;
  16. use App\Form\Type\SearchRangeType;
  17. use App\Service\EntitySortService;
  18. use App\Specification\Profile\ProfileHasApartments;
  19. use App\Specification\Profile\ProfileHasComments;
  20. use App\Specification\Profile\ProfileHasSelfie;
  21. use App\Specification\Profile\ProfileHasVideo;
  22. use App\Specification\Profile\ProfileIsApproved;
  23. use App\Specification\Profile\ProfileIsHavingPiercing;
  24. use App\Specification\Profile\ProfileIsHavingTattoo;
  25. use App\Specification\Profile\ProfileIsLocated;
  26. use App\Specification\Profile\ProfileIsProvidingExpress;
  27. use App\Specification\Profile\ProfileIsProvidingMostOfServices;
  28. use App\Specification\Profile\ProfileIsProvidingOneOfServices;
  29. use App\Specification\Profile\ProfileIsProvidingService;
  30. use App\Specification\Profile\ProfileIsProvidingTakeOut;
  31. use App\Specification\Profile\ProfileIsUpdatedAt;
  32. use App\Specification\Profile\ProfileWithAge;
  33. use App\Specification\Profile\ProfileWithApartmentsNightPrice;
  34. use App\Specification\Profile\ProfileWithApartmentsOneHourPrice;
  35. use App\Specification\Profile\ProfileWithApartmentsTwoHoursPrice;
  36. use App\Specification\Profile\ProfileWithBreastSize;
  37. use App\Specification\Profile\ProfileWithHairColor;
  38. use App\Specification\Profile\ProfileWithHeight;
  39. use App\Specification\Profile\ProfileWithNationality;
  40. use App\Specification\Profile\ProfileWithPrivateHaircut;
  41. use App\Specification\Profile\ProfileWithTakeOutOneHourPrice;
  42. use App\Specification\Profile\ProfileWithWeight;
  43. use Doctrine\ORM\EntityRepository;
  44. use Happyr\DoctrineSpecification\Spec;
  45. use Symfony\Bridge\Doctrine\Form\Type\EntityType;
  46. use Symfony\Component\Form\AbstractType;
  47. use Symfony\Component\Form\CallbackTransformer;
  48. use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
  49. use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
  50. use Symfony\Component\Form\Extension\Core\Type\SubmitType;
  51. use Symfony\Component\Form\FormBuilderInterface;
  52. use Symfony\Component\OptionsResolver\OptionsResolver;
  53. class SearchProfileForm extends AbstractType
  54. {
  55.     public function __construct(
  56.         private EntitySortService $sortService
  57.     ) {}
  58.     public function buildForm(FormBuilderInterface $builder, array $options): void
  59.     {
  60.         $builder->add(
  61.             $builder
  62.                 ->create('age'SearchRangeType::class)
  63.                 ->addModelTransformer($this->modelReverseTransformer(function (array $value): ?ProfileWithAge {
  64.                     if (!$value || (null === $value['min'] && null === $value['max'])) {
  65.                         return null;
  66.                     }
  67.                     return new ProfileWithAge($value['min'], $value['max']);
  68.                 }))
  69.         );
  70.         $builder->add(
  71.             $builder
  72.                 ->create('breast_size'SearchRangeType::class)
  73.                 ->addModelTransformer($this->modelReverseTransformer(function (array $value): ?ProfileWithBreastSize {
  74.                     if (!$value || (null === $value['min'] && null === $value['max'])) {
  75.                         return null;
  76.                     }
  77.                     return new ProfileWithBreastSize($value['min'], $value['max']);
  78.                 }))
  79.         );
  80.         $builder->add(
  81.             $builder
  82.                 ->create('height'SearchRangeType::class)
  83.                 ->addModelTransformer($this->modelReverseTransformer(function (array $value): ?ProfileWithHeight {
  84.                     if (!$value || (null === $value['min'] && null === $value['max'])) {
  85.                         return null;
  86.                     }
  87.                     return new ProfileWithHeight($value['min'], $value['max']);
  88.                 }))
  89.         );
  90.         $builder->add(
  91.             $builder
  92.                 ->create('weight'SearchRangeType::class)
  93.                 ->addModelTransformer($this->modelReverseTransformer(function (array $value): ?ProfileWithWeight {
  94.                     if (!$value || (null === $value['min'] && null === $value['max'])) {
  95.                         return null;
  96.                     }
  97.                     return new ProfileWithWeight($value['min'], $value['max']);
  98.                 }))
  99.         );
  100.         $builder->add(
  101.             $builder
  102.                 ->create('one_hour_price'SearchRangeType::class)
  103.                 ->addModelTransformer($this->modelReverseTransformer(function (array $value): ?\Happyr\DoctrineSpecification\Logic\OrX {
  104.                     if (!$value || (null === $value['min'] && null === $value['max'])) {
  105.                         return null;
  106.                     }
  107.                     return Spec::orX(new ProfileWithApartmentsOneHourPrice($value['min'], $value['max']), new ProfileWithTakeOutOneHourPrice($value['min'], $value['max']));
  108.                 }))
  109.         );
  110.         $builder->add(
  111.             $builder
  112.                 ->create('two_hours_price'SearchRangeType::class)
  113.                 ->addModelTransformer($this->modelReverseTransformer(function (array $value): ?ProfileWithApartmentsTwoHoursPrice {
  114.                     if (!$value || (null === $value['min'] && null === $value['max'])) {
  115.                         return null;
  116.                     }
  117.                     return new ProfileWithApartmentsTwoHoursPrice($value['min'], $value['max']);
  118.                 }))
  119.         );
  120.         $builder->add(
  121.             $builder
  122.                 ->create('night_price'SearchRangeType::class)
  123.                 ->addModelTransformer($this->modelReverseTransformer(function (array $value): ?ProfileWithApartmentsNightPrice {
  124.                     if (!$value || (null === $value['min'] && null === $value['max'])) {
  125.                         return null;
  126.                     }
  127.                     return new ProfileWithApartmentsNightPrice($value['min'], $value['max']);
  128.                 }))
  129.         );
  130.         $builder->add(
  131.             $builder
  132.                 ->create('approved'CheckboxType::class, ['required' => false])
  133.                 ->addModelTransformer($this->modelReverseTransformer(function ($value): ?ProfileIsApproved {
  134.                     if (!$value) {
  135.                         return null;
  136.                     }
  137.                     return new ProfileIsApproved();
  138.                 }))
  139.         );
  140.         $builder->add(
  141.             $builder
  142.                 ->create('with_video'CheckboxType::class, ['required' => false])
  143.                 ->addModelTransformer($this->modelReverseTransformer(function ($value): ?ProfileHasVideo {
  144.                     if (!$value) {
  145.                         return null;
  146.                     }
  147.                     return new ProfileHasVideo();
  148.                 }))
  149.         );
  150.         $builder->add(
  151.             $builder
  152.                 ->create('with_selfie'CheckboxType::class, ['required' => false])
  153.                 ->addModelTransformer($this->modelReverseTransformer(function ($value): ?ProfileHasSelfie {
  154.                     if (!$value) {
  155.                         return null;
  156.                     }
  157.                     return new ProfileHasSelfie();
  158.                 }))
  159.         );
  160.         $builder->add(
  161.             $builder
  162.                 ->create('with_comments'CheckboxType::class, ['required' => false])
  163.                 ->addModelTransformer($this->modelReverseTransformer(function ($value): ?ProfileHasComments {
  164.                     if (!$value) {
  165.                         return null;
  166.                     }
  167.                     return new ProfileHasComments();
  168.                 }))
  169.         );
  170.         $builder->add(
  171.             $builder
  172.                 ->create('has_tattoo'CheckboxType::class, ['required' => false])
  173.                 ->addModelTransformer($this->modelReverseTransformer(function ($value): ?ProfileIsHavingTattoo {
  174.                     if (!$value) {
  175.                         return null;
  176.                     }
  177.                     return new ProfileIsHavingTattoo(true);
  178.                 }))
  179.         );
  180.         $builder->add(
  181.             $builder
  182.                 ->create('has_piercing'CheckboxType::class, ['required' => false])
  183.                 ->addModelTransformer($this->modelReverseTransformer(function ($value): ?ProfileIsHavingPiercing {
  184.                     if (!$value) {
  185.                         return null;
  186.                     }
  187.                     return new ProfileIsHavingPiercing(true);
  188.                 }))
  189.         );
  190.         $builder->add(
  191.             $builder
  192.                 ->create('has_express'CheckboxType::class, ['required' => false])
  193.                 ->addModelTransformer($this->modelReverseTransformer(function ($value): ?ProfileIsProvidingExpress {
  194.                     if (!$value) {
  195.                         return null;
  196.                     }
  197.                     return new ProfileIsProvidingExpress();
  198.                 }))
  199.         );
  200.         $city $options['city'];
  201.         $builder->add(
  202.             $builder
  203.                 ->create('station'EntityType::class, [
  204.                     'required' => false,
  205.                     'multiple' => true,
  206.                     'expanded' => false,
  207.                     'placeholder' => 'Выберите',
  208.                     'class' => Station::class,
  209.                     'choice_label' => 'name',
  210.                     'query_builder' => function (EntityRepository $repo) use ($city) {
  211.                         $qb $repo->createQueryBuilder('station')
  212.                             ->where('station.city = :city')
  213.                             ->setParameter('city'$city)
  214.                             ->orderBy('station.name')
  215.                             ;
  216.                         $this->sortService->modifyQueryBuilderToSortByCurrentTranslation($qb'station''name');
  217.                         return $qb;
  218.                     },
  219.                 ])
  220.                 ->addModelTransformer($this->modelReverseTransformer(function ($values): ?\Happyr\DoctrineSpecification\Logic\OrX {
  221.                     if (!$values) {
  222.                         return null;
  223.                     }
  224.                     $specifications array_map(function ($value): ProfileIsLocated {
  225.                         return ProfileIsLocated::nearStation($value);
  226.                     }, $values->toArray());
  227.                     return Spec::orX(...$specifications);
  228.                 }))
  229.         );
  230.         $builder->add(
  231.             $builder
  232.                 ->create('recency'ChoiceType::class, [
  233.                     'required' => false,
  234.                     'multiple' => false,
  235.                     'expanded' => false,
  236.                     'placeholder' => 'Выберите',
  237.                     'choices' => [
  238.                         'За 24 часа' => ProfileIsUpdatedAt::WITHIN_DAY,
  239.                         'За неделю' => ProfileIsUpdatedAt::WITHIN_WEEK,
  240.                         'За 2 недели' => ProfileIsUpdatedAt::WITHIN_TWO_WEEKS,
  241.                         'За месяц' => ProfileIsUpdatedAt::WITHIN_MONTH,
  242.                     ],
  243.                 ])
  244.                 ->addModelTransformer($this->modelReverseTransformer(function ($value): ?ProfileIsUpdatedAt {
  245.                     if (!$value) {
  246.                         return null;
  247.                     }
  248.                     return ProfileIsUpdatedAt::within($value);
  249.                 }))
  250.         );
  251.         $builder->add(
  252.             $builder
  253.                 ->create('services'EntityType::class, [
  254.                     'required' => false,
  255.                     'multiple' => true,
  256.                     'expanded' => true,
  257.                     'class' => Service::class,
  258.                     'choice_label' => 'name',
  259.                     'choice_attr' => function ($choice$key$value): array {
  260.                         return ['service-group' => $choice->getGroup()];
  261.                     },
  262.                     'query_builder' => function (EntityRepository $repo) {
  263.                         return $repo->createQueryBuilder('service')
  264.                             ->addOrderBy('service.group')
  265.                             ->addOrderBy('service.id')
  266.                             ;
  267.                     },
  268.                 ])
  269.                 ->addModelTransformer($this->modelReverseTransformer(function ($value): ?ProfileIsProvidingMostOfServices {
  270.                     if (!$value) {
  271.                         return null;
  272.                     }
  273.                     return new ProfileIsProvidingMostOfServices($value->toArray());
  274.                 }))
  275.         );
  276.         $builder->add(
  277.             $builder
  278.                 ->create('hair_color'ChoiceType::class, [
  279.                     'required' => false,
  280.                     'multiple' => true,
  281.                     'expanded' => true,
  282.                     'choices' => HairColors::getList(),
  283.                 ])
  284.                 ->addModelTransformer($this->modelReverseTransformer(function ($values): ?\Happyr\DoctrineSpecification\Logic\OrX {
  285.                     if (!$values) {
  286.                         return null;
  287.                     }
  288.                     $specifications array_map(function ($value): ProfileWithHairColor {
  289.                         return new ProfileWithHairColor($value);
  290.                     }, $values);
  291.                     return Spec::orX(...$specifications);
  292.                 }))
  293.         );
  294.         $builder->add(
  295.             $builder
  296.                 ->create('private_haircut'ChoiceType::class, [
  297.                     'required' => false,
  298.                     'multiple' => true,
  299.                     'expanded' => true,
  300.                     'choices' => PrivateHaircuts::getList(),
  301.                 ])
  302.                 ->addModelTransformer($this->modelReverseTransformer(function ($values): ?\Happyr\DoctrineSpecification\Logic\OrX {
  303.                     if (!$values) {
  304.                         return null;
  305.                     }
  306.                     $specifications array_map(function ($value): ProfileWithPrivateHaircut {
  307.                         return new ProfileWithPrivateHaircut($value);
  308.                     }, $values);
  309.                     return Spec::orX(...$specifications);
  310.                 }))
  311.         );
  312.         $builder->add(
  313.             $builder
  314.                 ->create('nationality'ChoiceType::class, [
  315.                     'required' => false,
  316.                     'multiple' => true,
  317.                     'expanded' => true,
  318.                     'choices' => Nationalities::getList(),
  319.                 ])
  320.                 ->addModelTransformer($this->modelReverseTransformer(function ($values): ?\Happyr\DoctrineSpecification\Logic\OrX {
  321.                     if (!$values) {
  322.                         return null;
  323.                     }
  324.                     $specifications array_map(function ($value): ProfileWithNationality {
  325.                         return new ProfileWithNationality($value);
  326.                     }, $values);
  327.                     return Spec::orX(...$specifications);
  328.                 }))
  329.         );
  330.         $builder->add(
  331.             $builder
  332.                 ->create('apartments'CheckboxType::class, ['required' => false])
  333.                 ->addModelTransformer($this->modelReverseTransformer(function ($value): ?ProfileHasApartments {
  334.                     if (!$value) {
  335.                         return null;
  336.                     }
  337.                     return new ProfileHasApartments();
  338.                 }))
  339.         );
  340.         $builder->add(
  341.             $builder
  342.                 ->create('take_out_location'ChoiceType::class, [
  343.                     'required' => false,
  344.                     'multiple' => true,
  345.                     'expanded' => true,
  346.                     'choices' => TakeOutLocations::getList(),
  347.                 ])
  348.                 ->addModelTransformer($this->modelReverseTransformer(function ($values): ?\Happyr\DoctrineSpecification\Logic\OrX {
  349.                     if (!$values) {
  350.                         return null;
  351.                     }
  352.                     $specifications array_map(function ($value): ProfileIsProvidingTakeOut {
  353.                         return new ProfileIsProvidingTakeOut($value);
  354.                     }, $values);
  355.                     return Spec::orX(...$specifications);
  356.                 }))
  357.         );
  358.         $builder->add('search'SubmitType::class);
  359.         $builder->addModelTransformer(new FormToSpecTransformer());
  360.     }
  361.     public function configureOptions(OptionsResolver $resolver): void
  362.     {
  363.         $resolver->setRequired('city');
  364.         $resolver->setAllowedTypes('city'City::class);
  365.     }
  366.     private function modelReverseTransformer(callable $reverseTransformer): CallbackTransformer
  367.     {
  368.         return new CallbackTransformer(
  369.             function ($value) {
  370.                 return $value;
  371.             },
  372.             $reverseTransformer
  373.         );
  374.     }
  375. }