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)