tailsfadmin / tailsfadmin-bundle
Thème admin Symfony basé sur TailAdmin — bundle réutilisable (Symfony UX, Stimulus, AssetMapper)
Package info
github.com/thibmonier/tailsfadmin
Type:symfony-bundle
pkg:composer/tailsfadmin/tailsfadmin-bundle
Requires
- php: >=8.2
- symfony/asset: ^7.3 || ^8.0
- symfony/asset-mapper: ^7.3 || ^8.0
- symfony/console: ^7.3 || ^8.0
- symfony/form: ^7.3 || ^8.0
- symfony/framework-bundle: ^7.3 || ^8.0
- symfony/stimulus-bundle: ^2.20 || ^3.0
- symfony/translation: ^7.3 || ^8.0
- symfony/ux-twig-component: ^2.0 || ^3.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.75
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^12.2 || ^13.0
- symfony/browser-kit: ^7.3 || ^8.0
- symfony/http-client: ^7.3 || ^8.0
- symfony/security-core: ^7.3 || ^8.0
Suggests
- symfony/security-bundle: Pour filtrer les items de la sidebar par permission (clé `permission` + is_granted)
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-14 06:50:14 UTC
README
Thème admin Symfony basé sur TailAdmin — bundle réutilisable (Symfony UX, Stimulus, AssetMapper, Tailwind CSS v4).
Architecture du monorepo
Ce dépôt suit la structure définie dans ADR-002 :
tailsfadmin/
├── src/ # TailsfadminBundle (le produit, distribué sur Packagist)
│ ├── TailsfadminBundle.php # AbstractBundle (Symfony >= 6.1)
│ └── Info/ # Services du bundle
├── config/
│ └── services.yaml # Services DI du bundle (préfixe tailsfadmin.*)
├── templates/ # Templates Twig namespacés @Tailsfadmin
├── assets/
│ └── controllers/ # Contrôleurs Stimulus du bundle (ADR-003)
├── demo/ # Application de démo (consomme le bundle en local)
│ ├── src/Controller/ # Contrôleurs de la démo (usage seul)
│ └── templates/ # Templates de la démo
├── tests/ # Tests du bundle (unitaires)
├── docs/adr/ # Architecture Decision Records
└── composer.json # Package du bundle (type: symfony-bundle)
Règle d'isolation bundle / démo (DoD §6)
IMPORTANT : aucun code réutilisable ne doit vivre dans
demo/. La démo ne contient que de l'usage du bundle. Toute logique partageable appartient àsrc/.
Prérequis
- PHP >= 8.5
- Composer >= 2.0
- Symfony >= 7.3 || 8.x
Installation (développement — path repository)
# 1. Cloner le dépôt git clone https://github.com/thibmonier/tailsfadmin.git cd tailsfadmin # 2. Installer les dépendances du bundle composer install # 3. Installer la démo cd demo composer install # 4. Lancer la démo (Symfony CLI) symfony serve -d # ou : php -S localhost:8000 -t public
Installation depuis Packagist (projet tiers)
composer require tailsfadmin/tailsfadmin-bundle
Le bundle est activé automatiquement par Symfony Flex.
Configuration & prise en main
1. Assets (AssetMapper + importmap)
Les contrôleurs Stimulus du bundle vivent sous bundles/tailsfadmin. Stimulus
doit être démarré côté hôte (assets/bootstrap.js du skeleton). Déclarez le path
AssetMapper des contrôleurs dans votre config/packages/asset_mapper.yaml (le
prepend() du bundle ne survit pas au merge Flex ; la recette Flex — à venir —
l'automatisera) :
framework: asset_mapper: paths: 'assets/': '' 'vendor/tailsfadmin/tailsfadmin-bundle/assets/controllers': 'bundles/tailsfadmin' 'vendor/tailsfadmin/tailsfadmin-bundle/assets/vendor-src': 'bundles/tailsfadmin-vendor'
Dépendances JS tierces — commande d'installation. Cinq contrôleurs du bundle
s'appuient sur des bibliothèques tierces (ApexCharts, jsvectormap, flatpickr,
Dropzone, FullCalendar). Un bundle ne peut pas injecter d'entrées dans
l'importmap.php de l'hôte (l'importmap n'est pas une config mergeable — voir
ADR-007). Le bundle fournit
donc une commande qui ajoute ces pins et vendore les fichiers localement :
php bin/console tailsfadmin:assets:install
- Sans CDN au runtime : les libs sont téléchargées à l'installation puis
servies en local (ADR-004/006). La source de vérité des versions pinées est
config/importmap-entries.php, distribué avec le bundle. - Idempotente : relançable sans risque, n'ajoute que ce qui manque.
- Conflits de version : si votre app a déjà piné une lib dans une autre
version, la commande ne l'écrase pas ; elle signale le conflit. Forcez la
réécriture avec
--force. - Garde-fou : si une page utilise un composant dont la lib n'est pas installée, une erreur explicite en console rappelle la commande à lancer (au lieu du cryptique « Failed to resolve module specifier »).
Compilez ensuite les assets :
php bin/console tailwind:build # CSS Tailwind v4 (symfonycasts/tailwind-bundle)
php bin/console asset-map:compile
Les contrôleurs sans dépendance externe (
theme,sidebar,modal,dropdown,alert-dismiss,preloader,search,submenu) fonctionnent sans cette commande, via le seul path AssetMapper.
2. Thème CSS (Tailwind hôte)
Le bundle distribue son thème (tokens @theme, dark mode, classes composants
.menu-item*…) dans assets/styles/theme.css. Votre app le branche en
une seule ligne, dans sa propre entrée Tailwind v4 — sans recopier les
tokens ni le CSS des composants :
/* assets/styles/app.css de VOTRE application */ @import "tailwindcss"; @import "../../vendor/tailsfadmin/tailsfadmin-bundle/assets/styles/theme.css";
-
Standalone, sans Node : compilez avec le binaire
symfonycasts/tailwind-bundle(php bin/console tailwind:build), comme le reste de votre CSS. -
Scan du contenu du bundle :
theme.cssdéclare des@source(relatifs, donc portables depuisvendor/) vers les templates et contrôleurs du bundle, afin que les utilitaires qu'ils emploient soient bien générés chez vous. -
Dark mode : stratégie par classe — activez
.darksur<html>. L'échelle de gris n'est pas inversée (les composants portent des variantesdark:explicites). -
Rebranding : redéfinissez les tokens
--color-brand-*après l'import (la cascade:rootl'emporte sur@theme) — cela repeint boutons, liens et item de menu actif :@import "tailwindcss"; @import ".../theme.css"; :root { --color-brand-500: #7c3aed; --color-brand-600: #6d28d9; }
Besoin d'une entrée « tout-en-un » (Tailwind + thème) ? Importez plutôt
assets/styles/app.cssdu bundle. La démo (demo/assets/styles/app.css) illustre le pattern hôte ci-dessus.
3. Menu de la sidebar (config/packages/tailsfadmin.yaml)
Les label sont des clés de traduction (voir i18n ci-dessous) ou des libellés bruts :
tailsfadmin: default_locale: fr locales: ['fr', 'en'] # whitelist de la bascule de langue rtl_locales: ['ar'] menu: - group: menu.groups.menu items: - { label: menu.dashboard, path: /, icon: dashboard } - label: menu.tables path: /tables icon: tables children: - { label: menu.tables_basic, path: /tables/basic }
Filtrage par permission (optionnel). Chaque item (et sous-item) accepte une clé
permission. Si un composant de sécurité Symfony est installé, les items dont
l'utilisateur courant n'a pas l'autorisation sont masqués (is_granted), et un
groupe entièrement filtré n'est pas rendu. Sans sécurité, tout reste visible.
tailsfadmin: menu: - group: menu.groups.admin items: - { label: menu.users, path: /admin/users, icon: user-profile, permission: ROLE_ADMIN } - { label: menu.reports, path: /reports, icon: charts, permission: 'view:reports' }
Le MenuBuilder reçoit @?security.authorization_checker (injection optionnelle,
aucune dépendance dure). Pour l'activer, installez symfony/security-bundle.
4. Layout d'une page
{% extends '@Tailsfadmin/layout/admin.html.twig' %}
{% block breadcrumb %}<twig:tsf:Layout:Breadcrumb pageName="Tableau de bord" />{% endblock %}
{% block content %}
<twig:tsf:Ui:Card title="Bienvenue">
<twig:block name="body">Votre première page tailsfadmin.</twig:block>
</twig:tsf:Ui:Card>
{% endblock %}
Le layout attend une route nommée
home(logo de la sidebar) etlocale_switchsi vous utilisez le sélecteur de langue.
Personnaliser le header. Le composant tsf:Layout:Header expose des blocs
surchargeables — language, notifications, user_menu — pour brancher un vrai
menu utilisateur (déconnexion + CSRF), un flux de notifications réel, ou retirer le
sélecteur de langue si l'application ne définit pas de route locale_switch.
Surchargez {% block header %} du layout avec le composant et vos slots :
{% block header %}
<twig:tsf:Layout:Header>
{# Retirer le sélecteur de langue (pas de route locale_switch) #}
<twig:block name="language"></twig:block>
{# Vrai menu utilisateur #}
<twig:block name="user_menu">
<a href="{{ path('account') }}">{{ app.user.userIdentifier }}</a>
<form method="post" action="{{ path('app_logout') }}">
<input type="hidden" name="_csrf_token" value="{{ csrf_token('logout') }}">
<button type="submit">{{ 'Déconnexion'|trans }}</button>
</form>
</twig:block>
</twig:tsf:Layout:Header>
{% endblock %}
5. Internationalisation (optionnel)
Installez symfony/translation, réglez framework.default_locale + enabled_locales,
et fournissez vos catalogues. Le bundle expose ses propres traductions du chrome
(header, breadcrumb…) et les helpers Twig tsf_dir() / tsf_locales().
6. Catalogue des composants
Tous les composants <twig:tsf:… /> (props, slots, exemples) sont documentés
dans docs/components.md ; une galerie vivante est
servie sur /ui-kit dans la démo.
Commandes de développement
# Analyse statique (niveau max) composer phpstan # Vérification du style PSR-12 composer cs # Correction automatique du style composer cs-fix # Tests unitaires du bundle composer test # Tests fonctionnels de la démo cd demo && php bin/phpunit # Tests E2E (montage JS réel + audit accessibilité axe-core) cd demo && composer test:e2e # Lint des contrôleurs Stimulus (Biome) npx @biomejs/biome check assets/controllers/
Le versionnement suit SemVer ; les évolutions sont consignées dans CHANGELOG.md (format Keep a Changelog).
Vérifications DoD
# Conteneur DI — doit lister ≥ 1 service tailsfadmin.* cd demo && php bin/console debug:container tailsfadmin.bundle_info # Warmup du cache — doit passer sans erreur cd demo && php bin/console cache:warmup
Docker / FrankenPHP
La démo peut aussi être lancée via Docker Compose, sous FrankenPHP / PHP 8.5,
pour garantir un environnement reproductible (local = CI). Voir
demo/Dockerfile et compose.yaml pour le détail de l'image.
Démarrer / arrêter
# Construire l'image et démarrer le conteneur en arrière-plan docker compose up --build -d # Suivre les logs docker compose logs demo -f # Arrêter et supprimer le conteneur docker compose down
L'application est ensuite disponible sur http://localhost/.
Vérifier le conteneur
# Statut du healthcheck (doit passer à "healthy") docker compose ps # Version de PHP et de FrankenPHP dans le conteneur docker compose exec demo php -v docker compose exec demo frankenphp version
Changer le port hôte (erreur « port déjà utilisé »)
Si le port 80 (ou 443) est déjà occupé sur la machine hôte, définir
HOST_HTTP_PORT / HOST_HTTPS_PORT avant de lancer la commande :
HOST_HTTP_PORT=8080 HOST_HTTPS_PORT=8443 docker compose up --build -d
# → application disponible sur http://localhost:8080/
Ces variables peuvent aussi être placées dans un fichier .env à la racine
du dépôt (non versionné) pour éviter de les répéter à chaque commande.
Utilisation hors-ligne (pré-pull de l'image)
L'image de base dunglas/frankenphp:php8.5 peut être téléchargée à l'avance,
par exemple avant un déplacement sans accès réseau ou pour accélérer un
premier build :
docker pull dunglas/frankenphp:php8.5
Le docker compose up --build suivant réutilisera l'image déjà présente en
cache local pour l'étape FROM, sans nouveau téléchargement.
Stack technique
| Couche | Technologie |
|---|---|
| Backend | PHP 8.5, Symfony 8.1, AbstractBundle |
| Frontend | Stimulus / Symfony UX, AssetMapper |
| CSS | Tailwind CSS v4 |
| Tests | PHPUnit 12, WebTestCase |
| Qualité | PHPStan max, PHP CS Fixer (PSR-12) |
Licence
MIT — voir LICENSE