<?php
/**
* Created by simpson <simpsonwork@gmail.com>
* Date: 2019-04-05
* Time: 19:52
*/
namespace App\Form;
use App\Entity\Location\City;
use App\Entity\Location\Station;
use App\Entity\Profile\HairColors;
use App\Entity\Profile\Nationalities;
use App\Entity\Profile\PrivateHaircuts;
use App\Entity\Service;
use App\Entity\TakeOutLocations;
use App\Form\DataTransformer\FormToSpecTransformer;
use App\Form\Type\SearchRangeType;
use App\Service\EntitySortService;
use App\Specification\Profile\ProfileHasApartments;
use App\Specification\Profile\ProfileHasComments;
use App\Specification\Profile\ProfileHasSelfie;
use App\Specification\Profile\ProfileHasVideo;
use App\Specification\Profile\ProfileIsApproved;
use App\Specification\Profile\ProfileIsHavingPiercing;
use App\Specification\Profile\ProfileIsHavingTattoo;
use App\Specification\Profile\ProfileIsLocated;
use App\Specification\Profile\ProfileIsProvidingExpress;
use App\Specification\Profile\ProfileIsProvidingMostOfServices;
use App\Specification\Profile\ProfileIsProvidingOneOfServices;
use App\Specification\Profile\ProfileIsProvidingService;
use App\Specification\Profile\ProfileIsProvidingTakeOut;
use App\Specification\Profile\ProfileIsUpdatedAt;
use App\Specification\Profile\ProfileWithAge;
use App\Specification\Profile\ProfileWithApartmentsNightPrice;
use App\Specification\Profile\ProfileWithApartmentsOneHourPrice;
use App\Specification\Profile\ProfileWithApartmentsTwoHoursPrice;
use App\Specification\Profile\ProfileWithBreastSize;
use App\Specification\Profile\ProfileWithHairColor;
use App\Specification\Profile\ProfileWithHeight;
use App\Specification\Profile\ProfileWithNationality;
use App\Specification\Profile\ProfileWithPrivateHaircut;
use App\Specification\Profile\ProfileWithTakeOutOneHourPrice;
use App\Specification\Profile\ProfileWithWeight;
use Doctrine\ORM\EntityRepository;
use Happyr\DoctrineSpecification\Spec;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\CallbackTransformer;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class SearchProfileForm extends AbstractType
{
public function __construct(
private EntitySortService $sortService
) {}
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder->add(
$builder
->create('age', SearchRangeType::class)
->addModelTransformer($this->modelReverseTransformer(function (array $value): ?ProfileWithAge {
if (!$value || (null === $value['min'] && null === $value['max'])) {
return null;
}
return new ProfileWithAge($value['min'], $value['max']);
}))
);
$builder->add(
$builder
->create('breast_size', SearchRangeType::class)
->addModelTransformer($this->modelReverseTransformer(function (array $value): ?ProfileWithBreastSize {
if (!$value || (null === $value['min'] && null === $value['max'])) {
return null;
}
return new ProfileWithBreastSize($value['min'], $value['max']);
}))
);
$builder->add(
$builder
->create('height', SearchRangeType::class)
->addModelTransformer($this->modelReverseTransformer(function (array $value): ?ProfileWithHeight {
if (!$value || (null === $value['min'] && null === $value['max'])) {
return null;
}
return new ProfileWithHeight($value['min'], $value['max']);
}))
);
$builder->add(
$builder
->create('weight', SearchRangeType::class)
->addModelTransformer($this->modelReverseTransformer(function (array $value): ?ProfileWithWeight {
if (!$value || (null === $value['min'] && null === $value['max'])) {
return null;
}
return new ProfileWithWeight($value['min'], $value['max']);
}))
);
$builder->add(
$builder
->create('one_hour_price', SearchRangeType::class)
->addModelTransformer($this->modelReverseTransformer(function (array $value): ?\Happyr\DoctrineSpecification\Logic\OrX {
if (!$value || (null === $value['min'] && null === $value['max'])) {
return null;
}
return Spec::orX(new ProfileWithApartmentsOneHourPrice($value['min'], $value['max']), new ProfileWithTakeOutOneHourPrice($value['min'], $value['max']));
}))
);
$builder->add(
$builder
->create('two_hours_price', SearchRangeType::class)
->addModelTransformer($this->modelReverseTransformer(function (array $value): ?ProfileWithApartmentsTwoHoursPrice {
if (!$value || (null === $value['min'] && null === $value['max'])) {
return null;
}
return new ProfileWithApartmentsTwoHoursPrice($value['min'], $value['max']);
}))
);
$builder->add(
$builder
->create('night_price', SearchRangeType::class)
->addModelTransformer($this->modelReverseTransformer(function (array $value): ?ProfileWithApartmentsNightPrice {
if (!$value || (null === $value['min'] && null === $value['max'])) {
return null;
}
return new ProfileWithApartmentsNightPrice($value['min'], $value['max']);
}))
);
$builder->add(
$builder
->create('approved', CheckboxType::class, ['required' => false])
->addModelTransformer($this->modelReverseTransformer(function ($value): ?ProfileIsApproved {
if (!$value) {
return null;
}
return new ProfileIsApproved();
}))
);
$builder->add(
$builder
->create('with_video', CheckboxType::class, ['required' => false])
->addModelTransformer($this->modelReverseTransformer(function ($value): ?ProfileHasVideo {
if (!$value) {
return null;
}
return new ProfileHasVideo();
}))
);
$builder->add(
$builder
->create('with_selfie', CheckboxType::class, ['required' => false])
->addModelTransformer($this->modelReverseTransformer(function ($value): ?ProfileHasSelfie {
if (!$value) {
return null;
}
return new ProfileHasSelfie();
}))
);
$builder->add(
$builder
->create('with_comments', CheckboxType::class, ['required' => false])
->addModelTransformer($this->modelReverseTransformer(function ($value): ?ProfileHasComments {
if (!$value) {
return null;
}
return new ProfileHasComments();
}))
);
$builder->add(
$builder
->create('has_tattoo', CheckboxType::class, ['required' => false])
->addModelTransformer($this->modelReverseTransformer(function ($value): ?ProfileIsHavingTattoo {
if (!$value) {
return null;
}
return new ProfileIsHavingTattoo(true);
}))
);
$builder->add(
$builder
->create('has_piercing', CheckboxType::class, ['required' => false])
->addModelTransformer($this->modelReverseTransformer(function ($value): ?ProfileIsHavingPiercing {
if (!$value) {
return null;
}
return new ProfileIsHavingPiercing(true);
}))
);
$builder->add(
$builder
->create('has_express', CheckboxType::class, ['required' => false])
->addModelTransformer($this->modelReverseTransformer(function ($value): ?ProfileIsProvidingExpress {
if (!$value) {
return null;
}
return new ProfileIsProvidingExpress();
}))
);
$city = $options['city'];
$builder->add(
$builder
->create('station', EntityType::class, [
'required' => false,
'multiple' => true,
'expanded' => false,
'placeholder' => 'Выберите',
'class' => Station::class,
'choice_label' => 'name',
'query_builder' => function (EntityRepository $repo) use ($city) {
$qb = $repo->createQueryBuilder('station')
->where('station.city = :city')
->setParameter('city', $city)
->orderBy('station.name')
;
$this->sortService->modifyQueryBuilderToSortByCurrentTranslation($qb, 'station', 'name');
return $qb;
},
])
->addModelTransformer($this->modelReverseTransformer(function ($values): ?\Happyr\DoctrineSpecification\Logic\OrX {
if (!$values) {
return null;
}
$specifications = array_map(function ($value): ProfileIsLocated {
return ProfileIsLocated::nearStation($value);
}, $values->toArray());
return Spec::orX(...$specifications);
}))
);
$builder->add(
$builder
->create('recency', ChoiceType::class, [
'required' => false,
'multiple' => false,
'expanded' => false,
'placeholder' => 'Выберите',
'choices' => [
'За 24 часа' => ProfileIsUpdatedAt::WITHIN_DAY,
'За неделю' => ProfileIsUpdatedAt::WITHIN_WEEK,
'За 2 недели' => ProfileIsUpdatedAt::WITHIN_TWO_WEEKS,
'За месяц' => ProfileIsUpdatedAt::WITHIN_MONTH,
],
])
->addModelTransformer($this->modelReverseTransformer(function ($value): ?ProfileIsUpdatedAt {
if (!$value) {
return null;
}
return ProfileIsUpdatedAt::within($value);
}))
);
$builder->add(
$builder
->create('services', EntityType::class, [
'required' => false,
'multiple' => true,
'expanded' => true,
'class' => Service::class,
'choice_label' => 'name',
'choice_attr' => function ($choice, $key, $value): array {
return ['service-group' => $choice->getGroup()];
},
'query_builder' => function (EntityRepository $repo) {
return $repo->createQueryBuilder('service')
->addOrderBy('service.group')
->addOrderBy('service.id')
;
},
])
->addModelTransformer($this->modelReverseTransformer(function ($value): ?ProfileIsProvidingMostOfServices {
if (!$value) {
return null;
}
return new ProfileIsProvidingMostOfServices($value->toArray());
}))
);
$builder->add(
$builder
->create('hair_color', ChoiceType::class, [
'required' => false,
'multiple' => true,
'expanded' => true,
'choices' => HairColors::getList(),
])
->addModelTransformer($this->modelReverseTransformer(function ($values): ?\Happyr\DoctrineSpecification\Logic\OrX {
if (!$values) {
return null;
}
$specifications = array_map(function ($value): ProfileWithHairColor {
return new ProfileWithHairColor($value);
}, $values);
return Spec::orX(...$specifications);
}))
);
$builder->add(
$builder
->create('private_haircut', ChoiceType::class, [
'required' => false,
'multiple' => true,
'expanded' => true,
'choices' => PrivateHaircuts::getList(),
])
->addModelTransformer($this->modelReverseTransformer(function ($values): ?\Happyr\DoctrineSpecification\Logic\OrX {
if (!$values) {
return null;
}
$specifications = array_map(function ($value): ProfileWithPrivateHaircut {
return new ProfileWithPrivateHaircut($value);
}, $values);
return Spec::orX(...$specifications);
}))
);
$builder->add(
$builder
->create('nationality', ChoiceType::class, [
'required' => false,
'multiple' => true,
'expanded' => true,
'choices' => Nationalities::getList(),
])
->addModelTransformer($this->modelReverseTransformer(function ($values): ?\Happyr\DoctrineSpecification\Logic\OrX {
if (!$values) {
return null;
}
$specifications = array_map(function ($value): ProfileWithNationality {
return new ProfileWithNationality($value);
}, $values);
return Spec::orX(...$specifications);
}))
);
$builder->add(
$builder
->create('apartments', CheckboxType::class, ['required' => false])
->addModelTransformer($this->modelReverseTransformer(function ($value): ?ProfileHasApartments {
if (!$value) {
return null;
}
return new ProfileHasApartments();
}))
);
$builder->add(
$builder
->create('take_out_location', ChoiceType::class, [
'required' => false,
'multiple' => true,
'expanded' => true,
'choices' => TakeOutLocations::getList(),
])
->addModelTransformer($this->modelReverseTransformer(function ($values): ?\Happyr\DoctrineSpecification\Logic\OrX {
if (!$values) {
return null;
}
$specifications = array_map(function ($value): ProfileIsProvidingTakeOut {
return new ProfileIsProvidingTakeOut($value);
}, $values);
return Spec::orX(...$specifications);
}))
);
$builder->add('search', SubmitType::class);
$builder->addModelTransformer(new FormToSpecTransformer());
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setRequired('city');
$resolver->setAllowedTypes('city', City::class);
}
private function modelReverseTransformer(callable $reverseTransformer): CallbackTransformer
{
return new CallbackTransformer(
function ($value) {
return $value;
},
$reverseTransformer
);
}
}