Browse Source

Bascule des switch en objet plus propre

Sangfroid 4 days ago
parent
commit
bd4b70e789

+ 0 - 13
config/services.yaml

@@ -22,16 +22,3 @@ services:
 
 
     # add more service definitions when explicit configuration is needed
     # add more service definitions when explicit configuration is needed
     # please note that last definitions always *replace* previous ones
     # please note that last definitions always *replace* previous ones
-
-    App\Service\Mail:
-            arguments:
-                - '@mailer'
-                - '@twig'
-                - '%env(MAILER_FROM)%'
-                - '%env(MAILER_REPLY)%'
-                - '%env(MAILER_NAME)%'
-    
-    App\Service\Mattermost:
-        arguments:
-            $channelId: '%env(MATTERMOST_CHANNEL_ID)%'
-            $channelIdAdmin: '%env(MATTERMOST_CHANNEL_ID_ADMIN)%'

+ 55 - 0
src/Bascule/AbstractBasculeFilm.php

@@ -0,0 +1,55 @@
+<?php
+
+namespace App\Bascule;
+
+use App\Entity\Film;
+use App\Entity\User;
+
+/**
+ * Socle commun des bascules de film : les classes filles ne déclarent
+ * que leur configuration et la façon de lire l'état courant.
+ */
+abstract class AbstractBasculeFilm implements BasculeFilmInterface
+{
+    protected function __construct(
+        private readonly string $routeName,
+        private readonly string $iconeActif,
+        private readonly string $iconeInactif,
+        private readonly string $texteActif,
+        private readonly string $texteInactif,
+        private readonly bool $avecInfoBulle = false,
+    ) {
+    }
+
+    public function routeName(): string
+    {
+        return $this->routeName;
+    }
+
+    public function iconeActif(): string
+    {
+        return $this->iconeActif;
+    }
+
+    public function iconeInactif(): string
+    {
+        return $this->iconeInactif;
+    }
+
+    public function texteActif(): string
+    {
+        return $this->texteActif;
+    }
+
+    public function texteInactif(): string
+    {
+        return $this->texteInactif;
+    }
+
+    public function avecInfoBulle(): bool
+    {
+        return $this->avecInfoBulle;
+    }
+
+    abstract public function estActifPour(User $user, Film $film): bool;
+}

+ 30 - 0
src/Bascule/BasculeAVoir.php

@@ -0,0 +1,30 @@
+<?php
+
+namespace App\Bascule;
+
+use App\Entity\Film;
+use App\Entity\User;
+use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
+use Symfony\Component\DependencyInjection\Attribute\AsTaggedItem;
+
+#[Autoconfigure(tags: ['app.bascule_film'])]
+#[AsTaggedItem(index: 'a_voir')]
+final class BasculeAVoir extends AbstractBasculeFilm
+{
+    public function __construct()
+    {
+        parent::__construct(
+            routeName: 'maliste_modifier_a_voir',
+            iconeActif: 'fa-solid fa-bookmark fa-lg',
+            iconeInactif: 'fa-regular fa-bookmark text-secondary fa-lg',
+            texteActif: 'Supprimer ce film de votre liste à voir',
+            texteInactif: 'Ajouter ce film à votre liste à voir',
+            avecInfoBulle: true,
+        );
+    }
+
+    public function estActifPour(User $user, Film $film): bool
+    {
+        return (bool) $user->wantToSee($film);
+    }
+}

+ 27 - 0
src/Bascule/BasculeFilmInterface.php

@@ -0,0 +1,27 @@
+<?php
+
+namespace App\Bascule;
+
+use App\Entity\Film;
+use App\Entity\User;
+
+/**
+ * Contrat d'une bascule d'état pour un film, affichée par le composant
+ * Vue SwitchEtat.
+ */
+interface BasculeFilmInterface
+{
+    public function routeName(): string;
+
+    public function iconeActif(): string;
+
+    public function iconeInactif(): string;
+
+    public function texteActif(): string;
+
+    public function texteInactif(): string;
+
+    public function avecInfoBulle(): bool;
+
+    public function estActifPour(User $user, Film $film): bool;
+}

