akyos / ux-forms
Administrable contact forms and email templates for Symfony admin
Requires
- php: >=8.4
- akyos/ux-filters: ^2.0
- akyos/ux-table: ^2.0
- doctrine/orm: ^3.0
- symfony/config: ^7.0|^8.0
- symfony/dependency-injection: ^7.0|^8.0
- symfony/form: ^7.0|^8.0
- symfony/framework-bundle: ^7.0|^8.0
- symfony/http-kernel: ^7.0|^8.0
- symfony/mailer: ^7.0|^8.0
- symfony/mime: ^7.0|^8.0
- symfony/routing: ^7.0|^8.0
- symfony/security-core: ^7.0|^8.0
- symfony/translation: ^7.0|^8.0
- symfony/ux-live-component: ^3.0
- symfony/ux-twig-component: ^3.0
- symfony/validator: ^7.0|^8.0
- vich/uploader-bundle: ^2.0
README
Bundle Symfony pour gérer des formulaires de contact administrables : création de champs en back-office, affichage public, envoi d'e-mails de notification, historique des soumissions.
Sommaire
- Prérequis
- Installation
- Configuration
- Routes
- Utilisation
- Architecture
- Personnalisation (override)
- Modèle de données
- Variables d'e-mail
- Traductions
- Tests & fixtures
Prérequis
| Dépendance | Rôle |
|---|---|
| PHP ≥ 8.4 | |
| Symfony 7 ou 8 | Framework |
| Doctrine ORM 3 | Entités & migrations |
| Symfony Mailer | Notifications |
symfony/ux-live-component |
Formulaires admin & public réactifs |
symfony/ux-twig-component |
Composants Twig (twig:Admin:ContactForm:…) |
akyos/ux-filters |
Filtres des listes admin |
akyos/ux-table |
Tableaux paginés admin |
Côté application hôte, les templates du bundle s'appuient aussi sur :
symfony/ux-toolkit(Button, Card, Tabs, Dialog, Badge, Breadcrumb, Sidebar…)symfony/ux-icons(twig:ux:icon)- Tailwind CSS (classes utilitaires dans les templates)
Installation
1. Composer
Depuis un dépôt path (monorepo) :
{
"repositories": [
{ "type": "path", "url": "lib/ux-forms" }
],
"require": {
"akyos/ux-forms": "@dev"
}
}
Depuis un dépôt Git : adapter l'URL selon votre forge.
composer require akyos/ux-forms
2. Enregistrer le bundle
// config/bundles.php Akyos\UXForms\UXFormsBundle::class => ['all' => true],
Le bundle enregistre automatiquement :
- le mapping Doctrine ORM (
Akyos\UXForms\Entity) - les migrations (
Akyos\UXForms\Migrations) - les traductions (
lib/ux-forms/translations) - le namespace Twig
@UXForms - les Twig Components
Akyos\UXForms\Twig\Components\ - le chemin Asset Mapper
ux-forms/→lib/ux-forms/assets
3. Configuration
# config/packages/ux_forms.yaml ux_forms: admin_layout: admin/layouts/layout.html.twig allowed_roles: - ROLE_ADMIN - ROLE_SUPER_ADMIN sender_email: '%env(UX_FORMS_SENDER_EMAIL)%' sender_name: '%env(UX_FORMS_SENDER_NAME)%' public_base_url: '%env(DEFAULT_URI)%' email_layout_template: '@UXForms/email/layout.html.twig' email_notification_template: '@UXForms/email/contact_form_notification.html.twig' email_logo_directory: '%kernel.project_dir%/public/ux-forms/email' email_logo_uri_prefix: '/ux-forms/email'
Variables d'environnement typiques :
UX_FORMS_SENDER_EMAIL=noreply@example.com UX_FORMS_SENDER_NAME="Mon application" DEFAULT_URI=https://www.example.com
4. Mailer Symfony
Configurer un transport SMTP (ou autre) :
# config/packages/mailer.yaml framework: mailer: dsn: '%env(MAILER_DSN)%'
5. Routes
# config/routes/ux_forms.yaml ux_forms: resource: ../../vendor/akyos/ux-forms/config/routes.php type: php prefix: /admin name_prefix: admin_ # config/routes/ux_forms_public.yaml ux_forms_public: resource: ../../vendor/akyos/ux-forms/config/routes_public.php type: php
En monorepo, remplacer
vendor/akyos/ux-formsparlib/ux-forms.
6. Sécurité
Les contrôleurs admin utilisent ContactFormVoter (CONTACT_FORM_VIEW, EDIT, CREATE) et les rôles définis dans allowed_roles.
Exemple d'accès public au formulaire front :
# config/packages/security.yaml security: access_control: - { path: ^/contact-form, roles: PUBLIC_ACCESS } - { path: ^/admin, roles: ROLE_ADMIN }
Adapter selon votre modèle de rôles.
7. Sidebar admin (optionnel)
Inclure le fragment de navigation fourni :
{# templates/admin/layouts/sidebar.html.twig #} {{ include('@UXForms/admin/_sidebar.html.twig') }}
8. Stimulus (éditeur de champs admin)
Le drag-and-drop de l'éditeur de champs nécessite le contrôleur field_sortable :
# config/packages/stimulus.yaml stimulus: controller_paths: - '%kernel.project_dir%/assets/controllers' - '%kernel.project_dir%/vendor/akyos/ux-forms/assets/controllers'
9. Migrations
php bin/console doctrine:migrations:migrate
Les migrations du bundle créent notamment :
contact_form,contact_form_field,contact_form_recipientemail_template,email_layoutcontact_form_submission
10. Données de démo (optionnel)
php bin/console doctrine:fixtures:load --group=default
# ou charger Akyos\UXForms\DataFixtures\UXFormsFixtures manuellement
Configuration
| Clé | Défaut | Description |
|---|---|---|
admin_layout |
admin/layouts/layout.html.twig |
Layout Twig des pages admin du bundle. Exposé en global ux_forms_admin_layout. |
allowed_roles |
ROLE_ADMIN, ROLE_SUPER_ADMIN |
Rôles autorisés sur les écrans admin (via ContactFormVoter). |
sender_email |
noreply@localhost |
Expéditeur des e-mails de notification. |
sender_name |
SEFCA |
Nom affiché de l'expéditeur. |
public_base_url |
http://localhost |
URL publique pour les assets e-mail (logo). |
email_layout_template |
@UXForms/email/layout.html.twig |
Layout HTML des e-mails. |
email_notification_template |
@UXForms/email/contact_form_notification.html.twig |
Corps HTML de la notification. |
email_logo_directory |
public/ux-forms/email |
Répertoire d'upload du logo (admin). |
email_logo_uri_prefix |
/ux-forms/email |
Préfixe URI public du logo. |
Routes
Admin (préfixe /admin, noms admin_*)
| Nom | URL | Description |
|---|---|---|
admin_formulaires_index |
/admin/formulaires |
Liste des formulaires |
admin_formulaires_new |
/admin/formulaires/new |
Création |
admin_formulaires_edit |
/admin/formulaires/{id} |
Édition |
admin_formulaires_soumissions_index |
/admin/formulaires/soumissions |
Liste des soumissions |
admin_formulaires_soumissions_show |
/admin/formulaires/soumissions/{id} |
Détail d'une soumission |
admin_formulaires_modele_email |
/admin/formulaires/modele-email |
Modèle e-mail (logo, couleurs, pied de page) |
Front
| Nom | URL | Description |
|---|---|---|
contact_form_show |
/contact-form/{id} |
Page publique d'un formulaire |
Utilisation
Afficher un formulaire sur le front
Option A — route dédiée (déjà fournie) :
/contact-form/1
Option B — intégrer le LiveComponent dans n'importe quelle page :
{# Par entité #} <twig:Front:PublicForm :contactForm="contactForm" /> {# Ou par ID #} <twig:Front:PublicForm :contactFormId="1" />
Le composant gère validation, message de confirmation et soumission AJAX (LiveComponent).
Flux de soumission
Visiteur submit
→ PublicForm (LiveComponent)
→ ContactFormSubmissionHandlerInterface::handle()
→ ContactFormSubmissionService::submit()
1. Snapshot des champs en BDD (contact_form_submission)
2. Envoi e-mail via ContactFormMailer
3. Mise à jour du statut (sent / failed)
L'utilisateur voit le message de confirmation même si l'e-mail échoue ; l'échec est visible dans l'admin (liste + détail des soumissions).
Architecture
lib/ux-forms/
├── config/
│ ├── routes.php # Routes admin
│ ├── routes_public.php # Route front
│ └── services.yaml # Wiring DI
├── migrations/ # Migrations Doctrine
├── src/
│ ├── Contract/ # Points d'extension publics
│ ├── Controller/ # Admin + Front
│ ├── Entity/
│ ├── Mailer/
│ ├── Security/Voter/
│ ├── Service/
│ └── Twig/Components/ # LiveComponents admin & front
├── templates/ # Namespace @UXForms
├── translations/
└── assets/controllers/ # Stimulus (field-sortable)
Personnalisation (override)
1. Handler de soumission ⭐ (extension principale)
Interface :
// src/Contract/ContactFormSubmissionHandlerInterface.php public function handle(ContactForm $contactForm, array $values): void;
Implémentation par défaut : persistance + envoi e-mail + statut.
Override complet — remplacer le service :
// src/Contact/MySubmissionHandler.php namespace App\Contact; use Akyos\UXForms\Contract\ContactFormSubmissionHandlerInterface; use Akyos\UXForms\Entity\ContactForm; final class MySubmissionHandler implements ContactFormSubmissionHandlerInterface { public function handle(ContactForm $contactForm, array $values): void { // CRM, webhook, persistance custom… } }
# config/services.yaml Akyos\UXForms\Contract\ContactFormSubmissionHandlerInterface: class: App\Contact\MySubmissionHandler
Override partiel — décorateur autour du service bundle :
final class LoggingSubmissionHandler implements ContactFormSubmissionHandlerInterface { public function __construct( private ContactFormSubmissionHandler $inner, private LoggerInterface $logger, ) {} public function handle(ContactForm $contactForm, array $values): void { $this->inner->handle($contactForm, $values); $this->logger->info('Contact form submitted', ['id' => $contactForm->getId()]); } }
Akyos\UXForms\Service\ContactFormSubmissionHandler: class: App\Contact\LoggingSubmissionHandler arguments: $inner: '@Akyos\UXForms\Service\ContactFormSubmissionHandler.inner' Akyos\UXForms\Service\ContactFormSubmissionHandler.inner: class: Akyos\UXForms\Service\ContactFormSubmissionHandler
Réutiliser la logique bundle sans l'interface :
// Injection directe si besoin (CLI, listener…) public function __construct( private ContactFormSubmissionService $submissionService, ) {}
2. Templates Twig
Namespace : @UXForms/…
Copier un template dans votre app et surcharger via la convention Symfony :
templates/bundles/UXFormsBundle/
└── admin/contact_form/index.html.twig
Ou redéclarer un chemin Twig prioritaire. Templates couramment surchargés :
| Template | Usage |
|---|---|
@UXForms/admin/contact_form/*.html.twig |
Pages admin formulaires |
@UXForms/admin/contact_form_submission/*.html.twig |
Soumissions |
@UXForms/components/Front/PublicForm.html.twig |
Rendu public |
@UXForms/email/layout.html.twig |
Layout e-mail |
@UXForms/email/contact_form_notification.html.twig |
Corps notification |
@UXForms/admin/_sidebar.html.twig |
Navigation admin |
Config des templates e-mail sans copier les fichiers :
ux_forms: email_layout_template: 'email/my_layout.html.twig' email_notification_template: 'email/my_contact_notification.html.twig'
3. Layout admin
ux_forms: admin_layout: 'admin/my_layout.html.twig'
Le layout doit définir les blocs utilisés par les pages bundle (title, admin_breadcrumb, content). Les templates bundle étendent la variable globale ux_forms_admin_layout :
{% extends ux_forms_admin_layout %}
4. Sécurité & rôles
ux_forms: allowed_roles: - ROLE_GESTIONNAIRE - ROLE_ADMIN
Le voter ContactFormVoter accorde VIEW, EDIT, CREATE si l'utilisateur possède l'un de ces rôles.
Pour une logique plus fine, créer votre propre voter ou décorer le service existant (non prévu nativement — copier/étendre ContactFormVoter et remplacer le service).
5. Expéditeur & branding e-mail
Via config :
ux_forms: sender_email: 'contact@monapp.fr' sender_name: 'Mon App' public_base_url: 'https://monapp.fr' email_logo_directory: '%kernel.project_dir%/public/media/email' email_logo_uri_prefix: '/media/email'
Le logo et les couleurs se paramètrent aussi dans l'admin (/admin/formulaires/modele-email).
6. Traductions
Domaines fournis (fichiers *.fr.yaml) :
| Domaine | Contenu |
|---|---|
contact_form |
Admin formulaires |
contact_form_submission |
Admin soumissions |
email_template |
Modèle e-mail admin |
public_contact_form |
Formulaire public |
validators |
Contraintes |
Surcharge dans translations/ de l'app :
translations/contact_form.fr.yaml
translations/public_contact_form.fr.yaml
7. Routes
Les fichiers config/routes.php et config/routes_public.php du bundle peuvent être copiés dans l'app pour changer les URLs ou les noms, ou importés tels quels avec prefix / name_prefix.
Exemple — préfixer l'admin différemment :
ux_forms: resource: '@AkyosUXForms/config/routes.php' prefix: /backoffice name_prefix: backoffice_
8. Services internes (override avancé)
| Service | Rôle | Override |
|---|---|---|
ContactFormSubmissionHandlerInterface |
Point d'entrée soumission | ✅ Recommandé |
ContactFormSubmissionService |
Persistance + mail + statut | Via handler custom ou alias DI |
ContactFormMailer |
Envoi notification HTML/text | Alias + classe custom |
ContactFormEmailVariableResolver |
Variables {{prenom}}, {{contenu}} |
Alias si logique custom |
EmailLogoStorage |
Upload logo e-mail | Paramètres config ou service custom |
Exemple — mailer custom :
Akyos\UXForms\Mailer\ContactFormMailer: class: App\Mailer\BrandedContactFormMailer arguments: $senderEmail: '%akyos_ux_forms.sender_email%' $senderName: '%akyos_ux_forms.sender_name%' # … autres dépendances autowirées
9. Composants Twig / LiveComponents
Enregistrés sous le préfixe :
twig:Admin:ContactForm:Index
twig:Admin:ContactForm:Edit
twig:Admin:ContactForm:Delete
twig:Admin:ContactFormSubmission:Index
twig:Admin:EmailLayout:Edit
twig:Front:PublicForm
Pour surcharger un composant, créer une classe dans votre namespace avec le même nom n'est pas supporté directement ; préférer :
- surcharger le template
@UXForms/components/… - ou remplacer l'include dans vos propres pages admin
10. Ce qui n'est pas prévu pour override
| Élément | Alternative |
|---|---|
| Entités Doctrine | Ne pas modifier ; étendre via handler ou tables annexes |
| Migrations bundle | Ne pas éditer ; ajouter vos propres migrations |
Enum ContactFormFieldType |
Demander une évolution bundle ou champs custom via handler |
Modèle de données
| Entité | Description |
|---|---|
ContactForm |
Formulaire (libellé, actif, message confirmation, sujet/corps e-mail) |
ContactFormField |
Champ (type, libellé, position, layout ligne/colonne) |
ContactFormRecipient |
Destinataire notification |
EmailTemplate |
Modèle e-mail réutilisable (legacy / référentiel) |
EmailLayout |
Branding global (logo, couleur, pied de page) — singleton |
ContactFormSubmission |
Soumission persistée (sans FK vers ContactForm) |
ContactFormSubmission stocke un snapshot (contactFormLabel, fields JSON, submitterEmail, statut e-mail) pour conserver l'historique même après suppression du formulaire.
Types de champs : text, email, phone, date, select, textarea, checkbox.
Variables d'e-mail
Dans l'objet et le corps configurés en admin, syntaxe {{variable}} :
- Une variable par champ actif, dérivée du libellé (
Prénom→{{prenom}}, collision →{{prenom_12}}) {{contenu}}— récapitulatif textuel de tous les champs
Résolution : ContactFormEmailVariableResolver.
Traductions
Le bundle shippe du français uniquement (*.fr.yaml). L'app hôte peut ajouter d'autres locales en miroir.
Textes UI : ne jamais les hardcoder — toujours passer par les domaines ci-dessus.
Tests & fixtures
# Tests unitaires du bundle php vendor/bin/phpunit lib/ux-forms/tests/ # Tests d'intégration app hôte (exemple) php vendor/bin/phpunit tests/Controller/Admin/UXForms/
Fixtures : Akyos\UXForms\DataFixtures\UXFormsFixtures (formulaire démo + modèle e-mail + layout).
Checklist d'intégration rapide
-
composer require akyos/ux-forms - Bundle enregistré dans
bundles.php -
config/packages/ux_forms.yaml - Routes admin + public importées
- Mailer configuré (
MAILER_DSN) -
doctrine:migrations:migrate - Accès public
/contact-formdanssecurity.yaml - Sidebar :
include('@UXForms/admin/_sidebar.html.twig') - Stimulus : chemin
assets/controllersdu bundle - UX Toolkit + UX Icons disponibles dans l'app