02-ce-que-je-construis/bundles/tlr-symfony.md

tlr-symfony — bundle socle générique multisite

tlr-symfony est le socle générique de l'écosystème Telaria. Il fournit les briques réutilisables qui n'ont aucune dépendance IA : multisite, CMS moteur, Markdown, contact, i18n.

Champ Valeur
Package Composer tlr/symfony-bundle
Namespace Tlr\Symfony\
Pilote telaria-app (mĂŞme release flow)
Version en prod develop (5476856)

Spec fonctionnelle CMS : specs/telaria-cms.md Schéma BDD complet : ../bdd/schema.md Multisite VPS : ../vps/03-vhosts-multisite.md


Périmètre

tlr-symfony
├── Multisite     SiteResolver · SiteContext — un déploiement Symfony sert N domaines
├── CMS moteur    CmsContent · CmsTag · CmsImage · routes canoniques custom
├── Markdown      service de rendu Markdown → HTML (réutilisable)
├── Contact       formulaire de contact + rate limiter intégré
└── i18n          extraction automatique des chaînes

Résolution multisite

Un seul déploiement Symfony sert plusieurs domaines avec des contenus distincts. La résolution se fait par Host HTTP à chaque requête.

requĂŞte HTTP (host: telaria.dev)
        │
    SiteResolver  →  SELECT * FROM site WHERE host = 'telaria.dev'
        │                      (fallback : site.is_primary = true)
        â–Ľ
    SiteContext   →  injecté dans les services (Twig, mail, CMS)
        │
    CMS routing   →  cms_content.slug WHERE site_id = <site.id>

Couplage app → bundle : resolve_target_entities dans doctrine.yaml pour CmsUserInterface → App\Entity\User. Le bundle ne dépend pas de la classe User directement.


Entités

site — Domaine servi

Colonne Type Notes
id INT AUTO_INCREMENT PK
host VARCHAR(255) UNIQUE host canonique sans schéma (ex. telaria.dev)
aliases JSON nullable — hosts supplémentaires (ex. ["www.telaria.dev"])
slug VARCHAR(64) UNIQUE id stable /^[a-z0-9-]+$/ (ex. codexia)
label VARCHAR(128) nom affiché
locale_default VARCHAR(8) défaut fr
layout_template VARCHAR(128) template Twig de layout
theme VARCHAR(64) nullable — clé CSS du thème
brand_domain / brand_tld VARCHAR nullable
contact_email VARCHAR(255) nullable — destinataire formulaire de contact
sender_email VARCHAR(255) nullable — expéditeur mails sortants
home_slug VARCHAR(255) nullable — slug de la page CMS racine (/)
og_image_default VARCHAR(512) nullable
enabled BOOL défaut true
is_primary BOOL site de repli si aucun host ne matche — un seul attendu
created_at / updated_at DATETIME_IMMUTABLE PrePersist/PreUpdate

cms_content — Page ou contenu éditorial

Slug unique par site (contrainte composite UNIQUE(site_id, slug)).

Colonne Type Notes
id INT AUTO_INCREMENT PK
site_id INT FK → site.id
author_id INT FK → user.id via CmsUserInterface
title VARCHAR(255)
slug VARCHAR(255) auto-slugifié à la création — UNIQUE(site_id, slug)
excerpt TEXT nullable
markdown TEXT contenu source
status VARCHAR(20) draft | published | archived
published_at DATETIME nullable — posé au premier publish()
updated_at DATETIME PrePersist/PreUpdate
visibility VARCHAR(20) public | protected | private
type VARCHAR(50) page (défaut) | block (fragments non indexés)
noindex BOOL meta robots noindex + exclusion sitemap
og_image VARCHAR(512) nullable — surcharge OG image du site

cms_tag — Étiquette transverse

Vocabulaire partagé entre sites. Slug auto-généré depuis name.

cms_content_tag — Pivot ManyToMany

cms_content_id FK → cms_content.id / cms_tag_id FK → cms_tag.id.

cms_image — Image média

Upload sécurisé via FileUploader — validation MIME réelle (finfo, pas l'extension du fichier).

Colonne Type Notes
id INT AUTO_INCREMENT PK
filename VARCHAR(255) nom de fichier stocké
mime VARCHAR(100) type MIME validé par finfo
size INT octets
width / height INT nullable
checksum VARCHAR(64) SHA-256 — déduplication
created_at DATETIME
owner_id INT FK → user.id
site_id INT FK nullable → site.id SET NULL si site supprimé
content_id INT FK nullable → cms_content.id (rattachement optionnel)

Patterns Doctrine notables

Auto-mapping — Zéro fichier XML/YAML de mapping, zéro migration au bootstrap du bundle.

resolve_target_entities — CmsUserInterface → App\Entity\User (résolu dans doctrine.yaml de l'app). Le bundle ne dépend pas de la classe User.

Lifecycle callbacks — created_at / updated_at via #[PrePersist] / #[PreUpdate] sur les entités concernées.

Companion Entity — cms_content_seo (dans telaria-app) est une extension OneToOne de cms_content : la table cms_content appartient au bundle et ne peut pas être modifiée directement — le pattern companion permet d'ajouter des colonnes SEO côté app sans toucher au bundle.

Slug auto-généré — Le slug de cms_content est slugifié depuis le titre à la création (service interne). Contrainte UNIQUE(site_id, slug) : le CMS permet des slugs identiques entre sites différents.


Rate limiter (contact)

Le formulaire de contact utilise le composant RateLimiter Symfony (politique token_bucket). La destination est Site.contact_email. L'expéditeur est Site.sender_email.


Intégration dans l'app (doctrine.yaml)

doctrine:
  orm:
    resolve_target_entities:
      Tlr\Symfony\Contract\CmsUserInterface: App\Entity\User

Dépannage

Site non résolu — 404 ou site incorrect

# Vérifier les sites en base
php bin/console doctrine:query:sql "SELECT id, host, slug, is_primary, enabled FROM site"

# Host non dans la liste → SiteResolver prend le site is_primary=true comme fallback
# Corriger : ajouter le host ou l'alias dans le BO / app:sites:sync
php bin/console app:sites:sync

Slug de page introuvable

La contrainte UNIQUE(site_id, slug) : deux pages peuvent avoir le même slug sur des sites différents. Si une page ne s'affiche pas, vérifier que site_id pointe vers le bon site.

CMS content non publié

Statut draft ou archived → non visible en front (requête filtre status = 'published'). Publier depuis le back-office ou via Doctrine.


Voir aussi

Assistant documentaire

Posez une question sur la documentation. Les réponses citent leurs sources — un clic ouvre le document à gauche.

Loading…
Loading the web debug toolbar…
Attempt #