]> Raphaël G. Git Repositories - airbundle/blob - Controller/LocationController.php
8838d697415cadf84d408bc23616bf44bc3b4a9c
[airbundle] / Controller / LocationController.php
1 <?php declare(strict_types=1);
2
3 /*
4 * This file is part of the Rapsys AirBundle package.
5 *
6 * (c) Raphaël Gertz <symfony@rapsys.eu>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12 namespace Rapsys\AirBundle\Controller;
13
14 use Symfony\Component\HttpFoundation\Request;
15 use Symfony\Component\HttpFoundation\Response;
16
17 use Rapsys\AirBundle\Entity\Dance;
18 use Rapsys\AirBundle\Entity\Location;
19 use Rapsys\AirBundle\Entity\Session;
20
21 /**
22 * {@inheritdoc}
23 */
24 class LocationController extends DefaultController {
25 /**
26 * List all cities
27 *
28 * @param Request $request The request instance
29 * @return Response The rendered view
30 */
31 public function cities(Request $request): Response {
32 //Add cities
33 $this->context['cities'] = $this->doctrine->getRepository(Location::class)->findCitiesAsArray($this->period, 0);
34
35 //Add dances
36 $this->context['dances'] = $this->doctrine->getRepository(Dance::class)->findNamesAsArray();
37
38 //Create response
39 $response = new Response();
40
41 //Set modified
42 $this->modified = max(array_map(function ($v) { return $v['modified']; }, array_merge($this->context['cities'], $this->context['dances'])));
43
44 //Add city multi
45 foreach($this->context['cities'] as $id => $city) {
46 //Add city multi
47 $this->context['cities'][$id]['multimap'] = $this->map->getMultiMap($city['multimap'], $this->modified->getTimestamp(), $city['locations']);
48 }
49
50 //With logged user
51 if ($this->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
52 //Set last modified
53 $response->setLastModified(new \DateTime('-1 year'));
54
55 //Set as private
56 $response->setPrivate();
57 //Without logged user
58 } else {
59 //Set etag
60 //XXX: only for public to force revalidation by last modified
61 $response->setEtag(md5(serialize(array_merge($this->context['cities'], $this->context['dances']))));
62
63 //Set last modified
64 $response->setLastModified($this->modified);
65
66 //Set as public
67 $response->setPublic();
68
69 //Without role and modification
70 if ($response->isNotModified($request)) {
71 //Return 304 response
72 return $response;
73 }
74 }
75
76 //Set section
77 $this->context['title'] = $this->translator->trans('Libre Air cities');
78
79 //Set description
80 $this->context['description'] = $this->translator->trans('Libre Air city list');
81
82 //Set cities
83 $cities = array_map(function ($v) { return $v['in']; }, $this->context['cities']);
84
85 //Set dances
86 $dances = array_map(function ($v) { return $v['name']; }, $this->context['dances']);
87
88 //Set indoors
89 $indoors = array_reduce($this->context['cities'], function ($c, $v) { return array_merge($c, $v['indoors']); }, []);
90
91 //Set keywords
92 $this->context['keywords'] = array_values(
93 array_merge(
94 [
95 $this->translator->trans('Cities'),
96 $this->translator->trans('City list'),
97 $this->translator->trans('Listing'),
98 ],
99 $cities,
100 $indoors,
101 [
102 $this->translator->trans('calendar'),
103 $this->translator->trans('Libre Air')
104 ]
105 )
106 );
107
108 //Render the view
109 return $this->render('@RapsysAir/location/cities.html.twig', $this->context, $response);
110 }
111
112 /**
113 * Display city
114 *
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
118 *
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
123 */
124 public function city(Request $request, float $latitude, float $longitude, string $city): Response {
125 //Get city
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]));
128 }
129
130 //Add calendar
131 $this->context['calendar'] = $this->doctrine->getRepository(Session::class)->findAllByPeriodAsCalendarArray($this->period, !$this->isGranted('IS_AUTHENTICATED_REMEMBERED'), floatval($latitude), floatval($longitude));
132
133 //Set dances
134 $this->context['dances'] = [];
135
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'])) {
142 //Add dance
143 $this->context['dances'][$session['application']['dance']['id']] = $session['application']['dance'];
144 }
145 }
146 }
147
148 //Add locations
149 $this->context['locations'] = $this->doctrine->getRepository(Location::class)->findAllByLatitudeLongitudeAsArray(floatval($latitude), floatval($longitude), $this->period, 0);
150
151 //Set modified
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']))));
154
155 //Create response
156 $response = new Response();
157
158 //With logged user
159 if ($this->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
160 //Set last modified
161 $response->setLastModified(new \DateTime('-1 year'));
162
163 //Set as private
164 $response->setPrivate();
165 //Without logged user
166 } else {
167 //Set etag
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']))));
170
171 //Set last modified
172 $response->setLastModified($this->modified);
173
174 //Set as public
175 $response->setPublic();
176
177 //Without role and modification
178 if ($response->isNotModified($request)) {
179 //Return 304 response
180 return $response;
181 }
182 }
183
184 //Add multi
185 $this->context['multimap'] = $this->map->getMultiMap($this->context['city']['multimap'], $this->modified->getTimestamp(), $this->context['locations']);
186
187 //Set keywords
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')
194 ];
195
196 //With context dances
197 if (!empty($this->context['dances'])) {
198 //Set dances
199 $dances = array_map(function ($v) { return $v['name']; }, $this->context['dances']);
200
201 //Insert dances in keywords
202 array_splice($this->context['keywords'], 1, 0, $dances);
203
204 //Get textual dances
205 $dances = implode($this->translator->trans(' and '), array_filter(array_merge([implode(', ', array_slice($dances, 0, -1))], array_slice($dances, -1)), 'strlen'));
206
207 //Set title
208 $this->context['title'] = $this->translator->trans('%dances% %city%', ['%dances%' => $dances, '%city%' => $this->context['city']['in']]);
209
210 //Set description
211 $this->context['description'] = $this->translator->trans('%dances% indoor and outdoor calendar %city%', ['%dances%' => $dances, '%city%' => $this->context['city']['in']]);
212 } else {
213 //Set title
214 $this->context['title'] = $this->translator->trans('Dance %city%', ['%city%' => $this->context['city']['in']]);
215
216 //Set description
217 $this->context['description'] = $this->translator->trans('Indoor and outdoor dance calendar %city%', ['%city%' => $this->context['city']['in']]);
218 }
219
220 //Set locations description
221 $this->context['locations_description'] = $this->translator->trans('Libre Air location list %city%', ['%city%' => $this->context['city']['in']]);
222
223 //Render the view
224 return $this->render('@RapsysAir/location/city.html.twig', $this->context, $response);
225 }
226
227 /**
228 * List all locations
229 *
230 * @desc Display all locations
231 *
232 * @param Request $request The request instance
233 *
234 * @return Response The rendered view
235 */
236 public function index(Request $request): Response {
237 //Get locations
238 $this->context['locations'] = $this->doctrine->getRepository(Location::class)->findAllAsArray($this->period);
239
240 //Set modified
241 $this->modified = max(array_map(function ($v) { return $v['updated']; }, $this->context['locations']));
242
243 //Create response
244 $response = new Response();
245
246 //With logged user
247 if ($this->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
248 //Set last modified
249 $response->setLastModified(new \DateTime('-1 year'));
250
251 //Set as private
252 $response->setPrivate();
253 //Without logged user
254 } else {
255 //Set etag
256 //XXX: only for public to force revalidation by last modified
257 $response->setEtag(md5(serialize($this->context['locations'])));
258
259 //Set last modified
260 $response->setLastModified($this->modified);
261
262 //Set as public
263 $response->setPublic();
264
265 //Without role and modification
266 if ($response->isNotModified($request)) {
267 //Return 304 response
268 return $response;
269 }
270 }
271
272 //Add multi map
273 $this->context['multimap'] = $this->map->getMultiMap($this->translator->trans('Libre Air locations sector map'), $this->modified->getTimestamp(), $this->context['locations']);
274
275 //Set title
276 $this->context['title'] = $this->translator->trans('Libre Air locations');
277
278 //Set description
279 $this->context['description'] = $this->translator->trans('Libre Air location list');
280
281 //Set keywords
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')
287 ];
288
289 //Create location forms for role_admin
290 if ($this->isGranted('ROLE_ADMIN')) {
291 //Fetch all locations
292 $locations = $this->doctrine->getRepository(Location::class)->findAll();
293
294 //Init locations to context
295 $this->context['forms']['locations'] = [];
296
297 //Iterate on locations
298 foreach($this->context['locations'] as $id => $location) {
299 //Create LocationType form
300 $form = $this->factory->createNamed(
301 //Set form id
302 'locations_'.$id,
303 //Set form type
304 'Rapsys\AirBundle\Form\LocationType',
305 //Set form data
306 $locations[$location['id']],
307 //Set the form attributes
308 ['attr' => []]
309 );
310
311 //Refill the fields in case of invalid form
312 $form->handleRequest($request);
313
314 //Handle valid form
315 if ($form->isSubmitted() && $form->isValid()) {
316 //Get data
317 $data = $form->getData();
318
319 //Set updated
320 $data->setUpdated(new \DateTime('now'));
321
322 //Queue location save
323 $this->manager->persist($data);
324
325 //Flush to get the ids
326 $this->manager->flush();
327
328 //Add notice
329 $this->addFlash('notice', $this->translator->trans('Location %id% updated', ['%id%' => $location['id']]));
330
331 //Redirect to cleanup the form
332 return $this->redirectToRoute('rapsys_air_location', ['location' => $location['id']]);
333 }
334
335 //Add form to context
336 $this->context['forms']['locations'][$id] = $form->createView();
337 }
338
339 //Create LocationType form
340 $form = $this->factory->createNamed(
341 //Set form id
342 'locations',
343 //Set form type
344 'Rapsys\AirBundle\Form\LocationType',
345 //Set form data
346 new Location(),
347 //Set the form attributes
348 ['attr' => ['class' => 'col']]
349 );
350
351 //Refill the fields in case of invalid form
352 $form->handleRequest($request);
353
354 //Handle valid form
355 if ($form->isSubmitted() && $form->isValid()) {
356 //Get data
357 $data = $form->getData();
358
359 //Queue location save
360 $this->manager->persist($data);
361
362 //Flush to get the ids
363 $this->manager->flush();
364
365 //Add notice
366 $this->addFlash('notice', $this->translator->trans('Location created'));
367
368 //Redirect to cleanup the form
369 return $this->redirectToRoute('rapsys_air_location', ['location' => $data->getId()]);
370 }
371
372 //Add form to context
373 $this->context['forms']['location'] = $form->createView();
374 }
375
376 //Render the view
377 return $this->render('@RapsysAir/location/index.html.twig', $this->context);
378 }
379
380 /**
381 * List all sessions for the location
382 *
383 * Display all sessions for the location with an application or login form
384 *
385 * @TODO: add location edit form ???
386 *
387 * @param Request $request The request instance
388 * @param int $id The location id
389 * @param ?string $location The location slug
390 *
391 * @return Response The rendered view
392 */
393 public function view(Request $request, int $id, ?string $location): Response {
394 //Without location
395 if (empty($this->context['location'] = $this->doctrine->getRepository(Location::class)->findOneByIdAsArray($id, $this->locale))) {
396 //Throw 404
397 throw $this->createNotFoundException($this->translator->trans('Unable to find location: %id%', ['%id%' => $id]));
398 }
399
400 //With invalid slug
401 if ($location !== $this->context['location']['slug']) {
402 //Redirect on correctly spelled location
403 return $this->redirectToRoute('rapsys_air_location_view', ['id' => $this->context['location']['id'], 'location' => $this->context['location']['slug']], Response::HTTP_MOVED_PERMANENTLY);
404 }
405
406 //Fetch calendar
407 $this->context['calendar'] = $this->doctrine->getRepository(Session::class)->findAllByPeriodAsCalendarArray($this->period, !$this->isGranted('IS_AUTHENTICATED_REMEMBERED'), $this->context['location']['latitude'], $this->context['location']['longitude']);
408
409 //Set dances
410 $this->context['dances'] = [];
411
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'])) {
418 //Add dance
419 $this->context['dances'][$session['application']['dance']['id']] = $session['application']['dance'];
420 }
421 }
422 }
423
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);
426
427 //Set modified
428 //XXX: dance modified is already computed inside calendar modified
429 $this->modified = max(array_merge([$this->context['location']['updated']], array_map(function ($v) { return $v['modified']; }, array_merge($this->context['calendar'], $this->context['locations']))));
430
431 //Create response
432 $response = new Response();
433
434 //With logged user
435 if ($this->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
436 //Set last modified
437 $response->setLastModified(new \DateTime('-1 year'));
438
439 //Set as private
440 $response->setPrivate();
441 //Without logged user
442 } else {
443 //Set etag
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']))));
446
447 //Set last modified
448 $response->setLastModified($this->modified);
449
450 //Set as public
451 $response->setPublic();
452
453 //Without role and modification
454 if ($response->isNotModified($request)) {
455 //Return 304 response
456 return $response;
457 }
458 }
459
460 //Add multi map
461 $this->context['multimap'] = $this->map->getMultiMap($this->context['location']['multimap'], $this->modified->getTimestamp(), $this->context['locations']);
462
463 //Set keywords
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')
470 ];
471
472 //With dances
473 if (!empty($this->context['dances'])) {
474 //Set dances
475 $dances = array_map(function ($v) { return $v['name']; }, $this->context['dances']);
476
477 //Insert dances in keywords
478 array_splice($this->context['keywords'], 2, 0, $dances);
479
480 //Get textual dances
481 $dances = implode($this->translator->trans(' and '), array_filter(array_merge([implode(', ', array_slice($dances, 0, -1))], array_slice($dances, -1)), 'strlen'));
482
483 //Set title
484 $this->context['title'] = $this->translator->trans('%dances% %location%', ['%dances%' => $dances, '%location%' => $this->context['location']['atin']]);
485
486 //Set description
487 $this->context['description'] = $this->translator->trans('%dances% indoor and outdoor calendar %location%', ['%dances%' => $dances, '%location%' => $this->context['location']['at']]);
488 //Without dances
489 } else {
490 //Set title
491 $this->context['title'] = $this->translator->trans('Dance %location%', ['%location%' => $this->context['location']['atin']]);
492
493 //Set description
494 $this->context['description'] = $this->translator->trans('Indoor and outdoor dance calendar %location%', ['%location%' => $this->context['location']['at']]);
495 }
496
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']]);
499
500 //Set locations link
501 $this->context['locations_link'] = $this->context['location']['city']['link'];
502
503 //Set locations title
504 $this->context['locations_title'] = $this->context['location']['city']['title'].' ('.$this->context['location']['city']['id'].')';
505
506 //Set alternates
507 $this->context['alternates'] += $this->context['location']['alternates'];
508
509 //Render the view
510 return $this->render('@RapsysAir/location/view.html.twig', $this->context, $response);
511 }
512 }