src/Form/SearchProfileForm.php line 64

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