+ 33 - 0
src/Bascule/BasculeFilmRegistry.php

@@ -0,0 +1,33 @@
+<?php
+
+namespace App\Bascule;
+
+use Symfony\Component\DependencyInjection\Attribute\AutowireIterator;
+
+/**
+ * Registre des bascules de film disponibles, indexé par nom (l'attribut
+ * index du #[AsTaggedItem] de chaque classe).
+ *
+ * Pour enregistrer une nouvelle bascule : créer une classe qui étend
+ * AbstractBasculeFilm avec #[Autoconfigure(tags: ['app.bascule_film'])]
+ * et #[AsTaggedItem(index: 'nom')], c'est tout.
+ */
+final class BasculeFilmRegistry
+{
+    /** @var array<string, BasculeFilmInterface> */
+    private readonly array $bascules;
+
+    /** @param iterable<string, BasculeFilmInterface> $bascules */
+    public function __construct(
+        #[AutowireIterator('app.bascule_film', indexAttribute: 'index')]
+        iterable $bascules,
+    ) {
+        $this->bascules = iterator_to_array($bascules);
+    }
+
+    public function get(string $nom): BasculeFilmInterface
+    {
+        return $this->bascules[$nom]
+            ?? throw new \InvalidArgumentException(sprintf('Bascule de film inconnue : "%s" (types disponibles : %s)', $nom, implode(', ', array_keys($this->bascules))));
+    }
+}

+ 29 - 0
src/Bascule/BasculeVu.php

@@ -0,0 +1,29 @@
+<?php
+
+namespace App\Bascule;
+
+use App\Entity\Film;
+use App\Entity\User;
+use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
+use Symfony\Component\DependencyInjection\Attribute\AsTaggedItem;
+
+#[Autoconfigure(tags: ['app.bascule_film'])]
+#[AsTaggedItem(index: 'vu')]
+final class BasculeVu extends AbstractBasculeFilm
+{
+    public function __construct()
+    {
+        parent::__construct(
+            routeName: 'maliste_modifier_vus',
+            iconeActif: 'fa-solid fa-eye fa-lg text-success',
+            iconeInactif: 'fa-solid fa-eye-slash fa-lg text-secondary',
+            texteActif: 'Supprimer ce film de votre liste des films vus',
+            texteInactif: 'Marquer ce film comme vu',
+        );
+    }
+
+    public function estActifPour(User $user, Film $film): bool
+    {
+        return (bool) $user->haveSeen($film);
+    }
+}

+ 0 - 76
src/Enum/BasculeFilm.php

@@ -1,76 +0,0 @@
-<?php
-
-namespace App\Enum;
-
-use App\Entity\Film;
-use App\Entity\User;
-
-/**
- * Bascules d'état possibles pour un film, affichées par le composant
- * Vue SwitchEtat. Chaque cas centralise toute la configuration du
- * bouton : icônes, libellés, infobulle et route de bascule.
- *
- * Pour ajouter un nouveau type de bascule : ajouter un cas et compléter
- * les match() existants, aucun template ni composant JS à modifier.
- */
-enum BasculeFilm: string
-{
-    case A_VOIR = 'a-voir';
-    case VU = 'vu';
-
-    public function routeName(): string
-    {
-        return match ($this) {
-            self::A_VOIR => 'maliste_modifier_a_voir',
-            self::VU => 'maliste_modifier_vus',
-        };
-    }
-
-    public function iconeActif(): string
-    {
-        return match ($this) {
-            self::A_VOIR => 'fa-solid fa-bookmark fa-lg',
-            self::VU => 'fa-solid fa-eye fa-lg text-success',
-        };
-    }
-
-    public function iconeInactif(): string
-    {
-        return match ($this) {
-            self::A_VOIR => 'fa-regular fa-bookmark text-secondary fa-lg',
-            self::VU => 'fa-solid fa-eye-slash fa-lg text-secondary',
-        };
-    }
-
-    public function texteActif(): string
-    {
-        return match ($this) {
-            self::A_VOIR => 'Supprimer ce film de votre liste à voir',
-            self::VU => 'Supprimer ce film de votre liste des films vus',
-        };
-    }
-
-    public function texteInactif(): string
-    {
-        return match ($this) {
-            self::A_VOIR => 'Ajouter ce film à votre liste à voir',
-            self::VU => 'Marquer ce film comme vu',
-        };
-    }
-
-    public function avecInfoBulle(): bool
-    {
-        return match ($this) {
-            self::A_VOIR => true,
-            self::VU => false,
-        };
-    }
-
-    public function estActifPour(User $user, Film $film): bool
-    {
-        return match ($this) {
-            self::A_VOIR => (bool) $user->wantToSee($film),
-            self::VU => (bool) $user->haveSeen($film),
-        };
-    }
-}

