1 <?php 
declare(strict_types
=1); 
   4  * This file is part of the Rapsys AirBundle package. 
   6  * (c) Raphaël Gertz <symfony@rapsys.eu> 
   8  * For the full copyright and license information, please view the LICENSE 
   9  * file that was distributed with this source code. 
  12 namespace Rapsys\AirBundle\Repository
; 
  14 use Doctrine\ORM\Query\ResultSetMapping
; 
  16 use Symfony\Component\Routing\Generator\UrlGeneratorInterface
; 
  17 use Symfony\Component\Routing\RouterInterface
; 
  19 use Rapsys\AirBundle\Repository
; 
  24  * @TODO: use new window function syntax https://mariadb.com/kb/en/window-functions-overview/ MAX(updated) OVER (PARTITION updated) AS modified ??? 
  26 class LocationRepository 
extends Repository 
{ 
  32         public function findAll(): array { 
  33                 //Get all locations index by id 
  34                 return $this->createQueryBuilder('location', 'location.id')->getQuery()->getResult(); 
  38          * Find locations as array 
  40          * @param DatePeriod $period The period 
  41          * @return array The locations array 
  43         public function findAllAsArray(\DatePeriod 
$period): array { 
  45                 //TODO: ajouter pays ??? 
  54 FROM Rapsys\AirBundle\Entity\Location AS l 
  55 LEFT JOIN Rapsys\AirBundle\Entity\Session AS s ON (l.id = s.location_id) 
  57 ORDER BY COUNT(IF(s.date BETWEEN :begin AND :end, s.id, NULL)) DESC, COUNT(s.id) DESC, l.id 
  60                 //Replace bundle entity name by table name 
  61                 $req = str_replace($this->tableKeys
, $this->tableValues
, $req); 
  63                 //Get result set mapping instance 
  64                 //XXX: DEBUG: see ../blog.orig/src/Rapsys/BlogBundle/Repository/ArticleRepository.php 
  65                 $rsm = new ResultSetMapping(); 
  68                 //XXX: see vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/Types.php 
  69                 //addScalarResult($sqlColName, $resColName, $type = 'string'); 
  70                 $rsm->addScalarResult('id', 'id', 'integer') 
  71                         ->addScalarResult('title', 'title', 'string') 
  72                         ->addScalarResult('latitude', 'latitude', 'float') 
  73                         ->addScalarResult('longitude', 'longitude', 'float') 
  74                         ->addScalarResult('indoor', 'indoor', 'boolean') 
  75                         ->addScalarResult('count', 'count', 'integer') 
  76                         ->addScalarResult('updated', 'updated', 'datetime'); 
  80                         ->createNativeQuery($req, $rsm) 
  81                         ->setParameter('begin', $period->getStartDate()) 
  82                         ->setParameter('end', $period->getEndDate()) 
  88                 //Iterate on each city 
  89                 foreach($result as $data) { 
  93                                 'title' => $title = $this->translator
->trans($data['title']), 
  94                                 'latitude' => $data['latitude'], 
  95                                 'longitude' => $data['longitude'], 
  96                                 'updated' => $data['updated'], 
  98                                 'slug' => $location = $this->slugger
->slug($title), 
  99                                 'link' => $this->router
->generate('rapsysair_location_view', ['id' => $data['id'], 'location' => $this->slugger
->slug($location)]) 
 108          * Find cities as array 
 110          * @param DatePeriod $period The period 
 111          * @param int $count The session count 
 112          * @return array The cities array 
 114         public function findCitiesAsArray(\DatePeriod 
$period, int $count = 1): array { 
 118         SUBSTRING(a.zipcode, 1, 2) AS id, 
 120         ROUND(AVG(a.latitude), 6) AS latitude, 
 121         ROUND(AVG(a.longitude), 6) AS longitude, 
 122         GROUP_CONCAT(a.id ORDER BY a.pcount DESC, a.count DESC, a.id SEPARATOR "\\n") AS ids, 
 123         GROUP_CONCAT(a.title ORDER BY a.pcount DESC, a.count DESC, a.id SEPARATOR "\\n") AS titles, 
 124         GROUP_CONCAT(a.latitude ORDER BY a.pcount DESC, a.count DESC, a.id SEPARATOR "\\n") AS latitudes, 
 125         GROUP_CONCAT(a.longitude ORDER BY a.pcount DESC, a.count DESC, a.id SEPARATOR "\\n") AS longitudes, 
 126         GROUP_CONCAT(a.indoor ORDER BY a.pcount DESC, a.count DESC, a.id SEPARATOR "\\n") AS indoors, 
 127         GROUP_CONCAT(a.count ORDER BY a.pcount DESC, a.count DESC, a.id SEPARATOR "\\n") AS counts, 
 128         MAX(a.modified) AS modified 
 137                 GREATEST(l.created, l.updated, COALESCE(s.created, 0), COALESCE(s.updated, 0)) AS modified, 
 139                 COUNT(s.id) AS count, 
 140                 COUNT(IF(s.date BETWEEN :begin AND :end, s.id, NULL)) AS pcount 
 141         FROM Rapsys\AirBundle\Entity\Location AS l 
 142         LEFT JOIN Rapsys\AirBundle\Entity\Session AS s ON (l.id = s.location_id) 
 147 GROUP BY a.city, SUBSTRING(a.zipcode, 1, 2) 
 148 ORDER BY a.city, a.zipcode 
 151                 //Replace bundle entity name by table name 
 152                 $req = str_replace($this->tableKeys
, $this->tableValues
, $req); 
 154                 //Get result set mapping instance 
 155                 //XXX: DEBUG: see ../blog.orig/src/Rapsys/BlogBundle/Repository/ArticleRepository.php 
 156                 $rsm = new ResultSetMapping(); 
 159                 //XXX: see vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/Types.php 
 160                 //addScalarResult($sqlColName, $resColName, $type = 'string'); 
 161                 $rsm->addScalarResult('id', 'id', 'integer') 
 162                         ->addScalarResult('city', 'city', 'string') 
 163                         ->addScalarResult('latitude', 'latitude', 'float') 
 164                         ->addScalarResult('longitude', 'longitude', 'float') 
 165                         //XXX: is a string because of \n separator 
 166                         ->addScalarResult('ids', 'ids', 'string') 
 167                         //XXX: is a string because of \n separator 
 168                         ->addScalarResult('titles', 'titles', 'string') 
 169                         //XXX: is a string because of \n separator 
 170                         ->addScalarResult('latitudes', 'latitudes', 'string') 
 171                         //XXX: is a string because of \n separator 
 172                         ->addScalarResult('longitudes', 'longitudes', 'string') 
 173                         //XXX: is a string because of \n separator 
 174                         ->addScalarResult('indoors', 'indoors', 'string') 
 175                         //XXX: is a string because of \n separator 
 176                         ->addScalarResult('counts', 'counts', 'string') 
 177                         ->addScalarResult('modified', 'modified', 'datetime') 
 178                         ->addIndexByScalar('city'); 
 182                         ->createNativeQuery($req, $rsm) 
 183                         ->setParameter('begin', $period->getStartDate()) 
 184                         ->setParameter('end', $period->getEndDate()) 
 190                 //Iterate on each city 
 191                 foreach($result as $city => $data) { 
 193                         $titles = explode("\n", $data['titles']); 
 196                         $latitudes = explode("\n", $data['latitudes']); 
 199                         $longitudes = explode("\n", $data['longitudes']); 
 202                         $indoors = explode("\n", $data['indoors']); 
 205                         $counts = explode("\n", $data['counts']); 
 207                         //With unsufficient count 
 208                         if ($count && $counts[0] < $count) { 
 210                                 //XXX: count are sorted so only check first 
 215                         $data['locations'] = []; 
 217                         //Iterate on each location 
 218                         foreach(explode("\n", $data['ids']) as $k => $id) { 
 219                                 //With unsufficient count 
 220                                 if ($count && $counts[$k] < $count) { 
 222                                         //XXX: count are sorted so only check first 
 227                                 $data['locations'][] = [ 
 229                                         'title' => $location = $this->translator
->trans($titles[$k]), 
 230                                         'latitude' => floatval($latitudes[$k]), 
 231                                         'longitude' => floatval($longitudes[$k]), 
 232                                         'indoor' => $indoors[$k] == 0 ? $this->translator
->trans('outdoor') : $this->translator
->trans('indoor'), 
 233                                         'link' => $this->router
->generate('rapsysair_location_view', ['id' => $id, 'location' => $this->slugger
->slug($location)]) 
 240                                 'city' => $data['city'], 
 241                                 'in' => $this->translator
->trans('in '.$data['city']), 
 242                                 'indoors' => array_map(function ($v) { return $v 
== 0 ? $this
->translator
->trans('outdoor') : $this
->translator
->trans('indoor'); }, array_unique($indoors)), 
 243                                 'multimap' => $this->translator
->trans($data['city'].' sector map'), 
 244                                 'latitude' => $data['latitude'], 
 245                                 'longitude' => $data['longitude'], 
 246                                 'modified' => $data['modified'], 
 248                                 'slug' => $city = $this->slugger
->slug($data['city']), 
 249                                 'link' => $this->router
->generate('rapsysair_city_view', ['city' => $city, 'latitude' => $data['latitude'], 'longitude' => $data['longitude']]), 
 250                                 'locations' => $data['locations'] 
 259          * Find city by latitude and longitude as array 
 261          * @param float $latitude The latitude 
 262          * @param float $longitude The longitude 
 263          * @return ?array The cities array 
 265         public function findCityByLatitudeLongitudeAsArray(float $latitude, float $longitude): ?array { 
 269         SUBSTRING(l.zipcode, 1, 2) AS id, 
 271         ROUND(AVG(l.latitude), 6) AS latitude, 
 272         ROUND(AVG(l.longitude), 6) AS longitude, 
 273         MAX(l.updated) AS updated 
 274 FROM Rapsys\AirBundle\Entity\Location AS l 
 275 GROUP BY city, SUBSTRING(l.zipcode, 1, 2) 
 276 ORDER BY ACOS(SIN(RADIANS(:latitude))*SIN(RADIANS(l.latitude))+COS(RADIANS(:latitude))*COS(RADIANS(l.latitude))*COS(RADIANS(:longitude - l.longitude)))*40030.17/2/PI() 
 280                 //Replace bundle entity name by table name 
 281                 $req = str_replace($this->tableKeys
, $this->tableValues
, $req); 
 283                 //Get result set mapping instance 
 284                 //XXX: DEBUG: see ../blog.orig/src/Rapsys/BlogBundle/Repository/ArticleRepository.php 
 285                 $rsm = new ResultSetMapping(); 
 288                 //XXX: see vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/Types.php 
 289                 //addScalarResult($sqlColName, $resColName, $type = 'string'); 
 290                 $rsm->addScalarResult('id', 'id', 'integer') 
 291                         ->addScalarResult('city', 'city', 'string') 
 292                         ->addScalarResult('latitude', 'latitude', 'float') 
 293                         ->addScalarResult('longitude', 'longitude', 'float') 
 294                         ->addScalarResult('updated', 'updated', 'datetime') 
 295                         ->addIndexByScalar('city'); 
 299                         ->createNativeQuery($req, $rsm) 
 300                         ->setParameter('latitude', $latitude) 
 301                         ->setParameter('longitude', $longitude) 
 302                         ->getOneOrNullResult(); 
 305                 if ($result === null) { 
 312                         'id' => $result['id'], 
 313                         'city' => $result['city'], 
 314                         'latitude' => $result['latitude'], 
 315                         'longitude' => $result['longitude'], 
 316                         'updated' => $result['updated'], 
 317                         'in' => $this->translator
->trans('in '.$result['city']), 
 318                         'multimap' => $this->translator
->trans($result['city'].' sector map'), 
 320                         'slug' => $slug = $this->slugger
->slug($result['city']), 
 321                         'link' => $this->router
->generate('rapsysair_city_view', ['city' => $slug, 'latitude' => $result['latitude'], 'longitude' => $result['longitude']]) 
 326          * Find locations by latitude and longitude sorted by period as array 
 328          * @TODO: find all other locations when current one has no sessions ??? 
 330          * @param float $latitude The latitude 
 331          * @param float $longitude The longitude 
 332          * @param DatePeriod $period The period 
 333          * @param int $count The session count 
 334          * @param float $distance The distance 
 335          * @return array The locations array 
 337         public function findAllByLatitudeLongitudeAsArray(float $latitude, float $longitude, \DatePeriod 
$period, int $count = 1, float $distance = 15): array { 
 339                 $radius = 40030.17/2/pi(); 
 341                 //Compute min latitude 
 342                 $minlat = min(rad2deg(asin(sin(deg2rad($latitude))*cos($distance/$radius) + 
cos(deg2rad($latitude))*sin($distance/$radius)*cos(deg2rad(180)))), $latitude); 
 344                 //Compute max latitude 
 345                 $maxlat = max(rad2deg(asin(sin(deg2rad($latitude))*cos($distance/$radius) + 
cos(deg2rad($latitude))*sin($distance/$radius)*cos(deg2rad(0)))), $latitude); 
 347                 //Compute min longitude 
 348                 $minlong = fmod((rad2deg((deg2rad($longitude) + 
atan2(sin(deg2rad(-90))*sin($distance/$radius)*cos(deg2rad($minlat)), cos($distance/$radius) - sin(deg2rad($minlat)) * sin(deg2rad($minlat))))) + 
180), 360) - 180; 
 351                 $maxlong = fmod((rad2deg((deg2rad($longitude) + 
atan2(sin(deg2rad(90))*sin($distance/$radius)*cos(deg2rad($maxlat)), cos($distance/$radius) - sin(deg2rad($maxlat)) * sin(deg2rad($maxlat))))) + 
180), 360) - 180; 
 354                 //TODO: see old request before commit to sort session count, distance and then by id ? 
 355                 //TODO: see to sort by future session count, historical session count, distance and then by id ? 
 356                 //TODO: do the same for cities and city ? 
 365         MAX(GREATEST(a.modified, COALESCE(s.created, 0), COALESCE(s.updated, 0))) AS modified, 
 375                 GREATEST(l.created, l.updated) AS modified 
 376         FROM Rapsys\AirBundle\Entity\Location AS l 
 377         WHERE l.latitude BETWEEN :minlat AND :maxlat AND l.longitude BETWEEN :minlong AND :maxlong 
 380 LEFT JOIN Rapsys\AirBundle\Entity\Session s ON (s.location_id = a.id) 
 382 ORDER BY COUNT(IF(s.date BETWEEN :begin AND :end, s.id, NULL)) DESC, count DESC, a.id 
 385                 //Replace bundle entity name by table name 
 386                 $req = str_replace($this->tableKeys
, $this->tableValues
, $req); 
 388                 //Get result set mapping instance 
 389                 //XXX: DEBUG: see ../blog.orig/src/Rapsys/BlogBundle/Repository/ArticleRepository.php 
 390                 $rsm = new ResultSetMapping(); 
 393                 //XXX: see vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/Types.php 
 394                 //addScalarResult($sqlColName, $resColName, $type = 'string'); 
 395                 $rsm->addScalarResult('id', 'id', 'integer') 
 396                         ->addScalarResult('title', 'title', 'string') 
 397                         ->addScalarResult('latitude', 'latitude', 'float') 
 398                         ->addScalarResult('longitude', 'longitude', 'float') 
 399                         ->addScalarResult('created', 'created', 'datetime') 
 400                         ->addScalarResult('updated', 'updated', 'datetime') 
 401                         ->addScalarResult('modified', 'modified', 'datetime') 
 402                         ->addScalarResult('count', 'count', 'integer'); 
 406                         ->createNativeQuery($req, $rsm) 
 407                         ->setParameter('begin', $period->getStartDate()) 
 408                         ->setParameter('end', $period->getEndDate()) 
 409                         ->setParameter('minlat', $minlat) 
 410                         ->setParameter('maxlat', $maxlat) 
 411                         ->setParameter('minlong', $minlong) 
 412                         ->setParameter('maxlong', $maxlong) 
 418                 //Iterate on each location 
 419                 foreach($result as $id => $data) { 
 420                         //With active locations 
 421                         if ($count && $data['count'] < $count) { 
 422                                 //Skip unactive locations 
 429                                 'title' => $title = $this->translator
->trans($data['title']), 
 430                                 'latitude' => $data['latitude'], 
 431                                 'longitude' => $data['longitude'], 
 432                                 'created' => $data['created'], 
 433                                 'updated' => $data['updated'], 
 434                                 'modified' => $data['modified'], 
 435                                 'count' => $data['count'], 
 436                                 'slug' => $slug = $this->slugger
->slug($title), 
 437                                 'link' => $this->router
->generate('rapsysair_location_view', ['id' => $data['id'], 'location' => $slug]) 
 446          * Find locations by user id sorted by period as array 
 448          * @param int $userId The user id 
 449          * @param DatePeriod $period The period 
 450          * @return array The locations array 
 452         public function findAllByUserIdAsArray(int $userId, \DatePeriod 
$period, $distance = 15): array { 
 454                 //TODO: ajouter pays ??? 
 464         MAX(GREATEST(a.modified, COALESCE(s3.created, 0), COALESCE(s3.updated, 0))) AS modified, 
 466         COUNT(s3.id) AS tcount 
 476                 MAX(GREATEST(b.modified, COALESCE(s2.created, 0), COALESCE(s2.updated, 0))) AS modified, 
 477                 COUNT(s2.id) AS pcount 
 487                         GREATEST(l2.created, l2.updated) AS modified 
 493                         FROM applications AS a 
 494                         JOIN sessions AS s ON (s.id = a.session_id) 
 495                         JOIN locations AS l ON (l.id = s.location_id) 
 496                         WHERE a.user_id = :id 
 502                 WHERE ACOS(SIN(RADIANS(a.latitude))*SIN(RADIANS(l2.latitude))+COS(RADIANS(a.latitude))*COS(RADIANS(l2.latitude))*COS(RADIANS(a.longitude - l2.longitude)))*40030.17/2/PI() BETWEEN 0 AND :distance 
 507         LEFT JOIN sessions AS s2 ON (s2.location_id = b.id AND s2.date BETWEEN :begin AND :end) 
 512 LEFT JOIN sessions AS s3 ON (s3.location_id = a.id) 
 514 ORDER BY pcount DESC, tcount DESC, a.id 
 517                 //Replace bundle entity name by table name 
 518                 $req = str_replace($this->tableKeys
, $this->tableValues
, $req); 
 520                 //Get result set mapping instance 
 521                 //XXX: DEBUG: see ../blog.orig/src/Rapsys/BlogBundle/Repository/ArticleRepository.php 
 522                 $rsm = new ResultSetMapping(); 
 525                 //XXX: see vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/Types.php 
 526                 //addScalarResult($sqlColName, $resColName, $type = 'string'); 
 527                 $rsm->addScalarResult('id', 'id', 'integer') 
 528                         ->addScalarResult('title', 'title', 'string') 
 529                         ->addScalarResult('city', 'city', 'string') 
 530                         ->addScalarResult('latitude', 'latitude', 'float') 
 531                         ->addScalarResult('longitude', 'longitude', 'float') 
 532                         ->addScalarResult('created', 'created', 'datetime') 
 533                         ->addScalarResult('updated', 'updated', 'datetime') 
 534                         ->addScalarResult('modified', 'modified', 'datetime') 
 535                         ->addScalarResult('pcount', 'pcount', 'integer') 
 536                         ->addScalarResult('tcount', 'tcount', 'integer'); 
 540                         ->createNativeQuery($req, $rsm) 
 541                         ->setParameter('begin', $period->getStartDate()) 
 542                         ->setParameter('end', $period->getEndDate()) 
 543                         ->setParameter('id', $userId) 
 544                         ->setParameter('distance', $distance) 
 550                 //Iterate on each location 
 551                 foreach($result as $id => $data) { 
 552                         //With active locations 
 553                         if (!empty($result[0]['tcount']) && empty($data['tcount'])) { 
 554                                 //Skip unactive locations 
 561                                 'city' => $data['city'], 
 562                                 'title' => $title = $this->translator
->trans($data['title']), 
 563                                 'at' => $this->translator
->trans('at '.$data['title']), 
 564                                 'miniature' => $this->translator
->trans($data['title'].' miniature'), 
 565                                 'latitude' => $data['latitude'], 
 566                                 'longitude' => $data['longitude'], 
 567                                 'created' => $data['created'], 
 568                                 'updated' => $data['updated'], 
 569                                 'modified' => $data['modified'], 
 570                                 'pcount' => $data['pcount'], 
 571                                 'tcount' => $data['tcount'], 
 572                                 'slug' => $slug = $this->slugger
->slug($title), 
 573                                 'link' => $this->router
->generate('rapsysair_location_view', ['id' => $data['id'], 'location' => $slug]) 
 582          * Find location as array by id 
 584          * @param int $id The location id 
 585          * @param string $locale The locale 
 586          * @return array The location data 
 588         public function findOneByIdAsArray(int $id, string $locale): ?array { 
 599         MAX(GREATEST(l.created, l.updated, l2.created, l2.updated)) AS modified, 
 600         SUBSTRING(l.zipcode, 1, 2) AS city_id, 
 601         ROUND(AVG(l2.latitude), 6) AS city_latitude, 
 602         ROUND(AVG(l2.longitude), 6) AS city_longitude 
 603 FROM Rapsys\AirBundle\Entity\Location AS l 
 604 JOIN Rapsys\AirBundle\Entity\Location AS l2 ON (l2.city = l.city AND SUBSTRING(l2.zipcode, 1, 2) = SUBSTRING(l.zipcode, 1, 2)) 
 610                 //Replace bundle entity name by table name 
 611                 $req = str_replace($this->tableKeys
, $this->tableValues
, $req); 
 613                 //Get result set mapping instance 
 614                 //XXX: DEBUG: see ../blog.orig/src/Rapsys/BlogBundle/Repository/ArticleRepository.php 
 615                 $rsm = new ResultSetMapping(); 
 618                 //XXX: see vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/Types.php 
 619                 //addScalarResult($sqlColName, $resColName, $type = 'string'); 
 620                 $rsm->addScalarResult('id', 'id', 'integer') 
 621                         ->addScalarResult('title', 'title', 'string') 
 622                         ->addScalarResult('city', 'city', 'string') 
 623                         ->addScalarResult('latitude', 'latitude', 'float') 
 624                         ->addScalarResult('longitude', 'longitude', 'float') 
 625                         ->addScalarResult('indoor', 'indoor', 'boolean') 
 626                         ->addScalarResult('zipcode', 'zipcode', 'string') 
 627                         ->addScalarResult('modified', 'modified', 'datetime') 
 628                         ->addScalarResult('city_id', 'city_id', 'integer') 
 629                         ->addScalarResult('city_latitude', 'city_latitude', 'float') 
 630                         ->addScalarResult('city_longitude', 'city_longitude', 'float') 
 631                         ->addIndexByScalar('id'); 
 635                         ->createNativeQuery($req, $rsm) 
 636                         ->setParameter('id', $id) 
 637                         ->getOneOrNullResult(); 
 640                 if ($result === null) { 
 646                 $result['alternates'] = []; 
 649                 $route = 'rapsysair_location_view'; 
 652                 $routeParams = ['id' => $id]; 
 654                 //Iterate on each languages 
 655                 foreach($this->languages 
as $languageId => $language) { 
 656                         //Without current locale 
 657                         if ($languageId !== $locale) { 
 661                                 //Set route params locale 
 662                                 $routeParams['_locale'] = $languageId; 
 664                                 //Set route params location 
 665                                 $routeParams['location'] = $this->slugger
->slug($this->translator
->trans($result['title'], [], null, $languageId)); 
 667                                 //Iterate on each locales 
 668                                 foreach(array_keys($this->languages
) as $other) { 
 669                                         //Without other locale 
 670                                         if ($other !== $languageId) { 
 671                                                 //Set other locale title 
 672                                                 $titles[$other] = $this->translator
->trans($language, [], null, $other); 
 676                                 //Add alternates locale 
 677                                 $result['alternates'][substr($languageId, 0, 2)] = $result['alternates'][str_replace('_', '-', $languageId)] = [ 
 678                                         'absolute' => $this->router
->generate($route, $routeParams, UrlGeneratorInterface
::ABSOLUTE_URL
), 
 679                                         'relative' => $this->router
->generate($route, $routeParams), 
 680                                         'title' => implode('/', $titles), 
 681                                         'translated' => $this->translator
->trans($language, [], null, $languageId) 
 688                         'id' => $result['id'], 
 690                                 'id' => $result['city_id'], 
 691                                 'title' => $result['city'], 
 692                                 'in' => $this->translator
->trans('in '.$result['city']), 
 693                                 'link' => $this->router
->generate('rapsysair_city_view', ['city' => $result['city'], 'latitude' => $result['city_latitude'], 'longitude' => $result['city_longitude']]) 
 695                         'title' => $title = $this->translator
->trans($result['title']), 
 696                         'latitude' => $result['latitude'], 
 697                         'longitude' => $result['longitude'], 
 698                         'indoor' => $result['indoor'], 
 699                         'modified' => $result['modified'], 
 700                         'around' => $this->translator
->trans('around '.$result['title']), 
 701                         'at' => $this->translator
->trans('at '.$result['title']), 
 702                         'atin' => $this->translator
->trans('at '.$result['title']).' '.$this->translator
->trans('in '.$result['city']), 
 703                         'multimap' => $this->translator
->trans($result['title'].' sector map'), 
 705                         'slug' => $slug = $this->slugger
->slug($title), 
 706                         'link' => $this->router
->generate($route, ['_locale' => $locale, 'location' => $slug]+
$routeParams), 
 707                         'alternates' => $result['alternates'] 
 712          * Find complementary locations by session id 
 714          * @param int $id The session id 
 715          * @return array The other locations 
 717         public function findComplementBySessionId(int $id): array { 
 718                 //Fetch complement locations 
 719                 $ret = $this->getEntityManager() 
 720                           ->createQuery('SELECT l.id, l.title FROM Rapsys\AirBundle\Entity\Session s LEFT JOIN Rapsys\AirBundle\Entity\Session s2 WITH s2.id != s.id AND s2.slot = s.slot AND s2.date = s.date LEFT JOIN Rapsys\AirBundle\Entity\Location l WITH l.id != s.location AND (l.id != s2.location OR s2.location IS NULL) WHERE s.id = :sid GROUP BY l.id ORDER BY l.id') 
 721                         ->setParameter('sid', $id) 
 724                 //TODO: try to improve with: 
 725                 #->addIndexByScalar('city'); 
 728                 $ret = array_column($ret, 'id', 'title'); 
 734          * Find locations by user id 
 736          * @param int $id The user id 
 737          * @return array The user locations 
 739         public function findByUserId(int $userId): array { 
 741                 $req = 'SELECT l.id, l.title 
 742 FROM Rapsys\AirBundle\Entity\UserLocation AS ul 
 743 JOIN Rapsys\AirBundle\Entity\Location AS l ON (l.id = ul.location_id) 
 744 WHERE ul.user_id = :id'; 
 746                 //Replace bundle entity name by table name 
 747                 $req = str_replace($this->tableKeys
, $this->tableValues
, $req); 
 749                 //Get result set mapping instance 
 750                 //XXX: DEBUG: see ../blog.orig/src/Rapsys/BlogBundle/Repository/ArticleRepository.php 
 751                 $rsm = new ResultSetMapping(); 
 753                 //Declare result set for our request 
 754                 $rsm->addEntityResult('Rapsys\AirBundle\Entity\Location', 'l') 
 755                         ->addFieldResult('l', 'id', 'id') 
 756                         ->addFieldResult('l', 'title', 'title'); 
 760                         ->createNativeQuery($req, $rsm) 
 761                         ->setParameter('id', $userId)