]> Raphaël G. Git Repositories - airbundle/blob - Controller/LocationController.php
Inherit entity repository
[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);
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->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']));
49 }
50
51 //With logged user
52 if ($this->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
53 //Set last modified
54 $response->setLastModified(new \DateTime('-1 year'));
55
56 //Set as private
57 $response->setPrivate();
58 //Without logged user
59 } else {
60 //Set etag
61 //XXX: only for public to force revalidation by last modified
62 $response->setEtag(md5(serialize(array_merge($this->context['cities'], $this->context['dances']))));
63
64 //Set last modified
65 $response->setLastModified($this->modified);
66
67 //Set as public
68 $response->setPublic();
69
70 //Without role and modification
71 if ($response->isNotModified($request)) {
72 //Return 304 response
73 return $response;
74 }
75 }
76
77 //Set section
78 $this->context['title'] = $this->translator->trans('Libre Air cities');
79
80 //Set description
81 $this->context['description'] = $this->translator->trans('Libre Air city list');
82
83 //Set cities
84 $cities = array_map(function ($v) { return $v['in']; }, $this->context['cities']);
85
86 //Set dances
87 $dances = array_map(function ($v) { return $v['name']; }, $this->context['dances']);
88
89 //Set indoors
90 $indoors = array_reduce($this->context['cities'], function ($c, $v) { return array_merge($c, $v['indoors']); }, []);
91
92 //Set keywords
93 $this->context['keywords'] = array_values(
94 array_merge(
95 [
96 $this->translator->trans('Cities'),
97 $this->translator->trans('City list'),
98 $this->translator->trans('Listing'),
99 ],
100 $cities,
101 $indoors,
102 [
103 $this->translator->trans('calendar'),
104 $this->translator->trans('Libre Air')
105 ]
106 )
107 );
108
109 //Render the view
110 return $this->render('@RapsysAir/location/cities.html.twig', $this->context, $response);
111 }
112
113 /**
114 * Display city
115 *
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
119 *
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
124 */
125 public function city(Request $request, float $latitude, float $longitude, string $city): Response {
126 //Get city
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]));
129 }
130
131 //Add calendar
132 $this->context['calendar'] = $this->doctrine->getRepository(Session::class)->findAllByPeriodAsArray($this->period, $request->getLocale(), !$this->isGranted('IS_AUTHENTICATED_REMEMBERED'), floatval($latitude), floatval($longitude));
133
134 //Set dances
135 $this->context['dances'] = [];
136
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'])) {
143 //Add dance
144 $this->context['dances'][$session['application']['dance']['id']] = $session['application']['dance'];
145 }
146 }
147 }
148
149 //Add locations
150 $this->context['locations'] = $this->doctrine->getRepository(Location::class)->findAllByLatitudeLongitudeAsArray(floatval($latitude), floatval($longitude), $this->period);
151
152 //Set modified
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']))));
155
156 //Create response
157 $response = new Response();
158
159 //With logged user
160 if ($this->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
161 //Set last modified
162 $response->setLastModified(new \DateTime('-1 year'));
163
164 //Set as private
165 $response->setPrivate();
166 //Without logged user
167 } else {
168 //Set etag
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']))));
171
172 //Set last modified
173 $response->setLastModified($this->modified);
174
175 //Set as public
176 $response->setPublic();
177
178 //Without role and modification
179 if ($response->isNotModified($request)) {
180 //Return 304 response
181 return $response;
182 }
183 }
184
185 //Add multi
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']));
187
188 //Set keywords
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')
195 ];
196
197 //With context dances
198 if (!empty($this->context['dances'])) {
199 //Set dances
200 $dances = array_map(function ($v) { return $v['name']; }, $this->context['dances']);
201
202 //Insert dances in keywords
203 array_splice($this->context['keywords'], 1, 0, $dances);
204
205 //Get textual dances
206 $dances = implode($this->translator->trans(' and '), array_filter(array_merge([implode(', ', array_slice($dances, 0, -1))], array_slice($dances, -1)), 'strlen'));
207
208 //Set title
209 $this->context['title'] = $this->translator->trans('%dances% %city%', ['%dances%' => $dances, '%city%' => $this->context['city']['in']]);
210
211 //Set description
212 $this->context['description'] = $this->translator->trans('%dances% indoor and outdoor calendar %city%', ['%dances%' => $dances, '%city%' => $this->context['city']['in']]);
213 } else {
214 //Set title
215 $this->context['title'] = $this->translator->trans('Dance %city%', ['%city%' => $this->context['city']['in']]);
216
217 //Set description
218 $this->context['description'] = $this->translator->trans('Indoor and outdoor dance calendar %city%', ['%city%' => $this->context['city']['in']]);
219 }
220
221 //Set locations description
222 $this->context['locations_description'] = $this->translator->trans('Libre Air location list %city%', ['%city%' => $this->context['city']['in']]);
223
224 //Render the view
225 return $this->render('@RapsysAir/location/city.html.twig', $this->context, $response);
226 }
227
228 /**
229 * List all locations
230 *
231 * @desc Display all locations
232 *
233 * @param Request $request The request instance
234 *
235 * @return Response The rendered view
236 */
237 public function index(Request $request): Response {
238 //Get locations
239 $this->context['locations'] = $this->doctrine->getRepository(Location::class)->findAllAsArray($this->period);
240
241 //Set modified
242 $this->modified = max(array_map(function ($v) { return $v['updated']; }, $this->context['locations']));
243
244 //Create response
245 $response = new Response();
246
247 //With logged user
248 if ($this->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
249 //Set last modified
250 $response->setLastModified(new \DateTime('-1 year'));
251
252 //Set as private
253 $response->setPrivate();
254 //Without logged user
255 } else {
256 //Set etag
257 //XXX: only for public to force revalidation by last modified
258 $response->setEtag(md5(serialize($this->context['locations'])));
259
260 //Set last modified
261 $response->setLastModified($this->modified);
262
263 //Set as public
264 $response->setPublic();
265
266 //Without role and modification
267 if ($response->isNotModified($request)) {
268 //Return 304 response
269 return $response;
270 }
271 }
272
273 //Set latitudes
274 $latitudes = array_map(function ($v) { return $v['latitude']; }, $this->context['locations']);
275
276 //Set latitude
277 $latitude = round(array_sum($latitudes)/count($latitudes), 6);
278
279 //Set longitudes
280 $longitudes = array_map(function ($v) { return $v['longitude']; }, $this->context['locations']);
281
282 //Set longitude
283 $longitude = round(array_sum($longitudes)/count($longitudes), 6);
284
285 //Add multi map
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']));
287
288 //Set title
289 $this->context['title'] = $this->translator->trans('Libre Air locations');
290
291 //Set description
292 $this->context['description'] = $this->translator->trans('Libre Air location list');
293
294 //Set keywords
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')
300 ];
301
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();
306
307 //Init locations to context
308 $this->context['forms']['locations'] = [];
309
310 //Iterate on locations
311 foreach($this->context['locations'] as $id => $location) {
312 //Create LocationType form
313 $form = $this->factory->createNamed(
314 //Set form id
315 'locations_'.$id,
316 //Set form type
317 'Rapsys\AirBundle\Form\LocationType',
318 //Set form data
319 $locations[$location['id']],
320 //Set the form attributes
321 ['attr' => []]
322 );
323
324 //Refill the fields in case of invalid form
325 $form->handleRequest($request);
326
327 //Handle valid form
328 if ($form->isSubmitted() && $form->isValid()) {
329 //Get data
330 $data = $form->getData();
331
332 //Set updated
333 $data->setUpdated(new \DateTime('now'));
334
335 //Queue location save
336 $this->manager->persist($data);
337
338 //Flush to get the ids
339 $this->manager->flush();
340
341 //Add notice
342 $this->addFlash('notice', $this->translator->trans('Location %id% updated', ['%id%' => $location['id']]));
343
344 //Redirect to cleanup the form
345 return $this->redirectToRoute('rapsys_air_location', ['location' => $location['id']]);
346 }
347
348 //Add form to context
349 $this->context['forms']['locations'][$id] = $form->createView();
350 }
351
352 //Create LocationType form
353 $form = $this->factory->createNamed(
354 //Set form id
355 'locations',
356 //Set form type
357 'Rapsys\AirBundle\Form\LocationType',
358 //Set form data
359 new Location(),
360 //Set the form attributes
361 ['attr' => ['class' => 'col']]
362 );
363
364 //Refill the fields in case of invalid form
365 $form->handleRequest($request);
366
367 //Handle valid form
368 if ($form->isSubmitted() && $form->isValid()) {
369 //Get data
370 $data = $form->getData();
371
372 //Queue location save
373 $this->manager->persist($data);
374
375 //Flush to get the ids
376 $this->manager->flush();
377
378 //Add notice
379 $this->addFlash('notice', $this->translator->trans('Location created'));
380
381 //Redirect to cleanup the form
382 return $this->redirectToRoute('rapsys_air_location', ['location' => $data->getId()]);
383 }
384
385 //Add form to context
386 $this->context['forms']['location'] = $form->createView();
387 }
388
389 //Render the view
390 return $this->render('@RapsysAir/location/index.html.twig', $this->context);
391 }
392
393 /**
394 * List all sessions for the location
395 *
396 * Display all sessions for the location with an application or login form
397 *
398 * @TODO: add location edit form ???
399 *
400 * @param Request $request The request instance
401 * @param int $id The location id
402 *
403 * @return Response The rendered view
404 */
405 public function view(Request $request, int $id): Response {
406 //Without location
407 if (empty($this->context['location'] = $this->doctrine->getRepository(Location::class)->findOneByIdAsArray($id, $this->locale))) {
408 //Throw 404
409 throw $this->createNotFoundException($this->translator->trans('Unable to find location: %id%', ['%id%' => $id]));
410 }
411
412 //Fetch calendar
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']);
414
415 //Set dances
416 $this->context['dances'] = [];
417
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'])) {
424 //Add dance
425 $this->context['dances'][$session['application']['dance']['id']] = $session['application']['dance'];
426 }
427 }
428 }
429
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);
432
433 //Set modified
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']))));
436
437 //Create response
438 $response = new Response();
439
440 //With logged user
441 if ($this->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
442 //Set last modified
443 $response->setLastModified(new \DateTime('-1 year'));
444
445 //Set as private
446 $response->setPrivate();
447 //Without logged user
448 } else {
449 //Set etag
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']))));
452
453 //Set last modified
454 $response->setLastModified($this->modified);
455
456 //Set as public
457 $response->setPublic();
458
459 //Without role and modification
460 if ($response->isNotModified($request)) {
461 //Return 304 response
462 return $response;
463 }
464 }
465
466 //Add multi map
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']));
468
469 //Set keywords
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')
476 ];
477
478 //With dances
479 if (!empty($this->context['dances'])) {
480 //Set dances
481 $dances = array_map(function ($v) { return $v['name']; }, $this->context['dances']);
482
483 //Insert dances in keywords
484 array_splice($this->context['keywords'], 2, 0, $dances);
485
486 //Get textual dances
487 $dances = implode($this->translator->trans(' and '), array_filter(array_merge([implode(', ', array_slice($dances, 0, -1))], array_slice($dances, -1)), 'strlen'));
488
489 //Set title
490 $this->context['title'] = $this->translator->trans('%dances% %location%', ['%dances%' => $dances, '%location%' => $this->context['location']['atin']]);
491
492 //Set description
493 $this->context['description'] = $this->translator->trans('%dances% indoor and outdoor calendar %location%', ['%dances%' => $dances, '%location%' => $this->context['location']['at']]);
494 //Without dances
495 } else {
496 //Set title
497 $this->context['title'] = $this->translator->trans('Dance %location%', ['%location%' => $this->context['location']['atin']]);
498
499 //Set description
500 $this->context['description'] = $this->translator->trans('Indoor and outdoor dance calendar %location%', [ '%location%' => $this->context['location']['at'] ]);
501 }
502
503 //Set locations description
504 $this->context['locations_description'] = $this->translator->trans('Libre Air location list %location%', ['%location%' => $this->context['location']['atin']]);
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 }