src/Repository/SaloonRepository.php line 87

Open in your IDE?
  1. <?php
  2. /**
  3.  * Created by simpson <simpsonwork@gmail.com>
  4.  * Date: 2019-04-15
  5.  * Time: 20:20
  6.  */
  7. namespace App\Repository;
  8. use App\Entity\Location\City;
  9. use App\Entity\Saloon\Saloon;
  10. use App\Entity\User;
  11. use App\Service\Features;
  12. use App\Service\Map\MapClusterMinPriceDql;
  13. use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
  14. use Doctrine\ORM\AbstractQuery;
  15. use Doctrine\Persistence\ManagerRegistry;
  16. use Doctrine\ORM\QueryBuilder;
  17. use Happyr\DoctrineSpecification\Filter\Filter;
  18. use Happyr\DoctrineSpecification\Query\QueryModifier;
  19. use Porpaginas\Doctrine\ORM\ORMQueryResult;
  20. class SaloonRepository extends ServiceEntityRepository
  21. {
  22.     use SpecificationTrait;
  23.     use EntityIteratorTrait;
  24.     private Features $features;
  25.     public function __construct(ManagerRegistry $registryFeatures $features)
  26.     {
  27.         parent::__construct($registrySaloon::class);
  28.         $this->features $features;
  29.     }
  30.     /**
  31.      * Возвращает итератор по данным, необходимым для генерации файлов sitemap, в виде массивов с
  32.      * следующими ключами:
  33.      *  - id
  34.      *  - uri
  35.      *  - updatedAt
  36.      *  - city_uri
  37.      *
  38.      * @return iterable<array{id: int, uri: string, updatedAt: \DateTimeImmutable, city_uri: string}>
  39.      */
  40.     public function sitemapItemsIterator(): iterable
  41.     {
  42.         $qb $this->createQueryBuilder('saloon')
  43.             ->select('saloon.id, saloon.uriIdentity AS uri, saloon.updatedAt, city.uriIdentity AS city_uri')
  44.             ->join('saloon.city''city')
  45.             ->andWhere('saloon.deletedAt IS NULL');
  46.         return $qb->getQuery()->toIterable([], AbstractQuery::HYDRATE_ARRAY);
  47.     }
  48.     protected function modifyListingQueryBuilder(QueryBuilder $qbstring $alias): void
  49.     {
  50.         $qb
  51.             ->addSelect('city')
  52.             ->addSelect('station')
  53.             ->addSelect('thumbnail')
  54.             ->join(sprintf('%s.city'$alias), 'city')
  55.             ->leftJoin(sprintf('%s.stations'$alias), 'station')
  56.             ->leftJoin(sprintf('%s.thumbnail'$alias), 'thumbnail')
  57.         ;
  58.         $this->excludeHavingPlacementHiding($qb$alias);
  59.         if (!in_array('saloon_adboard_placements'$qb->getAllAliases())) {
  60.             $qb
  61.                 ->leftJoin(sprintf('%s.adBoardPlacement'$alias), 'saloon_adboard_placements')
  62.             ;
  63.         }
  64.         $qb->addSelect('saloon_adboard_placements');
  65.         //if($this->features->free_profiles()) {
  66.         if (!in_array('placement_hiding'$qb->getAllAliases())) {
  67.             $qb
  68.                 ->leftJoin(sprintf('%s.placementHiding'$alias), 'placement_hiding');
  69.         }
  70.         $qb->addSelect('placement_hiding');
  71.         //}
  72.     }
  73.     public function ofUriIdentityWithinCity(string $uriIdentityCity $city): ?Saloon
  74.     {
  75.         return $this->findOneBy([
  76.             'uriIdentity' => $uriIdentity,
  77.             'city' => $city,
  78.         ]);
  79.     }
  80.     /**
  81.      * Метод проверки уникальности анкет по URI не должен использовать никаких фильтров, кроме URI и города,
  82.      * поэтому QueryBuilder не используется
  83.      */
  84.     public function isUniqueUriIdentityExistWithinCity(string $uriIdentityCity $city): bool
  85.     {
  86.         $connection $this->_em->getConnection();
  87.         $stmt $connection->executeQuery('SELECT COUNT(id) FROM saloons WHERE uri_identity = ? AND city_id = ?', [$uriIdentity$city->getId()]);
  88.         $count $stmt->fetchOne();
  89.         return $count 0;
  90.     }
  91.     /**
  92.      * @return Saloon[]
  93.      */
  94.     public function ofOwner(User $owner): array
  95.     {
  96.         return $this->findBy([
  97.             'owner' => $owner,
  98.         ]);
  99.     }
  100.     public function searchLinkableToProfileByOwner(User $owner, ?string $queryint $limit 20): array
  101.     {
  102.         $qb $this->createQueryBuilder('saloon')
  103.             ->leftJoin('saloon.adBoardPlacement''placement')
  104.             ->leftJoin('saloon.thumbnail''thumbnail')
  105.             ->addSelect('placement')
  106.             ->addSelect('thumbnail')
  107.             ->andWhere('saloon.owner = :owner')
  108.             ->setParameter('owner'$owner)
  109.             ->orderBy('saloon.id''DESC')
  110.             ->setMaxResults($limit)
  111.         ;
  112.         if ($query) {
  113.             $qb
  114.                 ->andWhere('LOWER(JSON_UNQUOTE(JSON_EXTRACT(saloon.name, :json_path))) LIKE :query')
  115.                 ->setParameter('json_path''$.ru')
  116.                 ->setParameter('query''%' addcslashes(mb_strtolower(trim($query)), '%_') . '%')
  117.             ;
  118.         }
  119.         return $qb->getQuery()->getResult();
  120.     }
  121.     public function ofOwnerPaged(User $owner): ORMQueryResult
  122.     {
  123.         $qb $this->createQueryBuilder('saloon')
  124.             ->andWhere('saloon.owner = :owner')
  125.             ->setParameter('owner'$owner)
  126.         ;
  127.         return new ORMQueryResult($qb);
  128.     }
  129.     public function idsOfOwner(User $owner): array
  130.     {
  131.         $qb $this->createQueryBuilder('saloon')
  132.             ->select('saloon.id')
  133.             ->andWhere('saloon.owner = :owner')
  134.             ->setParameter('owner'$owner)
  135.         ;
  136.         return $qb->getQuery()->getResult('column_hydrator');
  137.     }
  138.     public function countActiveOfOwner(User $owner): int
  139.     {
  140.         $qb $this->createQueryBuilder('saloon')
  141.             ->select('COUNT(saloon.id)')
  142.             ->join('saloon.adBoardPlacement''saloon_adboard_placement')
  143.             ->andWhere('saloon.owner = :owner')
  144.             ->setParameter('owner'$owner)
  145.         ;
  146.         return (int)$qb->getQuery()->getSingleScalarResult();
  147.     }
  148.     /**
  149.      * Список активных салонов, привязанных к аккаунту
  150.      *
  151.      * @return Saloon[]
  152.      */
  153.     public function activeAndOwnedBy(User $owner): ORMQueryResult
  154.     {
  155.         $qb $this->createQueryBuilder('saloon')
  156.             ->join('saloon.adBoardPlacement''saloon_adboard_placement')
  157.             ->andWhere('saloon.owner = :owner')
  158.             ->setParameter('owner'$owner)
  159.         ;
  160.         return new ORMQueryResult($qb);
  161.     }
  162.     /**
  163.      * Список активных или скрытых салонов, привязанных к аккаунту
  164.      *
  165.      * @return Saloon[]
  166.      */
  167.     public function activeOrHiddenAndOwnedBy(User $owner): ORMQueryResult
  168.     {
  169.         $qb $this->createQueryBuilder('saloon')
  170.             ->leftJoin('saloon.adBoardPlacement''saloon_adboard_placement')
  171.             ->leftJoin('saloon.placementHiding''placement_hiding')
  172.             ->andWhere('saloon_adboard_placement IS NOT NULL OR placement_hiding IS NOT NULL')
  173.             ->andWhere('saloon.owner = :owner')
  174.             ->setParameter('owner'$owner)
  175.         ;
  176.         return new ORMQueryResult($qb);
  177.     }
  178.     /**
  179.      * Число всех салонов, привязанных к аккаунту
  180.      */
  181.     public function countAllOfOwner(User $owner): int
  182.     {
  183.         $qb $this->createQueryBuilder('saloon')
  184.             ->select('COUNT(saloon.id)')
  185.             ->andWhere('saloon.owner = :owner')
  186.             ->setParameter('owner'$owner)
  187.         ;
  188.         return (int)$qb->getQuery()->getSingleScalarResult();
  189.     }
  190.     public function getTimezonesListByUser(User $owner): array
  191.     {
  192.         $q $this->_em->createQuery(sprintf("
  193.                 SELECT c
  194.                 FROM %s c
  195.                 WHERE c.id IN (
  196.                     SELECT DISTINCT(c2.id)
  197.                     FROM %s saloon
  198.                     JOIN saloon.city c2
  199.                     WHERE saloon.owner = :user
  200.                 )
  201.             "$this->_em->getClassMetadata(City::class)->name$this->_em->getClassMetadata(Saloon::class)->name))
  202.             ->setParameter('user'$owner);
  203.         return $q->getResult();
  204.     }
  205.     private function excludeHavingPlacementHiding(QueryBuilder $qbstring $alias): void
  206.     {
  207.         if($this->features->free_profiles()) {
  208.             if (!in_array('placement_hiding'$qb->getAllAliases())) {
  209.                 $qb
  210.                     ->leftJoin(sprintf('%s.placementHiding'$alias), 'placement_hiding')
  211.                     ->andWhere(sprintf('placement_hiding IS NULL'))
  212.                 ;
  213.             }
  214.         }
  215.     }
  216.     public function deletedByPeriod(\DateTimeInterface $start\DateTimeInterface $end): array
  217.     {
  218.         $qb $this->createQueryBuilder('saloon')
  219.             ->join('saloon.city''city')
  220.             ->select('saloon.uriIdentity _saloon')
  221.             ->addSelect('city.uriIdentity _city')
  222.             ->andWhere('saloon.deletedAt >= :start')
  223.             ->andWhere('saloon.deletedAt <= :end')
  224.             ->setParameter('start'$start)
  225.             ->setParameter('end'$end)
  226.         ;
  227.         return $qb->getQuery()->getResult();
  228.     }
  229.     public function listForMapMatchingSpec(Filter|QueryModifier $specificationint $coordinatesRoundPrecision 3): array
  230.     {
  231.         /** @var QueryBuilder $qb */
  232.         $qb $this->createQueryBuilder($dqlAlias 's');
  233.         $qb->select(sprintf('GROUP_CONCAT(s.id), CONCAT(ROUND(MIN(s.mapCoordinate.latitude),5),\',\',ROUND(MIN(s.mapCoordinate.longitude),5)), count(s.id), CONCAT(ROUND(s.mapCoordinate.latitude,%1$s),\',\',ROUND(s.mapCoordinate.longitude,%1$s)) as coords'$coordinatesRoundPrecision));
  234.         $qb->groupBy('coords');
  235.         $specification->modify($qb$dqlAlias);
  236.         $qb->andWhere($specification->getFilter($qb$dqlAlias));
  237.         return $qb->getQuery()->getResult();
  238.     }
  239.     /**
  240.      * Clustered map points for JSON API mode=map.
  241.      * Representative point is the centroid (AVG), not MIN as in listForMapMatchingSpec().
  242.      */
  243.     public function listMapClustersMatchingSpec(Filter|QueryModifier $specificationint $coordinatesRoundPrecision): array
  244.     {
  245.         $this->getEntityManager()->getConnection()->executeQuery("
  246.             SET SESSION group_concat_max_len = 100000;
  247.         ");
  248.         $precision = (int) $coordinatesRoundPrecision;
  249.         /** @var QueryBuilder $qb */
  250.         $qb $this->createQueryBuilder($dqlAlias 's');
  251.         $qb->select(sprintf(
  252.             '%s, '
  253.             'GROUP_CONCAT(s.id ORDER BY s.id) AS ids, '
  254.             'COUNT(s.id) AS cnt, '
  255.             'ROUND(AVG(s.mapCoordinate.latitude), 5) AS lat, '
  256.             'ROUND(AVG(s.mapCoordinate.longitude), 5) AS lng, '
  257.             'CONCAT(ROUND(s.mapCoordinate.latitude, %2$d), \',\', ROUND(s.mapCoordinate.longitude, %2$d)) AS coords',
  258.             MapClusterMinPriceDql::clusterMinPriceSelect($dqlAlias),
  259.             $precision
  260.         ));
  261.         $qb->groupBy('coords');
  262.         $specification->modify($qb$dqlAlias);
  263.         $qb->andWhere($specification->getFilter($qb$dqlAlias));
  264.         return $qb->getQuery()->getResult();
  265.     }
  266.     public function getCommentedSaloonsPaged(User $owner): ORMQueryResult
  267.     {
  268.         $qb $this->createQueryBuilder('saloon')
  269.             ->join('saloon.comments''comment')
  270.             ->andWhere('saloon.owner = :owner')
  271.             ->setParameter('owner'$owner)
  272.             ->orderBy('comment.createdAt''DESC')
  273.         ;
  274.         return new ORMQueryResult($qb);
  275.     }
  276.     public function ofOwnerAndMasseurTypeWithPlacementFilterAndNameFilter(User $owner, ?string $nameFilter): array
  277.     {
  278.         $qb $this->queryBuilderOfOwnerAndNameFilter($owner$nameFilter);
  279.         return $qb->getQuery()->getResult();
  280.     }
  281.     private function queryBuilderOfOwnerAndNameFilter(User $owner, ?string $nameFilter): QueryBuilder
  282.     {
  283.         $qb $this->createQueryBuilder('saloon')
  284.             ->andWhere('saloon.owner = :owner')
  285.             ->setParameter('owner'$owner)
  286.         ;
  287.         if($nameFilter) {
  288.             $nameExpr $qb->expr()->orX(
  289.                 'LOWER(JSON_UNQUOTE(JSON_EXTRACT(saloon.name, :jsonPath))) LIKE :name_filter',
  290.                 \sprintf("REGEXP_REPLACE(saloon.phoneNumber, '-| ', '') LIKE :name_filter"),
  291.                 'LOWER(profile.phoneNumber) LIKE :name_filter',
  292.                 \sprintf("REGEXP_REPLACE(saloon.phoneNumber, '\+7', '8') LIKE :name_filter"),
  293.             );
  294.             $qb->setParameter('jsonPath''$.ru');
  295.             $qb->setParameter('name_filter''%'.addcslashes(mb_strtolower(str_replace(['('')'' ''-'], ''$nameFilter)), '%_').'%');
  296.             $qb->andWhere($nameExpr);
  297.         }
  298.         return $qb;
  299.     }
  300. }