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
, 0);
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->context
['cities'][$id]['multimap'] = $this->map
->getMultiMap($city['multimap'], $this->modified
->getTimestamp(), $city['locations']);
51 if ($this->checker
->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
53 $response->setLastModified(new \
DateTime('-1 year'));
56 $response->setPrivate();
60 //XXX: only for public to force revalidation by last modified
61 $response->setEtag(md5(serialize(array_merge($this->context
['cities'], $this->context
['dances']))));
64 $response->setLastModified($this->modified
);
67 $response->setPublic();
69 //Without role and modification
70 if ($response->isNotModified($request)) {
77 $this->context
['title']['page'] = $this->translator
->trans('Libre Air cities');
80 $this->context
['description'] = $this->translator
->trans('Libre Air city list');
83 $cities = array_map(function ($v) { return $v
['in']; }, $this->context
['cities']);
86 $dances = array_map(function ($v) { return $v
['name']; }, $this->context
['dances']);
89 $indoors = array_reduce($this->context
['cities'], function ($c, $v) { return array_merge($c
, $v
['indoors']); }, []);
92 $this->context
['keywords'] = array_values(
95 $this->translator
->trans('Cities'),
96 $this->translator
->trans('City list'),
97 $this->translator
->trans('Listing'),
102 $this->translator
->trans('calendar'),
103 $this->translator
->trans('Libre Air')
109 return $this->render('@RapsysAir/location/cities.html.twig', $this->context
, $response);
115 * @todo XXX: TODO: add <link rel="prev|next" for sessions or classes ? />
116 * @todo XXX: TODO: like described in: https://www.alsacreations.com/article/lire/1400-attribut-rel-relations.html#xnf-rel-attribute
117 * @todo XXX: TODO: or here: http://microformats.org/wiki/existing-rel-values#HTML5_link_type_extensions
119 * @param Request $request The request instance
120 * @param float $latitude The city latitude
121 * @param float $longitude The city longitude
122 * @return Response The rendered view
124 public function city(Request
$request, float $latitude, float $longitude, string $city): Response
{
126 if (!($this->context
['city'] = $this->doctrine
->getRepository(Location
::class)->findCityByLatitudeLongitudeAsArray(floatval($latitude), floatval($longitude)))) {
127 throw $this->createNotFoundException($this->translator
->trans('Unable to find city: %latitude%,%longitude%', ['%latitude%' => $latitude, '%longitude%' => $longitude]));
131 $this->context
['calendar'] = $this->doctrine
->getRepository(Session
::class)->findAllByPeriodAsCalendarArray($this->period
, !$this->checker
->isGranted('IS_AUTHENTICATED_REMEMBERED'), floatval($latitude), floatval($longitude));
134 $this->context
['dances'] = [];
136 //Iterate on each calendar
137 foreach($this->context
['calendar'] as $date => $calendar) {
138 //Iterate on each session
139 foreach($calendar['sessions'] as $sessionId => $session) {
140 //Session with application dance
141 if (!empty($session['application']['dance'])) {
143 $this->context
['dances'][$session['application']['dance']['id']] = $session['application']['dance'];
149 $this->context
['locations'] = $this->doctrine
->getRepository(Location
::class)->findAllByLatitudeLongitudeAsArray(floatval($latitude), floatval($longitude), $this->period
, 0);
152 //XXX: dance modified is already computed inside calendar modified
153 $this->modified
= max(array_merge([$this->context
['city']['updated']], array_map(function ($v) { return $v
['modified']; }, array_merge($this->context
['calendar'], $this->context
['locations']))));
156 $response = new Response();
159 if ($this->checker
->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
161 $response->setLastModified(new \
DateTime('-1 year'));
164 $response->setPrivate();
165 //Without logged user
168 //XXX: only for public to force revalidation by last modified
169 $response->setEtag(md5(serialize(array_merge($this->context
['city'], $this->context
['dances'], $this->context
['calendar'], $this->context
['locations']))));
172 $response->setLastModified($this->modified
);
175 $response->setPublic();
177 //Without role and modification
178 if ($response->isNotModified($request)) {
179 //Return 304 response
185 $this->context
['multimap'] = $this->map
->getMultiMap($this->context
['city']['multimap'], $this->modified
->getTimestamp(), $this->context
['locations']);
188 $this->context
['keywords'] = [
189 $this->context
['city']['city'],
190 $this->translator
->trans('Indoor'),
191 $this->translator
->trans('Outdoor'),
192 $this->translator
->trans('Calendar'),
193 $this->translator
->trans('Libre Air')
196 //With context dances
197 if (!empty($this->context
['dances'])) {
199 $dances = array_map(function ($v) { return $v
['name']; }, $this->context
['dances']);
201 //Insert dances in keywords
202 array_splice($this->context
['keywords'], 1, 0, $dances);
205 $dances = implode($this->translator
->trans(' and '), array_filter(array_merge([implode(', ', array_slice($dances, 0, -1))], array_slice($dances, -1)), 'strlen'));
208 $this->context
['title']['page'] = $this->translator
->trans('%dances% %city%', ['%dances%' => $dances, '%city%' => $this->context
['city']['in']]);
211 $this->context
['description'] = $this->translator
->trans('%dances% indoor and outdoor calendar %city%', ['%dances%' => $dances, '%city%' => $this->context
['city']['in']]);
214 $this->context
['title']['page'] = $this->translator
->trans('Dance %city%', ['%city%' => $this->context
['city']['in']]);
217 $this->context
['description'] = $this->translator
->trans('Indoor and outdoor dance calendar %city%', ['%city%' => $this->context
['city']['in']]);
220 //Set locations description
221 $this->context
['locations_description'] = $this->translator
->trans('Libre Air location list %city%', ['%city%' => $this->context
['city']['in']]);
224 return $this->render('@RapsysAir/location/city.html.twig', $this->context
, $response);
230 * @desc Display all locations
232 * @param Request $request The request instance
234 * @return Response The rendered view
236 public function index(Request
$request): Response
{
238 $this->context
['locations'] = $this->doctrine
->getRepository(Location
::class)->findAllAsArray($this->period
);
241 $this->modified
= max(array_map(function ($v) { return $v
['updated']; }, $this->context
['locations']));
244 $response = new Response();
247 if ($this->checker
->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
249 $response->setLastModified(new \
DateTime('-1 year'));
252 $response->setPrivate();
253 //Without logged user
256 //XXX: only for public to force revalidation by last modified
257 $response->setEtag(md5(serialize($this->context
['locations'])));
260 $response->setLastModified($this->modified
);
263 $response->setPublic();
265 //Without role and modification
266 if ($response->isNotModified($request)) {
267 //Return 304 response
273 $this->context
['multimap'] = $this->map
->getMultiMap($this->translator
->trans('Libre Air locations sector map'), $this->modified
->getTimestamp(), $this->context
['locations']);
276 $this->context
['title']['page'] = $this->translator
->trans('Libre Air locations');
279 $this->context
['description'] = $this->translator
->trans('Libre Air location list');
282 $this->context
['keywords'] = [
283 $this->translator
->trans('locations'),
284 $this->translator
->trans('location list'),
285 $this->translator
->trans('listing'),
286 $this->translator
->trans('Libre Air')
289 //Create location forms for role_admin
290 if ($this->checker
->isGranted('ROLE_ADMIN')) {
291 //Fetch all locations
292 $locations = $this->doctrine
->getRepository(Location
::class)->findAll();
294 //Init locations to context
295 $this->context
['forms']['locations'] = [];
297 //Iterate on locations
298 foreach($this->context
['locations'] as $id => $location) {
299 //Create LocationType form
300 $form = $this->factory
->createNamed(
304 'Rapsys\AirBundle\Form\LocationType',
306 $locations[$location['id']],
307 //Set the form attributes
311 //Refill the fields in case of invalid form
312 $form->handleRequest($request);
315 if ($form->isSubmitted() && $form->isValid()) {
317 $data = $form->getData();
320 $data->setUpdated(new \
DateTime('now'));
322 //Queue location save
323 $this->manager
->persist($data);
325 //Flush to get the ids
326 $this->manager
->flush();
329 $this->addFlash('notice', $this->translator
->trans('Location %id% updated', ['%id%' => $location['id']]));
331 //Redirect to cleanup the form
332 return $this->redirectToRoute('rapsysair_location', ['location' => $location['id']]);
335 //Add form to context
336 $this->context
['forms']['locations'][$id] = $form->createView();
339 //Create LocationType form
340 $form = $this->factory
->createNamed(
344 'Rapsys\AirBundle\Form\LocationType',
347 //Set the form attributes
348 ['attr' => ['class' => 'col']]
351 //Refill the fields in case of invalid form
352 $form->handleRequest($request);
355 if ($form->isSubmitted() && $form->isValid()) {
357 $data = $form->getData();
359 //Queue location save
360 $this->manager
->persist($data);
362 //Flush to get the ids
363 $this->manager
->flush();
366 $this->addFlash('notice', $this->translator
->trans('Location created'));
368 //Redirect to cleanup the form
369 return $this->redirectToRoute('rapsysair_location', ['location' => $data->getId()]);
372 //Add form to context
373 $this->context
['forms']['location'] = $form->createView();
377 return $this->render('@RapsysAir/location/index.html.twig', $this->context
);
381 * List all sessions for the location
383 * Display all sessions for the location with an application or login form
385 * @TODO: add location edit form ???
387 * @param Request $request The request instance
388 * @param int $id The location id
389 * @param ?string $location The location slug
391 * @return Response The rendered view
393 public function view(Request
$request, int $id, ?string $location): Response
{
395 if (empty($this->context
['location'] = $this->doctrine
->getRepository(Location
::class)->findOneByIdAsArray($id, $this->locale
))) {
397 throw $this->createNotFoundException($this->translator
->trans('Unable to find location: %id%', ['%id%' => $id]));
401 if ($location !== $this->context
['location']['slug']) {
402 //Redirect on correctly spelled location
403 return $this->redirectToRoute('rapsysair_location_view', ['id' => $this->context
['location']['id'], 'location' => $this->context
['location']['slug']], Response
::HTTP_MOVED_PERMANENTLY
);
407 $this->context
['calendar'] = $this->doctrine
->getRepository(Session
::class)->findAllByPeriodAsCalendarArray($this->period
, !$this->checker
->isGranted('IS_AUTHENTICATED_REMEMBERED'), $this->context
['location']['latitude'], $this->context
['location']['longitude']);
410 $this->context
['dances'] = [];
412 //Iterate on each calendar
413 foreach($this->context
['calendar'] as $date => $calendar) {
414 //Iterate on each session
415 foreach($calendar['sessions'] as $sessionId => $session) {
416 //Session with application dance
417 if (!empty($session['application']['dance'])) {
419 $this->context
['dances'][$session['application']['dance']['id']] = $session['application']['dance'];
424 //Get locations at less than 2 km
425 $this->context
['locations'] = $this->doctrine
->getRepository(Location
::class)->findAllByLatitudeLongitudeAsArray($this->context
['location']['latitude'], $this->context
['location']['longitude'], $this->period
, 2);
428 //XXX: dance modified is already computed inside calendar modified
429 $this->modified
= max(array_merge([$this->context
['location']['modified']], array_map(function ($v) { return $v
['modified']; }, array_merge($this->context
['calendar'], $this->context
['locations']))));
432 $response = new Response();
435 if ($this->checker
->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
437 $response->setLastModified(new \
DateTime('-1 year'));
440 $response->setPrivate();
441 //Without logged user
444 //XXX: only for public to force revalidation by last modified
445 $response->setEtag(md5(serialize(array_merge($this->context
['location'], $this->context
['calendar'], $this->context
['locations']))));
448 $response->setLastModified($this->modified
);
451 $response->setPublic();
453 //Without role and modification
454 if ($response->isNotModified($request)) {
455 //Return 304 response
461 $this->context
['multimap'] = $this->map
->getMultiMap($this->context
['location']['multimap'], $this->modified
->getTimestamp(), $this->context
['locations']);
464 $this->context
['keywords'] = [
465 $this->context
['location']['title'],
466 $this->context
['location']['city']['title'],
467 $this->translator
->trans($this->context
['location']['indoor']?'Indoor':'Outdoor'),
468 $this->translator
->trans('Calendar'),
469 $this->translator
->trans('Libre Air')
473 if (!empty($this->context
['dances'])) {
475 $dances = array_map(function ($v) { return $v
['name']; }, $this->context
['dances']);
477 //Insert dances in keywords
478 array_splice($this->context
['keywords'], 2, 0, $dances);
481 $dances = implode($this->translator
->trans(' and '), array_filter(array_merge([implode(', ', array_slice($dances, 0, -1))], array_slice($dances, -1)), 'strlen'));
484 $this->context
['title']['page'] = $this->translator
->trans('%dances% %location%', ['%dances%' => $dances, '%location%' => $this->context
['location']['atin']]);
487 $this->context
['description'] = $this->translator
->trans('%dances% indoor and outdoor calendar %location%', ['%dances%' => $dances, '%location%' => $this->context
['location']['at']]);
491 $this->context
['title']['page'] = $this->translator
->trans('Dance %location%', ['%location%' => $this->context
['location']['atin']]);
494 $this->context
['description'] = $this->translator
->trans('Indoor and outdoor dance calendar %location%', ['%location%' => $this->context
['location']['at']]);
497 //Set locations description
498 $this->context
['locations_description'] = $this->translator
->trans('Libre Air location list %location% %city%', ['%location%' => $this->context
['location']['around'], '%city%' => $this->context
['location']['city']['in']]);
501 $this->context
['locations_link'] = $this->context
['location']['city']['link'];
503 //Set locations title
504 $this->context
['locations_title'] = $this->context
['location']['city']['title'].' ('.$this->context
['location']['city']['id'].')';
507 $this->context
['alternates'] +
= $this->context
['location']['alternates'];
510 return $this->render('@RapsysAir/location/view.html.twig', $this->context
, $response);