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\Controller
;
14 use Symfony\Component\HttpFoundation\Request
;
15 use Symfony\Component\HttpFoundation\Response
;
17 use Rapsys\AirBundle\Entity\Dance
;
18 use Rapsys\AirBundle\Entity\Location
;
19 use Rapsys\AirBundle\Entity\Session
;
24 class LocationController
extends DefaultController
{
28 * @param Request $request The request instance
29 * @return Response The rendered view
31 public function cities(Request
$request): Response
{
33 $this->context
['cities'] = $this->doctrine
->getRepository(Location
::class)->findCitiesAsArray($this->period
);
36 $this->context
['dances'] = $this->doctrine
->getRepository(Dance
::class)->findNamesAsArray();
39 $response = new Response();
42 $this->modified
= max(array_map(function ($v) { return $v
['modified']; }, array_merge($this->context
['cities'], $this->context
['dances'])));
45 foreach($this->context
['cities'] as $id => $city) {
47 #$this->osm->getMultiImage($city['link'], $city['osm'], $this->modified->getTimestamp(), $city['latitude'], $city['longitude'], $city['locations'], $this->osm->getMultiZoom($city['latitude'], $city['longitude'], $city['locations'], 16));
48 $this->context
['cities'][$id]['multimap'] = $this->map
->getMultiMap($city['multimap'], $this->modified
->getTimestamp(), $city['latitude'], $city['longitude'], $city['locations'], $this->map
->getMultiZoom($city['latitude'], $city['longitude'], $city['locations']));
52 if ($this->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
54 $response->setLastModified(new \
DateTime('-1 year'));
57 $response->setPrivate();
61 //XXX: only for public to force revalidation by last modified
62 $response->setEtag(md5(serialize(array_merge($this->context
['cities'], $this->context
['dances']))));
65 $response->setLastModified($this->modified
);
68 $response->setPublic();
70 //Without role and modification
71 if ($response->isNotModified($request)) {
78 $this->context
['title'] = $this->translator
->trans('Libre Air cities');
81 $this->context
['description'] = $this->translator
->trans('Libre Air city list');
84 $cities = array_map(function ($v) { return $v
['in']; }, $this->context
['cities']);
87 $dances = array_map(function ($v) { return $v
['name']; }, $this->context
['dances']);
90 $indoors = array_reduce($this->context
['cities'], function ($c, $v) { return array_merge($c
, $v
['indoors']); }, []);
93 $this->context
['keywords'] = array_values(
96 $this->translator
->trans('Cities'),
97 $this->translator
->trans('City list'),
98 $this->translator
->trans('Listing'),
103 $this->translator
->trans('calendar'),
104 $this->translator
->trans('Libre Air')
110 return $this->render('@RapsysAir/location/cities.html.twig', $this->context
, $response);
116 * @todo XXX: TODO: add <link rel="prev|next" for sessions or classes ? />
117 * @todo XXX: TODO: like described in: https://www.alsacreations.com/article/lire/1400-attribut-rel-relations.html#xnf-rel-attribute
118 * @todo XXX: TODO: or here: http://microformats.org/wiki/existing-rel-values#HTML5_link_type_extensions
120 * @param Request $request The request instance
121 * @param float $latitude The city latitude
122 * @param float $longitude The city longitude
123 * @return Response The rendered view
125 public function city(Request
$request, float $latitude, float $longitude, string $city): Response
{
127 if (!($this->context
['city'] = $this->doctrine
->getRepository(Location
::class)->findCityByLatitudeLongitudeAsArray(floatval($latitude), floatval($longitude)))) {
128 throw $this->createNotFoundException($this->translator
->trans('Unable to find city: %latitude%,%longitude%', ['%latitude%' => $latitude, '%longitude%' => $longitude]));
132 $this->context
['calendar'] = $this->doctrine
->getRepository(Session
::class)->findAllByPeriodAsArray($this->period
, $request->getLocale(), !$this->isGranted('IS_AUTHENTICATED_REMEMBERED'), floatval($latitude), floatval($longitude));
135 $this->context
['dances'] = [];
137 //Iterate on each calendar
138 foreach($this->context
['calendar'] as $date => $calendar) {
139 //Iterate on each session
140 foreach($calendar['sessions'] as $sessionId => $session) {
141 //Session with application dance
142 if (!empty($session['application']['dance'])) {
144 $this->context
['dances'][$session['application']['dance']['id']] = $session['application']['dance'];
150 $this->context
['locations'] = $this->doctrine
->getRepository(Location
::class)->findAllByLatitudeLongitudeAsArray(floatval($latitude), floatval($longitude), $this->period
);
153 //XXX: dance modified is already computed inside calendar modified
154 $this->modified
= max(array_merge([$this->context
['city']['updated']], array_map(function ($v) { return $v
['modified']; }, array_merge($this->context
['calendar'], $this->context
['locations']))));
157 $response = new Response();
160 if ($this->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
162 $response->setLastModified(new \
DateTime('-1 year'));
165 $response->setPrivate();
166 //Without logged user
169 //XXX: only for public to force revalidation by last modified
170 $response->setEtag(md5(serialize(array_merge($this->context
['city'], $this->context
['dances'], $this->context
['calendar'], $this->context
['locations']))));
173 $response->setLastModified($this->modified
);
176 $response->setPublic();
178 //Without role and modification
179 if ($response->isNotModified($request)) {
180 //Return 304 response
186 $this->context
['multimap'] = $this->map
->getMultiMap($this->context
['city']['multimap'], $this->modified
->getTimestamp(), $latitude, $longitude, $this->context
['locations'], $this->map
->getMultiZoom($latitude, $longitude, $this->context
['locations']));
189 $this->context
['keywords'] = [
190 $this->context
['city']['city'],
191 $this->translator
->trans('Indoor'),
192 $this->translator
->trans('Outdoor'),
193 $this->translator
->trans('Calendar'),
194 $this->translator
->trans('Libre Air')
197 //With context dances
198 if (!empty($this->context
['dances'])) {
200 $dances = array_map(function ($v) { return $v
['name']; }, $this->context
['dances']);
202 //Insert dances in keywords
203 array_splice($this->context
['keywords'], 1, 0, $dances);
206 $dances = implode($this->translator
->trans(' and '), array_filter(array_merge([implode(', ', array_slice($dances, 0, -1))], array_slice($dances, -1)), 'strlen'));
209 $this->context
['title'] = $this->translator
->trans('%dances% %city%', ['%dances%' => $dances, '%city%' => $this->context
['city']['in']]);
212 $this->context
['description'] = $this->translator
->trans('%dances% indoor and outdoor calendar %city%', ['%dances%' => $dances, '%city%' => $this->context
['city']['in']]);
215 $this->context
['title'] = $this->translator
->trans('Dance %city%', ['%city%' => $this->context
['city']['in']]);
218 $this->context
['description'] = $this->translator
->trans('Indoor and outdoor dance calendar %city%', ['%city%' => $this->context
['city']['in']]);
221 //Set locations description
222 $this->context
['locations_description'] = $this->translator
->trans('Libre Air location list %city%', ['%city%' => $this->context
['city']['in']]);
225 return $this->render('@RapsysAir/location/city.html.twig', $this->context
, $response);
231 * @desc Display all locations
233 * @param Request $request The request instance
235 * @return Response The rendered view
237 public function index(Request
$request): Response
{
239 $this->context
['locations'] = $this->doctrine
->getRepository(Location
::class)->findAllAsArray($this->period
);
242 $this->modified
= max(array_map(function ($v) { return $v
['updated']; }, $this->context
['locations']));
245 $response = new Response();
248 if ($this->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
250 $response->setLastModified(new \
DateTime('-1 year'));
253 $response->setPrivate();
254 //Without logged user
257 //XXX: only for public to force revalidation by last modified
258 $response->setEtag(md5(serialize($this->context
['locations'])));
261 $response->setLastModified($this->modified
);
264 $response->setPublic();
266 //Without role and modification
267 if ($response->isNotModified($request)) {
268 //Return 304 response
274 $latitudes = array_map(function ($v) { return $v
['latitude']; }, $this->context
['locations']);
277 $latitude = round(array_sum($latitudes)/count($latitudes), 6);
280 $longitudes = array_map(function ($v) { return $v
['longitude']; }, $this->context
['locations']);
283 $longitude = round(array_sum($longitudes)/count($longitudes), 6);
286 $this->context
['multimap'] = $this->map
->getMultiMap($this->translator
->trans('Libre Air locations sector map'), $this->modified
->getTimestamp(), $latitude, $longitude, $this->context
['locations'], $this->map
->getMultiZoom($latitude, $longitude, $this->context
['locations']));
289 $this->context
['title'] = $this->translator
->trans('Libre Air locations');
292 $this->context
['description'] = $this->translator
->trans('Libre Air location list');
295 $this->context
['keywords'] = [
296 $this->translator
->trans('locations'),
297 $this->translator
->trans('location list'),
298 $this->translator
->trans('listing'),
299 $this->translator
->trans('Libre Air')
302 //Create location forms for role_admin
303 if ($this->isGranted('ROLE_ADMIN')) {
304 //Fetch all locations
305 $locations = $this->doctrine
->getRepository(Location
::class)->findAll();
307 //Init locations to context
308 $this->context
['forms']['locations'] = [];
310 //Iterate on locations
311 foreach($this->context
['locations'] as $id => $location) {
312 //Create LocationType form
313 $form = $this->factory
->createNamed(
317 'Rapsys\AirBundle\Form\LocationType',
319 $locations[$location['id']],
320 //Set the form attributes
324 //Refill the fields in case of invalid form
325 $form->handleRequest($request);
328 if ($form->isSubmitted() && $form->isValid()) {
330 $data = $form->getData();
333 $data->setUpdated(new \
DateTime('now'));
335 //Queue location save
336 $this->manager
->persist($data);
338 //Flush to get the ids
339 $this->manager
->flush();
342 $this->addFlash('notice', $this->translator
->trans('Location %id% updated', ['%id%' => $location['id']]));
344 //Redirect to cleanup the form
345 return $this->redirectToRoute('rapsys_air_location', ['location' => $location['id']]);
348 //Add form to context
349 $this->context
['forms']['locations'][$id] = $form->createView();
352 //Create LocationType form
353 $form = $this->factory
->createNamed(
357 'Rapsys\AirBundle\Form\LocationType',
360 //Set the form attributes
361 ['attr' => ['class' => 'col']]
364 //Refill the fields in case of invalid form
365 $form->handleRequest($request);
368 if ($form->isSubmitted() && $form->isValid()) {
370 $data = $form->getData();
372 //Queue location save
373 $this->manager
->persist($data);
375 //Flush to get the ids
376 $this->manager
->flush();
379 $this->addFlash('notice', $this->translator
->trans('Location created'));
381 //Redirect to cleanup the form
382 return $this->redirectToRoute('rapsys_air_location', ['location' => $data->getId()]);
385 //Add form to context
386 $this->context
['forms']['location'] = $form->createView();
390 return $this->render('@RapsysAir/location/index.html.twig', $this->context
);
394 * List all sessions for the location
396 * Display all sessions for the location with an application or login form
398 * @TODO: add location edit form ???
400 * @param Request $request The request instance
401 * @param int $id The location id
403 * @return Response The rendered view
405 public function view(Request
$request, int $id): Response
{
407 if (empty($this->context
['location'] = $this->doctrine
->getRepository(Location
::class)->findOneByIdAsArray($id, $this->locale
))) {
409 throw $this->createNotFoundException($this->translator
->trans('Unable to find location: %id%', ['%id%' => $id]));
413 $this->context
['calendar'] = $this->doctrine
->getRepository(Session
::class)->findAllByPeriodAsArray($this->period
, $this->locale
, !$this->isGranted('IS_AUTHENTICATED_REMEMBERED'), $this->context
['location']['latitude'], $this->context
['location']['longitude']);
416 $this->context
['dances'] = [];
418 //Iterate on each calendar
419 foreach($this->context
['calendar'] as $date => $calendar) {
420 //Iterate on each session
421 foreach($calendar['sessions'] as $sessionId => $session) {
422 //Session with application dance
423 if (!empty($session['application']['dance'])) {
425 $this->context
['dances'][$session['application']['dance']['id']] = $session['application']['dance'];
430 //Get locations at less than 2 km
431 $this->context
['locations'] = $this->doctrine
->getRepository(Location
::class)->findAllByLatitudeLongitudeAsArray($this->context
['location']['latitude'], $this->context
['location']['longitude'], $this->period
, 2);
434 //XXX: dance modified is already computed inside calendar modified
435 $this->modified
= max(array_merge([$this->context
['location']['updated']], array_map(function ($v) { return $v
['modified']; }, array_merge($this->context
['calendar'], $this->context
['locations']))));
438 $response = new Response();
441 if ($this->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
443 $response->setLastModified(new \
DateTime('-1 year'));
446 $response->setPrivate();
447 //Without logged user
450 //XXX: only for public to force revalidation by last modified
451 $response->setEtag(md5(serialize(array_merge($this->context
['location'], $this->context
['calendar'], $this->context
['locations']))));
454 $response->setLastModified($this->modified
);
457 $response->setPublic();
459 //Without role and modification
460 if ($response->isNotModified($request)) {
461 //Return 304 response
467 $this->context
['multimap'] = $this->map
->getMultiMap($this->context
['location']['multimap'], $this->modified
->getTimestamp(), $this->context
['location']['latitude'], $this->context
['location']['longitude'], $this->context
['locations'], $this->map
->getMultiZoom($this->context
['location']['latitude'], $this->context
['location']['longitude'], $this->context
['locations']));
470 $this->context
['keywords'] = [
471 $this->context
['location']['title'],
472 $this->context
['location']['city'],
473 $this->translator
->trans($this->context
['location']['indoor']?'Indoor':'Outdoor'),
474 $this->translator
->trans('Calendar'),
475 $this->translator
->trans('Libre Air')
479 if (!empty($this->context
['dances'])) {
481 $dances = array_map(function ($v) { return $v
['name']; }, $this->context
['dances']);
483 //Insert dances in keywords
484 array_splice($this->context
['keywords'], 2, 0, $dances);
487 $dances = implode($this->translator
->trans(' and '), array_filter(array_merge([implode(', ', array_slice($dances, 0, -1))], array_slice($dances, -1)), 'strlen'));
490 $this->context
['title'] = $this->translator
->trans('%dances% %location%', ['%dances%' => $dances, '%location%' => $this->context
['location']['atin']]);
493 $this->context
['description'] = $this->translator
->trans('%dances% indoor and outdoor calendar %location%', ['%dances%' => $dances, '%location%' => $this->context
['location']['at']]);
497 $this->context
['title'] = $this->translator
->trans('Dance %location%', ['%location%' => $this->context
['location']['atin']]);
500 $this->context
['description'] = $this->translator
->trans('Indoor and outdoor dance calendar %location%', [ '%location%' => $this->context
['location']['at'] ]);
503 //Set locations description
504 $this->context
['locations_description'] = $this->translator
->trans('Libre Air location list %location%', ['%location%' => $this->context
['location']['atin']]);
507 $this->context
['alternates'] +
= $this->context
['location']['alternates'];
510 return $this->render('@RapsysAir/location/view.html.twig', $this->context
, $response);