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('rapsys_air_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.updated) 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, 3) 
 148 ORDER BY a.city, SUBSTRING(a.zipcode, 1, 3) 
 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('rapsys_air_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('rapsys_air_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, 3) 
 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('rapsys_air_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 ? 
 364         MAX(a.updated) AS modified, 
 373         FROM Rapsys\AirBundle\Entity\Location AS l 
 374         WHERE l.latitude BETWEEN :minlat AND :maxlat AND l.longitude BETWEEN :minlong AND :maxlong 
 377 LEFT JOIN Rapsys\AirBundle\Entity\Session s ON (s.location_id = a.id) 
 379 ORDER BY COUNT(IF(s.date BETWEEN :begin AND :end, s.id, NULL)) DESC, count DESC, a.id 
 382                 //Replace bundle entity name by table name 
 383                 $req = str_replace($this->tableKeys
, $this->tableValues
, $req); 
 385                 //Get result set mapping instance 
 386                 //XXX: DEBUG: see ../blog.orig/src/Rapsys/BlogBundle/Repository/ArticleRepository.php 
 387                 $rsm = new ResultSetMapping(); 
 390                 //XXX: see vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/Types.php 
 391                 //addScalarResult($sqlColName, $resColName, $type = 'string'); 
 392                 $rsm->addScalarResult('id', 'id', 'integer') 
 393                         ->addScalarResult('title', 'title', 'string') 
 394                         ->addScalarResult('latitude', 'latitude', 'float') 
 395                         ->addScalarResult('longitude', 'longitude', 'float') 
 396                         ->addScalarResult('updated', 'updated', 'datetime') 
 397                         ->addScalarResult('modified', 'modified', 'datetime') 
 398                         ->addScalarResult('count', 'count', 'integer'); 
 402                         ->createNativeQuery($req, $rsm) 
 403                         ->setParameter('begin', $period->getStartDate()) 
 404                         ->setParameter('end', $period->getEndDate()) 
 405                         ->setParameter('minlat', $minlat) 
 406                         ->setParameter('maxlat', $maxlat) 
 407                         ->setParameter('minlong', $minlong) 
 408                         ->setParameter('maxlong', $maxlong) 
 414                 //Iterate on each location 
 415                 foreach($result as $id => $data) { 
 416                         //With active locations 
 417                         if ($count && $data['count'] < $count) { 
 418                                 //Skip unactive locations 
 425                                 'title' => $title = $this->translator
->trans($data['title']), 
 426                                 'latitude' => $data['latitude'], 
 427                                 'longitude' => $data['longitude'], 
 428                                 'updated' => $data['updated'], 
 429                                 'modified' => $data['modified'], 
 430                                 'count' => $data['count'], 
 431                                 'slug' => $slug = $this->slugger
->slug($title), 
 432                                 'link' => $this->router
->generate('rapsys_air_location_view', ['id' => $data['id'], 'location' => $slug]) 
 441          * Find locations by user id sorted by period as array 
 443          * @param int $userId The user id 
 444          * @param DatePeriod $period The period 
 445          * @return array The locations array 
 447         public function findAllByUserIdAsArray(int $userId, \DatePeriod 
$period, $distance = 15): array { 
 449                 //TODO: ajouter pays ??? 
 460         COUNT(s3.id) AS tcount 
 469                 COUNT(s2.id) AS pcount, 
 470                 MAX(b.updated) AS modified 
 484                         FROM applications AS a 
 485                         JOIN sessions AS s ON (s.id = a.session_id) 
 486                         JOIN locations AS l ON (l.id = s.location_id) 
 487                         WHERE a.user_id = :id 
 493                 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 
 498         LEFT JOIN sessions AS s2 ON (s2.location_id = b.id AND s2.date BETWEEN :begin AND :end) 
 503 LEFT JOIN sessions AS s3 ON (s3.location_id = a.id) 
 505 ORDER BY pcount DESC, tcount DESC, a.id 
 508                 //Replace bundle entity name by table name 
 509                 $req = str_replace($this->tableKeys
, $this->tableValues
, $req); 
 511                 //Get result set mapping instance 
 512                 //XXX: DEBUG: see ../blog.orig/src/Rapsys/BlogBundle/Repository/ArticleRepository.php 
 513                 $rsm = new ResultSetMapping(); 
 516                 //XXX: see vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/Types.php 
 517                 //addScalarResult($sqlColName, $resColName, $type = 'string'); 
 518                 $rsm->addScalarResult('id', 'id', 'integer') 
 519                         ->addScalarResult('title', 'title', 'string') 
 520                         ->addScalarResult('city', 'city', 'string') 
 521                         ->addScalarResult('latitude', 'latitude', 'float') 
 522                         ->addScalarResult('longitude', 'longitude', 'float') 
 523                         ->addScalarResult('updated', 'updated', 'datetime') 
 524                         ->addScalarResult('pcount', 'pcount', 'integer') 
 525                         ->addScalarResult('tcount', 'tcount', 'integer') 
 526                         ->addScalarResult('modified', 'modified', 'datetime'); 
 530                         ->createNativeQuery($req, $rsm) 
 531                         ->setParameter('begin', $period->getStartDate()) 
 532                         ->setParameter('end', $period->getEndDate()) 
 533                         ->setParameter('id', $userId) 
 534                         ->setParameter('distance', $distance) 
 540                 //Iterate on each location 
 541                 foreach($result as $id => $data) { 
 542                         //With active locations 
 543                         if (!empty($result[0]['tcount']) && empty($data['tcount'])) { 
 544                                 //Skip unactive locations 
 551                                 'city' => $data['city'], 
 552                                 'title' => $title = $this->translator
->trans($data['title']), 
 553                                 'at' => $this->translator
->trans('at '.$data['title']), 
 554                                 'miniature' => $this->translator
->trans($data['title'].' miniature'), 
 555                                 'latitude' => $data['latitude'], 
 556                                 'longitude' => $data['longitude'], 
 557                                 'updated' => $data['updated'], 
 558                                 'pcount' => $data['pcount'], 
 559                                 'tcount' => $data['tcount'], 
 560                                 'modified' => $data['modified'], 
 561                                 'slug' => $slug = $this->slugger
->slug($title), 
 562                                 'link' => $this->router
->generate('rapsys_air_location_view', ['id' => $data['id'], 'location' => $slug]) 
 571          * Find location as array by id 
 573          * @param int $id The location id 
 574          * @param string $locale The locale 
 575          * @return array The location data 
 577         public function findOneByIdAsArray(int $id, string $locale): ?array { 
 588         MAX(l2.updated) AS updated, 
 589         SUBSTRING(l.zipcode, 1, 2) AS city_id, 
 590         ROUND(AVG(l2.latitude), 6) AS city_latitude, 
 591         ROUND(AVG(l2.longitude), 6) AS city_longitude 
 592 FROM Rapsys\AirBundle\Entity\Location AS l 
 593 JOIN Rapsys\AirBundle\Entity\Location AS l2 ON (l2.city = l.city AND SUBSTRING(l.zipcode, 1, 3) = SUBSTRING(l.zipcode, 1, 3)) 
 599                 //Replace bundle entity name by table name 
 600                 $req = str_replace($this->tableKeys
, $this->tableValues
, $req); 
 602                 //Get result set mapping instance 
 603                 //XXX: DEBUG: see ../blog.orig/src/Rapsys/BlogBundle/Repository/ArticleRepository.php 
 604                 $rsm = new ResultSetMapping(); 
 607                 //XXX: see vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/Types.php 
 608                 //addScalarResult($sqlColName, $resColName, $type = 'string'); 
 609                 $rsm->addScalarResult('id', 'id', 'integer') 
 610                         ->addScalarResult('title', 'title', 'string') 
 611                         ->addScalarResult('city', 'city', 'string') 
 612                         ->addScalarResult('latitude', 'latitude', 'float') 
 613                         ->addScalarResult('longitude', 'longitude', 'float') 
 614                         ->addScalarResult('indoor', 'indoor', 'boolean') 
 615                         ->addScalarResult('zipcode', 'zipcode', 'string') 
 616                         ->addScalarResult('updated', 'updated', 'datetime') 
 617                         ->addScalarResult('city_id', 'city_id', 'integer') 
 618                         ->addScalarResult('city_latitude', 'city_latitude', 'float') 
 619                         ->addScalarResult('city_longitude', 'city_longitude', 'float') 
 620                         ->addIndexByScalar('id'); 
 624                         ->createNativeQuery($req, $rsm) 
 625                         ->setParameter('id', $id) 
 626                         ->getOneOrNullResult(); 
 629                 if ($result === null) { 
 635                 $result['alternates'] = []; 
 638                 $route = 'rapsys_air_location_view'; 
 641                 $routeParams = ['id' => $id]; 
 643                 //Iterate on each languages 
 644                 foreach($this->languages 
as $languageId => $language) { 
 645                         //Without current locale 
 646                         if ($languageId !== $locale) { 
 650                                 //Set route params locale 
 651                                 $routeParams['_locale'] = $languageId; 
 653                                 //Set route params location 
 654                                 $routeParams['location'] = $this->slugger
->slug($this->translator
->trans($result['title'], [], null, $languageId)); 
 656                                 //Iterate on each locales 
 657                                 foreach(array_keys($this->languages
) as $other) { 
 658                                         //Without other locale 
 659                                         if ($other !== $languageId) { 
 660                                                 //Set other locale title 
 661                                                 $titles[$other] = $this->translator
->trans($language, [], null, $other); 
 665                                 //Add alternates locale 
 666                                 $result['alternates'][substr($languageId, 0, 2)] = $result['alternates'][str_replace('_', '-', $languageId)] = [ 
 667                                         'absolute' => $this->router
->generate($route, $routeParams, UrlGeneratorInterface
::ABSOLUTE_URL
), 
 668                                         'relative' => $this->router
->generate($route, $routeParams), 
 669                                         'title' => implode('/', $titles), 
 670                                         'translated' => $this->translator
->trans($language, [], null, $languageId) 
 677                         'id' => $result['id'], 
 679                                 'id' => $result['city_id'], 
 680                                 'title' => $result['city'], 
 681                                 'in' => $this->translator
->trans('in '.$result['city']), 
 682                                 'link' => $this->router
->generate('rapsys_air_city_view', ['city' => $result['city'], 'latitude' => $result['city_latitude'], 'longitude' => $result['city_longitude']]) 
 684                         'title' => $title = $this->translator
->trans($result['title']), 
 685                         'latitude' => $result['latitude'], 
 686                         'longitude' => $result['longitude'], 
 687                         'indoor' => $result['indoor'], 
 688                         'updated' => $result['updated'], 
 689                         'around' => $this->translator
->trans('around '.$result['title']), 
 690                         'at' => $this->translator
->trans('at '.$result['title']), 
 691                         'atin' => $this->translator
->trans('at '.$result['title']).' '.$this->translator
->trans('in '.$result['city']), 
 692                         'multimap' => $this->translator
->trans($result['title'].' sector map'), 
 694                         'slug' => $slug = $this->slugger
->slug($title), 
 695                         'link' => $this->router
->generate($route, ['_locale' => $locale, 'location' => $slug]+
$routeParams), 
 696                         'alternates' => $result['alternates'] 
 701          * Find complementary locations by session id 
 703          * @param int $id The session id 
 704          * @return array The other locations 
 706         public function findComplementBySessionId(int $id): array { 
 707                 //Fetch complement locations 
 708                 $ret = $this->getEntityManager() 
 709                           ->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') 
 710                         ->setParameter('sid', $id) 
 713                 //TODO: try to improve with: 
 714                 #->addIndexByScalar('city'); 
 717                 $ret = array_column($ret, 'id', 'title'); 
 723          * Find locations by user id 
 725          * @param int $id The user id 
 726          * @return array The user locations 
 728         public function findByUserId(int $userId): array { 
 730                 $req = 'SELECT l.id, l.title 
 731 FROM Rapsys\AirBundle\Entity\UserLocation AS ul 
 732 JOIN Rapsys\AirBundle\Entity\Location AS l ON (l.id = ul.location_id) 
 733 WHERE ul.user_id = :id'; 
 735                 //Replace bundle entity name by table name 
 736                 $req = str_replace($this->tableKeys
, $this->tableValues
, $req); 
 738                 //Get result set mapping instance 
 739                 //XXX: DEBUG: see ../blog.orig/src/Rapsys/BlogBundle/Repository/ArticleRepository.php 
 740                 $rsm = new ResultSetMapping(); 
 742                 //Declare result set for our request 
 743                 $rsm->addEntityResult('Rapsys\AirBundle\Entity\Location', 'l') 
 744                         ->addFieldResult('l', 'id', 'id') 
 745                         ->addFieldResult('l', 'title', 'title'); 
 749                         ->createNativeQuery($req, $rsm) 
 750                         ->setParameter('id', $userId)