<?php
namespace Aqarmap\Bundle\ListingBundle\Repository;
use Aqarmap\Bundle\ListingBundle\Constant\ListingCategories;
use Aqarmap\Bundle\ListingBundle\Constant\ListingStatus;
use Aqarmap\Bundle\ListingBundle\Entity\Location;
use Aqarmap\Bundle\ListingBundle\Entity\PropertyType;
use Aqarmap\Bundle\MainBundle\Constant\Locales;
use Aqarmap\Bundle\SearchBundle\Entity\SearchResultCombinations;
use Aqarmap\Bundle\UserBundle\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\Events;
use Doctrine\ORM\Internal\Hydration\IterableResult;
use Doctrine\ORM\NonUniqueResultException;
use Doctrine\ORM\NoResultException;
use Doctrine\ORM\Query;
use Doctrine\ORM\Query\Expr;
use Doctrine\ORM\QueryBuilder;
use Gedmo\Translatable\Query\TreeWalker\TranslationWalker;
use Gedmo\Translatable\TranslatableListener;
use Gedmo\Tree\Entity\Repository\NestedTreeRepository;
class LocationRepository extends NestedTreeRepository
{
public const RECENT_SEARCHED_LOCATIONS_COUNT = 16;
public const ONE_RESULT_NUMBER = 1;
public function __construct(EntityManagerInterface $manager)
{
parent::__construct($manager, $manager->getClassMetadata(Location::class));
}
public function search(array $criteria)
{
$QueryBuilder = $this->createQueryBuilder('lo');
if (isset($criteria['is_disabled'])) {
$QueryBuilder
->andWhere($QueryBuilder->expr()
->eq('lo.disabled', ':disabled'))
->setParameter('disabled', $criteria['is_disabled']);
}
if (isset($criteria['level'])) {
$QueryBuilder
->andWhere($QueryBuilder->expr()->eq('lo.level', ':level'))
->setParameter('level', $criteria['level']);
}
// Search
if (!empty($criteria['search']) && !empty($criteria['q'])) {
$criteria['q'] = trim($criteria['q']);
switch ($criteria['search']) {
case 'location_id':
$QueryBuilder
->andWhere($QueryBuilder->expr()->in('lo.id', ':location_id'))
->setParameter('location_id', $criteria['q']);
break;
case 'parent_id':
$QueryBuilder
->andWhere($QueryBuilder->expr()->in('lo.parent', ':parent_id'))
->setParameter('parent_id', $criteria['q']);
break;
case 'title':
$QueryBuilder
->join('lo.translations', 'lot', Expr\Join::WITH, 'lot.field = :title_field')
->andWhere($QueryBuilder->expr()->orX(
$QueryBuilder->expr()->like('lot.content', ':title'),
$QueryBuilder->expr()->like('lo.title', ':title')
))
->setParameter('title_field', 'title')
->setParameter('title', "{$criteria['q']}%");
break;
}
}
return $QueryBuilder;
}
/**
* @param bool $searchableOnly
*
* @internal param null $locale
*
* @return array
*/
public function getLocationsList($searchableOnly = false, $estimatable = false)
{
$QueryBuilder = $this->createQueryBuilder('lo');
$QueryBuilder
->andWhere($QueryBuilder->expr()->isNull('lo.parent'));
$QueryBuilder
->andWhere($QueryBuilder->expr()->eq('lo.disabled', ':disabled'))
->setParameter('disabled', false, \PDO::PARAM_BOOL);
if ($searchableOnly) {
$QueryBuilder
->andWhere($QueryBuilder->expr()->eq('lo.searchable', ':is_searchable'))
->setParameter('is_searchable', true, \PDO::PARAM_BOOL);
}
if ($estimatable) {
$QueryBuilder
->andWhere($QueryBuilder->expr()->eq('lo.estimate', ':is_estimatable'))
->setParameter('is_estimatable', true, \PDO::PARAM_BOOL);
}
$query = $QueryBuilder->getQuery();
$query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, 'Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker');
return $query;
}
public function getLocationFlat($criteria)
{
$criteria = array_merge([
'searchable' => null,
'estimatable' => null,
'level' => null,
], $criteria);
$QueryBuilder = $this->createQueryBuilder('lo');
$QueryBuilder
->andWhere($QueryBuilder->expr()->eq('lo.disabled', ':disabled'))
->setParameter('disabled', false, \PDO::PARAM_BOOL);
if ((int) $criteria['searchable']) {
$QueryBuilder
->andWhere($QueryBuilder->expr()->eq('lo.searchable', ':is_searchable'))
->setParameter('is_searchable', true, \PDO::PARAM_BOOL);
}
if ((int) $criteria['estimatable']) {
$QueryBuilder
->andWhere($QueryBuilder->expr()->eq('lo.estimate', ':is_estimatable'))
->setParameter('is_estimatable', true, \PDO::PARAM_BOOL);
}
if (null !== $criteria['level']) {
$QueryBuilder
->andWhere($QueryBuilder->expr()->eq('lo.level', ':level'))
->setParameter('level', $criteria['level']);
}
$query = $QueryBuilder->getQuery();
$query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, 'Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker');
return $query;
}
/**
* Get location children flat.
*
* @return Location|array
*/
public function getLocationChildrenFlat(Location $location)
{
$QueryBuilder = $this->createQueryBuilder('lo');
$QueryBuilder
->andWhere($QueryBuilder->expr()->eq('lo.parent', ':parent_id'))
->setParameter('parent_id', $location);
return $QueryBuilder->getQuery();
}
/**
* Get location children flat Array of Results.
*
* @param string $locale
*
* @return Location|array
*/
public function getLocationChildrenFlatResults(Location $location, $locale = null)
{
$QueryBuilder = $this->createQueryBuilder('lo');
$QueryBuilder
->andWhere($QueryBuilder->expr()->eq('lo.parent', ':parent_id'))
->setParameter('parent_id', $location);
$query = $QueryBuilder
->getQuery()
->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, TranslationWalker::class);
if ($locale) {
$query->setHint(TranslatableListener::HINT_TRANSLATABLE_LOCALE, $locale);
}
return $query->getArrayResult();
}
/**
* @internal param null $locale
*
* @return Query
*
* @internal param User $user
* @internal param bool $iteration
*/
public function getSectionLocationsWithLiveListings(Section $section)
{
$QueryBuilder = $this->createQueryBuilder('lo');
$QueryBuilder
->leftJoin('lo.listings', 'lol');
$QueryBuilder
->andWhere($QueryBuilder->expr()->eq('lo.disabled', ':disabled'))
->setParameter('disabled', false, \PDO::PARAM_BOOL);
if ($section) {
$QueryBuilder
->andWhere($QueryBuilder->expr()->eq('lol.section', ':section'))
->setParameter('section', $section);
}
$QueryBuilder
->andWhere($QueryBuilder->expr()->eq('lol.status', ':status'))
->setParameter('status', ListingStatus::LIVE);
$query = $QueryBuilder->getQuery();
$query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, 'Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker');
return $query->getResult();
}
/**
* @return array
*
* @internal param int $level
* @internal param bool $searchableOnly
* @internal param null $locale
*/
public function getLeveledLocations(array $criteria)
{
$QueryBuilder = $this->createQueryBuilder('lo');
$QueryBuilder
->andWhere($QueryBuilder->expr()->eq('lo.disabled', ':disabled'))
->setParameter('disabled', false, \PDO::PARAM_BOOL);
if (!empty($criteria['parent'])) {
if ('no-parent' == $criteria['parent']) {
$QueryBuilder
->andWhere($QueryBuilder->expr()->isNull('lo.parent'));
} else {
$QueryBuilder
->andWhere($QueryBuilder->expr()->eq('lo.parent', ':parent'))
->setParameter('parent', $criteria['parent']);
}
}
if (isset($criteria['level'])) {
$QueryBuilder
->andWhere($QueryBuilder->expr()->lte('lo.level', ':level'))
->setParameter('level', $criteria['level']);
}
$QueryBuilder->orderBy('lo.listingsCounter', 'desc');
$query = $QueryBuilder->getQuery();
$query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, 'Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker');
return $query->getResult();
}
public function getLeveledLocationsArray(array $criteria)
{
$queryBuilder = $this->createQueryBuilder('location');
$queryBuilder
->select('location.id as id, COALESCE(translation.content, location.title) as title, location.slug as slug')
->leftJoin(
'location.translations',
'translation',
Expr\Join::WITH,
'translation.locale = :locale and translation.field = :title and translation.object = location.id'
)
->andWhere($queryBuilder->expr()->eq('location.disabled', ':disabled'))
->setParameter('locale', $criteria['locale'])
->setParameter('title', 'title')
->setParameter('disabled', false, \PDO::PARAM_BOOL);
if (!empty($criteria['parent'])) {
if ('no-parent' == $criteria['parent']) {
$queryBuilder->andWhere($queryBuilder->expr()->isNull('location.parent'));
} else {
$queryBuilder
->andWhere($queryBuilder->expr()->eq('location.parent', ':parent'))
->setParameter('parent', $criteria['parent']);
}
}
if (!empty($criteria['level'])) {
$queryBuilder
->andWhere($queryBuilder->expr()->lte('location.level', ':level'))
->setParameter('level', $criteria['level']);
}
if (!empty($criteria['searchResultsCombinationMinimumCount'])) {
$queryBuilder->join(
SearchResultCombinations::class,
'searchResultCombinations',
Expr\Join::WITH,
'searchResultCombinations.location = location.id'
)
->andWhere($queryBuilder->expr()->gte('searchResultCombinations.searchResultCount', ':searchResultCount'))
->setParameter('searchResultCount', $criteria['searchResultsCombinationMinimumCount']);
if (isset($criteria['propertyType'])) {
$queryBuilder->andWhere($queryBuilder->expr()->eq('searchResultCombinations.propertyType', ':propertyType'))
->setParameter('propertyType', $criteria['propertyType']);
}
if (isset($criteria['section'])) {
$queryBuilder->andWhere($queryBuilder->expr()->eq('searchResultCombinations.section', ':section'))
->setParameter('section', $criteria['section']);
}
if (!empty($criteria['searchResultsCombinationMax'])) {
$queryBuilder->addSelect('MAX(searchResultCombinations.searchResultCount) as searchResultCount');
} else {
$queryBuilder->addSelect('searchResultCombinations.searchResultCount as searchResultCount');
}
$queryBuilder->orderBy('searchResultCombinations.searchResultCount', 'desc');
} else {
$queryBuilder->orderBy('location.listingsCounter', 'desc');
}
return $queryBuilder;
}
/**
* @return Location
*/
public function getSearchableOrNeighbourhoodNearestParent(Location $location)
{
$QueryBuilder = $this->createQueryBuilder('lo');
$QueryBuilder
->andWhere('lo.root = :root')
->andWhere('lo.left <= :left')
->andWhere('lo.right >= :right')
->setParameter('left', $location->getLeft())
->setParameter('right', $location->getRight())
->setParameter('root', $location->getRoot());
$QueryBuilder
->andWhere('lo.neighborhoodFilter = :is_neighbourhood OR lo.searchable = :is_searchable')
->setParameter('is_neighbourhood', true, \PDO::PARAM_BOOL)
->setParameter('is_searchable', true, \PDO::PARAM_BOOL);
$QueryBuilder
->orderBy('lo.level', 'DESC')
->setMaxResults(1);
$query = $QueryBuilder->getQuery();
$query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, 'Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker');
return $query->getOneOrNullResult();
}
// --------------------------------------------------------------------
/**
* Get location children.
*
* @return Location|array
*/
public function getLocationChildren(Location $location)
{
$locationChildren = $this->children($location);
$location = array_merge([$location], $locationChildren);
return $location;
}
/**
* @return array
*/
public function getLocationChildrenIds(Location $location)
{
$locationChildrenIds = $this->childrenIds($location);
$location = array_merge([$location->getId()], array_column(array_values($locationChildrenIds), 'id'));
return $location;
}
/**
* @return array
*/
public function childrenIds(Location $location)
{
$queryBuilder = $this->createQueryBuilder('location');
$queryBuilder->select('location.id');
$queryBuilder->where($queryBuilder->expr()->lt('location.right', $location->getRight()));
$queryBuilder->andWhere($queryBuilder->expr()->gt('location.left', $location->getLeft()));
$queryBuilder->andWhere($queryBuilder->expr()->eq('location.root', ':rid'));
$queryBuilder->setParameter('rid', $location->getRoot());
return $queryBuilder
->getQuery()
->enableResultCache(1800, sprintf('location_children_ids_%d_%d', $location->getRight(), $location->getLeft()))
->getResult();
}
// --------------------------------------------------------------------
/**
* Get neighbourhood locations.
*
* @return Location|array
*/
public function getNeighbourhoodLocations($criteria = [])
{
$QueryBuilder = $this->createQueryBuilder('lo');
$QueryBuilder
->andWhere($QueryBuilder->expr()->eq('lo.neighborhoodFilter', ':is_true'))
->setParameter('is_true', true, \PDO::PARAM_BOOL);
$QueryBuilder
->andWhere($QueryBuilder->expr()->eq('lo.disabled', ':disabled'))
->setParameter('disabled', false, \PDO::PARAM_BOOL);
if (isset($criteria['level'])) {
$QueryBuilder
->andWhere($QueryBuilder->expr()->eq('lo.level', ':level'))
->setParameter('level', $criteria['level']);
}
if (isset($criteria['maxLevel'])) {
$QueryBuilder
->andWhere($QueryBuilder->expr()->lte('lo.level', ':maxLevel'))
->setParameter('maxLevel', $criteria['maxLevel']);
}
$query = $QueryBuilder->getQuery();
return $query->getResult();
}
/**
* Get neighbourhood sub-locations.
*
* @return Location|array
*/
public function getNeighbourhoodSubLocations(Location $location, PropertyType $propertyType)
{
$QueryBuilder = $this->createQueryBuilder('lo')
->join('lo.locationStatistics', 'location_statistics', Expr\Join::LEFT_JOIN);
$QueryBuilder
->andWhere($QueryBuilder->expr()->eq('lo.parent', ':location'))
->setParameter('location', $location);
$QueryBuilder
->andWhere($QueryBuilder->expr()->eq('lo.disabled', ':disabled'))
->setParameter('disabled', false, \PDO::PARAM_BOOL);
$QueryBuilder
->andWhere($QueryBuilder->expr()->eq('location_statistics.propertyType', ':property_type'))
->setParameter('property_type', $propertyType);
$QueryBuilder
->andWhere($QueryBuilder->expr()->eq('lo.neighborhoodFilter', ':is_true'))
->setParameter('is_true', true, \PDO::PARAM_BOOL);
$QueryBuilder
->andWhere($QueryBuilder->expr()->isNotNull('location_statistics.avgPrice'));
$QueryBuilder
->andWhere($QueryBuilder->expr()->neq('location_statistics.avgPrice', 0));
$QueryBuilder
->orderBy('location_statistics.avgPrice', 'DESC');
$query = $QueryBuilder->getQuery();
return $query->getResult();
}
/**
* Get neighbourhood Compounds.
*
* @param null $state
*
* @return Location|array
*/
public function getNeighbourhoodCompounds($locations = null, $state = null)
{
$QueryBuilder = $this->createQueryBuilder('lo')
->join('lo.locationCompound', 'lc', Expr\Join::INNER_JOIN);
$QueryBuilder
->andWhere($QueryBuilder->expr()->eq('lo.compoundFilter', ':is_true'))
->setParameter('is_true', true, \PDO::PARAM_BOOL);
if ($locations) {
$QueryBuilder
->andWhere($QueryBuilder->expr()->orX(
$QueryBuilder->expr()->in('lo.parent', ':locations')
))
->setParameter('locations', $locations);
}
if ($state) {
$QueryBuilder
->andWhere($QueryBuilder->expr()->eq('lc.state', ':state'))
->setParameter('state', $state);
}
$query = $QueryBuilder->getQuery();
return $query->getResult();
}
/**
* @return array
*/
public function generateNearestLocations(Location $location, $distance)
{
$QueryBuilder = $this->createQueryBuilder('lo');
$QueryBuilder
->andWhere($QueryBuilder->expr()->eq('lo.disabled', ':disabled'))
->setParameter('disabled', false, \PDO::PARAM_BOOL);
$QueryBuilder
->andWhere($QueryBuilder->expr()->eq('lo.level', ':level'))
->setParameter('level', $location->getLevel());
$QueryBuilder
->andWhere($QueryBuilder->expr()->neq('lo', ':location'))
->setParameter('location', $location);
if ($location->getParent()) {
$QueryBuilder
->andWhere($QueryBuilder->expr()->eq('lo.parent', ':parent'))
->setParameter('parent', $location->getParent());
}
$QueryBuilder = $this->getNearestLocationsFormulaQuery($QueryBuilder, $location, $distance);
$query = $QueryBuilder->getQuery();
return $query->getResult();
}
/**
* @return Query
*/
public function generateNearestLocationsQuery(array $criteria)
{
/** @var Location $location */
$location = $criteria['location'];
$listingQueryBuilder = $this->_em->createQueryBuilder();
$listingQueryBuilder = $listingQueryBuilder
->select('MAX(li.id)')
->from(Listing::class, 'li')
->andWhere($listingQueryBuilder->expr()->eq('li.status', ListingStatus::LIVE))
->andWhere($listingQueryBuilder->expr()->neq('li.category', ListingCategories::PROJECTS));
if (isset($criteria['propertyType']) && $criteria['propertyType']) {
$listingQueryBuilder->andWhere($listingQueryBuilder->expr()->in('li.propertyType', $criteria['propertyType']));
}
if (isset($criteria['section']) && $criteria['section']) {
$listingQueryBuilder->andWhere($listingQueryBuilder->expr()->eq('li.section', $criteria['section']));
}
$queryBuilder = $this->createQueryBuilder('lo');
$queryBuilder->addSelect('ST_Distance_Sphere( Point(:longitude, :latitude), Point(lo.lon, lo.lat)) AS distance');
$queryBuilder
->andWhere($queryBuilder->expr()->eq('lo.disabled', ':disabled'))
->setParameter('disabled', false, \PDO::PARAM_BOOL);
$queryBuilder->innerJoin(
'lo.listings',
'listings',
Expr\Join::WITH,
$queryBuilder->expr()->eq(
'listings.id',
sprintf('(%s)', $listingQueryBuilder
->andWhere($listingQueryBuilder->expr()->eq('li.location', 'lo.id'))
->setMaxResults(1)->getQuery()->getDQL())
)
);
$queryBuilder
->andWhere($queryBuilder->expr()->neq('lo', ':location'))
->setParameter('location', $location);
if (isset($criteria['excludedSubLocations']) && $criteria['excludedSubLocations']) {
$queryBuilder
->andWhere($queryBuilder->expr()->notIn('lo', ':subLocations'))
->setParameter('subLocations', $this->getLocationChildrenIds($location));
}
$queryBuilder->andWhere($queryBuilder->expr()->neq('lo.listingsCounter', 0));
$queryBuilder->andWhere($queryBuilder->expr()->lt('ABS(lo.lat)', 90));
$queryBuilder->andWhere($queryBuilder->expr()->lt('ABS(lo.lon)', 180));
$queryBuilder->andWhere($queryBuilder->expr()->isNotNull('lo.lat'));
$queryBuilder->andWhere($queryBuilder->expr()->isNotNull('lo.lon'));
$queryBuilder
->having('distance <= :maxDistance')
->setParameter('maxDistance', $criteria['maxDistance'] * 1000)
->setParameter('latitude', $location->getCenterLat())
->setParameter('longitude', $location->getCenterLng());
$queryBuilder->orderBy('distance');
return $queryBuilder->getQuery();
}
public function getNearestLocationsFormulaQuery(QueryBuilder $queryBuilder, Location $location, $distance): QueryBuilder
{
return $queryBuilder
->having('( 3959 * acos(cos(radians(:latitude))'.
'* cos( radians( lo.lat ) )'.
'* cos( radians( lo.lon )'.
'- radians(:longitude) )'.
'+ sin( radians(:latitude) )'.
'* sin( radians( lo.lat ) ) ) ) < :distance')
->setParameter('distance', $distance)
->setParameter('latitude', $location->getCenterLat())
->setParameter('longitude', $location->getCenterLng());
}
/**
* Get Nearest Compounds.
*
* @return Location|array
*/
public function getNearestLocations(array $locations = [], bool $limit = true, int $max = 3)
{
$QueryBuilder = $this->createQueryBuilder('lo')
->join('lo.nearestLocations', 'nearest_locations', Expr\Join::INNER_JOIN);
$QueryBuilder
->andWhere($QueryBuilder->expr()->eq('lo.searchable', ':is_searchable'))
->setParameter('is_searchable', true, \PDO::PARAM_BOOL);
$QueryBuilder
->andWhere($QueryBuilder->expr()->in('nearest_locations.id', ':locations'))
->setParameter('locations', $locations);
if ($limit) {
$QueryBuilder->setMaxResults($max);
}
return $QueryBuilder->getQuery()->enableResultCache()->getResult();
}
/**
* @internal param null $locale
*
* @param string $locale
* @param null $levelLimit
*
* @return array
*/
public function getAllSearchableLocationsList($levelLimit = null, $locale = null)
{
$QueryBuilder = $this->createQueryBuilder('lo');
$QueryBuilder
->andWhere($QueryBuilder->expr()->eq('lo.disabled', ':disabled'))
->setParameter('disabled', false, \PDO::PARAM_BOOL);
if ($levelLimit) {
$QueryBuilder
->andWhere($QueryBuilder->expr()->lt('lo.level', ':level'))
->setParameter('level', $levelLimit);
}
$query = $QueryBuilder
->getQuery()
->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, TranslationWalker::class);
if ($locale) {
$query->setHint(TranslatableListener::HINT_TRANSLATABLE_LOCALE, $locale);
}
return $query->getResult();
}
/**
* @return array
*/
public function getAllLocationsCount()
{
$QueryBuilder = $this->createQueryBuilder('lo')->select('count(lo.id)');
$QueryBuilder
->andWhere($QueryBuilder->expr()->eq('lo.disabled', ':disabled'))
->setParameter('disabled', false, \PDO::PARAM_BOOL);
return $QueryBuilder->getQuery()->getSingleScalarResult();
}
/**
* Get Nearest Neighbourhoods or Compounds.
*
* @return Location|array
*/
public function getNearestNeighbourhoods(Location $location)
{
$QueryBuilder = $this->createQueryBuilder('lo')
->join('lo.nearestLocations', 'nearest_locations', Expr\Join::INNER_JOIN);
if ($location->getCompoundFilter()) {
$QueryBuilder
->andWhere($QueryBuilder->expr()->eq('lo.compoundFilter', ':is_true'))
->setParameter('is_true', true, \PDO::PARAM_BOOL);
}
if ($location->getNeighborhoodFilter()) {
$QueryBuilder
->andWhere($QueryBuilder->expr()->eq('lo.neighborhoodFilter', ':is_true'))
->setParameter('is_true', true, \PDO::PARAM_BOOL);
}
$QueryBuilder
->andWhere($QueryBuilder->expr()->eq('nearest_locations.id', ':location'))
->setParameter('location', $location);
$QueryBuilder->setMaxResults(3);
$query = $QueryBuilder->getQuery();
return $query->getResult();
}
/**
* @param bool $forceLocale
*
* @return Location
*/
public function getLocationLike($location, $forceLocale = true)
{
$queryBuilder = $this->createQueryBuilder('p')
->where('p.title LIKE :location')
->setParameter('location', '%'.$location.'%')
->getQuery()
->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, 'Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker');
if ($forceLocale) {
$queryBuilder->setHint(TranslatableListener::HINT_TRANSLATABLE_LOCALE, 'en');
}
return $queryBuilder->setMaxResults(1)
->getOneOrNullResult();
}
/**
* Get locations by keyword.
*
* @return Query
*/
public function getLocationByKeyword(?string $keyword = null)
{
return $this->createQueryBuilder('l')
->select('l')
->where('FIND_IN_SET(:keyword, l.keyword) > 0')
->setParameter('keyword', $keyword)
->getQuery();
}
public function getWhereHasBumpableListings()
{
$queryBuilder = $this->createQueryBuilder('l');
return $queryBuilder
->select('l, li, lf')
->join('l.listings', 'li', Expr\Join::WITH, 'l.id=li.location')
->join('li.listingFeatures', 'lf', Expr\Join::WITH, 'li.id=lf.listing')
->where($queryBuilder->expr()->eq('li.isBumped', ':isBumped'))
->setParameter(':isBumped', true, \PDO::PARAM_BOOL)
->andWhere($queryBuilder->expr()->isNull('lf.expiresAt'))
->andWhere('lf.bumpsCount < lf.bumpUpOccurrences')
->orWhere($queryBuilder->expr()->isNull('lf.bumpsCount'))
->andWhere($queryBuilder->expr()->lte('lf.nextBumpDate', ':nextBumpDate'))
->setParameter(':nextBumpDate', (new \DateTime())->setTime(0, 0, 0))
->orderBy('lf.nextBumpDate', 'DESC')
->addOrderBy('lf.id', 'DESC')
->addOrderBy('lf.listing', 'DESC')
->setMaxResults(100)
->getQuery()
->getResult();
}
/**
* @param bool $status
*
* @throws \Doctrine\ORM\OptimisticLockException
*/
public function changeStatus(Location $location, $status = true)
{
$queryBuilder = $this->createQueryBuilder('location');
$locations = $this->getChildren($location);
$locations[] = $location;
$locationsIds = [];
foreach ($locations as $childLocation) {
$locationsIds[] = $childLocation->getId();
}
$disableStatus = $queryBuilder
->set('location.disabled', (int) $status)
->update()
->where($queryBuilder->expr()->in('location.id', $locationsIds))
->getQuery()
->execute();
$em = $this->getEntityManager();
foreach ($locations as $childLocation) {
$childLocation->setDisabled((bool) $status);
$em->getEventManager()->dispatchEvent(
Events::postUpdate,
new LifecycleEventArgs($childLocation, $em)
);
}
$em->flush();
return $disableStatus;
}
/**
* Get Location full path.
*
* @param string $locale
* @param string $separator
*
* @return string
*/
public function getLocationFullPath(Location $location, $locale, $separator = ' / ')
{
$nodes = $this->getPathQueryBuilder($location)
->getQuery()
->setHint(
Query::HINT_CUSTOM_OUTPUT_WALKER,
TranslationWalker::class
)->setHint(
TranslatableListener::HINT_TRANSLATABLE_LOCALE,
$locale
)
->getArrayResult();
$locationFullPath = $this->buildTree($nodes, [
'decorate' => true,
'rootOpen' => '',
'rootClose' => '',
'childOpen' => function ($node) use ($nodes, $separator) {
if ($node['id'] === $nodes[0]['id']) {
return '';
}
return $separator;
},
'childClose' => '',
]);
return implode($separator, array_reverse(explode($separator, $locationFullPath)));
}
/**
* Get Path of Location.
*
* @param string $locale
*
* @return array
*/
public function getLocationPathNodes(Location $location, $locale = null)
{
return $this->getPathQueryBuilder($location)
->getQuery()
->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, TranslationWalker::class)
->setHint(TranslatableListener::HINT_TRANSLATABLE_LOCALE, $locale)
->getArrayResult();
}
/**
* @param null $locale
*/
public function getLocationPathNodesResult(Location $location, $locale = null)
{
return $this
->getPathQueryBuilder($location)
->getQuery()
->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, TranslationWalker::class)
->setHint(TranslatableListener::HINT_TRANSLATABLE_LOCALE, $locale)
->getResult();
}
public function getSameLevelLocations(Location $location, ?int $limit = null, array $excluded = [])
{
$queryBuilder = $this->createQueryBuilder('lo');
$queryBuilder
->where($queryBuilder->expr()->eq('lo.level', ':level'))
->setParameter('level', $location->getLevel())
->andWhere($queryBuilder->expr()->notIn('lo.id', ':excluded'))
->setParameter('excluded', array_merge($excluded, [$location]));
if ($limit) {
$queryBuilder->setMaxResults($limit);
}
return $queryBuilder->getQuery()->getResult();
}
/**
* Find locations by slug indexed by id.
*
* @return array
*/
public function getBySlugs(array $slugs = [])
{
if (empty($slugs)) {
return [];
}
$queryBuilder = $this->createQueryBuilder('location', 'location.id');
return $queryBuilder
->andWhere($queryBuilder->expr()->in('location.slug', $slugs))
->getQuery()
->getResult();
}
/**
* @param string $locale
*
* @return string
*/
public function getRootLocation(Location $location, $locale = 'en')
{
$queryBuilder = $this->getPathQueryBuilder($location);
$queryBuilder = $queryBuilder->setMaxResults(self::ONE_RESULT_NUMBER);
$rootLocationQuery = $queryBuilder->getQuery()->setHint(
Query::HINT_CUSTOM_OUTPUT_WALKER,
TranslationWalker::class
)->setHint(
TranslatableListener::HINT_TRANSLATABLE_LOCALE,
$locale
);
return $rootLocationQuery->getOneOrNullResult();
}
/**
* @param int|string $idOrSlug
*
* @throws NonUniqueResultException
*/
public function findOneByIdOrSlug($idOrSlug)
{
$queryBuilder = $this->createQueryBuilder('l');
return $queryBuilder
->andWhere($queryBuilder->expr()->eq('l.slug', ':slug'))
->setParameter('slug', (string) $idOrSlug)
->orWhere($queryBuilder->expr()->eq('l.id', ':id'))
->setParameter('id', $idOrSlug)
->getQuery()
->getOneOrNullResult();
}
/**
* @param int $limit
*
* @return array
*/
public function getMostSearched($limit = self::RECENT_SEARCHED_LOCATIONS_COUNT)
{
$queryBuilder = $this->createQueryBuilder('l');
return $queryBuilder
->addSelect('l')
->groupBy('l.id')
->orderBy('l.listingsCounter', 'desc')
->setMaxResults($limit)
->getQuery()
->getResult();
}
/**
* Get locations count by keyword.
*
* @return Query
*/
public function getLocationCountByKeyword(?string $keyword = null)
{
return $this->createQueryBuilder('l')
->select('COUNT(l.id)')
->where('FIND_IN_SET(:keyword, l.keyword) > 0')
->setParameter('keyword', $keyword)
->getQuery()
->setMaxResults(1);
}
/**
* Get locations title by ids.
*
* @return Query
*/
public function findTitleById(array $ids, string $locale = 'en')
{
$queryBuilder = $this->createQueryBuilder('l');
return $queryBuilder->select('l.id, l.title')
->where($queryBuilder->expr()->in('l.id', ':ids'))
->setParameter('ids', $ids)
->getQuery()
->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, TranslationWalker::class)
->setHint(TranslatableListener::HINT_TRANSLATABLE_LOCALE, $locale);
}
public function getParentLocationByLevel(Location $location, int $level = 1, string $locale = 'en')
{
return $this
->getPathQueryBuilder($location)
->setFirstResult($level)
->setMaxResults(1)
->getQuery()
->setHint(
Query::HINT_CUSTOM_OUTPUT_WALKER,
TranslationWalker::class
)
->setHint(TranslatableListener::HINT_TRANSLATABLE_LOCALE, $locale)
->getArrayResult();
}
/**
* @return Query
*/
public function getParentsIds(Location $location)
{
$queryBuilder = $this->getParents($location);
$queryBuilder->select('location.id');
return $queryBuilder->getQuery();
}
/**
* @return Query
*/
public function getParentsBreadCrumbData(Location $location, string $locale = Locales::AR)
{
$queryBuilder = $this->getParents($location);
$queryBuilder->select('location.id, location.title, location.slug');
return $queryBuilder->getQuery()
->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, TranslationWalker::class)
->setHint(TranslatableListener::HINT_TRANSLATABLE_LOCALE, $locale)
;
}
/**
* @return Location|null
*
* @throws NonUniqueResultException
*/
public function getFirstRoot()
{
$queryBuilder = $this->createQueryBuilder('location');
return $queryBuilder
->setMaxResults(1)
->getQuery()
->getOneOrNullResult();
}
/**
* @return int|mixed|string
*/
public function getPathNodeByLevel(Location $location, int $level = 1)
{
$queryBuilder = $this->getPathQueryBuilder($location);
$queryBuilder->andWhere($queryBuilder->expr()->eq('node.level', ':level'))
->setParameter('level', $level);
return $queryBuilder->getQuery()->getResult();
}
/**
* @return array
*/
public function getLevelOneLocations()
{
$queryBuilder = $this->getEntityManager()->createQueryBuilder();
$queryBuilder->addSelect('l.id');
$queryBuilder->addSelect('l.title');
$queryBuilder->from('AqarmapListingBundle:Location', 'l');
$queryBuilder->andWhere($queryBuilder->expr()->eq('l.level', ':level'))
->setParameter('level', 1);
$queryBuilder->andWhere($queryBuilder->expr()->eq('l.disabled', ':disabled'))
->setParameter('disabled', 0);
$query = $queryBuilder
->getQuery()
->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, TranslationWalker::class)
->setHint(TranslatableListener::HINT_TRANSLATABLE_LOCALE, 'en');
return $query->getArrayResult();
}
/**
* @return QueryBuilder
*/
public function getLevelOneLocationsQuery()
{
$locationQueryBuilder = $this->getEntityManager()->createQueryBuilder();
$locationQueryBuilder
->addSelect('locations.title AS locationTitle')
->from('AqarmapListingBundle:Location', 'locations');
$locationQueryBuilder
->andWhere($locationQueryBuilder->expr()->eq('locations.disabled', ':disabled'))
->setParameter('disabled', false);
$locationQueryBuilder
->andWhere($locationQueryBuilder->expr()->eq('locations.level', ':level'))
->setParameter('level', 1);
return $locationQueryBuilder;
}
/**
* @return array
*/
public function getLocationChildrenDQL(array $aliases)
{
$leftAlias = $aliases['leftAlias'];
$rightAlias = $aliases['rightAlias'];
$rootAlias = $aliases['rootAlias'];
$leftLocationDQL = $this->getEntityManager()->createQueryBuilder()
->select("$leftAlias.left")
->from('AqarmapListingBundle:Location', "$leftAlias");
$leftLocationDQL
->where($leftLocationDQL->expr()->eq("$leftAlias.id", 'locations.id'))
->getDQL();
$rightLocationDQL = $this->getEntityManager()->createQueryBuilder()
->select("$rightAlias.right")
->from('AqarmapListingBundle:Location', "$rightAlias");
$rightLocationDQL
->where($rightLocationDQL->expr()->eq("$rightAlias.id", 'locations.id'))
->getDQL();
$rootLocationDQL = $this->getEntityManager()->createQueryBuilder()
->select("$rootAlias.root")
->from('AqarmapListingBundle:Location', "$rootAlias");
$rootLocationDQL
->where($rootLocationDQL->expr()->eq("$rootAlias.id", 'locations.id'))
->getDQL();
return [
'leftLocationDQL' => $leftLocationDQL,
'rightLocationDQL' => $rightLocationDQL,
'rootLocationDQL' => $rootLocationDQL,
];
}
public function getIterableLocations(): IterableResult
{
$qb = $this->createQueryBuilder('location');
return $qb->select('location')
->where($qb->expr()->isNotNull('location.parent'))
->getQuery()->iterate();
}
/**
* @return mixed[]
*
* @throws \Doctrine\DBAL\DBALException
*/
public function getLocationParentsAndChildrenIds(Location $location)
{
$statement = "
SELECT
CASE WHEN locations.root = {$location->getRoot()} AND
locations._right > {$location->getRight()} AND
locations._left < {$location->getLeft()}
THEN locations.id
ELSE NULL
END AS parents,
CASE WHEN locations.root = {$location->getRoot()} AND
locations._right < {$location->getRight()} AND
locations._left > {$location->getLeft()}
THEN locations.id
ELSE NULL
END AS children
FROM locations
WHERE (locations.root = {$location->getRoot()} AND
locations._right >= {$location->getRight()} AND
locations._left <= {$location->getLeft()}) OR
(locations.root = {$location->getRoot()} AND
locations._right < {$location->getRight()} AND
locations._left > {$location->getLeft()});";
$statement = $this->getEntityManager()->getConnection()->prepare($statement);
$statement->execute();
return $statement->fetchAll();
}
/**
* @return Query
*/
public function getLocationsData(array $locations = [], array $selections = [], bool $translations = false)
{
$queryBuilder = $this->createQueryBuilder('location');
$selectStatement = '';
foreach ($selections as $index => $selection) {
$selectStatement .= "location.{$selection}";
if ($index !== \count($selections) - 1) {
$selectStatement .= ', ';
}
}
if ($translations) {
$queryBuilder
->join('location.translations', 'translations');
$queryBuilder
->andWhere($queryBuilder->expr()->eq('translations.field', ':field'))
->setParameter(':field', 'title')
->andWhere($queryBuilder->expr()->eq('translations.locale', ':locale'))
->setParameter(':locale', Locales::EN);
$selectStatement .= ', translations.content, translations.locale';
}
$queryBuilder->select($selectStatement);
if (!empty($locations)) {
$queryBuilder->andWhere($queryBuilder->expr()->in('location.id', ':id'))
->setParameter('id', $locations);
}
return $queryBuilder->getQuery();
}
/**
* @param array $selections = []
*
* @return Query
*/
public function getParent(int $locationId, array $selections = [], ?string $locale = null)
{
$queryBuilder = $this->createQueryBuilder('location');
$selectStatement = '';
foreach ($selections as $index => $selection) {
$selectStatement .= "location.{$selection}";
if ($index !== \count($selections) - 1) {
$selectStatement .= ', ';
}
}
$queryBuilder->select($selectStatement)
->andWhere($queryBuilder->expr()->eq('location.parent', ':parent'))
->setParameter('id', $locationId);
$query = $queryBuilder->getQuery();
if ($locale) {
$query->setHint(
Query::HINT_CUSTOM_OUTPUT_WALKER,
'Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker'
);
$query->setHint(TranslatableListener::HINT_TRANSLATABLE_LOCALE, $locale);
}
return $queryBuilder->getQuery();
}
public function getUserLiveLocationsWithParents(int $user)
{
$queryBuilder = $this->createQueryBuilder('location');
$queryBuilder->select('location.id as id, IDENTITY(location.parent) as parent, location.level as level');
$queryBuilder->join('location.listings', 'listings', Expr\Join::WITH, 'location.id=listings.location');
$queryBuilder->andWhere($queryBuilder->expr()->eq('listings.user', ':user'))
->setParameter(':user', $user)
->andWhere($queryBuilder->expr()->eq('listings.status', ':status'))
->setParameter(':status', ListingStatus::LIVE);
return $queryBuilder->getQuery()->getResult();
}
/**
* @return array
*/
public function getChildrenIds($right, $left, $root)
{
$queryBuilder = $this->createQueryBuilder('location');
$queryBuilder->select('location.id');
$queryBuilder->where($queryBuilder->expr()->lt('location.right', $right));
$queryBuilder->andWhere($queryBuilder->expr()->gt('location.left', $left));
$queryBuilder->andWhere($queryBuilder->expr()->eq('location.root', ':rid'));
$queryBuilder->setParameter('rid', $root);
return $queryBuilder->getQuery()->getResult();
}
/**
* @return Query
*/
public function getChildrenObjects(Location $location, $excludedLocations = null)
{
$queryBuilder = $this->createQueryBuilder('location');
$queryBuilder->where($queryBuilder->expr()->lt('location.right', $location->getRight()));
$queryBuilder->andWhere($queryBuilder->expr()->gt('location.left', $location->getLeft()));
$queryBuilder->andWhere($queryBuilder->expr()->eq('location.root', ':rid'));
$queryBuilder->setParameter('rid', $location->getRoot());
$queryBuilder->andWhere($queryBuilder->expr()->neq('location', ':location'));
$queryBuilder->setParameter('location', $location);
$queryBuilder->andWhere($queryBuilder->expr()->notIn('location', $excludedLocations));
return $queryBuilder->getQuery();
}
/**
* @return QueryBuilder
*/
public function getLocationsByLevel($criteria)
{
[
'selectType' => $selectType,
'lessThanOrEqualLevel' => $lessThanOrEqualLevel,
'moreThanLevel' => $moreThanLevel,
] = array_merge([
'selectType' => null,
'lessThanOrEqualLevel' => null,
'moreThanLevel' => null,
], $criteria);
$queryBuilder = $this->createQueryBuilder('locations');
if ('count' === $selectType) {
$queryBuilder->select('COUNT(locations.id)');
}
if ($lessThanOrEqualLevel) {
$queryBuilder
->andWhere($queryBuilder->expr()->lte('locations.level', ':level'))
->setParameter('level', $lessThanOrEqualLevel);
} elseif ($moreThanLevel) {
$queryBuilder
->andWhere($queryBuilder->expr()->gt('locations.level', ':level'))
->setParameter('level', $moreThanLevel);
}
return $queryBuilder;
}
/**
* Get all parents ids for location.
*
* @param int|Location $location
*/
public function getAllLocationParentsIds($location, bool $withRoot = true): array
{
$location = \is_int($location) ? $this->find($location) : $location;
$queryBuilder = $this->getPathQueryBuilder($location);
$queryBuilder->select('node.id');
$queryBuilder->andWhere($queryBuilder->expr()->neq('node.id', ':location'));
$queryBuilder->setParameter('location', $location->getId());
if (false == $withRoot) {
$queryBuilder->andWhere($queryBuilder->expr()->isNotNull('node.parent'));
}
return $queryBuilder->getQuery()->getResult();
}
/**
* @return array
*/
public function getMostLocationHasListingsForUser(User $user)
{
$queryBuilder = $this->createQueryBuilder('location');
$queryBuilder->join('location.listings', 'listing')->addSelect('listing');
$queryBuilder->andWhere($queryBuilder->expr()->eq('listing.status', ':status'));
$queryBuilder->andWhere($queryBuilder->expr()->eq('listing.user', ':user'));
$queryBuilder->setParameter('status', ListingStatus::LIVE);
$queryBuilder->setParameter('user', $user);
$queryBuilder->addSelect('COUNT(listing.id) AS listing_count');
$queryBuilder->groupBy('location.id');
$queryBuilder->orderBy('listing_count', 'DESC');
return $queryBuilder->setMaxResults(self::ONE_RESULT_NUMBER)->getQuery()->getOneOrNullResult();
}
/**
* @return array
*/
public function getLocationTranslations($entity)
{
$queryBuilder = $this->createQueryBuilder('location');
$queryBuilder->select('location.alias as aliasAr', 'location.title as titleAr', 'location.description as descriptionAr', 'translation.field', 'translation.content');
$queryBuilder->join('location.translations', 'translation', Expr\Join::WITH, 'translation.object = location.id');
$queryBuilder->andWhere($queryBuilder->expr()->eq('translation.locale', ':locale'))->setParameter('locale', 'en');
$queryBuilder->andWhere($queryBuilder->expr()->eq('location.id', ':id'))->setParameter('id', $entity->getId());
$results = $queryBuilder->getQuery()->getArrayResult();
$data = [];
foreach ($results as $result) {
$data['titleAr'] = $result['titleAr'];
$data['aliasAr'] = $result['aliasAr'];
$data['descriptionAr'] = $result['descriptionAr'];
if ('title' == $result['field']) {
$data['titleEn'] = $result['content'];
}
if ('alias' == $result['field']) {
$data['aliasEn'] = $result['content'];
}
if ('description' == $result['field']) {
$data['descriptionEn'] = $result['content'];
}
}
$data['titleAr'] ??= '';
$data['titleEn'] ??= '';
$data['aliasAr'] ??= '';
$data['aliasEn'] ??= '';
$data['descriptionAr'] ??= '';
$data['descriptionEn'] ??= '';
return $data;
}
/**
* @return array
*/
public function getCompoundLocations(array $criteria)
{
$queryBuilder = $this->createQueryBuilder('location');
$queryBuilder->select('location.id');
if (isset($criteria['title'])) {
$queryBuilder->andWhere($queryBuilder->expr()->like('location.title', ':title'))
->setParameter('title', "%{$criteria['title']}%");
}
return $queryBuilder->getQuery()->getResult();
}
/**
* @return void
*/
public function updateIsCompoundLocation(array $locationIDs)
{
$queryBuilder = $this->createQueryBuilder('location');
return $queryBuilder->update()
->set('location.isCompoundLocation', ':status')
->setParameter('status', 1)
->where($queryBuilder->expr()->in('location', ':locations'))
->setParameter('locations', $locationIDs)
->getQuery()
->execute();
}
public function getLocationsBySlugs(array $locationSlugs): array
{
$queryBuilder = $this->createQueryBuilder('l');
$query = $queryBuilder->select('l')
->where($queryBuilder->expr()->in('l.slug', ':locationSlugs'))
->setParameter('locationSlugs', $locationSlugs)
->getQuery();
return $query->getResult();
}
public function getHomeCompoundLocations(int $limit): array
{
$queryBuilder = $this->createQueryBuilder('location');
$query = $queryBuilder
->where($queryBuilder->expr()->in('location.homeFilter', ':homeFilter'))
->setParameter('homeFilter', true, \PDO::PARAM_BOOL)
->andWhere($queryBuilder->expr()->in('location.isCompoundLocation', ':isCompoundLocation'))
->setParameter('isCompoundLocation', true, \PDO::PARAM_BOOL)
->andWhere($queryBuilder->expr()->isNotNull('location.listing'))
;
$queryBuilder->setMaxResults($limit);
return $query->getQuery()->enableResultCache(3600)->getResult();
}
/**
* @throws NonUniqueResultException
* @throws NoResultException
*/
public function getListingsCount(Location $location): int
{
$queryBuilder = $this->createQueryBuilder('location');
$children = $this->getLocationChildrenIds($location);
$queryBuilder->select('COUNT(listing.id) as listingsCount')
->join('location.listings', 'listing')
->andWhere($queryBuilder->expr()->in('location.id', ':locationId'))
->andWhere($queryBuilder->expr()->eq('listing.status', ':status'))
->setParameters([
'locationId' => $children,
'status' => ListingStatus::LIVE,
]);
return $queryBuilder->getQuery()->getSingleScalarResult();
}
/**
* @return QueryBuilder
*/
private function getParents(Location $location)
{
$queryBuilder = $this->createQueryBuilder('location');
$queryBuilder->where($queryBuilder->expr()->lt('location.left', ':left'))
->andWhere($queryBuilder->expr()->gt('location.right', ':right'))
->andWhere($queryBuilder->expr()->eq('location.root', ':root'))
->setParameter('left', $location->getLeft())
->setParameter('right', $location->getRight())
->setParameter('root', $location->getRoot());
return $queryBuilder;
}
}