src/Entity/Commun/Utilisateur.php line 577

Open in your IDE?
  1. <?php
  2. namespace App\Entity\Commun;
  3. use App\Entity\AssistantMaternel\AssistantMaternel;
  4. use App\Entity\Parametrage\Profil;
  5. use App\Entity\Commun\UtilisateurPreferenceNotif;
  6. use Doctrine\ORM\Mapping as ORM;
  7. use JsonSerializable;
  8. use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
  9. use Symfony\Component\Security\Core\User\EquatableInterface;
  10. use Symfony\Component\Security\Core\User\UserInterface;
  11. use Symfony\Component\Validator\Constraints as Assert;
  12. use Symfony\Component\Validator\GroupSequenceProviderInterface;
  13. //TODO : https://github.com/symfony/symfony/issues/16833
  14. //      ajouter un UniqueEntity("email", repositoryMethod="uniqueEmailFromAssmat")
  15. /**
  16.  * @see https://blog.dev-web.io/2017/12/16/symfony-4-gestion-utilisateurs-sans-fosuserbundle/
  17.  * @ORM\Entity(repositoryClass="App\Repository\Commun\UtilisateurRepository")
  18.  * @ORM\Table(name="efc.utilisateur",indexes={@ORM\Index(name="utilisateur_id_fonctionnel_idx", columns={"id_fonctionnel"}),@ORM\Index(name="utilisateur_email_idx", columns={"email"})})
  19.  * 
  20.  * @UniqueEntity(
  21.  *     fields={"email"},
  22.  *     message="Cette adresse email est déjà utilisée par un utilisateur actif ou supprimé.",
  23.  *     ignoreNull=true
  24.  * )
  25.  * @UniqueEntity(
  26.  *     fields={"id_fonctionnel"},
  27.  *     message="Cet identifiant est déjà utilisé par un utilisateur actif ou supprimé.",
  28.  *     repositoryMethod="uniqueIdFonctionnel",
  29.  *     ignoreNull=true
  30.  * )
  31.  * @see https://pehapkari.cz/blog/2017/02/18/symfony-validator-conditional-constraints/
  32.  * @see https://symfony.com/doc/current/validation/sequence_provider.html
  33.  * @Assert\GroupSequenceProvider()
  34.  */
  35. class Utilisateur implements UserInterfaceJsonSerializableEquatableInterfaceGroupSequenceProviderInterface
  36. {
  37.     use IdTrait;
  38.     /**
  39.      * @var string
  40.      *
  41.      * @ORM\Column(type="string", length=50)
  42.      */
  43.     private $nom;
  44.     /**
  45.      * @var string
  46.      *
  47.      * @ORM\Column(type="string", length=50)
  48.      */
  49.     private $prenom;
  50.     /**
  51.      * Pas de contrainte d'unicité en base car on peut avoir plusieurs email=null
  52.      * (pour les assmats)
  53.      * La contrainte est gérée avec UniqueEntity qui gère les null multiples
  54.      * 
  55.      * @var string
  56.      * 
  57.      * @Assert\Email(
  58.      *     message = "L'adresse mail '{{ value }}' n'est pas valide.",
  59.      * )
  60.      * 
  61.      * La regex permet de transmettre une contrainte de validation avec un format plus strict au niveau du navigateur
  62.      * @Assert\Regex(
  63.      *     pattern="/^([a-zA-Z0-9'_\-\.\+]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5})$/",
  64.      *     match=true,
  65.      *     message="L'adresse mail '{{ value }}' n'est pas valide."
  66.      * )
  67.      * @ORM\Column(type="string", nullable=true)
  68.      */
  69.     private $email;
  70.     /**
  71.      * @ORM\OneToOne(targetEntity="App\Entity\Commun\CodeConfirmationEmail", inversedBy="utilisateur")
  72.      * @ORM\JoinColumn(name="id_code_confirmation_email", referencedColumnName="id")
  73.      */
  74.     private $code_confirmation_email;
  75.     /**
  76.      * @var string
  77.      *
  78.      * @ORM\Column(type="string")
  79.      */
  80.     private $password;
  81.     /**
  82.      * @var bool
  83.      *
  84.      * @ORM\Column(type="boolean", nullable=false, options={"default":true})
  85.      */
  86.     private $actif true;
  87.     /**
  88.      * @ORM\Column(type="boolean", nullable=false, options={"default":false})
  89.      */
  90.     private $supprime false;
  91.     /**
  92.      * @var string
  93.      *
  94.      * @ORM\Column(type="string", nullable=true)
  95.      */
  96.     private $id_fonctionnel;
  97.     /**
  98.      * Profil (ensemble de droits) de l'utilisateurs
  99.      * Relation unidirectionnelle
  100.      * (rarement besoin de trouver les utilisateurs associés à un profil)
  101.      * 
  102.      * @ORM\ManyToOne(targetEntity="App\Entity\Parametrage\Profil")
  103.      * @ORM\JoinColumn(name="id_profil")
  104.      */
  105.     private $profil;
  106.     /**
  107.      * Assistant maternel optionnel
  108.      * 
  109.      * @ORM\OneToOne(targetEntity="App\Entity\AssistantMaternel\AssistantMaternel", mappedBy="utilisateur")
  110.      */
  111.     private $assistant_maternel;
  112.     /**
  113.      * Secteur de PMI pour les puéricultrices
  114.      * 
  115.      * @Assert\NotNull(message="Les utilisateurs ayant le profil Puéricultrice doivent être associés à un secteur", groups={"puericultrice"})
  116.      * 
  117.      * @ORM\ManyToOne(targetEntity="App\Entity\Referentiel\SecteurPmi")
  118.      * @ORM\JoinColumn(name="id_secteur_pmi")
  119.      */
  120.     private $secteur_pmi;
  121.     /**
  122.      * Préférences de notifications, pour les utilisateurs qui ont le role ALERTES
  123.      * 
  124.      * @ORM\OneToMany(targetEntity="App\Entity\Commun\UtilisateurPreferenceNotif", mappedBy="utilisateur")
  125.      * @ORM\OrderBy({"type_notification" = "ASC"})
  126.      */
  127.     private $preferences_notification;
  128.     /**
  129.      * Token pour l'activation ou la réinitialisation de mot de passe
  130.      * 
  131.      * @ORM\Column(type="string", nullable=true)
  132.      */
  133.     private $code_activation;
  134.     /**
  135.      * Date limite de validité du lien d'activation
  136.      * 
  137.      * @ORM\Column(type="date", nullable=true)
  138.      */
  139.     private $date_limite_code_activation;
  140.     /**
  141.      * Timestamp du dernier token d'authent SSO utilisé
  142.      * Vérifié afin de garantir que chaque token est utilisé seulement 1 fois
  143.      * 
  144.      * @ORM\Column(type="datetime", nullable=true)
  145.      */
  146.     private $sso_date_dernier_token;
  147.     /**
  148.      * Liste des communes consultables, dans le cas des profils partenaire
  149.      * Non applicable si autre profil
  150.      * 
  151.      * @ORM\ManyToMany(targetEntity="App\Entity\Referentiel\Commune")
  152.      * @ORM\JoinTable(name="efc.utilisateur_commune")
  153.      */
  154.     private $communes;
  155.     public function __construct()
  156.     {
  157.         $this->communes = new \Doctrine\Common\Collections\ArrayCollection();
  158.     }
  159.     /**
  160.      * {@inheritdoc}
  161.      */
  162.     public function getUsername()
  163.     {
  164.         return $this->getEmail();
  165.     }
  166.     /**
  167.      * {@inheritdoc}
  168.      */
  169.     public function getAuteurModification()
  170.     {
  171.         if ($this->supprime || ($this->estAssistantMaternel() && $this->getAssistantMaternel()->getSupprime())) {
  172.             return $this->getNom() . " " $this->getPrenom() . " (supprimé)";
  173.         }
  174.         return $this->getNom() . " " $this->getPrenom();
  175.     }
  176.     /**
  177.      * {@inheritdoc}
  178.      */
  179.     public function getEmail()
  180.     {
  181.         // pour les assmats, l'email est porté par les infos assmat
  182.         if ($this->estAssistantMaternel()) {
  183.             return $this->getAssistantMaternel()->getEmail();
  184.         }
  185.         // sinon, l'email est porté par l'utilisateur
  186.         return $this->email;
  187.     }
  188.     /**
  189.      * Get code_confirmation_email
  190.      *
  191.      * @return CodeConfirmationEmail
  192.      */
  193.     public function getCodeConfirmationEmail()
  194.     {
  195.         return $this->code_confirmation_email;
  196.     }
  197.     /**
  198.      * {@inheritdoc}
  199.      */
  200.     public function setEmail(string $email)
  201.     {
  202.         // pour les assmats, l'email est porté par les infos assmat
  203.         if ($this->estAssistantMaternel()) {
  204.             $this->getAssistantMaternel()->setEmail($email);
  205.         } else {
  206.             // sinon, l'email est porté par l'utilisateur
  207.             $this->email $email;
  208.         }
  209.         return $this;
  210.     }
  211.     /**
  212.      * Set code_confirmation_email
  213.      *
  214.      * @param CodeConfirmationEmail code_confirmation_email
  215.      *
  216.      * @return AssistantMaternel
  217.      */
  218.     public function setCodeConfirmationEmail($code_confirmation_email)
  219.     {
  220.         $this->code_confirmation_email $code_confirmation_email;
  221.         return $this;
  222.     }
  223.     public function getNom()
  224.     {
  225.         return $this->nom;
  226.     }
  227.     public function setNom($nom)
  228.     {
  229.         $this->nom $nom;
  230.         return $this;
  231.     }
  232.     public function getPrenom()
  233.     {
  234.         return $this->prenom;
  235.     }
  236.     public function setPrenom($prenom)
  237.     {
  238.         $this->prenom $prenom;
  239.         return $this;
  240.     }
  241.     public function getIdFonctionnel()
  242.     {
  243.         return $this->id_fonctionnel;
  244.     }
  245.     public function setIdFonctionnel($id_fonctionnel)
  246.     {
  247.         $this->id_fonctionnel $id_fonctionnel;
  248.         return $this;
  249.     }
  250.     public function getActif()
  251.     {
  252.         return $this->actif;
  253.     }
  254.     public function setActif($actif)
  255.     {
  256.         // https://stackoverflow.com/questions/4775294/parsing-a-string-into-a-boolean-value-in-php
  257.         $this->actif filter_var($actifFILTER_VALIDATE_BOOLEAN);
  258.         return $this;
  259.     }
  260.     public function getSupprime()
  261.     {
  262.         return $this->supprime;
  263.     }
  264.     public function setSupprime($supprime)
  265.     {
  266.         // https://stackoverflow.com/questions/4775294/parsing-a-string-into-a-boolean-value-in-php
  267.         $this->supprime filter_var($supprimeFILTER_VALIDATE_BOOLEAN);
  268.         return $this;
  269.     }
  270.     /**
  271.      * Les communes consultables par l'utilisateur si c'est un profil Partenaire
  272.      * @return App\Entity\Referentiel\Commune[]
  273.      */
  274.     public function getCommunes(): \Doctrine\Common\Collections\Collection
  275.     {
  276.         return $this->communes;
  277.     }
  278.     /**
  279.      * La liste des communes en texte, limité à 3
  280.      * @return string
  281.      */
  282.     public function getCommunesAsString()
  283.     {
  284.         $communesU $this->getCommunes();
  285.         switch (count($communesU)) {
  286.             case 0:
  287.                 // Si aucune commune sélectionnée, toutes sont autorisées
  288.                 return "toutes";
  289.             case 1:
  290.                 return $communesU->first()->getLibelle();
  291.             case 2:
  292.                 return $communesU->first()->getLibelle() . ", " $communesU->next()->getLibelle();
  293.             case 3:
  294.                 return $communesU->first()->getLibelle() . ", " $communesU->next()->getLibelle() . ", " $communesU->next()->getLibelle();
  295.             default:
  296.                 // + de 3 communes
  297.                 $nb count($communesU) - 3;
  298.                 return $communesU->first()->getLibelle() . ", " $communesU->next()->getLibelle() . ", " $communesU->next()->getLibelle() . " et $nb autre(s)";
  299.         }
  300.     }
  301.     public function getProfil()
  302.     {
  303.         return $this->profil;
  304.     }
  305.     public function setProfil(Profil $profil)
  306.     {
  307.         $this->profil $profil;
  308.         return $this;
  309.     }
  310.     /**
  311.      * {@inheritdoc}
  312.      * 
  313.      * @return string
  314.      */
  315.     public function getPassword()
  316.     {
  317.         // Méthode à implémenter de UserInterface, donc j'ai gardé password en nom de champ.
  318.         return $this->password;
  319.     }
  320.     /**
  321.      * {@inheritdoc}
  322.      */
  323.     public function setPassword(string $password)
  324.     {
  325.         $this->password $password;
  326.         return $this;
  327.     }
  328.     /**
  329.      * {@inheritdoc}
  330.      */
  331.     public function getRoles(): array
  332.     {
  333.         $roles $this->profil->getRoles();
  334.         return array_unique($roles);
  335.     }
  336.     /**
  337.      * Retourne le salt qui a servi à coder le mot de passe.
  338.      *
  339.      * {@inheritdoc}
  340.      */
  341.     public function getSalt()
  342.     {
  343.         // See "Do you need to use a Salt?" at https://symfony.com/doc/current/cookbook/security/entity_provider.html
  344.         // we're using bcrypt in security.yml to encode the password, so
  345.         // the salt value is built-in and you don't have to generate one
  346.         return null;
  347.     }
  348.     /**
  349.      * Removes sensitive data from the user.
  350.      *
  351.      * {@inheritdoc}
  352.      */
  353.     public function eraseCredentials(): void
  354.     {
  355.         // Nous n'avons pas besoin de cette methode car nous n'utilions pas de plainPassword
  356.         // Mais elle est obligatoire car comprise dans l'interface UserInterface
  357.     }
  358.     /**
  359.      * {@inheritdoc}
  360.      *
  361.      * @param UserInterface $user
  362.      * @return bool
  363.      */
  364.     public function isEqualTo(UserInterface $user)
  365.     {
  366.         // $user est l'utilisateur qu'on vient de récupérer dans la BDD à partir de la clé primaire
  367.         // Si $user est supprimé ou désactivé, on renvoie toujours false
  368.         // Cela a pour effet de déconnecter l'utilisateur
  369.         if ($user instanceof Utilisateur) {
  370.             if ($user->getSupprime() || !$user->getActif()) {
  371.                 return false;
  372.             }
  373.         }
  374.         // Comportement par défaut
  375.         return
  376.                 $this->getUsername() === $user->getUsername() &&
  377.                 $this->getPassword() === $user->getPassword() &&
  378.                 $this->getSalt() === $user->getSalt();
  379.     }
  380.     /**
  381.      * Set assistant_maternel
  382.      *
  383.      * @param AssistantMaternel $assistant_maternel
  384.      *
  385.      * @return Utilisateur
  386.      */
  387.     public function setAssistantMaternel($assistant_maternel)
  388.     {
  389.         $this->assistant_maternel $assistant_maternel;
  390.         $this->email null;
  391.         return $this;
  392.     }
  393.     /**
  394.      * Get assistant_maternel
  395.      *
  396.      * @return AssistantMaternel
  397.      */
  398.     public function getAssistantMaternel()
  399.     {
  400.         return $this->assistant_maternel;
  401.     }
  402.     /**
  403.      * Set secteur_pmi
  404.      *
  405.      * @param SecteurPmi $secteur_pmi
  406.      *
  407.      * @return Utilisateur
  408.      */
  409.     public function setSecteurPmi($secteur_pmi)
  410.     {
  411.         $this->secteur_pmi $secteur_pmi;
  412.         return $this;
  413.     }
  414.     /**
  415.      * Get secteur_pmi
  416.      *
  417.      * @return SecteurPmi
  418.      */
  419.     public function getSecteurPmi()
  420.     {
  421.         return $this->secteur_pmi;
  422.     }
  423.     /**
  424.      * Set preferences_notification
  425.      *
  426.      * @param $preference_notifs liste des prefernces de notification de l'utilisateur
  427.      *
  428.      * @return Utilisateur
  429.      */
  430.     public function setPreferencesNotification($preference_notifs)
  431.     {
  432.         $this->preferences_notification $preference_notifs;
  433.         return $this;
  434.     }
  435.     /**
  436.      * Get preferences_notification
  437.      *
  438.      * @return liste des preferences de notification de l'utilisateur
  439.      */
  440.     public function getPreferencesNotification()
  441.     {
  442.         return $this->preferences_notification;
  443.     }
  444.     /**
  445.      * Set code_activation
  446.      *
  447.      * @param string $code_activation
  448.      *
  449.      * @return Utilisateur
  450.      */
  451.     public function setCodeActivation($code_activation)
  452.     {
  453.         $this->code_activation $code_activation;
  454.         return $this;
  455.     }
  456.     /**
  457.      * Get code_activation
  458.      *
  459.      * @return string
  460.      */
  461.     public function getCodeActivation()
  462.     {
  463.         return $this->code_activation;
  464.     }
  465.     /**
  466.      * Set date_limite_code_activation
  467.      *
  468.      * @param DateTime $date_limite_code_activation
  469.      *
  470.      * @return Utilisateur
  471.      */
  472.     public function setDateLimiteCodeActivation($date_limite_code_activation)
  473.     {
  474.         $this->date_limite_code_activation $date_limite_code_activation;
  475.         return $this;
  476.     }
  477.     /**
  478.      * Get date_limite_code_activation
  479.      *
  480.      * @return DateTime
  481.      */
  482.     public function getDateLimiteCodeActivation()
  483.     {
  484.         return $this->date_limite_code_activation;
  485.     }
  486.     public function getTimestampDernierToken()
  487.     {
  488.         if ($this->sso_date_dernier_token) {
  489.             return $this->sso_date_dernier_token->getTimestamp();
  490.         }
  491.         return null;
  492.     }
  493.     public function setTimestampDernierToken(int $timestamp)
  494.     {
  495.         $date = new \DateTime();
  496.         $date->setTimestamp($timestamp);
  497.         $this->sso_date_dernier_token $date;
  498.     }
  499.     /**
  500.      * L'utilisateur est assmat ?
  501.      *
  502.      * @return bool
  503.      */
  504.     public function estAssistantMaternel()
  505.     {
  506.         // s'il existe un assmat lié à l'utilisateur, l'utilisateur est assmat
  507.         return !is_null($this->getAssistantMaternel());
  508.     }
  509.     /*
  510.      * Implémentation de JsonSerializable
  511.      * Ne liste que les champs modifiables par les utilisateurs
  512.      */
  513.     public function jsonSerialize()
  514.     {
  515.         return [
  516.             'id' => $this->id,
  517.             'email' => $this->email,
  518.             'nom' => $this->nom,
  519.             'prenom' => $this->prenom,
  520.             'supprime' => $this->supprime,
  521.             'actif' => $this->actif,
  522.             'profil' => $this->profil === null null $this->profil->getLibelle(),
  523.             'code_activation' => $this->code_activation,
  524.             'date_limite_code_activation' => $this->date_limite_code_activation,
  525.             'secteur' => $this->secteur_pmi === null null $this->secteur_pmi->getLibelle(),
  526.             'notification' => $this->getPreferencesNotification(),
  527.             'password' => $this->password,
  528.         ];
  529.     }
  530.     /**
  531.      * Obtient une nouvelle instance d'Utilisateur, initialisée à partir du tableau associatif fourni en paramètre
  532.      *
  533.      * @param array $data
  534.      * @return Utilisateur
  535.      */
  536.     public static function createFromArray(array $data)
  537.     {
  538.         $newInstance = new Utilisateur();
  539.         return $newInstance->initFromArray($data);
  540.     }
  541.     /**
  542.      * mappe les propriétés sur l'instance à partir d'un tableau associatif
  543.      *
  544.      * @param array $data
  545.      * @return void
  546.      */
  547.     public function initFromArray(array $data)
  548.     {
  549.         foreach ($data as $prop => $value) {
  550.             $this->{$prop} = $value;
  551.         }
  552.         return $this;
  553.     }
  554.     /**
  555.      * {@inheritdoc}
  556.      * @see https://pehapkari.cz/blog/2017/02/18/symfony-validator-conditional-constraints/
  557.      * @see https://symfony.com/doc/current/validation/sequence_provider.html
  558.      * @return type
  559.      */
  560.     public function getGroupSequence()
  561.     {
  562.         // La liste des validation groups à appliquer
  563.         $groups = array();
  564.         // Dans tous les cas, on valide le validation group "Utilisateur"
  565.         // Ce groupe existe par défaut et regroupe toutes les contraintes dont le validation group n'est pas précisé
  566.         $groups[] = 'Utilisateur';
  567.         // Dans le cas où l'utilisateur est puéricultrice, on valide aussi le groupe "puericultrice"
  568.         if ($this->getProfil()->getId() === \App\Entity\Parametrage\EnumProfil::PUER) {
  569.             $groups[] = 'puericultrice';
  570.         }
  571.         // Tableau imbriqué : valider les 2 groupes en même temps (=remonter toutes les erreurs)
  572.         return array($groups);
  573.     }
  574. }