Add contact page with form, email template, and tests

- Create ContactController with GET/POST handling and MailerService integration
- Create contact page template with name, surname, email, message form
- Create dedicated email template for contact messages
- Update navbar links (desktop + mobile) to point to /contact route
- Add ContactControllerTest with 5 tests covering form submission and validation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Serreau Jovann
2026-03-19 10:19:41 +01:00
parent b5020eae66
commit 28763e7ee1
5 changed files with 231 additions and 2 deletions

View File

@@ -0,0 +1,55 @@
<?php
namespace App\Controller;
use App\Service\MailerService;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class ContactController extends AbstractController
{
#[Route('/contact', name: 'app_contact', methods: ['GET', 'POST'])]
public function index(Request $request, MailerService $mailerService): Response
{
if ($request->isMethod('POST')) {
$name = trim($request->request->getString('name'));
$surname = trim($request->request->getString('surname'));
$email = trim($request->request->getString('email'));
$message = trim($request->request->getString('message'));
if ('' === $name || '' === $surname || '' === $email || '' === $message) {
$this->addFlash('error', 'Tous les champs sont obligatoires.');
return $this->redirectToRoute('app_contact');
}
$html = $this->renderView('email/contact.html.twig', [
'name' => $name,
'surname' => $surname,
'email' => $email,
'message' => $message,
]);
$mailerService->sendEmail(
to: 'contact@e-cosplay.fr',
subject: sprintf('Contact de %s %s', $surname, $name),
content: $html,
replyTo: $email,
withUnsubscribe: false,
);
$this->addFlash('success', 'Votre message a bien ete envoye. Nous vous repondrons dans les plus brefs delais.');
return $this->redirectToRoute('app_contact');
}
return $this->render('contact/index.html.twig', [
'breadcrumbs' => [
['name' => 'Accueil', 'url' => '/'],
['name' => 'Contact', 'url' => '/contact'],
],
]);
}
}

View File

@@ -84,7 +84,7 @@
<div class="hidden lg:flex items-center space-x-1">
<a href="{{ path('app_home') }}" itemprop="url" class="px-3 py-2 text-xs font-black uppercase tracking-widest transition-all bg-yellow-400 border-2 border-gray-900 shadow-[2px_2px_0px_rgba(0,0,0,1)]"><span itemprop="name">Accueil</span></a>
<a href="#" itemprop="url" class="px-3 py-2 text-xs font-black uppercase tracking-widest transition-all hover:text-indigo-600"><span itemprop="name">Evenements</span></a>
<a href="#" itemprop="url" class="px-3 py-2 text-xs font-black uppercase tracking-widest transition-all hover:text-indigo-600"><span itemprop="name">Contact</span></a>
<a href="{{ path('app_contact') }}" itemprop="url" class="px-3 py-2 text-xs font-black uppercase tracking-widest transition-all hover:text-indigo-600"><span itemprop="name">Contact</span></a>
</div>
<div class="flex items-center space-x-4 border-l-4 border-gray-900 pl-6 h-full">
@@ -112,7 +112,7 @@
<div class="p-4 space-y-2 uppercase font-black italic">
<a href="{{ path('app_home') }}" class="block p-3 border-2 border-transparent hover:border-gray-900 hover:bg-gray-50" role="menuitem">Accueil</a>
<a href="#" class="block p-3 border-2 border-transparent hover:border-gray-900 hover:bg-gray-50" role="menuitem">Evenements</a>
<a href="#" class="block p-3 border-2 border-transparent hover:border-gray-900 hover:bg-gray-50" role="menuitem">Contact</a>
<a href="{{ path('app_contact') }}" class="block p-3 border-2 border-transparent hover:border-gray-900 hover:bg-gray-50" role="menuitem">Contact</a>
{% if app.user %}
<a href="{{ path('app_account') }}" class="block p-3 border-2 border-transparent hover:border-gray-900 hover:bg-gray-50" role="menuitem">Mon espace</a>
{% else %}

View File

