PrestaConcept
Nos réalisations
Nos métiers
  • Découvrez nos métiers
  • Développement web sur mesure

    Nous développons en méthode agile des back sous le framework PHP Symfony, des front en Angular.

  • Maintenance d'applications

    Maintien en condition opérationnelle de votre plateforme Symfony.

  • Expertise Symfony

    Coaching, formation, audit et conseil.

  • Hébergement et Infogérance

    Une expertise de l'hébergement depuis plus de 15 ans et l’infogérance de centaines de machines en production actuellement.

  • Qui sommes nous
  • Découvrez Prestaconcept
  • PrestaConcept

    Notre histoire, nos convictions, notre vision... Découvrez ce qui nous anime !

  • L'équipe

    Plus de 15 ans minimum d'expérience sur Symfony.

  • Nos engagements RSE

    Une société engagée pour un numérique responsable.

  • Nos convictions

    Co-construction, transparence.. Les principes qui guident nos collaborations !

  • Nous rejoindre

    Envie de nous rejoindre ? Consultez nos offres !

  • Blog
    J'ai un projet Nous contacter
    J'ai un projet Nous contacter Menu
    • Accueil
    • Blog
    • Tech
    • Comment désactiver certains listeners lors de certaines commandes

    Blog

    Comment désactiver certains listeners lors de certaines commandes

    php snippet symfony
    Yann Eugoné
    Yann Eugoné CTO
    Publié le mardi 11 janvier 2022

    Une solution simple et élégante, utilisant l'injection de services tagués, pour vous donner la possibilité de désactiver certains listeners lors de l'exécution de certaines commandes.

    A quoi ça sert ?

    C'est souvent pour des problématiques de performance, lors de traitements en masse.

    Un exemple ?
    Votre site dispose d'un sitemap (généré, car plus rapide).
    Votre sitemap est alimenté par des URLs en provenance d'une base de données.
    Dès que vos données changent, vous devez re-générer ce sitemap.
    Vous disposz d'un listener qui, dès qu'un changement est détecté, demande la re-génération.
    Problème : lors du chargement des fixtures, la re-génération du sitemap est demandée plusieurs fois.

    1. c'est inutile, il suffirait de recharger le sitemap une seule fois, à la fin
    2. c'est lent, selon le volume de données, cela peut pénaliser la durée de chargement de vos fixtures

    Une solution simple et élégante ?

    Il s'agit d'utiliser les mécaniques de Symfony et de respecter les bonnes pratiques de développement.
    Notamment le principe ouvert/fermé de SOLID : je souhaite que chaque listener puisse déclarer les commande sur lesquelles celui-ci ne veut pas être exécuté, plutôt que d'avoir une classe/configuration qui dispose de la connaissance de l'ensemble des règles.

    La première étape est de déclarer une interface que l'on pourra poser sur chacun de ces listeners :

    <?php
    
    namespace App\Listener;
    
    /**
     * Un listener qui implémente cette interface
     * s'autorise à être désactivé lors de certaines commandes.
     *
     * Le plus souvent, désactiver un listener se fait
     * pour améliorer les performances de certaines commandes.
     */
    interface ListenerDisabledForCommand
    {
        /**
         * @return string[]
         */
        public static function getCommandsWhereDisabled(): array;
    }
    

    Ensuite, nous allons écrire un listener, qui s'abonnera à l'événement "une commande est sur le point de démarrer", et qui désactivera les listeners qui ne doivent pas être enregistrés sur la commande en question.

    <?php
    
    namespace App\Listener;
    
    use Doctrine\Common\EventSubscriber;
    use Doctrine\ORM\EntityManagerInterface;
    use Generator;
    use Symfony\Component\Console\Event\ConsoleCommandEvent;
    use Symfony\Component\EventDispatcher\EventDispatcherInterface;
    use Symfony\Component\EventDispatcher\EventSubscriberInterface;
    
    /**
     * Ce listener permet de désactiver d'autres listeners lors de l'exécution
     * de certaines commandes.
     *
     * Pour utiliser cette fonctionnalité un listener doit implémenter
     * l'interface {@see ListenerDisabledForCommand}.
     */
    final class DisableListenersForCommandListener implements EventSubscriberInterface
    {
        public function __construct(
            /**
             * @var iterable&ListenerDisabledForCommand[]
             */
            private iterable $listenersToDisable,
            private EntityManagerInterface $doctrine,
            private EventDispatcherInterface $eventDispatcher,
        ) {
        }
    
        public static function getSubscribedEvents(): Generator
        {
            yield ConsoleCommandEvent::class => 'onConsoleCommand';
        }
    
        public function onConsoleCommand(ConsoleCommandEvent $event): void
        {
            $command = $event->getCommand();
            if ($command === null) {
                return;
            }
    
            foreach ($this->listenersToDisable as $listener) {
                if (!\in_array($command->getName(), $listener::getCommandsWhereDisabled(), true)) {
                    continue;
                }
    
                $this->removeDoctrineSubscriber($listener);
                $this->removeSymfonySubscriber($listener);
            }
        }
    
        private function removeSymfonySubscriber(object $subscriber): void
        {
            if ($subscriber instanceof EventSubscriberInterface) {
                $this->eventDispatcher->removeSubscriber($subscriber);
            }
        }
    
        private function removeDoctrineSubscriber(object $subscriber): void
        {
            if (!$subscriber instanceof EventSubscriber) {
                return;
            }
    
            $this->doctrine->getEventManager()->removeEventSubscriber($subscriber);
        }
    }
    

    Enfin, via quelques lignes de configuration, nous allons ajouter un tag à tous nos listeners qui implémentent notre interface.
    Et injecter dans notre listener, tous les services qui ont été tagués avec ce tag.

    services:
        _instanceof:
            App\Listener\ListenerDisabledForCommand:
                tags: ['app.listener_disabled_for_command']
    
        App\Listener\DisableListenersForCommandListener:
            arguments:
                $listenersToDisable: !tagged_iterator { tag: app.listener_disabled_for_command }
    

    Exemple

    Reprenons notre exemple plus haut : nous avons un listener qui demande la régénération du sitemap dès que certaines entités sont créées/modifiées/supprimées et nous voulons que ce listener ne soit pas appelé lors du chargement des fixtures.

    <?php
    
    namespace App\Sitemap;
    
    use App\Listener\ListenerDisabledForCommand;
    use Doctrine\Common\EventSubscriber;
    
    final class TriggerSitemapDumpListener implements EventSubscriber, ListenerDisabledForCommand
    {
        /**
         * Afin d'éviter de re-générer le sitemap à chaque flush lors du chargement des fixtures,
         * ce listener est désactivé sur la commande associée.
         */
        public static function getCommandsWhereDisabled(): array
        {
            return ['doctrine:fixtures:load'];
        }
    }
    

    note : le code de ce listener a volontairement été simplifié, car ce n'est pas vraiment le propos.
    Vous pouvez cependant retrouver le code en entier dans le snippet associé

    Blog

    Pour continuer votre lecture ...

    Tech

    Le pattern Décorateur avec Symfony

    Par Yann Eugoné 16/03/2022

    Apprenez à découper votre code devenu trop complexe avec le pattern décorateur, en vous aidant de Symfony.

    Lire la suite
    Tech

    Comment re-générer le sitemap au changement d'une entité

    Par Yann Eugoné 09/11/2021

    Comment re-générer votre sitemap en asynchrone lorsque les modifications apportées en back-office l'impactent.

    Lire la suite
    Tech

    Le pattern Stratégie avec Symfony

    Par Maximilien Delangle 09/02/2023

    C'est un modèle de conception de logiciel qui permet de séparer l'algorithme d'une classe de son exécution. Il est utilisé pour résoudre les problèmes de complexité et de maintenance dans les applications.

    Lire la suite

    Vous avez un projet Laravel ?

    Nous sommes spécialisés en Symfony, et grâce à Web^ID, l’agence sœur du groupe Agile Invest, nous couvrons aussi toute l’expertise Laravel.

    Découvrir Web^ID

    Une question, un projet ?
    Planifiez un échange avec nous !

    Choisissez votre date
    PrestaConcept - Groupe Agile Invest
    5, imp. Morel, 69003 Lyon +33 (0)4 78 54 45 45
    Suivez-nous
    Ecoindex B

    Ce site internet est un site basse consommation. En savoir plus sur l'Ecoindex

    Nos réalisations

  • Logiciel de mise en conformité réglementaire
  • Application de suivi de production des centrales éoliennes
  • Outil d'aide à la décision
  • Portail client
  • Nos métiers

  • Développement sur-mesure
  • Reprise d'application Symfony
  • Expertise Symfony
  • Hébergement & Infogérance
  • Qui sommes-nous

  • PrestaConcept
  • Groupe Agile Invest
  • L'équipe
  • Engagement RSE
  • Blog

  • Tech
  • Méthodologie
  • PrestaConcept
  • RSE
  • © 2025 PrestaConcept
    Mentions légales Politique de confidentialité 🍪
    Retour en haut de page