src/Controller/ProfileFilterController.php line 60

Open in your IDE?
  1. <?php
  2. namespace App\Controller;
  3. use App\Dto\ProfileFilterFormData;
  4. use App\Dto\ProfileFilterFormDataFilters;
  5. use App\Dto\ProfileListSpecification;
  6. use App\Entity\Location\City;
  7. use App\Form\ProfileFilterForm;
  8. use App\Service\Filter\FilterModeResolver;
  9. use App\Service\Map\ProfileMapFilterService;
  10. use App\Service\ProfileFiltersCatalogBuilder;
  11. use App\Service\ProfileFiltersQueryResolver;
  12. use App\Service\ProfileList;
  13. use App\Service\ProfileListingDataCreator;
  14. use App\Service\ProfileListingRecommendationsFiller;
  15. use App\Service\ProfileFilterService;
  16. use OpenApi\Attributes as OA;
  17. use Porpaginas\Page;
  18. use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
  19. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  20. use Symfony\Component\HttpFoundation\JsonResponse;
  21. use Symfony\Component\HttpFoundation\Request;
  22. use Symfony\Component\HttpFoundation\Response;
  23. use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
  24. use Symfony\Component\Routing\Annotation\Route;
  25. use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
  26. class ProfileFilterController extends AbstractController
  27. {
  28.     use ResponseTrait;
  29.     public function __construct(
  30.         private ProfileListingDataCreator $profileListingDataCreator,
  31.         private ProfileFilterService $profilesFilterService,
  32.         private ProfileList $profileList,
  33.         private ProfileListingRecommendationsFiller $profileListingRecommendationsFiller,
  34.         private ProfileFiltersCatalogBuilder $filtersCatalogBuilder,
  35.         private ProfileFiltersQueryResolver $filtersQueryResolver,
  36.         private FilterModeResolver $filterModeResolver,
  37.         private ProfileMapFilterService $profileMapFilterService,
  38.         private NormalizerInterface $normalizer,
  39.     ) {}
  40.     #[ParamConverter("city"converter"city_converter")]
  41.     public function form(City $cityRequest $request): Response
  42.     {
  43.         if(!$request->isXmlHttpRequest()) {
  44.             throw new BadRequestHttpException();
  45.         }
  46.         $listing $request->request->get('listing');
  47.         $listingParameters json_decode($request->request->get('listingParams'), true);
  48.         $formData = new ProfileFilterFormData($listing$listingParameters, new ProfileFilterFormDataFilters());
  49.         $listingData $this->profileListingDataCreator->getListingDataForFilter($listing$listingParameters$city);
  50.         /** @var ProfileListSpecification $specs */
  51.         $specs $listingData['specs'];
  52.         //холостой вызов, чтобы сформировался запрос и записался в свойство ProfileList
  53.         $this->profileList->listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacement($city$specs->spec(), $specs->additionalSpecs(), $specs->genders());
  54.         $this->profilesFilterService->checkProfilesPresenceByFilters($city$listingData['listingTypeName']);
  55.         $profilePresenceByFilters $this->profilesFilterService->profilesPresenceByFilters();
  56.         $form $this->createForm(ProfileFilterForm::class, $formData, [
  57.             'action'          => $this->generateUrl('profile_filters.filter', ['city' => $city->getUriIdentity()]),
  58.             'csrf_protection' => false,
  59.             'form_data' => $formData,
  60.             'profiles_presence_by_filters' => $profilePresenceByFilters,
  61.         ]);
  62.         return $this->prepareForXhr($this->render('ProfileList/filter/form.html.twig', [
  63.             'profiles_presence_by_filters' => $profilePresenceByFilters,
  64.             'profile_filter_form' => $form->createView(),
  65.         ]));
  66.     }
  67.     #[ParamConverter("city"converter"city_converter")]
  68.     public function filter(City $cityRequest $requestProfileFilterService $profilesFilterService): Response
  69.     {
  70.         if(!$request->isXmlHttpRequest()) {
  71.             throw new BadRequestHttpException();
  72.         }
  73.         $form $this->createForm(ProfileFilterForm::class, new ProfileFilterFormData('', [], new ProfileFilterFormDataFilters()), [
  74.             'action' => $this->generateUrl('profile_filters.filter', ['city' => $city->getUriIdentity()]),
  75.             'csrf_protection' => false,
  76.         ]);
  77.         $form->handleRequest($request);
  78.         if (false === $form->isSubmitted() || false === $form->isValid()) {
  79.             throw new BadRequestHttpException('Form is not submitted');
  80.         }
  81.         /** @var ProfileFilterFormData $formData */
  82.         $formData $form->getData();
  83. //        $specs = $this->profileListSpecificationService->{$listingType}(...$listingArgs);
  84.         $listingData $this->profileListingDataCreator->getListingDataForFilter($formData->listing$formData->listingParameters$city);
  85.         $specs $listingData['specs'];
  86.         $listingTypeName $listingData['listingTypeName'];
  87.         $result $profilesFilterService->filter($city$listingTypeName$formData->filters$specs->spec(), $specs->additionalSpecs(), $specs->genders());
  88.         $presence $profilesFilterService->profilesPresenceByFilters();
  89.         $recommendationsFill = [
  90.             'profiles' => $result,
  91.             'applied' => false,
  92.             'original_count' => $result instanceof Page $result->totalCount() : 0,
  93.         ];
  94.         if ($result instanceof Page) {
  95.             $recommendationsFill $this->profileListingRecommendationsFiller->fill($city$result$specs->genders());
  96.         }
  97.         $form $this->createForm(ProfileFilterForm::class, $formData, [
  98.             'action' => $this->generateUrl('profile_filters.filter', ['city' => $city->getUriIdentity()]),
  99.             'csrf_protection' => false,
  100.             'form_data' => $formData,
  101.             'profiles_presence_by_filters' => $presence,
  102.         ]);
  103.         return $this->prepareForXhr($this->render('ProfileList/profile_filter.profiles.partial.html.twig', [
  104.             'profiles' => $recommendationsFill['profiles'],
  105.             'recommendationSpec' => \App\Specification\ElasticSearch\ProfileIsLocated::withinCity($city),
  106.             'recommendations_fill_applied' => $recommendationsFill['applied'],
  107.             'recommendations_fill_original_count' => $recommendationsFill['original_count'],
  108.             'show_pagination' => true,
  109.             'profiles_presence_by_filters' => $presence,
  110.             'profile_filter_form' => $form->createView(),
  111.         ]));
  112.     }
  113.     #[Route("/api/profiles/filter"name"api.profile_filter"methods: ["GET"], format"json")]
  114.     #[OA\Tag(name"ProfileFilter")]
  115.     #[OA\Parameter(name"page"in"query"schema: new OA\Schema(type"integer", default: 1minimum1))]
  116.     #[OA\Parameter(name"mode"in"query"description"Response mode: list (default) or map"schema: new OA\Schema(type"string"enum: ["list""map"], default: "list"))]
  117.     #[OA\Parameter(name"scale"in"query"description"Map zoom level (0–21), used only with mode=map"schema: new OA\Schema(type"integer", default: 0minimum0maximum21))]
  118.     #[OA\Parameter(name"city"in"query"description"City id or uriIdentity (required for search)"schema: new OA\Schema(oneOf: [new OA\Schema(type"integer"), new OA\Schema(type"string")]))]
  119.     #[OA\Response(
  120.         response200,
  121.         description'Filter catalog (no search params) or search results',
  122.         content: new OA\JsonContent(
  123.             oneOf: [
  124.                 new OA\Schema(ref"#/components/schemas/ProfileFiltersCatalog"),
  125.                 new OA\Schema(ref"#/components/schemas/ProfileFilteredListingPage"),
  126.                 new OA\Schema(ref"#/components/schemas/ProfileFiltersMapResult"),
  127.             ]
  128.         )
  129.     )]
  130.     #[OA\Response(response400description'Search requested without valid city, or invalid mode/scale')]
  131.     public function filterApi(Request $request): JsonResponse
  132.     {
  133.         if (!$this->filtersQueryResolver->hasSearchParams($request)) {
  134.             return $this->json([
  135.                 'filters' => $this->filtersCatalogBuilder->build(),
  136.             ]);
  137.         }
  138.         $mode $this->filterModeResolver->resolve($request);
  139.         [$city$filters$appliedFilters] = $this->filtersQueryResolver->resolve($request);
  140.         if ($mode->isMap()) {
  141.             $result $this->profileMapFilterService->filter($city$filters$mode->scale());
  142.             $map $this->normalizer->normalize($result);
  143.             if (!is_array($map)) {
  144.                 $map = ['data' => $map];
  145.             }
  146.             return $this->json(array_merge(['filters' => $appliedFilters], $map));
  147.         }
  148.         $listingData $this->profileListingDataCreator->getListingDataForFilter('listByCity', [], $city);
  149.         /** @var ProfileListSpecification $specs */
  150.         $specs $listingData['specs'];
  151.         $result $this->profilesFilterService->filter(
  152.             $city,
  153.             $listingData['listingTypeName'],
  154.             $filters,
  155.             $specs->spec(),
  156.             $specs->additionalSpecs(),
  157.             $specs->genders()
  158.         );
  159.         $page $this->normalizer->normalize($result);
  160.         if (!is_array($page)) {
  161.             $page = ['data' => $page];
  162.         }
  163.         return $this->json(array_merge(['filters' => $appliedFilters], $page));
  164.     }
  165. }