@@ -0,0 +1,74 @@
{% extends 'base.html.twig' %}
{% block title %}Contact - E-Ticket{% endblock %}
{% block description %}Contactez l'equipe E-Ticket pour toute question sur la plateforme de billetterie associative{% endblock %}
{% block body %}
<div style="max-width:50rem;margin:0 auto;padding:3rem 1rem;">
<h1 class="text-3xl font-black uppercase tracking-tighter italic" style="border-bottom:4px solid #111827;display:inline-block;margin-bottom:0.5rem;">Contact</h1>
<p class="font-bold text-gray-600 italic" style="margin-bottom:2rem;">Une question, une demande ? Ecrivez-nous.</p>
{% for message in app.flashes('success') %}
<div style="border:4px solid #111827;padding:1rem 1.5rem;margin-bottom:2rem;background:#d1fae5;box-shadow:4px 4px 0 rgba(0,0,0,1);">
<p class="font-black text-sm">{{ message }}</p>
</div>
{% endfor %}
{% for message in app.flashes('error') %}
<div style="border:4px solid #111827;padding:1rem 1.5rem;margin-bottom:2rem;background:#fee2e2;box-shadow:4px 4px 0 rgba(0,0,0,1);">
<p class="font-black text-sm">{{ message }}</p>
</div>
{% endfor %}
<form method="post" action="{{ path('app_contact') }}" style="display:flex;flex-direction:column;gap:1.5rem;">
<div style="display:flex;flex-wrap:wrap;gap:1.5rem;">
<div style="flex:1;min-width:200px;">
<label for="contact_name" class="text-xs font-black uppercase tracking-widest" style="display:block;margin-bottom:0.5rem;">Nom</label>
<input type="text" id="contact_name" name="name" required
style="width:100%;padding:0.75rem 1rem;border:3px solid #111827;font-weight:700;outline:none;"
class="focus:border-indigo-600"
placeholder="Dupont">
</div>
<div style="flex:1;min-width:200px;">
<label for="contact_surname" class="text-xs font-black uppercase tracking-widest" style="display:block;margin-bottom:0.5rem;">Prenom</label>
<input type="text" id="contact_surname" name="surname" required
style="width:100%;padding:0.75rem 1rem;border:3px solid #111827;font-weight:700;outline:none;"
class="focus:border-indigo-600"
placeholder="Jean">
</div>
</div>
<div>
<label for="contact_email" class="text-xs font-black uppercase tracking-widest" style="display:block;margin-bottom:0.5rem;">Email</label>
<input type="email" id="contact_email" name="email" required
style="width:100%;padding:0.75rem 1rem;border:3px solid #111827;font-weight:700;outline:none;"
class="focus:border-indigo-600"
placeholder="jean.dupont@exemple.fr">
</div>
<div>
<label for="contact_message" class="text-xs font-black uppercase tracking-widest" style="display:block;margin-bottom:0.5rem;">Message</label>
<textarea id="contact_message" name="message" required rows="6"
style="width:100%;padding:0.75rem 1rem;border:3px solid #111827;font-weight:700;outline:none;resize:vertical;"
class="focus:border-indigo-600"
placeholder="Votre message..."></textarea>
</div>
<div>
<button type="submit"
style="padding:0.75rem 2rem;border:3px solid #111827;box-shadow:4px 4px 0 rgba(0,0,0,1);cursor:pointer;"
class="bg-yellow-400 font-black uppercase text-sm tracking-widest hover:bg-indigo-600 hover:text-white transition-all">
Envoyer
</button>
</div>
</form>
<div style="border:4px solid #111827;padding:1.5rem;background:#f9fafb;box-shadow:6px 6px 0 rgba(0,0,0,1);margin-top:3rem;">
<h2 class="text-lg font-black uppercase" style="margin-bottom:0.5rem;">Autres moyens de contact</h2>
<ul style="list-style:disc;padding-left:1.5rem;" class="text-sm font-bold text-gray-700">
<li>Email : <a href="mailto:contact@e-cosplay.fr" class="text-indigo-600 hover:underline">contact@e-cosplay.fr</a></li>
<li>Adresse : 42 rue de Saint-Quentin, 02800 Beautor, France</li>
</ul>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,24 @@
{% extends 'email/base.html.twig' %}
{% block title %}Nouveau message de contact{% endblock %}
{% block content %}
<h2>Nouveau message de contact</h2>
<table style="width:100%;border-collapse:collapse;margin-bottom:24px;">
<tr>
<td style="padding:8px 12px;font-weight:700;font-size:14px;color:#18181b;border-bottom:1px solid #e4e4e7;width:120px;">Nom</td>
<td style="padding:8px 12px;font-size:14px;color:#3f3f46;border-bottom:1px solid #e4e4e7;">{{ name }}</td>
</tr>
<tr>
<td style="padding:8px 12px;font-weight:700;font-size:14px;color:#18181b;border-bottom:1px solid #e4e4e7;">Prenom</td>
<td style="padding:8px 12px;font-size:14px;color:#3f3f46;border-bottom:1px solid #e4e4e7;">{{ surname }}</td>
</tr>
<tr>
<td style="padding:8px 12px;font-weight:700;font-size:14px;color:#18181b;border-bottom:1px solid #e4e4e7;">Email</td>
<td style="padding:8px 12px;font-size:14px;color:#3f3f46;border-bottom:1px solid #e4e4e7;"><a href="mailto:{{ email }}" style="color:#7c3aed;text-decoration:none;">{{ email }}</a></td>
</tr>
</table>
<div style="background:#f4f4f5;border-radius:8px;padding:16px;font-size:15px;line-height:1.6;color:#3f3f46;">
{{ message|nl2br }}
</div>
{% endblock %}

View File

@@ -0,0 +1,76 @@
<?php
namespace App\Tests\Controller;
use App\Service\MailerService;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class ContactControllerTest extends WebTestCase
{
public function testContactPageReturnsSuccess(): void
{
$client = static::createClient();
$client->request('GET', '/contact');
self::assertResponseIsSuccessful();
}
public function testContactFormSubmitRedirectsWithSuccess(): void
{
$client = static::createClient();
$mailer = $this->createMock(MailerService::class);
$mailer->expects(self::once())->method('sendEmail');
static::getContainer()->set(MailerService::class, $mailer);
$client->request('POST', '/contact', [
'name' => 'Dupont',
'surname' => 'Jean',
'email' => 'jean@exemple.fr',
'message' => 'Bonjour, je voudrais des informations.',
]);
self::assertResponseRedirects('/contact');
$client->followRedirect();
self::assertSelectorExists('.font-black.text-sm');
}
public function testContactFormEmptyFieldsRedirectsWithError(): void
{
$client = static::createClient();
$client->request('POST', '/contact', [
'name' => '',
'surname' => 'Jean',
'email' => 'jean@exemple.fr',
'message' => 'Bonjour',
]);
self::assertResponseRedirects('/contact');
}
public function testContactFormAllEmptyRedirectsWithError(): void
{
$client = static::createClient();
$client->request('POST', '/contact', [
'name' => '',
'surname' => '',
'email' => '',
'message' => '',
]);
self::assertResponseRedirects('/contact');
}
public function testContactFormWhitespaceOnlyRedirectsWithError(): void
{
$client = static::createClient();
$client->request('POST', '/contact', [
'name' => ' ',
'surname' => 'Jean',
'email' => 'jean@exemple.fr',
'message' => 'Bonjour',
]);
self::assertResponseRedirects('/contact');
}
}