<?php
namespace App\Controller;
use App\Entity\Location\City;
use App\Entity\Saloon\Saloon;
use App\Event\Profile\ProfilesShownEvent;
use App\Form\FilterMapForm;
use App\Repository\CityRepository;
use App\Repository\ProfileRepository;
use App\Repository\ReadModel\ProfileMapReadModel;
use App\Repository\SaloonRepository;
use App\Repository\ServiceRepository;
use App\Service\Features;
use App\Service\ProfileList;
use App\Specification\Profile\ProfileHasMapCoordinates;
use App\Specification\Profile\ProfileIdINOrderedByINValues;
use App\Specification\Profile\ProfileIsLocated;
use App\Specification\QueryModifier\PossibleSaloonAdBoardPlacement;
use App\Specification\QueryModifier\PossibleSaloonPlacementHiding;
use App\Specification\Saloon\SaloonIsNotHidden;
use App\Specification\QueryModifier\SaloonThumbnail;
use App\Specification\Saloon\SaloonIsActive;
use Happyr\DoctrineSpecification\Spec;
use Psr\Cache\CacheItemPoolInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Component\Asset\Packages;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Contracts\Cache\ItemInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class MapController extends AbstractController
{
use ProfileMinPriceTrait;
const MAP_PROFILES_CACHE_ITEM_NAME = 'map_profiles_';
public function __construct(
private ProfileRepository $profileRepository,
private CityRepository $cityRepository,
private Features $features,
private Packages $assetPackage,
private SaloonRepository $saloonRepository,
private ProfileList $profileList,
private EventDispatcherInterface $eventDispatcher,
private ServiceRepository $serviceRepository,
private CacheItemPoolInterface $profilesFilterCache,
) {}
#[ParamConverter("city", converter: "city_converter")]
public function page(City $city): Response
{
return $this->render('Map/page.html.twig', [
'cityUriIdentity' => $city->getUriIdentity(),
'cityLatitude' => $city->getMapCoordinate()->getLatitude(),
'cityLongitude' => $city->getMapCoordinate()->getLongitude(),
'multipleCities' => (int)$this->features->multiple_cities(),
]);
}
public function form(City $city): Response
{
$form = $this->createForm(FilterMapForm::class, null, ['data' => ['city_id' => $city->getId()]]);
return $this->render('Map/form.html.twig', [
'form' => $form->createView(),
]);
}
public function filter(Request $request, TranslatorInterface $translator): Response
{
$params = json_decode($request->request->get('form'), true);
$form = $this->createForm(FilterMapForm::class);
$form->submit($params);
$city = $this->cityRepository->find($params['city_id']);
$services = $this->serviceRepository->allIndexedById();
$profiles = $this->profileList->list(
$city, null, $form->getData(), [new ProfileHasMapCoordinates()], true, null,
ProfileList::ORDER_NONE, null, false, [$this, 'cachedMapProfilesResultByIds']
);
$out = [];
foreach ($profiles as /** @var ProfileMapReadModel $profile */$profile) {
if(!$profile->mapLatitude || !$profile->mapLongitude)
continue;
$path = $profile->avatar['path'];
$path = str_starts_with($path, '/') ? $path : substr($path, 6, -4);
$hasApartment = $profile->apartmentOneHourPrice || $profile->apartmentTwoHoursPrice || $profile->apartmentNightPrice;
$hasTakeout = $profile->takeOutOneHourPrice || $profile->takeOutTwoHoursPrice || $profile->takeOutNightPrice;
$tags = [];
if($hasApartment && !$hasTakeout)
$tags[] = 1;
elseif(!$hasApartment && $hasTakeout)
$tags[] = 2;
elseif ($hasApartment && $hasTakeout)
$tags[] = 3;
foreach ($profile->services as $serviceId) {
$serviceName = mb_strtolower($services[$serviceId]->getName()->getTranslation('ru'));
switch($serviceName) {
case 'секс классический': $tags[] = 4; break;
case 'секс анальный': $tags[] = 5; break;
case 'минет без резинки': $tags[] = 6; break;
case 'куннилингус': $tags[] = 7; break;
case 'окончание в рот': $tags[] = 8; break;
case 'массаж': if(false === in_array(9, $tags)) $tags[] = 9; break;
}
}
$out[] = [
1,
(float)rtrim(substr($profile->mapLatitude, 0, 7), '0'),
(float)rtrim(substr($profile->mapLongitude, 0, 7), '0'),
$profile->uriIdentity,
$profile->name,
$path, //$profile->avatar['path'],
//$profile->avatar['type'] ? 'avatar' : 'photo',
str_replace(' ', '', $profile->phoneNumber),
$profile->station ?? 0,
$profile->apartmentOneHourPrice ?? $profile->takeOutOneHourPrice ?? 0,
$profile->apartmentTwoHoursPrice ?? $profile->takeOutTwoHoursPrice ?? 0,
$profile->apartmentNightPrice ?? $profile->takeOutNightPrice ?? 0,
(int)$profile->isApproved,
(int)$profile->isMasseur,
(int)$profile->hasComments,
(int)$profile->hasSelfies,
(int)$profile->hasVideos,
$profile->age ?? 0,
$profile->breastSize ?? 0,
$profile->height ?? 0,
$profile->weight ?? 0,
$tags,
$profile->id,
];
}
if(count($params) == 2) { //только id города и токен, фильтров нет
$specs = Spec::andX(
$this->features->free_profiles() ? new SaloonIsNotHidden() : new SaloonIsActive(),
new PossibleSaloonPlacementHiding(),
new PossibleSaloonAdBoardPlacement(),
new SaloonThumbnail(),
ProfileIsLocated::withinCity($city)
);
$saloons = $this->saloonRepository->matchingSpecRaw($specs, null, false);
$outSaloons = [];
foreach ($saloons as /** @var Saloon $saloon */ $saloon) {
$photoPath = null !== ($mainPhoto = $saloon->getThumbnail()) ? $mainPhoto->getPath() : '';
$photoPath = str_starts_with($photoPath, '/') ? $photoPath : str_replace(".jpg", "", substr($photoPath, 6));
$outSaloons[] = [
2,
(float)rtrim(substr($saloon->getMapCoordinate()->getLatitude(), 0, 7), '0'),
(float)rtrim(substr($saloon->getMapCoordinate()->getLongitude(), 0, 7), '0'),
$saloon->getUriIdentity(),
$translator->trans($saloon->getName()),
$photoPath,
//'thumb',
str_replace(' ', '', $saloon->getPhoneNumber()),
$saloon->getStations()->count() ? $saloon->getStations()->first()->getId() : 0,
$saloon->getApartmentsPricing()->getOneHourPrice() ?? $saloon->getTakeOutPricing()->getOneHourPrice() ?? 0,
$saloon->getApartmentsPricing()->getTwoHoursPrice() ?? $saloon->getTakeOutPricing()->getTwoHoursPrice() ?? 0,
$saloon->getApartmentsPricing()->getNightPrice() ?? $saloon->getTakeOutPricing()->getNightPrice() ?? 0,
//$saloon->getExpressPricing()->isProvided() ? $saloon->getExpressPricing()->getPrice() ?? 0 : 0,
];
}
$out = array_merge($out, $outSaloons);
}
return $this->json($out);
}
public function processProfileShows(Request $request, ProfileRepository $profileRepository): JsonResponse
{
$id = $request->query->get('id');
$profile = $profileRepository->find($id);
if($profile) {
$this->eventDispatcher->dispatch(new ProfilesShownEvent([$profile->getId()], 'map'), ProfilesShownEvent::NAME);
}
return $this->json([]);
}
public function cachedMapProfilesResultByIds(ProfileIdINOrderedByINValues $specification)
{
$key = sha1(self::MAP_PROFILES_CACHE_ITEM_NAME . implode(',', $specification->getIds()));
return $this->profilesFilterCache->get($key, function (ItemInterface $item) use ($specification) {
return $this->profileRepository->fetchMapProfilesByIds($specification);
});
}
}