05/03/2023

3. Pages d'événement et de lieu

Lu 595 fois Licence Creative Commons

Page d'événement

Pour afficher plus d'informations et de fonctionnalités pour un événement, nous allons créer une page dédiée. Commencer par ajouter une méthode dans EventController:

#[Route('/{id}', name: 'page')]
public function eventPage(Event $event): Response
{
    return $this->render('event/page.html.twig', [
        'event' => $event,
    ]);
}

La route indique la présence d'un paramètre id. Nous voudrions que Symfony récupère automatiquement l'entité Event dont l'ID correspond au paramètre présent dans l'URL. Nous allons donc utiliser un Param Converter du bundle sensio/framework-extra-bundle:

composer require sensio/framework-extra-bundle

Note: ce bundle est actuellement abandonné. Bien que très populaire depuis Symfony 2, il n'apporte plus de fonctionnalité par rapport au coeur du framework. Les Param Converters sont remplacés par des Argument Value Resolvers mais le EntityValueResolver n'est disponible qu'à partir de Symfony 6.2.

Dans le template /templates/_includes/event_card.html.twig, définir le lien vers la page de l'événement:

{# ...#}
<h2 class="mb-2"><a href="{{ path('event_page', {id: event.id}) }}">{{ event.title }}</a></h2>
{# ...#}

Le tableau passé en second argument à la fonction path() permet d'indiquer les paramètres de la route. Quand une clé ne correspond à aucun paramètre, alors c'est ajouté en paramètre GET.

Enfin, créer le template /templates/event/page.html.twig:

{% extends "_template.html.twig" %}

{% block title event.title %}

{% block content %}
    <div class="section">
        <div class="columns">
            <div class="column is-one-third">
                <figure class="image is-4by5">
                    <img src="https://loremflickr.com/480/600/event,music,poster?lock={{ event.id }}-" alt="{{ event.title }}">
                </figure>
            </div>
            <div class="column">
                <h1 class="title is-underlined">{{ event.title }}</h1>

                <div class="columns">
                    <div class="column">
                        <div class="notification is-info is-light">
                            <i class="fa-regular fa-calendar"></i> le {{ event.startAt|format_date(locale='fr', pattern='d MMM YYYY') }}<br>
                            <i class="fa-solid fa-clock"></i> de {{ event.startAt|date('H\\hi') }} à {{ event.endAt|date('H\\hi') }}
                        </div>
                    </div>
                    <div class="column">
                        {% if event.venue is null %}
                            <div class="notification is-danger is-light">
                                <i class="fa-regular fa-circle-question"></i> Lieu secret
                            </div>
                        {% else %}
                            <div class="notification is-primary is-light">
                                <i class="fa-solid fa-location-dot"></i> <a href="#">{{ event.venue.name }}</a><br>
                                {{ event.venue.address }}
                            </div>
                        {% endif %}
                    </div>
                </div>

                <p>{{ event.description }}</p>
            </div>
        </div>
    </div>
{% endblock %}

Page de lieu

De la même manière, créer une nouvelle route dans EventController:

#[Route('/venue/{id}', name: 'venue_page')]
public function venuePage(Venue $venue): Response
{
    return $this->render('event/venue.html.twig', [
        'venue' => $venue,
    ]);
}

Ajouter les liens dans les templates /templates/_includes/event_card.html.twig et /templates/event/page.html.twig:

{# /templates/_includes/event_card.html.twig #}
{% else %}
    <i class="fa-solid fa-location-dot"></i>
    <a href="{{ path('event_venue_page', {id: event.venue.id}) }}">
        {{ event.venue.name }}
    </a>
    <br>
{% endif %}
{# ... #}
{# /templates/event/page.html.twig #}
<div class="notification is-primary is-light">
    <i class="fa-solid fa-location-dot"></i>
    <a href="{{ path('event_venue_page', {id: event.venue.id}) }}">{{ event.venue.name }}</a><br>
    {{ event.venue.address }}
</div>
{# ... #}

Puis créer le template /templates/event/venue.html.twig:

{% extends "_template.html.twig" %}

{% block title venue.name %}

{% block content %}
    <div class="section">
        <div class="content">
            <h1>{{ venue.name }}</h1>
            <p>{{ venue.address }}</p>
            <h3>Événements:</h3>
        </div>
    </div>
{% endblock %}

On souhaite maintenant ajouter un système d'onglet pour lister les événements à venir et ceux passés. Commençons par créer 2 getters dans l'entité Venue en utilisant Collection::filter() et Criteria::orderBy():

use Doctrine\Common\Collections\Criteria;

// ...

/**
 * Liste des prochains événements
 * @return Collection<int, Event>
 */
public function getUpcomingEvents(): Collection
{
	return $this->events
		->filter(fn (Event $event) => $event->getStartAt() > new \DateTime())
		->matching(Criteria::create()->orderBy(['startAt' => 'ASC']))
	;
}

/**
 * Liste des événements passés
 * @return Collection<int, Event>
 */
public function getPastEvents(): Collection
{
	return $this->events
		->filter(fn (Event $event) => $event->getStartAt() < new \DateTime())
		->matching(Criteria::create()->orderBy(['startAt' => 'DESC']))
	;
}

On ajoute ensuite les onglets dans le template avec des liens qui ajouteront un paramètre GET tab pour la sélection d'onglet. L'onglet des événements à venir sera actif par défaut:

<div class="section">
	{# ... #}

	<div class="tabs is-fullwidth">
		<ul>
			<li class="{{ app.request.query.get('tab', 'upcoming') == 'upcoming' ? 'is-active' : '' }}">
				<a href="{{ path('event_venue_page', {id: venue.id, tab: 'upcoming'}) }}">
					<span class="icon is-small"><i class="fa-regular fa-star"></i></span>
					<span>À venir ({{ venue.upcomingEvents|length }})</span>
				</a>
			</li>
			<li class="{{ app.request.query.get('tab') == 'past' ? 'is-active' : '' }}">
				<a href="{{ path('event_venue_page', {id: venue.id, tab: 'past'}) }}">
					<span class="icon is-small"><i class="fa-solid fa-clock-rotate-left"></i></span>
					<span>Passés ({{ venue.pastEvents|length }})</span>
				</a>
			</li>
		</ul>
	</div>
</div>

On utilise ici la variable globale app de Twig qui permet notamment d'accéder à l'objet Request. Il nous est donc possible de vérifier la valeur du paramètre tab dans l'URL depuis le template.

Enfin, ajouter les 2 blocs des onglets:

<div class="section">
	{# ... #}

	<div class="{{ app.request.query.get('tab', 'upcoming') == 'upcoming' ? '' : 'is-hidden' }}">
		{% for event in venue.upcomingEvents %}
			{% include '_includes/event_card.html.twig' with {event} %}
		{% else %}
			<div class="card">
				<div class="card-content">
					<div class="content">
						<p class="title is-4">
							<span class="icon"><i class="fa-regular fa-face-frown"></i></span>
							<span>Oups !</span>
						</p>
						<p class="subtitle is-6">
							<strong>{{ venue.name }}</strong> n'a aucun événement prévu prochainement.
						</p>
					</div>
				</div>
			</div>
		{% endfor %}
	</div>

	<div class="{{ app.request.query.get('tab') == 'past' ? '' : 'is-hidden' }}">
		{% for event in venue.pastEvents %}
			{% include '_includes/event_card.html.twig' with {event} %}
		{% else %}
			<div class="card">
				<div class="card-content">
					<div class="content">
						<p class="title is-4">
							<span class="icon"><i class="fa-regular fa-face-frown"></i></span>
							<span>Oups !</span>
						</p>
						<p class="subtitle is-6">
							<strong>{{ venue.name }}</strong> n'a aucun événement passé pour le moment.
						</p>
					</div>
				</div>
			</div>
		{% endfor %}
	</div>
</div>