+ 4 - 3
src/Service/Mail.php

@@ -3,6 +3,7 @@
 namespace App\Service;
 namespace App\Service;
 
 
 use App\Entity\User;
 use App\Entity\User;
+use Symfony\Component\DependencyInjection\Attribute\Autowire;
 use Symfony\Component\Mailer\MailerInterface;
 use Symfony\Component\Mailer\MailerInterface;
 use Symfony\Component\Mime\Address;
 use Symfony\Component\Mime\Address;
 use Symfony\Component\Mime\Email;
 use Symfony\Component\Mime\Email;
@@ -24,9 +25,9 @@ class Mail
     public function __construct(
     public function __construct(
         protected MailerInterface $mailer,
         protected MailerInterface $mailer,
         protected Environment $templating,
         protected Environment $templating,
-        protected string $from,
-        protected string $reply,
-        protected string $name
+        #[Autowire('%env(MAILER_FROM)%')] protected string $from,
+        #[Autowire('%env(MAILER_REPLY)%')] protected string $reply,
+        #[Autowire('%env(MAILER_NAME)%')] protected string $name
     )
     )
     {
     {
 
 

+ 3 - 2
src/Service/Mattermost.php

@@ -5,6 +5,7 @@ namespace App\Service;
 use App\Entity\Film;
 use App\Entity\Film;
 use App\Entity\User;
 use App\Entity\User;
 use Symfony\Bundle\SecurityBundle\Security;
 use Symfony\Bundle\SecurityBundle\Security;
+use Symfony\Component\DependencyInjection\Attribute\Autowire;
 use Symfony\Component\HttpFoundation\RequestStack;
 use Symfony\Component\HttpFoundation\RequestStack;
 use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
 use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
 use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
 use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
@@ -20,8 +21,8 @@ class Mattermost
      */
      */
     public function __construct(
     public function __construct(
         protected HttpClientInterface $mattermostClient,
         protected HttpClientInterface $mattermostClient,
-        protected string $channelId,
-        protected string $channelIdAdmin,
+        #[Autowire('%env(MATTERMOST_CHANNEL_ID)%')] protected string $channelId,
+        #[Autowire('%env(MATTERMOST_CHANNEL_ID_ADMIN)%')] protected string $channelIdAdmin,
         protected UrlGeneratorInterface $router,
         protected UrlGeneratorInterface $router,
         protected RequestStack $requestStack,
         protected RequestStack $requestStack,
         protected Security $security,
         protected Security $security,

+ 25 - 0
src/Twig/BasculeExtension.php

@@ -0,0 +1,25 @@
+<?php
+
+namespace App\Twig;
+
+use App\Bascule\BasculeFilmRegistry;
+use Twig\Extension\AbstractExtension;
+use Twig\TwigFunction;
+
+/**
+ * Fournit les objets de bascule aux templates Twig :
+ * {{ bascule_film('a_voir') }} retourne App\Bascule\BasculeFilmInterface.
+ */
+class BasculeExtension extends AbstractExtension
+{
+    public function __construct(private readonly BasculeFilmRegistry $registry)
+    {
+    }
+
+    public function getFunctions(): array
+    {
+        return [
+            new TwigFunction('bascule_film', [$this->registry, 'get']),
+        ];
+    }
+}

+ 2 - 2
templates/videotheque/liste_tableaux.html.twig

@@ -34,10 +34,10 @@
 			<tr data-auteur="{{ film.authered.nomComplet }}">
 			<tr data-auteur="{{ film.authered.nomComplet }}">
 				{% if is_granted('IS_AUTHENTICATED_REMEMBERED') %}
 				{% if is_granted('IS_AUTHENTICATED_REMEMBERED') %}
 					<td>
 					<td>
-						{{ include('videotheque/_bascule_film.html.twig', { bascule: enum('App\\Enum\\BasculeFilm').A_VOIR, film: film }) }}
+						{{ include('videotheque/_bascule_film.html.twig', { bascule: bascule_film('a_voir'), film: film }) }}
 					</td>
 					</td>
 					<td>
 					<td>
-						{{ include('videotheque/_bascule_film.html.twig', { bascule: enum('App\\Enum\\BasculeFilm').VU, film: film }) }}
+						{{ include('videotheque/_bascule_film.html.twig', { bascule: bascule_film('vu'), film: film }) }}
 					</td>
 					</td>
 					<td>
 					<td>
 						{%  if film.authered is defined %}
 						{%  if film.authered is defined %}

+ 2 - 2
templates/videotheque/liste_vignettes.html.twig

@@ -25,10 +25,10 @@
 				<div class="d-flex">
 				<div class="d-flex">
 					{% if is_granted('IS_AUTHENTICATED_REMEMBERED') %}
 					{% if is_granted('IS_AUTHENTICATED_REMEMBERED') %}
 						<div class="me-3">
 						<div class="me-3">
-							{{ include('videotheque/_bascule_film.html.twig', { bascule: enum('App\\Enum\\BasculeFilm').VU, film: film, tag: 'button', classe: 'btn' }) }}
+							{{ include('videotheque/_bascule_film.html.twig', { bascule: bascule_film('vu'), film: film, tag: 'button', classe: 'btn' }) }}
 						</div>
 						</div>
 						<div class="me-3">
 						<div class="me-3">
-							{{ include('videotheque/_bascule_film.html.twig', { bascule: enum('App\\Enum\\BasculeFilm').A_VOIR, film: film, tag: 'button', classe: 'btn text-primary' }) }}
+							{{ include('videotheque/_bascule_film.html.twig', { bascule: bascule_film('a_voir'), film: film, tag: 'button', classe: 'btn text-primary' }) }}
 						</div>
 						</div>
 					{% endif %}
 					{% endif %}
 					<div class="">
 					<div class="">

+ 2 - 2
templates/videotheque/voirfilm.html.twig

@@ -3,8 +3,8 @@
 {% block title %}{{ parent() }} - Fiche film - {{ film.titre }}{% endblock %}
 {% block title %}{{ parent() }} - Fiche film - {{ film.titre }}{% endblock %}
 {% block titre %}
 {% block titre %}
     {% if is_granted('IS_AUTHENTICATED_REMEMBERED') %}
     {% if is_granted('IS_AUTHENTICATED_REMEMBERED') %}
-        {{ include('videotheque/_bascule_film.html.twig', { bascule: enum('App\\Enum\\BasculeFilm').A_VOIR, film: film }) }}
-        {{ include('videotheque/_bascule_film.html.twig', { bascule: enum('App\\Enum\\BasculeFilm').VU, film: film }) }}
+        {{ include('videotheque/_bascule_film.html.twig', { bascule: bascule_film('a_voir'), film: film }) }}
+        {{ include('videotheque/_bascule_film.html.twig', { bascule: bascule_film('vu'), film: film }) }}
     {% endif %}
     {% endif %}
     {{ film.titre }}
     {{ film.titre }}