Le contexte
Sur le panneau d'admin de baccalaureat.sn, je gère les logs de téléchargement des annales. Je voulais afficher deux colonnes qui ne correspondent à aucune propriété mappée de l'entité : le libellé humain de l'épreuve et un badge premium.
Le symptôme
public function configureFields(string $pageName): iterable
{
    yield TextField::new('epreuveLabel', 'Épreuve');
    yield TextField::new('isPremium', 'Premium');
}
Résultat : colonnes vides, ou exception Neither the property "epreuveLabel" nor one of the methods ... exist.
Pourquoi ça casse
EasyAdmin part du principe qu'un Field correspond à une propriété accessible via PropertyAccess. Pour un champ qui n'existe pas sur l'entité, il faut le déclarer virtuel.
La solution
yield TextField::new('epreuveLabel', 'Épreuve')
    ->setVirtual(true)
    ->formatValue(fn ($value, $entity) => sprintf(
        '%s — %s (%s)',
        $entity->getMatiere(),
        $entity->getSerie(),
        $entity->getAnnee()
    ));
Pour un rendu HTML, un template dédié :
yield TextField::new('isPremium', 'Premium')
    ->setVirtual(true)
    ->setTemplatePath('admin/field/premium_badge.html.twig');
{# templates/admin/field/premium_badge.html.twig #}
{% if entity.instance.account and entity.instance.account.isPremium %}
    Premium
{% else %}
    Gratuit
{% endif %}
Point qui m'a coûté une heure : dans un template de champ EasyAdmin, l'entité réelle est sous entity.instance, pas entity.
Ce que je retiens
  • setVirtual(true) dès qu'un champ ne correspond pas à une propriété Doctrine.
  • formatValue() reçoit ($value, $entity) — pratique pour composer une valeur.
  • Dans les templates de champ : entity.instance pour l'objet.