diff --git a/en/orm/validation.rst b/en/orm/validation.rst index 22fcea1343..1f96969f47 100644 --- a/en/orm/validation.rst +++ b/en/orm/validation.rst @@ -393,7 +393,7 @@ primary key. You can enforce ``existsIn`` to pass when nullable parts of your composite foreign key are null:: - // Example: A composite primary key within NodesTable is (id, site_id). + // Example: A composite primary key within NodesTable is (parent_id, site_id). // A Node may reference a parent Node but does not need to. In latter case, parent_id is null. // Allow this rule to pass, even if fields that are nullable, like parent_id, are null: $rules->add($rules->existsIn( diff --git a/fr/controllers/components/pagination.rst b/fr/controllers/components/pagination.rst index a6af976127..6a78bb81c5 100644 --- a/fr/controllers/components/pagination.rst +++ b/fr/controllers/components/pagination.rst @@ -5,13 +5,16 @@ Pagination .. php:class:: PaginatorComponent -Les principaux défis lors de la création d'une application flexible et -ergonomique sont le design et d'avoir une interface utilisateur intuitive. +.. deprecated:: 4.4.0 + Le Component paginator est déprécié depuis 4.4.0 et sera supprimé dans 5.0. + +L'un des principaux défis pour créer une application flexible et ergonomique est +la conception d'une interface utilisateur intuitive. De nombreuses applications ont tendance à augmenter rapidement en taille et en -complexité, et les designers ainsi que les programmeurs trouvent même qu'ils -sont incapables de faire face a l'affichage de centaines ou de milliers -d'enregistrements. Réécrire prend du temps, et les performances et la -satisfaction des utilisateurs peut en pâtir. +complexité, et les designers comme les programmeurs se retrouvent incapables de +faire face à l'affichage de centaines ou de milliers d'enregistrements. Réécrire +prend du temps, et les performances et la satisfaction des utilisateurs peuvent +en pâtir. Afficher un nombre raisonnable d'enregistrements par page a toujours été une partie critique dans toutes les applications et cause régulièrement de nombreux @@ -19,30 +22,18 @@ maux de tête aux développeurs. CakePHP allège le fardeau des développeurs en fournissant un moyen efficace de paginer les données. La pagination dans CakePHP se fait par un Component dans le controller, pour -faciliter la création des requêtes de pagination. Dans la Vue, -:php:class:`~Cake\\View\\Helper\\PaginatorHelper` est utilisé pour faciliter la -génération de la pagination, des liens et des boutons. +faciliter la création des requêtes de pagination. Vous utilisez ensuite +:php:class:`~Cake\\View\\Helper\\PaginatorHelper` dans vos templates de vue pour +générer les contrôles de pagination. -Utiliser Controller::paginate() -=============================== +Usage basique +============= -Dans le controller, nous commençons par définir les conditions de la requête de -pagination qui seront utilisées par défaut dans la variable ``$paginate`` du -controller. Ces conditions, vont servir de base à vos requêtes de pagination. -Elles sont complétées par les paramètres ``sort``, ``direction``, ``limit`` et -``page`` passés dans l'URL. Ici, il est important de noter que la clé ``order`` -doit être définie dans une structure en tableau comme ci-dessous:: +Pour paginer une requête, nous commençons par charger le +``PaginatorComponent``:: class ArticlesController extends AppController { - - public $paginate = [ - 'limit' => 25, - 'order' => [ - 'Articles.title' => 'asc' - ] - ]; - public function initialize(): void { parent::initialize(); @@ -50,43 +41,57 @@ doit être définie dans une structure en tableau comme ci-dessous:: } } -Vous pouvez aussi inclure d'autres options -:php:meth:`~Cake\\ORM\\Table::find()`, comme ``fields``:: +Une fois qu'il est chargé, nous pouvons paginer une classe de table de l'ORM ou +un objet ``Query``:: + + public function index() + { + // Paginer une table de l'ORM. + $this->set('articles', $this->paginate($this->Articles)); + + // Paginer une requête en cours de construction. + $query = $this->Articles->find('published'); + $this->set('articles', $this->paginate($query)); + } + +Utilisation avancée +=================== + +Le ``PaginatorComponent`` peut supporter des cas de figure plus complexes en +configurant la propriété ``$paginate`` du controller ou l'argument ``$settings`` +de ``paginate()``. Ces conditions servent de base pour vos requêtes de +pagination. On leur ajoute ensuite les paramètres ``sort``, ``direction``, +``limit``, et ``page`` passés dans l'URL:: class ArticlesController extends AppController { public $paginate = [ - 'fields' => ['Articles.id', 'Articles.created'], 'limit' => 25, 'order' => [ 'Articles.title' => 'asc' ] ]; - - public function initialize(): void - { - parent::initialize(); - $this->loadComponent('Paginator'); - } } -Alors que vous pouvez passer la plupart des options de query à partir de la -propriété paginate, il est souvent plus propre et simple de mettre vos options +.. tip:: + L'option par défaut ``order`` doit être définie dans un tableau. + +Cependant vous pouvez passer dans vos paramètres de pagination n'importe quelle +option supportée par :php:meth:`~Cake\\ORM\\Table::find()`, telle que +``fields``. Il est souvent plus propre et simple de mettre vos options de pagination dans une :ref:`custom-find-methods`. vous pouvez définir l'utilisation de la pagination du finder en configurant l'option ``findType``:: class ArticlesController extends AppController { - public $paginate = [ 'finder' => 'published', ]; } -Comme les méthodes finder personnalisées peuvent aussi être prises en options, -voici comment vous pouvez passer des options dans une méthode de finder -personnalisée dans la propriété paginate:: +Si votre finder attend d'autres options, vous pouvez les passer comme des +valeurs pour le finder:: class ArticlesController extends AppController { @@ -96,87 +101,78 @@ personnalisée dans la propriété paginate:: { $tags = $this->request->getParam('pass'); - $customFinderOptions = [ + $optionsPersonnaliseesPourLeFinder = [ 'tags' => $tags ]; - // la méthode de finder personnalisée est appelée findTagged dans - // ArticlesTable.php - // elle devrait ressembler à ceci: + // Nous utilisons ici l'argument $settings pour paginate(). + // Mais on pourrait utiliser la même structure dans $this->paginate + // + // Notre finder personnalisé s'appelle findTagged dans ArticlesTable.php + // C'est pourquoi nous utilisons une clé `tagged`. + // Voici à quoi devrait ressembler notre finder: // public function findTagged(Query $query, array $options) { - // ainsi vous utilisez tagged en clé - $this->paginate = [ + $settings = [ 'finder' => [ - 'tagged' => $customFinderOptions + 'tagged' => $optionsPersonnaliseesPourLeFinder ] ]; - $articles = $this->paginate($this->Articles); + $articles = $this->paginate($this->Articles, $settings); $this->set(compact('articles', 'tags')); } } En plus de définir les valeurs de pagination générales, vous pouvez définir -plus d'un jeu de pagination par défaut dans votre controller, vous avez juste -à nommer les clés du tableau d'après le model que vous souhaitez configurer:: +plusieurs jeux de pagination par défaut dans votre controller. Vous pouvez +utiliser le nom de chaque modèle comme clé dans la propriété ``$paginate``:: class ArticlesController extends AppController { - public $paginate = [ 'Articles' => [], 'Authors' => [], ]; } -Les valeurs des clés ``Articles`` et ``Authors`` peuvent contenir toutes -les propriétés qu'un model/clé sans ``$paginate`` peut contenir. - -Une fois que la variable ``$paginate`` à été définie, nous pouvons -utiliser la méthode :php:meth:`~Cake\\Controller\\Controller::paginate()` pour -créer les données paginées et ajouter le ``PaginatorHelper`` s'il n'a pas déjà -été ajouté. La méthode paginate du controller va retourner l'ensemble des -résultats de la requête paginée, et définir les meta-données de pagination de -la requête. Vous pouvez accéder aux meta-données de pagination avec -``$this->request->getParam('paging')``. un exemple plus complet de l'utilisation -de ``paginate()`` serait:: - - class ArticlesController extends AppController - { - - public function index() - { - $this->set('articles', $this->paginate()); - } - } +Les tableaux sous les clés ``Articles`` et ``Auteurs`` peuvent contenir toutes +les clés que ``$paginate`` peut contenir habituellement. -Par défaut la méthode ``paginate()`` va utiliser le model par défaut pour un -controller. Vous pouvez aussi passer la requête résultante d'une méthode find:: +Une fois que nous avons utilisé ``paginate()`` pour créer des résultats, des +paramètres de pagination seront ajoutés à l'objet ``request`` du controller. +Vous pouvez accéder aux métadonnées de pagination par +``$this->request->getAttribute('paging')``. - public function index() - { - $query = $this->Articles->find('popular')->where(['author_id' => 1]); - $this->set('articles', $this->paginate($query)); - } +Pagination Simple +================= -Si vous voulez paginer un model différent, vous pouvez lui fournir une requête -l'objet table lui-même, ou son nom:: +Par défaut, la pagination utilise une requête ``count()`` pour calculer le +nombre de résultats, de manière à pouvoir afficher les liens vers des numéros de +pages. Sur de grandes quantités de données, ce décompte peut devenir très +coûteux. Dans les situations où n'avez besoin que des boutons 'Précédent' et +'Suivant', vous pouvez utiliser le paginateur 'simple' qui ne lance pas de +requête de comptage:: - //Utiliser une query - $comments = $this->paginate($commentsTable->find()); + public function initialize(): void + { + parent::initialize(); - // Utiliser le nom du model. - $comments = $this->paginate('Comments'); + // Charger le component de pagination avec la stratégie simple. + $this->loadComponent('Paginator', [ + 'className' => 'Simple', + ]); + } - // Utiliser un objet table. - $comments = $this->paginate($commentTable); +En utilisant le ``SimplePaginator``, vous ne pourrez pas générer des liens vers +des numéros de pages, ni des compteurs de données, ni un lien vers la dernière +page, ni le nombre total d'enregistrements. -Utiliser Directement Paginator +Utiliser Paginator Directement ============================== -Si vous devez paginer des données d'un autre component, vous pouvez utiliser -directement PaginatorComponent. Il fournit une API similaire à la méthode -du controller:: +Si vous devez paginer des données depuis un autre component, vous pouvez +utiliser directement PaginatorComponent. Il fournit une API similaire à la +méthode du controller:: $articles = $this->Paginator->paginate($articleTable->find(), $config); @@ -184,21 +180,20 @@ du controller:: $articles = $this->Paginator->paginate($articleTable, $config); Le premier paramètre doit être l'objet query à partir d'un find sur l'objet -table duquel vous souhaitez paginer les résultats. En option, vous pouvez passer -l'objet table et laisser la query être construite pour vous. Le second paramètre -doit être le tableau des configurations à utiliser pour la pagination. Ce -tableau doit avoir la même structure que la propriété ``$paginate`` +table dont vous souhaitez paginer les résultats. Au choix, vous pouvez aussi +passer l'objet table et laisser le paginator construire la requête pour vous. Le +second paramètre est le tableau de configuration à utiliser pour la pagination. +Ce tableau doit avoir la même structure que la propriété ``$paginate`` dans un controller. Quand on pagine un objet ``Query``, l'option ``finder`` -sera ignorée. Il faut que vous passiez la query que vous souhaitez voir -paginée. +sera ignorée. Vous devez passer la query que vous souhaitez paginer. .. _paginating-multiple-queries: -Requêtes de Paginating Multiple -=============================== +Paginer Plusieurs Requêtes +========================== -Vous pouvez paginer plusieurs models dans une unique action de controller en -utilisant l'option ``scope``, à la fois via la propriété ``$paginate`` d'un +Vous pouvez paginer plusieurs models dans la même action du controller en +utilisant l'option ``scope`` à la fois dans la propriété ``$paginate`` du controller et dans l'appel à la méthode ``paginate()``:: // Paginate property @@ -219,17 +214,58 @@ utilisée pour paginer les tags et les articles en même temps:: /dashboard?article[page]=1&tag[page]=3 Consulter la section :ref:`paginator-helper-multiple` pour savoir comment -générer les elements HTML scopés et les URLS pour la pagination. +générer les éléments HTML scopés et les URLS pour la pagination. + +Pour paginer plusieurs fois le même modèle dans une même action du controller, +vous devez définir un alias du modèle. Consultez :ref:`table-registry-usage` +pour plus de détails sur l'utilisation du registre de tables:: + + // Dans une action de controller + $this->paginate = [ + 'ArticlesTable' => [ + 'scope' => 'published_articles', + 'limit' => 10, + 'order' => [ + 'id' => 'desc', + ], + ], + 'UnpublishedArticlesTable' => [ + 'scope' => 'unpublished_articles', + 'limit' => 10, + 'order' => [ + 'id' => 'desc', + ], + ], + ]; + + $publishedArticles = $this->paginate( + $this->Articles->find('all', [ + 'scope' => 'published_articles' + ])->where(['published' => true]) + ); + + // Charge un autre objet table pour permettre de les différencier dans le component de pagination + $unpublishedArticlesTable = $this->fetchTable('UnpublishedArticles', [ + 'className' => 'App\Model\Table\ArticlesTable', + 'table' => 'articles', + 'entityClass' => 'App\Model\Entity\Article', + ]); + + $unpublishedArticles = $this->paginate( + $unpublishedArticlesTable->find('all', [ + 'scope' => 'unpublished_articles' + ])->where(['published' => false]) + ); Contrôler les Champs Utilisés pour le Tri ========================================= -Par défaut le tri peut être fait sur n'importe quelle colonne d'une table. -Ceci n'est parfois pas souhaité puisque cela permet aux utilisateurs de trier sur -des colonnes non indexées qui peuvent être compliquées à trier. Vous pouvez -définir la liste blanche des champs qui peut être triée en utilisant +Par défaut, vous pouvez trier sur n'importe quelle colonne non virtuelle d'une +table. Ce n'est pas toujours souhaitable puisque cela permet aux utilisateurs de +trier sur des colonnes non indexées qui peuvent être longues à trier. Vous +pouvez définir la liste des champs pouvant être triés en utilisant l'option ``sortableFields``. Cette option est nécessaire quand vous voulez trier -sur des données associées, ou des champs calculés qui peuvent faire parti de +sur des données associées, ou des champs calculés qui peuvent faire partie de la requête de pagination:: public $paginate = [ @@ -238,20 +274,20 @@ la requête de pagination:: ] ]; -Toute requête qui tente de trier les champs qui ne sont pas dans la liste -blanche sera ignorée. +Toute requête qui tentera de trier les champs qui ne sont pas dans cette liste +sera ignorée. Limiter le Nombre Maximum de Lignes par Page ============================================ -Le nombre de résultat qui sont récupérés et montrés à l'utilisateur est configuré par le -paramètre ``limit``. En général on ne souhaite pas permettre aux -utilisateurs de récupérer toutes les lignes d'un ensemble paginé. L'option -``maxLimit`` permet à ce que personne ne puisse définir cette limite trop haute -depuis l'extérieur. Par défaut, CakePHP limite le nombre maximum de lignes qui -peuvent être récupérées à 100. Si cette valeur par défaut n'est pas approprié pour votre -application, vous pouvez l'ajuster dans les options de pagination, par exemple -en le réduisant à ``10``:: +Le nombre de résultats récupérés pour chaque page peut être configuré par +l'utilisateur dans le paramètre ``limit``. D'une manière générale, il n'est pas +souhaitable que l'utilisateur puisse récupérer toutes les lignes d'un ensemble +paginé. L'option ``maxLimit`` permet que personne ne puisse définir de limite +trop haute depuis l'extérieur. Par défaut, CakePHP limite à un maximum de 100 le +nombre de lignes par page. Si cette valeur par défaut n'est pas appropriée pour +votre application, vous pouvez l'ajuster dans les options de pagination, par +exemple en le réduisant à ``10``:: public $paginate = [ // Autres clés ici. @@ -264,7 +300,7 @@ sera réduite à la valeur ``maxLimit``. Faire des Jointures d'Associations Supplémentaires ================================================== -Des associations supplémentaires peuvent être chargées à la table paginée en +Vous pouvez charger des associations supplémentaires depuis la table paginée en utilisant le paramètre ``contain``:: public function index() @@ -276,18 +312,17 @@ utilisant le paramètre ``contain``:: $this->set('articles', $this->paginate($this->Articles)); } -Requêtes de Page Out of Range -============================= +Requêtes de Page Inexistante +============================ -PaginatorComponent va lancer une ``NotFoundException`` quand on essaie d'accéder à -une page non existante, par exemple le nombre de page demandé est supérieur au total -du nombre de pages. +Quand on essaie d'accéder à une page inexistante, c'est-à-dire lorsque le numéro +de page demandé est supérieur au nombre total de pages, PaginatorComponent lance +une ``NotFoundException``. -Ainsi vous pouvez soit laisser s'afficher la page d'erreur normale, soit -utiliser un bloc try catch et faire des actions appropriées quand une +Ainsi, vous avez le choix entre laisser la page d'erreur normale s'afficher, ou +utiliser un bloc try catch et exécuter des actions appropriées lorsqu'une ``NotFoundException`` est attrapée:: - // Prior to 3.6 use Cake\Network\Exception\NotFoundException use Cake\Http\Exception\NotFoundException; public function index() @@ -295,8 +330,8 @@ utiliser un bloc try catch et faire des actions appropriées quand une try { $this->paginate(); } catch (NotFoundException $e) { - // Faire quelque chose ici comme rediriger vers la première ou dernière page. - // $this->request->getParam('paging') vous donnera les infos demandées. + // Faire quelque chose ici, comme rediriger vers la première ou la dernière page. + // $this->request->getAttribute('paging') vous donnera les infos nécessaires. } } @@ -304,7 +339,7 @@ Pagination dans la Vue ====================== Consultez la documentation :php:class:`~Cake\\View\\Helper\\PaginatorHelper` -pour savoir comment créer des liens de navigation paginés. +pour savoir comment créer des liens de navigation dans la pagination. .. meta:: :title lang=fr: Pagination diff --git a/fr/core-libraries/email.rst b/fr/core-libraries/email.rst index a83e4937fc..e990b4c379 100644 --- a/fr/core-libraries/email.rst +++ b/fr/core-libraries/email.rst @@ -1,11 +1,11 @@ -Email -##### +Mailer +###### .. php:namespace:: Cake\Mailer -.. php:class:: Email(mixed $profile = null) +.. php:class:: Mailer(string|array|null $profile = null) -``Email`` est une nouvelle classe pour envoyer des emails. Avec cette classe, +``Mailer`` est une classe utilitaire pour envoyer des emails. Avec cette classe, vous pouvez envoyer des emails depuis n'importe endroit de votre application. Utilisation basique @@ -13,426 +13,375 @@ Utilisation basique Premièrement, vous devez vous assurer que la classe est chargée:: - use Cake\Mailer\Email; + use Cake\Mailer\Mailer; -Après avoir chargé ``Email``, vous pouvez envoyer un email avec ce qui suit:: +Après avoir chargé ``Mailer``, vous pouvez envoyer un email de la façon +suivante:: - $email = new Email('default'); - $email->setFrom(['moi@example.com' => 'Mon Site']) + $mailer = new Mailer('default'); + $mailer->setFrom(['moi@example.com' => 'Mon Site']) ->setTo('toi@example.com') ->setSubject('À propos') ->send('Mon message'); -Puisque les méthodes de setter d'``Email`` retournent l'instance de la classe, -vous pouvez définir ses propriétés avec le chaînage des méthodes. +Les méthodes setter de ``Mailer`` retournent l'instance de la classe, ce qui +fait que vous pouvez définir ses propriétés par un chaînage de méthodes. -``Email`` comporte plusieurs méthodes pour définir les destinataires - +``Mailer`` comporte plusieurs méthodes pour définir les destinataires - ``setTo()``, ``setCc()``, ``setBcc()``, ``addTo()``, ``addCc()`` et -``addBcc()``. La principale -différence est que les trois premières méthodes vont réinitialiser ce qui était -déjà défini et les suivantes vont ajouter plus de destinataires dans leur champs -respectifs:: - - $email = new Email(); - $email->setTo('to@example.com', 'To Example'); - $email->addTo('to2@example.com', 'To2 Example'); +``addBcc()``. La principale différence est que les trois premières méthodes vont +écraser ce que vous auriez déjà défini, tandis que les suivantes vont seulement +ajouter d'autres destinataires dans leurs champs respectifs:: + + $mailer = new Mailer(); + $mailer->setTo('to@example.com', 'To Example'); + $mailer->addTo('to2@example.com', 'To2 Example'); // Les destinataires de l'email sont: to@example.com et to2@example.com - $email->setTo('test@example.com', 'ToTest Example'); + $mailer->setTo('test@example.com', 'ToTest Example'); // Le destinataire de l'email est: test@example.com Choisir l'émetteur ------------------ -Quand on envoie des emails de la part d'autre personne, c'est souvent une -bonne idée de définir l'émetteur original en utilisant le header Sender. -Vous pouvez faire ceci en utilisant ``setSender()``:: +Quand on envoie des emails de la part d'une autre personne, il est généralement +souhaitable de définir l'émetteur original avec le header Sender. Vous pouvez le +faire en utilisant ``setSender()``:: - $email = new Email(); - $email->setSender('app@example.com', 'MyApp emailer'); + $mailer = new Mailer(); + $mailer->setSender('app@example.com', 'MyApp emailer'); .. note:: - C'est aussi une bonne idée de définir l'enveloppe de l'émetteur quand on - envoie un mail de la part d'une autre personne. Cela les empêche d'obtenir - tout message sur la délivrance. + Quand vous envoyez un mail de la part d'une autre personne, il est également + préférable de définir l'émetteur de l'enveloppe . Cela lui évite de recevoir + des messages en cas de problèmes de distribution du mail. .. _email-configuration: Configuration ============= -Les configurations des profiles du Mailer et du transport d'emails sont définies +Les configurations des profils du Mailer et du transport d'emails sont définies dans les fichiers de configuration de votre application. Les clés ``Email`` et -``EmailTransport`` définissent respectivement les configurations des profiles du -mailer et du transport des emails. Pendant le bottstrap de l'application, les -paramètres de configuration sont passés de la classe ``Configure`` aux classes +``MailerTransport`` définissent respectivement les configurations des profils du +mailer et du transport des emails. Pendant le bootstrap de l'application, les +paramètres de configuration sont repris de la classe ``Configure`` vers les classes ``Mailer`` et ``TransportFactory`` en utilisant ``setConfig()``. En définissant -des profiles et des transports, vous pouvez libérer le code de votre application -de toutes données de configuration, et éviter des duplications de code qui -rendent plus difficiles la maintenance et le déploiement. +des profils et des transports, vous pouvez alléger le code de votre application +de toutes les données de configuration, et éviter des duplications de code qui +vous compliqueraient la maintenance et le déploiement. Pour charger une configuration prédéfinie, vous pouvez utiliser la méthode -``setProfile()`` ou la passer au constructeur d'``Email``:: +``setProfile()`` ou la passer au constructeur de ``Mailer``:: - $email = new Email(); - $email->setProfile('default'); + $mailer = new Mailer(); + $mailer->setProfile('default'); - //ou dans le constructeur:: - $email = new Email('default'); + // ou dans le constructeur:: + $mailer = new Mailer('default'); -Plutôt que de passer une chaîne avec le bon nom de configuration prédéfini, -vous pouvez aussi juste charger un tableau d'options:: +Plutôt que de passer une chaîne avec le nom d'une configuration prédéfinie, vous +pouvez aussi charger tout simplement un tableau d'options:: - $email = new Email(); - $email->setProfile(['from' => 'me@example.org', 'transport' => 'my_custom']); + $mailer = new Mailer(); + $mailer->setProfile(['from' => 'moi@example.org', 'transport' => 'transport_perso']); // ou dans le constructeur:: - $email = new Email(['from' => 'me@example.org', 'transport' => 'my_custom']); + $mailer = new Mailer(['from' => 'moi@example.org', 'transport' => 'transport_perso']); .. _email-configurations: -Profiles de Configurations --------------------------- - -Définir des profiles de délivrance vous permet d'ajouter les configurations -habituelles d'email dans des profiles réutilisables. Votre application peut -avoir autant de profiles que nécessaire. Les clés de configuration suivantes -sont utilisées: - -- ``'from'``: Email ou un tableau d'emmeteur. Regardez ``Email::setFrom()``. -- ``'sender'``: Email ou un tableau d'émetteur réel. Regardez - ``Email::setSender()``. -- ``'to'``: Email ou un tableau de destination. Regardez ``Email::setTo()``. -- ``'cc'``: Email ou un tableau de copy carbon. Regardez ``Email::setCc()``. -- ``'bcc'``: Email ou un tableau de copy carbon blind. Regardez - ``Email::setBcc()``. -- ``'replyTo'``: Email ou un tableau de répondre à cet e-mail. Regardez - ``Email::setReplyTo()``. -- ``'readReceipt'``: Adresse Email ou un tableau d'adresses pour recevoir un - récepissé de lecture. Regardez ``Email::readReceipt()``. -- ``'returnPath'``: Adresse Email ou un tableau des adresses à retourner si - vous avez une erreur. Regardez ``Email::setReturnPath()``. -- ``'messageId'``: ID du Message de l'e-mail. Regardez - ``Email::setMessageId()``. -- ``'subject'``: Sujet du message. Regardez ``Email::setSubject()``. +Profils de Configurations +------------------------- + +Le fait de définir des profils d'envoi vous permet de consolider les paramètres +habituels des emails dans des profils réutilisables. Votre application peut +avoir autant de profils que nécessaire. Voici les clés de configuration +utilisées: + +- ``'from'``: Mailer ou un tableau d'expéditeur. Voir ``Mailer::setFrom()``. +- ``'sender'``: Mailer ou un tableau d'expéditeur réel. Voir + ``Mailer::setSender()``. +- ``'to'``: Mailer ou un tableau de destinataires. Voir ``Mailer::setTo()``. +- ``'cc'``: Mailer ou un tableau de destinataires en copie. Voir + ``Mailer::setCc()``. +- ``'bcc'``: Mailer ou un tableau de destinataires en copie cachée. Voir + ``Mailer::setBcc()``. +- ``'replyTo'``: Mailer ou un tableau d'adresse de réponse. Voir + ``Mailer::setReplyTo()``. +- ``'readReceipt'``: Adresse Mailer ou un tableau d'adresse pour recevoir un + accusé de réception. Voir ``Mailer::setReadReceipt()``. +- ``'returnPath'``: Adresse Mailer ou un tableau d'adresse à laquelle écrire + en cas d'erreur. Voir ``Mailer::setReturnPath()``. +- ``'messageId'``: ID du message de l'e-mail. Voir ``Mailer::setMessageId()``. +- ``'subject'``: Sujet du message. Voir ``Mailer::setSubject()``. - ``'message'``: Contenu du message. Ne définissez pas ce champ si vous - utilisez un contenu rendu. -- ``'priority'``: Priorité de l'email, exprimée avec un nombre (généralement de - 1 à 5, 1 étant la priorité la plus haute). -- ``'headers'``: Headers à inclure. Regardez ``Email::setHeaders()``. -- ``'viewRenderer'``: Si vous utilisez un contenu rendu, définissez le nom de - classe de la vue. Regardez ``ViewBuilder::setClassName()``. -- ``'template'``: Si vous utilisez un contenu rendu, définissez le nom du - template. Regardez ``Email::template()``. -- ``'theme'``: Theme utilisé pour le rendu du template. Voir - ``Email::setTheme()``. -- ``'layout'``: Si vous utilisez un contenu rendu, définissez le layout à - rendre. Regardez ``ViewBuilder::setTemplate()``. + utilisez un contenu rendu par l'application. +- ``'priority'``: Nombre exprimant la priorité de l'email (généralement de 1 à + 5, 1 étant la priorité la plus haute). +- ``'headers'``: En-têtes à inclure. Voir ``Mailer::setHeaders()``. +- ``'viewRenderer'``: Si vous utilisez un contenu généré par l'application, + définissez ici le nom de classe de la vue. Voir + ``ViewBuilder::setClassName()``. +- ``'template'``: Si vous utilisez un contenu généré par l'application, + définissez ici le nom du template. Voir ``Mailer::setTemplate()``. +- ``'theme'``: Thème utilisé pour le rendu du template. Voir + ``Mailer::setTheme()``. +- ``'layout'``: Si vous utilisez un contenu généré par l'application, définissez + ici le layout à rendre. Voir ``ViewBuilder::setTemplate()``. - ``'autoLayout'``: Si vous voulez rendre un template sans layout, définissez ce - champ à ``false``. See ``ViewBuilder::disableAutoLayout()``. -- ``'viewVars'``: Si vous utilisez un contenu rendu, définissez le tableau avec - les variables devant être rendus dans la vue. Regardez - ``Email::viewVars()``. -- ``'attachments'``: Liste des fichiers à attacher. Regardez - ``Email::attachments()``. -- ``'emailFormat'``: Format de l'email (html, text ou both). Regardez - ``Email::setEmailFormat()``. -- ``'transport'``: Nom du Transport. Regardez - :php:meth:`~Cake\\Mailer\\Email::configTransport()`. -- ``'log'``: Niveau de Log pour connecter les headers de l'email headers et le - message. ``true`` va utiliser LOG_DEBUG. Regardez :ref:`logging-levels`. + champ à ``false``. Voir ``ViewBuilder::disableAutoLayout()``. +- ``'viewVars'``: Si vous utilisez un contenu généré par l'application, + définissez ici le tableau contenant les variables devant être rendues dans la + vue. Voir ``Mailer::setViewVars()``. +- ``'attachments'``: Liste des pièces jointes. Voir + ``Mailer::setAttachments()``. +- ``'emailFormat'``: Format de l'email (html, text ou both). Voir + ``Mailer::setEmailFormat()``. +- ``'transport'``: Nom de la configuration du transport. Voir + :ref:`email-transport`. +- ``'log'``: Niveau de log pour la journalisation des headers et du message. + ``true`` utilisera LOG_DEBUG. Voir :ref:`logging-levels`. Notez que les logs seront émis sous le scope nommé ``email``. - Regardez aussi :ref:`logging-scopes`. -- ``'helpers'``: Tableau de helpers utilisés dans le template email. + Voir aussi :ref:`logging-scopes`. +- ``'helpers'``: Tableau de helpers utilisés dans le template de l'email. ``ViewBuilder::setHelpers()``/``ViewBuilder::addHelpers()`` -Toutes ces configurations sont optionnelles, excepté ``'from'``. - .. note:: - Les valeurs des clés ci-dessus utilisant Email ou un tableau, comme from, - to, cc etc... seront passées en premier paramètre des méthodes - correspondantes. L'equivalent pour - ``Email::setFrom('my@example.com', 'My Site')`` sera défini comme - ``'from' => ['my@example.com' => 'My Site']`` dans votre config. + Les valeurs des clés ci-dessus qui utilisent Mailer ou un tableau, telles + que from, to, cc, etc, seront passées comme premier paramètre des méthodes + correspondantes. L'équivalent de + ``Mailer::setFrom('mon@example.com', 'Mon Site')`` pourrait être défini par + ``'from' => ['mon@example.com' => 'Mon Site']`` dans votre configuration. Définir les Headers ------------------- -Dans ``Email``, vous êtes libre de définir les headers que vous souhaitez. -Si vous migrez pour utiliser Email, n'oubliez pas de mettre le préfixe -``X-`` dans vos headers. +Dans ``Mailer``, vous êtes libre de définir les headers que vous souhaitez. +N'oubliez pas d'ajouter le préfixe ``X-`` pour vos headers personnalisés. -Regardez ``Email::setHeaders()`` et ``Email::addHeaders()`` +Voir ``Mailer::setHeaders()`` et ``Mailer::addHeaders()`` -Envoyer les Emails Templatés ----------------------------- +Envoyer des Mails d'après un Template +------------------------------------- -Les Emails sont souvent bien plus que de simples message textes. Afin de -faciliter cela, CakePHP fournit une façon d'envoyer les emails en utilisant la -:doc:`view layer ` de CakePHP. +Les mails sont souvent bien plus que de simples messages avec du texte. Pour +vous faciliter la vie, CakePHP propose d'envoyer des emails en utilisant la +:doc:`couche de rendu ` de CakePHP. -Les templates pour les emails se placent dans un dossier spécial appelé -``Email`` dans le répertoire ``Template`` de votre application. Les templates -des emails peuvent aussi utiliser les layouts et éléments tout comme les -templates normales:: +Les templates pour les emails se trouvent dans le dossier spécial +``templates/email`` de votre application. Les vues d'emails peuvent aussi +utiliser des layouts et des éléments, comme les vues normales:: - $email = new Email(); - $email - ->setTemplate('welcome', 'fancy') + $mailer = new Mailer(); + $mailer ->setEmailFormat('html') ->setTo('bob@example.com') ->setFrom('app@domain.com') - ->send(); + ->viewBuilder() + ->setTemplate('bienvenue') + ->setLayout('sympa'); + + $mailer->deliver(); -Ce qui est au-dessus utilise **templates/email/html/welcome.php** pour la -vue, et **templates/layout/email/html/fancy.php** pour le layout. Vous pouvez -aussi envoyer des messages email templaté multipart:: +Ceci utilisera **templates/email/html/bienvenue.php** comme vue, et +**templates/layout/email/html/sympa.php** comme layout. Vous pouvez aussi +envoyer des emails templatés multipart:: - $email = new Email(); - $email - ->setTemplate('welcome', 'fancy') - ->setEmailFormat('both') + $mailer = new Mailer(); + $mailer + ->setTemplate('bienvenue', 'sympa') + ->setMailerFormat('both') ->setTo('bob@example.com') ->setFrom('app@domain.com') ->send(); -Ceci utiliserait les fichiers de template suivants: + $mailer->deliver(); + +Ce qui utiliserait les fichiers de template suivants: + +* **templates/email/text/bienvenue.php** +* **templates/layout/email/text/sympa.php** +* **templates/email/html/bienvenue.php** +* **templates/layout/email/html/sympa.php** -* **templates/email/text/welcome.php** -* **templates/layout/email/text/fancy.php** -* **templates/email/html/welcome.php** -* **templates/layout/email/html/fancy.php** +Quand vous envoyez des emails templatés, vous avez la possibilité d'envoyer en +``text``, ``html`` ou ``both``. -Quand on envoie les emails templatés, vous avez la possibilité d'envoyer soit -``text``, ``html`` soit ``both``. +Vous pouvez définir toute la configuration se rapportant à la vue à partir du +*view builder* obtenu par ``Mailer::viewBuilder()``, comme vous le feriez dans +un controller. -Vous pouvez définir des variables de vue avec ``Email::viewVars()``:: +Vous pouvez définir des variables de vue avec ``Mailer::setViewVars()``:: - $email = new Email('templated'); - $email->viewVars(['value' => 12345]); + $mailer = new Mailer('templated'); + $mailer->setViewVars(['value' => 12345]); -Dans votre email template, vous pouvez utiliser ceux-ci avec:: +Ou bien vous pouvez utiliser les méthodes du *view builder* +``ViewBuilder::setVar()`` et ``ViewBuilder::setVars()``. -

Ici est votre valeur:

+Dans votre template d'email, vous pouvez les utiliser ainsi:: -Vous pouvez aussi utiliser les helpers dans les emails, un peu comme vous -pouvez dans des fichiers de template normaux. Par défaut, seul -:php:class:`HtmlHelper` est chargé. Vous pouvez chargez des helpers -supplémentaires en utilisant la méthode ``ViewBuilder::addHelpers()``:: +

Voici votre valeur:

- $email->viewBuilder()->addHelpers(['Html', 'Custom', 'Text']); +Vous pouvez aussi utiliser les helpers dans les emails, un peu comme vous le +faites dans des fichiers de template normaux. Seul :php:class:`HtmlHelper` est +chargé par défaut. Vous pouvez charger d'autres helpers en utilisant la méthode +``ViewBuilder::addHelpers()``:: -Quand vous ajoutez des helpers, assurez vous d'inclure 'Html' ou il sera + $mailer->viewBuilder()->addHelpers(['Html', 'Perso', 'Text']); + +Quand vous ajoutez des helpers, assurez-vous d'inclure 'Html' sinon il sera retiré des helpers chargés dans votre template d'email. .. note:: Dans les versions antérieures à 4.3.0, vous deviez utilisez ``setHelpers()`` à la place. -Si vous voulez envoyer un email en utilisant templates dans un plugin, vous -pouvez utiliser la :term:`syntaxe de plugin` familière pour le faire:: +Si vous voulez envoyer un email en utilisant des templates dans un plugin, vous +pouvez le faire en utilisant la :term:`syntaxe de plugin` qui doit vous être +familière:: - $email = new Email(); - $email->setTemplate('Blog.new_comment', 'Blog.auto_message'); + $mailer = new Mailer(); + $mailer->viewBuilder()->setTemplate('Blog.nouveau_commentaire'); -Ce qui est au-dessus utiliserait les templates à partir d'un plugin de Blog par -exemple. +Ceci utiliserait par exemple les templates du plugin Blog. -Dans certains cas, vous devez remplacer le template par défaut fourni par -les plugins. Vous pouvez faire ceci en utilisant les themes en disant à Email -d'utiliser le bon theme en utilisant la méthode ``Email::theme()``:: +Dans certains cas, vous pouvez avoir besoin de substituer le template par défaut +fourni par les plugins. Vous pouvez le faire en utilisant les thèmes:: - $email = new Email(); - $email->setTemplate('Blog.new_comment', 'Blog.auto_message'); - $email->setTheme('TestTheme'); + $mailer->viewBuilder() + ->setTemplate('Blog.nouveau_commentaire') + ->setLayout('Blog.auto_message') + ->setTheme('TestTheme'); -Ceci vous permet de remplacer le template `new_comment` dans votre theme sans -modifier le plugin Blog. Le fichier de template devra être créé dans le +Cela vous permet de remplacer le template `nouveau_commentaire` dans votre theme +sans modifier le plugin Blog. Le fichier de template devra être créé sous le chemin suivant: -**templates/plugin/TestTheme/plugin/Blog/email/text/new_comment.php**. +**templates/plugin/TestTheme/plugin/Blog/email/text/nouveau_commentaire.php**. -Envoyer les pièces jointes +Envoyer des Pièces Jointes ========================== -.. php:method:: setAttachments($attachments = null) +.. php:method:: setAttachments($attachments) -Vous pouvez aussi attacher des fichiers aux messages d'email. Il y a quelques -formats différents qui dépendent de quel type de fichier vous avez, et comment -vous voulez que les noms de fichier apparaissent dans le mail de réception du +Vous pouvez aussi rattacher des pièces jointes aux emails. Il y a quelques +formats différents selon le type de fichier vous avez, et la façon dont vous +voulez que les noms de fichiers apparaissent dans la boîte de réception du client: -1. Chaîne de caractères: ``$email->setAttachments('/full/file/path/file.png')`` va - attacher ce fichier avec le nom file.png. -2. Tableau: ``$email->setAttachments(['/full/file/path/file.png'])`` aura le - même comportement qu'en utilisant une chaîne de caractères. -3. Tableau avec clé: - ``$email->setAttachments(['photo.png' => '/full/some_hash.png'])`` va - attacher some_hash.png avec le nom photo.png. Le récipiendaire va voir - photo.png, pas some_hash.png. -4. Tableaux imbriqués:: - - $email->setAttachments([ +1. Tableau: ``$mailer->setAttachments(['/chemin/complet/vers/le/fichier.png'])`` + attacher ce fichier avec le nom fichier.png. +2. Tableau avec clé: + ``$mailer->setAttachments(['photo.png' => '/chemin/un_hash.png'])`` va + attacher un_hash.png avec le nom photo.png. Le destinataire verra photo.png, + pas some_hash.png. +3. Tableaux imbriqués:: + + $mailer->setAttachments([ 'photo.png' => [ - 'file' => '/full/some_hash.png', + 'file' => '/chemin/un_hash.png', 'mimetype' => 'image/png', - 'contentId' => 'my-unique-id' + 'contentId' => 'mon-id-unique' ] ]); - Ce qui est au-dessus va attacher le fichier avec différent mimetype et avec - un content ID personnalisé (Quand vous définissez le content ID, la pièce - jointe est transformée en inline). Le mimetype et contentId sont optionnels - dans ce formulaire. + Ceci attachera le fichier avec un mimetype différent et avec un content ID + personnalisé (quand vous définissez le content ID, la pièce jointe est + transformée en inline). Le mimetype et le contentId sont optionnels sous + cette forme. - 4.1. Quand vous utilisez ``contentId``, vous pouvez utiliser le fichier dans - corps HTML comme ````. + 3.1. Quand vous utilisez le ``contentId``, vous pouvez utiliser le fichier + dans le corps HTML avec ````. - 4.2. Vous pouvez utiliser l'option ``contentDisposition`` pour désactiver le + 3.2. Vous pouvez utiliser l'option ``contentDisposition`` pour désactiver le header ``Content-Disposition`` pour une pièce jointe. C'est utile pour l'envoi d'invitations ical à des clients utilisant outlook. - 4.3 Au lieu de l'option ``file``, vous pouvez fournir les contenus de - fichier en chaîne en utilisant l'option ``data``. Cela vous permet - d'attacher les fichiers sans avoir besoin de chemins de fichier vers eux. - -.. deprecated:: 3.4.0 - Utilisez ``setAttachments()`` à la place de ``attachments()``. - -Utiliser les Transports -======================= - -Les Transports sont des classes destinées à envoyer l'email selon certain -protocoles ou méthodes. CakePHP supporte les transports Mail (par défaut), -Debug et SMTP. - -Pour configurer votre méthode, vous devez utiliser la méthode -:php:meth:`Cake\\Mailer\\Email::transport()` ou avoir le transport dans -votre configuration:: - - $email = new Email(); - - // Use a named transport already configured using Email::setConfigTransport() - $email->setTransport('gmail'); - - // Use a constructed object. - $transport = new DebugTransport(); - $email->setTransport($transport); - -Créer des Transports Personnalisés ----------------------------------- - -Vous pouvez créer vos transports personnalisés pour intégrer avec d'autres -systèmes email (comme SwiftMailer). Pour créer votre transport, créez tout -d'abord le fichier **src/Mailer/Transport/ExampleTransport.php** (où -Exemple est le nom de votre transport). Pour commencer, votre fichier devrait -ressembler à cela:: - - namespace App\Mailer\Transport; - - use Cake\Mailer\AbstractTransport; - use Cake\Mailer\Email; - - class ExampleTransport extends AbstractTransport - { - public function send(Email $email) - { - // Logique d'exécution - } - } + 3.3 Au lieu de l'option ``file``, vous pouvez fournir les contenus de + fichiers en tant que chaîne de caractères en utilisant l'option ``data``. + Cela vous permet d'attacher des fichiers sans avoir besoin d'un chemin sur le + disque. -Vous devez intégrer la méthode ``send(Email $email)`` avec votre -logique personnalisée. En option, vous pouvez intégrer la méthode -``config($config)``. ``config()`` est appelée avant send() et vous permet -d'accepter les configurations de l'utilisateur. Par défaut, cette méthode -met la configuration dans l'attribut protégé ``$_config``. +Assouplir les Règles de Validation d'Adresses +--------------------------------------------- -Si vous avez besoin d'appeler des méthodes supplémentaires sur le transport -avant l'envoi, vous pouvez utiliser -:php:meth:`Cake\\Mailer\\Email::transportClass()` pour obtenir une -instance du transport. Exemple:: - - $yourInstance = $email->setTransport('your')->transportClass(); - $yourInstance->myCustomMethod(); - $email->send(); - -Faciliter les Règles de Validation des Adresses ------------------------------------------------ - -.. php:method:: setEmailPattern($pattern = null) +.. php:method:: setEmailPattern($pattern) Si vous avez des problèmes de validation lors de l'envoi vers des adresses -non conformes, vous pouvez faciliter le patron utilisé pour valider les -adresses email. C'est parfois nécessaire quand il s'agit de certains +considérées comme non conformes, vous pouvez assouplir le pattern utilisé pour +valider les adresses email. C'est parfois nécessaire quand il s'agit de certains ISP Japonais:: - $email = new Email('default'); + $mailer = new Mailer('default'); - // Relax le patron d'email, ainsi vous pouvez envoyer - // vers des adresses non conformes - $email->setEmailPattern($newPattern); + // Assouplir le pattern d'email, de façon à pouvoir écrire + // à des adresses non conformes. + $mailer->setEmailPattern($newPattern); Envoyer des Messages Rapidement =============================== -Parfois vous avez besoin d'une façon rapide d'envoyer un email, et vous n'avez -pas particulièrement envie en même temps de définir un tas de configuration. -:php:meth:`Cake\\Mailer\\Email::deliver()` est présent pour ce cas. +Parfois vous avez besoin d'un moyen rapide d'envoyer un email, et vous n'avez +pas particulièrement envie de définir une tonne de configuration juste pour +cela. :php:meth:`Cake\\Mailer\\Mailer::deliver()` est fait pour vous. Vous pouvez créer votre configuration dans -:php:meth:`Cake\\Mailer\\Email::config()`, ou utiliser un -tableau avec toutes les options dont vous aurez besoin et utiliser -la méthode statique ``Email::deliver()``. +:php:meth:`Cake\\Mailer\\Email::config()`, ou utiliser un tableau avec toutes +les options dont avez besoin, puis utiliser la méthode statique +``Mailer::deliver()``. Exemple:: - Email::deliver('you@example.com', 'Subject', 'Message', ['from' => 'me@example.com']); + Mailer::deliver('toi@example.com', 'Sujet', 'Message', ['from' => 'moi@example.com']); -Cette méthode va envoyer un email à you@example.com, à partir de me@example.com -avec le sujet Subject et le contenu Message. +Cette méthode enverra un email à toi@example.com, à partir de moi@example.com +avec le sujet "Sujet" et le contenu "Message". -Le retour de ``deliver()`` est une instance de :php:class:`Cake\\Mailer\\Email` -avec l'ensemble des configurations. Si vous ne voulez pas envoyer l'email -maintenant, et souhaitez configurer quelques trucs avant d'envoyer, vous pouvez -passer le 5ème paramètre à ``false``. +La valeur de retour de ``deliver()`` est une instance +:php:class:`Cake\\Mailer\\Email` entièrement configurée. Si vous ne voulez pas +envoyer l'email tout de suite et souhaitez configurer encore certaines choses +avant de l'envoyer, vous pouvez définir le 5ème paramètre à ``false``. Le 3ème paramètre est le contenu du message ou un tableau avec les variables -(quand on utilise le contenu rendu). +(quand vous utilisez un contenu généré par l'application). -Le 4ème paramètre peut être un tableau avec les configurations ou une chaîne de -caractères avec le nom de configuration dans ``Configure``. +Le 4ème paramètre peut être un tableau avec la configuration ou une chaîne de +caractères avec le nom d'une configuration figurant dans ``Configure``. -Si vous voulez, vous pouvez passer les to, subject et message à null et faire -toutes les configurations dans le 4ème paramètre (en tableau ou en utilisant -``Configure``). -Vérifiez la liste des :ref:`configurations ` pour voir -toutes les configs acceptées. +Si vous voulez, vous pouvez passer null pour les arguments *to*, *subject* et +*message*, et passer toutes les configurations dans le 4ème paramètre (en +tableau ou en utilisant ``Configure``). +Faites un tour par la liste des :ref:`configurations ` +pour connaître toutes les configs acceptées. -Envoyer des Emails depuis CLI -============================= +Envoyer des Emails en Ligne de Commande +======================================= -Quand vous envoyez des emails à travers un script CLI (Shells, Tasks, ...), -vous devez définir manuellement le nom de domaine que Email doit utiliser. -Il sera utilisé comme nom d'hôte pour l'id du message (puisque il n'y a pas -de nom d'hôte dans un environnement CLI):: +Quand vous envoyez des emails depuis un script CLI (Shells, Tasks, ...), vous +devez définir manuellement le nom de domaine à utiliser pour Mailer. Il sera +utilisé comme nom d'hôte pour l'id du message (puisqu'il n'y a pas de nom d'hôte +dans un environnement CLI):: $email->setDomain('www.example.org'); - // Resulte en ids de message comme ```` (valid) - // au lieu de ``` (invalid) + // Envoie des ids de message tels que ```` (valide) + // au lieu de ``` (invalide) -Un id de message valide peut permettre à ce message de ne pas finir dans un -dossier de spam. +Un id de message valide peut permettre à ce message de ne pas finir dans les +spams. -Créer des emails réutilisables +Créer des Emails Réutilisables ============================== -Les ``Mailers`` vous permettent de créer des emails réutilisables pour votre -application. Ils peuvent aussi servir à contenir plusieurs configurations -d'emails en un seul et même endroit. Cela vous permet de garder votre code -DRY ainsi que la configuration d'emails en dehors des autres parties -constituant votre application. +Jusqu'à présent, nous avons vu comment utiliser la classe ``Mailer`` pour créer +et envoyer des emails. Mais la principale fonctionnalité d'un mailer est de vous +permettre de créer des emails réutilisables n'importe où dans votre application. +Ils peuvent aussi servir à contenir différentes configurations d'emails en un +seul et même endroit, ce qui vous aide à garder votre code DRY et à déplacer la +configuration des emails en dehors des autres parties de votre application. -Dans cet exemple, vous allez créer un ``Mailer`` qui contient des emails liés -aux utilisateurs. Pour créer votre ``UserMailer``, créez un fichier +Dans cet exemple, vous allez créer un ``Mailer`` qui contient des emails +dépendant des utilisateurs. Pour créer votre ``UserMailer``, créez un fichier **src/Mailer/UserMailer.php**. Le contenu de ce fichier devra ressembler à ceci:: namespace App\Mailer; @@ -445,8 +394,9 @@ aux utilisateurs. Pour créer votre ``UserMailer``, créez un fichier { $this ->setTo($user->email) - ->setSubject(sprintf('Welcome %s', $user->name)) - ->setTemplate('welcome_mail', 'custom'); // Par défaut le template avec le même nom que le nom de la méthode est utilisé. + ->setSubject(sprintf('Bienvenue %s', $user->name)) + ->viewBuilder() + ->setTemplate('message_de_bienvenue', 'personnalisé'); // Par défaut le template utilisé a le même nom que la méthode. } public function resetPassword($user) @@ -454,19 +404,19 @@ aux utilisateurs. Pour créer votre ``UserMailer``, créez un fichier $this ->setTo($user->email) ->setSubject('Reset password') - ->set(['token' => $user->token]); + ->setViewVars(['token' => $user->token]); } } Dans notre exemple, nous avons créé deux méthodes, une pour envoyer l'email de bienvenue et l'autre pour envoyer un email de réinitialisation de mot de passe. -Chacune de ces méthodes prend une ``Entity`` ``User`` et utilise ses propriétés +Chacune de ces méthodes reçoit une ``Entity`` ``User`` et utilise ses propriétés pour configurer chacun des emails. Vous pouvez maintenant utiliser votre ``UserMailer`` pour envoyer tous les -emails liés aux utilisateurs depuis n'importe où dans l'application. Par -exemple, si vous souhaitez envoyer l'email de bienvenue, vous pouvez faire la -chose suivante:: +emails dépendant des utilisateurs, depuis n'importe quel endroit de votre +application. Par exemple, pour envoyer l'email de bienvenue, vous pouvez faire +la chose suivante:: namespace App\Controller; @@ -478,7 +428,7 @@ chose suivante:: public function register() { - $user = $this->Users->newEntity(); + $user = $this->Users->newEmptyEntity(); if ($this->request->is('post')) { $user = $this->Users->patchEntity($user, $this->request->getData()) if ($this->Users->save($user)) { @@ -489,11 +439,11 @@ chose suivante:: } } -Si vous voulez complétement séparer l'envoi de l'email de bienvenue du code de -l'application, vous pouvez utiliser votre ``UserMailer`` via l'évènement -``Model.afterSave``. En utilisant un évènement, vous pouvez complètement -séparer la logique d'envoi d'emails du reste de votre logique "utilisateurs". -Vous pourriez par exemple ajouter ce qui suit à votre ``UserMailer``:: +Si vous voulez complètement séparer l'envoi de l'email de bienvenue du code de +l'application, votre ``UserMailer`` peut écouter l'évènement +``Model.afterSave``. En utilisant l'évènement, vous pouvez séparer complètement +la logique d'envoi d'emails du reste de votre logique "utilisateurs". +Vous pourriez par exemple ajouter ceci à votre ``UserMailer``:: public function implementedEvents() { @@ -502,19 +452,290 @@ Vous pourriez par exemple ajouter ce qui suit à votre ``UserMailer``:: ]; } - public function onRegistration(Event $event, EntityInterface $entity, ArrayObject $options) + public function onRegistration(EventInterface $event, EntityInterface $entity, ArrayObject $options) { if ($entity->isNew()) { $this->send('welcome', [$entity]); } } -L'objet mailer serait ainsi enregistré en tant qu'écouteur (listeners) -d'événement et la méthode ``onRegistration()`` serait appellée à chaque fois -que l'événement ``Model.afterSave`` serait déclenché. Plus d'information sur -comment enregistrer des objets écouteurs d'événements sont disponibles dans la -documentation :ref:`registering-event-listeners`. +L'objet mailer sera ainsi enregistré en tant qu'écouteur (*listener*) +d'événement et la méthode ``onRegistration()`` sera appelée chaque fois que +l'événement ``Model.afterSave`` sera déclenché. + + // attache un gestionnaire d'événements sur Users + $this->Users->getEventManager()->on($this->getMailer('User')); + +.. note:: + + Plus d'informations sur la façon d'enregistrer des écouteurs d'événements + dans la documentation :ref:`registering-event-listeners`. + +.. _email-transport: + +Configurer les Transports +========================= + +Les emails sont délivrés par des 'transports'. Divers transports vous permettent +d'envoyer des messages par la fonction ``mail()`` de PHP, par des serveurs SMTP, +voire pas du tout, ce qui est utile pour le débogage. La configuration des +transports vous permet de garder les données de configuration en-dehors du code +de votre application, et simplifie le déploiement puisque vous pouvez changer +facilement les données de configuration. Voici un exemple de configuration de +transport:: + + // Dans config/app.php + 'EmailTransport' => [ + // Exemple de configuration de Mail + 'default' => [ + 'className' => 'Mail', + ], + // Exemple de configuration SMTP + 'gmail' => [ + 'host' => 'smtp.gmail.com', + 'port' => 587, + 'username' => 'mon@gmail.com', + 'password' => 'secret', + 'className' => 'Smtp', + 'tls' => true + ] + ], + +Vous pouvez aussi configurer les transports pendant l'exécution, en utilisant +``TransportFactory::setConfig()``:: + + use Cake\Mailer\TransportFactory; + + // Définit un transport SMTP + TransportFactory::setConfig('gmail', [ + 'host' => 'ssl://smtp.gmail.com', + 'port' => 465, + 'username' => 'mon@gmail.com', + 'password' => 'secret', + 'className' => 'Smtp' + ]); + +Vous pouvez configurer des serveurs SMTP SSL, tels que GMail. Pour ce faire, +ajoutez le préfixe ``ssl://`` dans le nom d'hôte et configurez le numéro de port +de façon correspondante. Vous pouvez aussi activer le protocole SMTP TLS en +utilisant l'option ``tls``:: + + use Cake\Mailer\TransportFactory; + + TransportFactory::setConfig('gmail', [ + 'host' => 'smtp.gmail.com', + 'port' => 587, + 'username' => 'mon@gmail.com', + 'password' => 'secret', + 'className' => 'Smtp', + 'tls' => true + ]); + +La configuration ci-dessus active la communication TLS pour les emails. + +Pour configurer votre mailer pour qu'il utilise un transport spécifique, vous +pouvez utiliser la méthode :php:meth:`Cake\\Mailer\\Mailer::setTransport()` ou +placer le transport dans votre configuration:: + + // Utilise un transport nommé déjà configuré dans TransportFactory::setConfig() + $mailer->setTransport('gmail'); + + // Utilise un objet construit. + $mailer->setTransport(new \Cake\Mailer\Transport\DebugTransport()); + +.. warning:: + Pour que cela fonctionne avec Google, vous devrez activer l'accès aux + applications moins sécurisées: + `Allowing less secure apps to access your account `__. + +.. note:: +   `Configuration SMTP Gmail `__. + +.. note:: + Pour utiliser SSL et SMTP, SSL devra être configuré dans votre installation + PHP. + +Les options de configuration peuvent aussi être fournies en chaîne :term:`DSN`. +C'est utile quand vous travaillez avec des variables d'environnement ou des +fournisseurs :term:`PaaS`:: + + TransportFactory::setConfig('default', [ + 'url' => 'smtp://mon@gmail.com:secret@smtp.gmail.com:587?tls=true', + ]); + +Quand vous utilisez une chaîne DSN, vous pouvez définir d'autres paramètres ou +options en tant qu'arguments query string. + +.. php:staticmethod:: drop($key) + +Une fois configurés, les transports ne peuvent plus être modifiés. Pour modifier +un transport, vous devez d'abord le supprimer puis le reconfigurer. + +Créer des Transports Personnalisés +---------------------------------- + +Vous pouvez créer vos propres transports pour des situations telles que l'envoi +d'emails par des services comme SendGrid, MailGun ou Postmark. Pour créer votre +transport, commencez par créer le fichier +**src/Mailer/Transport/ExampleTransport.php** (où 'Example' est le nom de votre +transport). Au départ, votre fichier doit ressembler à cela:: + + namespace App\Mailer\Transport; + + use Cake\Mailer\AbstractTransport; + use Cake\Mailer\Message; + + class ExampleTransport extends AbstractTransport + { + public function send(Message $message): array + { + // Faire quelque chose. + } + } + +Vous devez implémenter la méthode ``send(Message $message)`` avec votre propre +logique. + +Envoyer des Emails sans utiliser Mailer +======================================= + +Le ``Mailer`` est une classe à haut niveau d'abstraction, qui agit commme un +pont entre les classes ``Cake\Mailer\Message``, ``Cake\Mailer\Renderer`` et +``Cake\Mailer\\AbstractTransport`` pour configuer les emails avec une interface +fluide. + +Si vous voulez, vous pouvez aussi utiliser ces classes directement avec le +Mailer. + +Par exemple:: + + $render = new \Cake\Mailer\Renderer(); + $render->viewBuilder() + ->setTemplate('perso') + ->setLayout('brillant'); + + $message = new \Cake\Mailer\Message(); + $message + ->setFrom('admin@cakephp.org') + ->setTo('user@foo.com') + ->setBody($render->render()); + + $transport = new \Cake\Mailer\Transport\MailTransport(); + $result = $transport->send($message); + +Vous pouvez aussi écarter le ``Renderer`` et définir directement le corps du +message avec les méthodes ``Message::setBodyText()`` et +``Message::setBodyHtml()``. + +.. _email-testing: + +Tester les Mailers +================== + +Pour tester les mailers, ajoutez ``Cake\TestSuite\EmailTrait`` à vos cas de +test. Le ``MailerTrait`` utilise les crochets de PHPUnit pour remplacer les +transports d'emails de votre application par un proxy qui intercepte les +messages et vous permet de faire des assertions sur le mail qui aurait été +envoyé. + +Pour commencer à tester les emails, ajoutez le trait à votre cas de test, et +chargez les routes au cas où vos emails auraient besoin de générer des URL:: + + namespace App\Test\TestCase\Mailer; + + use App\Mailer\WelcomeMailer; + use App\Model\Entity\User; + + use Cake\TestSuite\EmailTrait; + use Cake\TestSuite\TestCase; + + class WelcomeMailerTestCase extends TestCase + { + use EmailTrait; + + public function setUp(): void + { + parent::setUp(); + $this->loadRoutes(); + } + } + +Supposons maintenant que nous ayons un mailer qui consiste à envoyer des emails +de bienvenue quand un nouvel utilisateur s'inscrit. Nous voulons vérifier que le +sujet et le corps du message contiennent le nom de l'utilisateur:: + + // dans notre classe WelcomeMailerTestCase. + public function testName() + { + $user = new User([ + 'name' => 'Alice Alittea', + 'email' => 'alice@example.org', + ]); + $mailer = new WelcomeMailer(); + $mailer->send('welcome', [$user]); + + $this->assertMailSentTo($user->email); + $this->assertMailContainsText('Bonjour ' . $user->name); + $this->assertMailContainsText('Bienvenue dans CakePHP!'); + } + +Méthodes d'Assertion +-------------------- + +Le trait ``Cake\TestSuite\EmailTrait`` fournit les assertions suivantes:: + + // Un nombre précis d'emails ont été envoyés + $this->assertMailCount($count); + + // Aucun email n'a été envoyé + $this->assertNoMailSent(); + + // Un email a été envoyé à une certaine adresse + $this->assertMailSentTo($address); + + // Un email a été envoyé par une certaine adresse + $this->assertMailSentFrom($emailAddress); + $this->assertMailSentFrom([$emailAddress => $displayName]); + + // Un email contient un certain contenu + $this->assertMailContains($contents); + + // Un email contient un certain contenu HTML + $this->assertMailContainsHtml($contents); + + // Un email contient un certain contenu en texte brut + $this->assertMailContainsText($contents); + + // Un email contient une certaine valeur dans un getter de Message (par exemple "subject") + $this->assertMailSentWith($expected, $parameter); + + // L'email à l'index spécifié a été envoyé à une certaine adresse + $this->assertMailSentToAt($at, $address); + + // L'email à l'index spécifié a été envoyé depuis une certaine adresse + $this->assertMailSentFromAt($at, $address); + + // L'email à l'index spécifié contient un certain contenu + $this->assertMailContainsAt($at, $contents); + + // L'email à l'index spécifié contient un certain contenu HTML + $this->assertMailContainsHtmlAt($at, $contents); + + // L'email à l'index spécifié contient un certain contenu en texte brut + $this->assertMailContainsTextAt($at, $contents); + + // Un email contient une certaine pièce jointe + $this->assertMailContainsAttachment('test.png'); + + // L'email à l'index spécifié contient une certaine valeur dans un getter de Message (par example, "cc") + $this->assertMailSentWithAt($at, $expected, $parameter); + + // Le sujet d'un email contient un certain texte + $this->assertMailSubjectContains('Offre gratuite'); + + // L'email à l'index spécifié a un sujet qui contient un certain texte + $this->assertMailSubjectContainsAt(1, 'Offre gratuite'); .. meta:: - :title lang=fr: Email + :title lang=fr: Mailer :keywords lang=fr: envoyer mail,email emmetteur sender,envelope sender,classe php,database configuration,sending emails,meth,shells,smtp,transports,attributes,array,config,flexibilité,php email,nouvel email,sending email,models diff --git a/fr/development/testing.rst b/fr/development/testing.rst index f62d860478..3b90417638 100755 --- a/fr/development/testing.rst +++ b/fr/development/testing.rst @@ -1,21 +1,21 @@ Testing ####### -CakePHP fournit un support de test intégré compréhensible. CakePHP permet +CakePHP fournit un support de test intégré complet. CakePHP permet l'intégration de `PHPUnit `_. En plus de toutes les fonctionnalités offertes par PHPUnit, CakePHP offre quelques fonctionnalités supplémentaires pour faciliter le test. Cette section va couvrir l'installation -de PHPUnit, comment commencer avec le Test Unitaire, et comment vous pouvez -utiliser les extensions que CakePHP offre. +de PHPUnit, comment démarrer avec les Tests Unitaires, et comment utiliser les +extensions offertes par CakePHP. Installer PHPUnit ================= -CakePHP utilise PHPUnit comme framework de test sous-jacent. PHPUnit est le -standard de-facto pour le test unitaire dans PHP. Il offre un ensemble de +CakePHP utilise PHPUnit comme framework de test sous-jacent. PHPUnit est *de +facto* le standard des tests unitaires en PHP. Il offre un ensemble de fonctionnalités profondes et puissantes pour s'assurer que votre code fait ce -que vous pensez qu'il doit faire. PHPUnit peut être installé avec le `PHAR -package `__ ou avec +que vous pensez qu'il fait. PHPUnit peut être installé soit avec le `package PHAR +`__, soit avec `Composer `_. Installer PHPUnit avec Composer @@ -27,8 +27,9 @@ Pour installer PHPUnit avec Composer: $ php composer.phar require --dev phpunit/phpunit:"^8.5" -Ceci va ajouter la dépendance à la section ``require-dev`` de votre -``composer.json``, et ensuite installer PHPUnit avec vos autres dépendances. +Cela va ajouter la dépendance à la section ``require-dev`` de votre +``composer.json``, et ensuite installer PHPUnit au même endroit que vos autres +dépendances. Vous pouvez maintenant lancer PHPUnit en utilisant: @@ -63,9 +64,9 @@ lancer vos tests: Tester la Configuration de la Base de Données ============================================= -Souvenez-vous qu'il faut avoir debug activé dans votre fichier -**config/app_local.php** avant de lancer des tests. Vous devrez aussi vous assurer -d'ajouter une configuration de base de données ``test`` dans **config/app_local.php**. +Pensez à activer le débogage dans votre fichier **config/app_local.php** avant +de lancer des tests. Vous devrez aussi vous assurer d'avoir ajouté une +configuration de base de données ``test`` dans **config/app_local.php**. Cette configuration est utilisée par CakePHP pour les tables de fixture et les données:: @@ -83,13 +84,14 @@ données:: .. note:: C'est une bonne idée de faire une base de données de test différente de - votre base de données actuelle. Cela évitera toute erreur embarrassante - pouvant arriver plus tard. + votre base de données actuelle. Cela évitera les erreurs embarrassantes qui + ne manqueront pas d'arriver. Vérifier la Configuration Test ============================== -Après avoir installé PHPUnit et configuré la configuration de la base de données de ``test``, vous pouvez vous assurer que vous êtes prêt à écrire et lancer +Après avoir installé PHPUnit et configuré la configuration de la base de données +de ``test``, vous pouvez vous assurer que vous êtes prêt à écrire et lancer vos propres tests en lançant un de ceux présents dans le cœur: .. code-block:: console @@ -100,9 +102,9 @@ vos propres tests en lançant un de ceux présents dans le cœur: # Pour un PHPUnit installé avec Composer $ vendor/bin/phpunit -Ce qui est au-dessus va lancer tous les tests que vous avez, ou vous indiquer +Ceci va lancer tous les tests que vous avez, ou vous indiquer qu'aucun test n'a été lancé. Pour lancer un test spécifique, vous pouvez fournir -le chemin au test en paramètre de PHPUnit. Par exemple, si vous aviez un cas +le chemin du test en paramètre à PHPUnit. Par exemple, si vous aviez un cas de test pour la classe ArticlesTable, vous pourriez le lancer avec: .. code-block:: console @@ -125,7 +127,7 @@ conventions. En ce qui concerne les tests: #. Les fichiers PHP contenant les tests doivent être dans votre répertoire ``tests/TestCase/[Type]``. -#. Les noms de ces fichiers doivent finir par **Test.php** plutôt que juste +#. Les noms de ces fichiers doivent finir par **Test.php** et pas seulement **.php**. #. Les classes contenant les tests doivent étendre ``Cake\TestSuite\TestCase``, ``Cake\TestSuite\IntegrationTestCase`` ou ``\PHPUnit\Framework\TestCase``. @@ -141,7 +143,7 @@ Créer Votre Premier Cas de Test =============================== Dans l'exemple suivant, nous allons créer un cas de test pour une méthode de -helper très simple. Le helper que nous allons tester sera le formatage d'une +helper très simple. Le helper que nous allons tester fomatera une barre de progression HTML. Notre helper ressemblera à cela:: namespace App\View\Helper; @@ -160,11 +162,11 @@ barre de progression HTML. Notre helper ressemblera à cela:: } } -C'est un exemple très simple, mais ce sera utile pour montrer comment vous -pouvez créer un cas de test simple. Après avoir créé et sauvegardé notre +C'est un exemple très simple, mais ce sera utile pour montrer comment +créer un cas de test simple. Après avoir créé et sauvegardé notre helper, nous allons créer le fichier de cas de tests dans **tests/TestCase/View/Helper/ProgressHelperTest.php**. Dans ce fichier, nous -allons commencer avec ce qui suit:: +allons commencer avec ceci:: namespace App\Test\TestCase\View\Helper; @@ -188,9 +190,9 @@ allons commencer avec ce qui suit:: Nous compléterons ce squelette dans une minute. Nous avons ajouté deux méthodes pour commencer. Tout d'abord ``setUp()``. Cette méthode est appelée avant chaque méthode de *test* dans une classe de cas de test. -Les méthodes de configuration devraient initialiser les objets souhaités -pour le test, et faire toute configuration souhaitée. Dans notre configuration -nous ajouterons ce qui suit:: +Ces méthodes d'initialisation devraient initialiser les objets dont nous aurons +besoin pour le test, et faire toute configuration nécessaire. Dans notre méthode +d'initialisation, nous allons ajouter ce code:: public function setUp(): void { @@ -200,11 +202,11 @@ nous ajouterons ce qui suit:: } Appeler la méthode parente est importante dans les cas de test, puisque -``TestCase::setUp()`` fait un certain nombre de choses comme fabriquer les +``TestCase::setUp()`` fait un certain nombre de choses comme sauvegarder les valeurs dans :php:class:`~Cake\\Core\\Configure` et stocker les chemins dans :php:class:`~Cake\\Core\\App`. -Ensuite, nous allons remplir les méthodes de test. Nous utiliserons quelques +Ensuite, nous allons remplir la méthode de test. Nous utiliserons quelques assertions pour nous assurer que notre code crée la sortie que nous attendons:: public function testBar(): void @@ -217,35 +219,35 @@ assertions pour nous assurer que notre code crée la sortie que nous attendons:: $this->assertStringContainsString('width: 33%', $result); } -Le test ci-dessus est simple mais montre le potentiel bénéfique de l'utilisation +C'est un test simple mais il montre le bénéfice potentiel de l'utilisation des cas de test. Nous utilisons ``assertStringContainsString()`` pour nous assurer que notre helper retourne une chaîne qui contient le contenu que nous attendons. Si le résultat ne contient pas le contenu attendu le test sera un échec, et nous savons que notre code est incorrect. En utilisant les cas de test, vous pouvez décrire la relation entre un ensemble -d'entrées connues et leur sortie attendue. Cela vous aide à être plus confiant -sur le code que vous écrivez puisque vous pouvez vérifier que le code que vous -écrivez remplit les attentes et les assertions que vos tests font. De plus, -puisque les tests sont du code, ils peuvent être re-lancés dès que vous faîtes -un changement. Cela évite la création de nouveaux bugs. +d'entrées connues et leur sortie attendue. Cela vous rend plus confiant dans +le code que vous écrivez puisque vous pouvez vérifier que le code que vous avez +déjà écrit remplit les attentes et les assertions vérifiées dans vos tests. De +plus, puisque les tests sont du code, ils peuvent être re-lancés à chaque +changement. Cela évite la création de nouveaux bugs. .. note:: L'EventManager est remis à blanc pour chaque méthode de test. Cela signifie - que lorsque vous lancez plusieurs tests ensemble, vous perdez les écouteurs - d'events qui ont été enregistrés dans config/bootstrap.php puisque le - bootstrap n'est exécuté qu'une seule fois. + que lorsque vous lancez plusieurs tests en une fois, vous perdez les + écouteurs d'events qui ont été enregistrés dans config/bootstrap.php puisque + le bootstrap n'est exécuté qu'une seule fois. .. _running-tests: Lancer les Tests ================ -Une fois que vous avez installé PHPUnit et que quelques cas de tests sont -écrits, vous pouvez lancer les cas de test très fréquemment. C'est une -bonne idée de lancer les tests avant de committer tout changement pour aider -à s'assurer que vous n'avez rien cassé. +Une fois que vous aurez installé PHPUnit et écrit quelques cas de tests, vous +voudrez probablement lancer les cas de test très fréquemment. C'est une +bonne idée de lancer les tests avant de committer chaque changement pour aider +à vous assurer que vous n'avez rien cassé. En utilisant ``phpunit``, vous pouvez lancer les tests de votre application. Pour lancer vos tests d'application, vous pouvez simplement lancer: @@ -256,7 +258,8 @@ Pour lancer vos tests d'application, vous pouvez simplement lancer: php phpunit.phar -Si vous avez cloné la `source de CakePHP à partir de GitHub `__ +Si vous avez cloné le `code source de CakePHP à partir de GitHub +`__ et que vous souhaitez exécuter les tests unitaires de CakePHP, n'oubliez pas d'exécuter la commande suivante de ``Composer`` avant de lancer ``phpunit`` pour que toutes les dépendances soient installées: @@ -266,8 +269,8 @@ que toutes les dépendances soient installées: composer install À partir du répertoire racine de votre application. Pour lancer les tests pour -un plugin qui fait parti de la source de votre application, d'abord faîtes la -commande ``cd`` vers le répertoire du plugin, ensuite utilisez la commande +un plugin qui fait partie de la source de votre application, appliquez d'abord +la commande ``cd`` vers le répertoire du plugin, puis utilisez la commande ``phpunit`` qui correspond à la façon dont vous avez installé phpunit: .. code-block:: console @@ -288,12 +291,12 @@ projet dans un répertoire séparé et installer ses dépendances: php ~/composer.phar install php ~/phpunit.phar -Filtrer les Cas de Test (TestCase) ----------------------------------- +Filtrer les Cas de Test +----------------------- -Quand vous avez des cas de test plus larges, vous pouvez lancer un -sous-ensemble de méthodes de test quand vous essayez de travailler sur un -cas unique d'échec. Avec l'exécuteur CLI vous pouvez utiliser une option pour +Quand vous avez de nombreux cas de test et que vous travaillez sur un seul test +qui échoue, vous préférerez lancer seulement une partie des méthodes de test. +Avec l'exécuteur en ligne de commande vous pouvez utiliser une option pour filtrer les méthodes de test: .. code-block:: console @@ -306,18 +309,18 @@ casse pour filtrer les méthodes de test à lancer. Générer une Couverture de Code (Code Coverage) ---------------------------------------------- -Vous pouvez générer un rapport de couverture de code en une ligne de -commande en utilisant les outils de couverture de code intégrés à PHPUnit. -PHPUnit va générer un ensemble de fichiers en HTML statique contenant les -résultats de la couverture. Vous pouvez générer une couverture pour un cas de -test en faisant ce qui suit: +Vous pouvez générer un rapport de couverture de code depuis la ligne de commande +en utilisant les outils de couverture de code intégrés à PHPUnit. +PHPUnit va générer un ensemble de fichiers statiques en HTML contenant les +résultats de couverture. Vous pouvez générer un rapport de couverture pour un +seul cas de test de la façon suivante: .. code-block:: console $ phpunit --coverage-html webroot/coverage tests/TestCase/Model/Table/ArticlesTableTest -Cela mettra la couverture des résultats dans le répertoire webroot de votre -application. Vous pourrez voir les résultats en allant à +Cela placera le résultat de couverture de code dans le répertoire webroot de +votre application. Vous pourrez voir les résultats en consultant ``http://localhost/votre_app/coverage``. Vous pouvez aussi utiliser ``phpdbg`` pour générer la couverture des résultats à @@ -331,78 +334,79 @@ des rapports de couverture: Combiner les Suites de Test pour les Plugins -------------------------------------------- -Souvent, votre application sera composé de plusieurs plugins. Dans ces +Souvent, votre application sera composée de plusieurs plugins. Dans ces situations, il peut être assez fastidieux d'effectuer des tests pour chaque -plugin. Vous pouvez faire des tests pour chaque plugin qui compose votre -application en ajoutant une section ```` supplémentaire au fichier -``phpunit.xml.dist`` de votre application: +plugin. Vous pouvez faire lancer des tests pour chaque plugin qui compose votre +application en ajoutant une section ```` supplémentaire dans le +fichier ``phpunit.xml.dist`` de votre application: .. code-block:: xml - + ./tests/TestCase - - ./plugins/Forum/tests/TestCase + + ./plugins/Forum/tests/TestCase/ -Les tests supplémentaires ajoutés à l'élément ```` seront exécutés -automatiquement quand quand vous utiliserez ``phpunit``. +Les suites de tests supplémentaires ajoutées à l'élément ```` seront +exécutées automatiquement quand vous utiliserez ``phpunit``. -Si vous utilisez ```` pour utiliser les fixtures à partir des -plugins que vous avez installé avec composer, le fichier ``composer.json`` du -plugin doit ajouter le namespace de la fixture à la section autoload. Exemple:: +Si vous vous servez de ```` pour utiliser des fixtures à partir de +plugins que vous avez installés avec composer, le fichier ``composer.json`` du +plugin doit ajouter le namespace de la fixture dans la section autoload. +Par exemple:: - "autoload": { + "autoload-dev": { "psr-4": { - "PluginName\\Test\\Fixture\\": "tests\\Fixture" + "PluginName\\Test\\Fixture\\": "tests/Fixture/" } }, -Les Callbacks du Cycle de Vie des Cas de Test -============================================= +Callbacks du Cycle de Vie des Cas de Test +========================================= -Les cas de Test ont un certain nombre de callbacks de cycle de vie que vous -pouvez utiliser quand vous faîtes les tests: +Les cas de test de nombreux callbacks que vous pouvez utiliser quand pendant les +tests: * ``setUp`` est appelé avant chaque méthode de test. Doit être utilisé pour - créer les objets qui vont être testés, et initialiser toute donnée pour le - test. Toujours se rappeler d'appeler ``parent::setUp()``. -* ``tearDown`` est appelé après chaque méthode de test. Devrait être utilisé - pour nettoyer une fois que le test est terminé. Toujours se rappeler - d'appeler ``parent::tearDown()``. -* ``setupBeforeClass`` est appelé une fois avant que les méthodes de test - aient commencées dans un cas. Cette méthode doit être *statique*. -* ``tearDownAfterClass`` est appelé une fois après que les méthodes de test - ont commencé dans un cas. Cette méthode doit être *statique*. + créer les objets qui vont être testés, et initialiser les données pour le + test. Rappelez-vous de toujours appeler ``parent::setUp()``. +* ``tearDown`` est appelé après chaque méthode de test. Doit être utilisé pour + tout nettoyer une fois que le test est terminé. Rappelez-vous de toujours + appeler ``parent::tearDown()``. +* ``setupBeforeClass`` est appelé une fois dans chaque cas de test avant que les + méthodes de test aient démarré. Cette méthode doit être *statique*. +* ``tearDownAfterClass`` est appelé une fois dans chaque cas de test après que + les méthodes de test aient démarré. Cette méthode doit être *statique*. .. _test-fixtures: Fixtures ======== -Quand on teste du code qui dépend de models et d'une base de données, on -peut utiliser les **fixtures** comme une façon de créer un état initial pour les -tests de votre application. En utilisant les données de fixture vous réduiser -les étapes de configuration répétitives dans vos tests. Les fixtures sont bien -adaptées pour des données communes, ou partagées entre de nombreux tests, voire -tous. Les données qui ne sont utiles que dans quelques tests devraient plutôt -être crées dans les tests qui en ont besoin. +Pour tester du code qui dépend de models et d'une base de données, il est +possible d'utiliser les **fixtures** comme une façon de créer un état initial +pour les tests de votre application. En utilisant des données de fixture, vous +réduisez des étapes de configuration répétitives dans vos tests. Les fixtures +sont bien adaptées pour des données qui sont communes, ou partagées entre de +nombreux tests, voire tous. Les données qui ne sont utiles que dans quelques +tests devraient plutôt être créées dans les tests qui en ont besoin. CakePHP utilise la connexion nommée ``test`` dans votre fichier de configuration -**config/app.php**. Si la connexion n'est pas utilisable, une exception -sera levée et vous ne pourrez pas utiliser les fixtures de la base de données. +**config/app.php**. Si cette connexion n'est pas utilisable, une exception +sera levée et vous ne pourrez pas utiliser les fixtures de base de données. -CakePHP effectue ce qui suit pendant le déroulement d'un test: +CakePHP accomplit les étapes suivantes pendant le déroulement d'un test: -#. Crée les tables pour chacune des fixtures nécessaires. -#. Remplit les tables avec les données. -#. Lance les méthodes de test. -#. Vide les tables de fixture. +#. Création des tables pour chacune des fixtures nécessaires. +#. Remplissage des tables avec des données. +#. Lancement des méthodes de test. +#. Vidage des tables de fixture. Le schéma pour les fixtures est créé au début d'un test par des migrations ou un dump SQL. @@ -411,21 +415,22 @@ Connexions de Test ------------------ Par défaut, CakePHP va faire un alias pour chaque connexion de votre -application. Chaque connexion définie dans le bootstrap de votre application qui -ne commence pas par ``test_``, va avoir un alias avec le prefix ``test_`` créé. -Les alias de connexion assurent que vous n'utiliserez pas accidentellement la -mauvaise connexion dans les cas de test. Les alias de connexion sont -transparents pour le reste de votre application. Par exemple, si vous utilisez -la connexion 'default', à la place, vous obtiendrez la connexion ``test`` dans -les cas de test. Si vous utilisez la connexion 'replica', la suite de tests va -tenter d'utiliser 'test_replica'. +application. Pour chaque connexion définie dans le bootstrap de votre +application qui ne commence pas par ``test_``, un alias va être créé avec le +préfixe ``test_``. +Le fait d'ajouter des alias de connexions garantit que vous n'utiliserez pas +accidentellement la mauvaise connexion dans les cas de test. Les alias de +connexions sont transparents pour le reste de votre application. Par exemple, si +vous utilisez la connexion 'default', vous obtiendrez à la place la connexion +``test`` dans les cas de test. Si vous utilisez la connexion 'replica', la suite +de tests tentera d'utiliser 'test_replica'. .. _fixture-phpunit-configuration: Configuration de PHPUnit ------------------------ -Avant de pouvoir utiliser les fixtures vous devez vous assurer que votre +Avant d'utiliser les fixtures vous devez vous assurer que votre ``phpunit.xml`` contienne l'extension fixture: .. code-block:: xml @@ -531,7 +536,7 @@ Gestionnaires d'Etat des Fixtures --------------------------------- Par défaut, CakePHP réinitialise l'état des fixtures à la fin de chaque test en -tronquant toutes les tabes dans la base de données. Cette opération peut devenir +tronquant toutes les tables dans la base de données. Cette opération peut devenir coûteuse quand votre application grossit. Si vous utilisez ``TransactionStrategy``, chaque méthode de test sera lancée à l'intérieur d'une transaction suivie d'un rollback à la fin du test. Cela peut améliorer vos @@ -549,9 +554,8 @@ du test:: class ArticlesTableTest extends TestCase { /** - * Crée la stratégie de fixation utilisée pour ce cas de test. - * Vous pouvez utiliser une classe/un trait de base pour modifier - * plusieurs classes. + * Crée la stratégie de fixtures utilisée pour ce cas de test. + * Vous pouvez utiliser une classe/un trait pour modifier plusieurs classes. */ protected function getFixtureStrategy(): FixtureStrategyInterface { @@ -583,22 +587,22 @@ Créez un fichier nommé **ArticlesFixture.php** dans votre répertoire public $records = [ [ - 'title' => 'First Article', - 'body' => 'First Article Body', + 'title' => 'Premier Article', + 'body' => 'Contenu du premier Article', 'published' => '1', 'created' => '2007-03-18 10:39:23', 'modified' => '2007-03-18 10:41:31' ], [ - 'title' => 'Second Article', - 'body' => 'Second Article Body', + 'title' => 'Deuxième Article', + 'body' => 'Contenu du deuxième Article', 'published' => '1', 'created' => '2007-03-18 10:41:23', 'modified' => '2007-03-18 10:43:31' ], [ - 'title' => 'Third Article', - 'body' => 'Third Article Body', + 'title' => 'Troisième Article', + 'body' => 'Contenu du troisième Article', 'published' => '1', 'created' => '2007-03-18 10:43:23', 'modified' => '2007-03-18 10:45:31' @@ -608,25 +612,25 @@ Créez un fichier nommé **ArticlesFixture.php** dans votre répertoire .. note:: - Il est recommandé de ne pas ajouter manuellement les valeurs aux colonnes - qui s'incrémentent automatiquement car cela interfère avec la génération + Il est recommandé de ne pas ajouter manuellement des valeurs dans les + colonnesavec autoincrément car cela interfère avec la génération de séquence dans PostgreSQL et SQLServer. La propriété ``$connection`` définit la source de données que la fixture va utiliser. Si votre application utilise plusieurs sources de données, vous devriez faire correspondre les fixtures avec les sources de données du model, -mais préfixé avec ``test_``. +en ajoutant le préfixe ``test_``. Par exemple, si votre model utilise la source de données ``mydb``, votre fixture devra utiliser la source de données ``test_mydb``. Si la connexion -``test_mydb`` n'existe pas, vos models vont utiliser la source de données -``test`` par défaut. Les sources de données de fixture doivent être préfixées -par ``test`` pour réduire la possibilité de trucher accidentellement toutes -les données de votre application quand vous lancez des tests. - -Nous pouvons définir un ensemble d'enregistrements qui seront remplis après que -la table de fixture est créée. Le format est assez simple, ``$records`` est un -tableau d'enregistrements. Chaque item dans ``$records`` doit être -un enregistrement (une seule ligne). A l'intérieur de chaque ligne, il doit y +``test_mydb`` n'existe pas, vos models utiliseront la source de données +``test`` par défaut. Les sources de données des fixtures doivent être préfixées +par ``test`` pour réduire la possibilité de tronquer accidentellement toutes +les données de votre application en lançant vos tests. + +Nous pouvons définir un ensemble d'enregistrements qui seront insérés après la +création de la table de fixture. Le format parle de lui-même. ``$records`` est +un tableau d'enregistrements. Chaque item dans ``$records`` correspond à +un enregistrement (une seule ligne). À l'intérieur de chaque ligne, il doit y avoir un tableau associatif des colonnes et valeurs pour la ligne. Gardez juste à l'esprit que tous les enregistrements dans le tableau ``$records`` doivent avoir les mêmes clés car les lignes sont insérées en une seule requête SQL. @@ -654,8 +658,8 @@ méthode ``init()`` des fixtures:: { $this->records = [ [ - 'title' => 'First Article', - 'body' => 'First Article Body', + 'title' => 'Premier Article', + 'body' => 'Contenu du premier Article', 'published' => '1', 'created' => date('Y-m-d H:i:s'), 'modified' => date('Y-m-d H:i:s'), @@ -669,35 +673,22 @@ méthode ``init()`` des fixtures:: Quand vous surchargez ``init()``, rappelez-vous juste de toujours appeler ``parent::init()``. -Charger les Fixtures dans vos Tests (TestCase) ----------------------------------------------- +Charger les Fixtures dans vos Tests +----------------------------------- -Après avoir créé vos fixtures, vous pouvez les utiliser dans vos cas de test. -Dans chaque cas de test vous devriez charger les fixtures dont vous aurez -besoin. Vous devriez charger une fixture pour chaque model qui aura une requête -lancée contre elle. Pour charger les fixtures, vous définissez la propriété +Une fois que vous avez créé vos fixtures, vous pouvez les utiliser dans vos cas +de test. Vous devez charger dans chaque cas de test les fixtures dont vous aurez +besoin. Vous devriez charger une fixture pour chaque model sur lequel une +requête sera exécutée. Pour charger les fixtures, définissez la propriété ``$fixtures`` dans votre model:: class ArticleTest extends TestCase { - protected $fixtures = ['app.articles', 'app.comments']; - } - -Ce qui est au-dessus va charger les fixtures d'Article et de Comment à partir -du répertoire de fixture de l'application. Vous pouvez aussi charger les -fixtures à partir du cœur de CakePHP ou des plugins:: - - class ArticlesTest extends TestCase - { - public $fixtures = ['plugin.DebugKit.articles', 'plugin.MyVendorName/MyPlugin.messages', 'core.comments']; + protected $fixtures = ['app.Articles', 'app.Comments']; } -Utiliser le préfixe ``core`` va charger les fixtures à partir de CakePHP, et -utiliser un nom de plugin en préfixe chargera la fixture à partir d'un plugin -nommé. - À partir de 4.1.0 vous pouvez utiliser ``getFixtures()`` pour définir votre -liste de fixtures avec une méthode:: +liste de fixtures depuis une méthode:: public function getFixtures(): array { @@ -705,46 +696,62 @@ liste de fixtures avec une méthode:: 'app.Articles', 'app.Comments', ]; + } + +Ceci va charger les fixtures d'Article et de Comment à partir du répertoire +Fixture de votre application. Vous pouvez aussi charger des fixtures du cœur de +CakePHP ou des plugins:: + class ArticlesTest extends TestCase + { + protected $fixtures = [ + 'plugin.DebugKit.Articles', + 'plugin.MonNomDeVendor/MonPlugin.Messages', + 'core.Comments' + ]; } -Vous pouvez charger les fixtures dans les sous-répertoires. -Utiliser plusieurs répertoires peut faciliter l'organisation de vos fixtures si -vous avez une application plus grande. Pour charger les fixtures dans les -sous-répertoires, incluez simplement le nom du sous-répertoire dans le nom de -la fixture:: +Utiliser le préfixe ``core`` va charger des fixtures de CakePHP, et utiliser un +nom de plugin en préfixe chargera la fixture à partir de ce plugin. - class ArticlesTableTest extends CakeTestCase +Vous pouvez charger les fixtures dans des sous-répertoires. Si votre application +est volumineuse, il est plus facile d'organiser vos fixtures en utilisant +plusieurs répertoires. Pour charger des fixtures dans des sous-répertoires, +incluez simplement le nom du sous-répertoire dans le nom de la fixture:: + + class ArticlesTest extends CakeTestCase { - protected $fixtures = ['app.blog/articles', 'app.blog/comments']; + protected $fixtures = ['app.Blog/Articles', 'app.Blog/Comments']; } Dans l'exemple ci-dessus, les deux fixtures seront chargées à partir de -``tests/Fixture/blog/``. +``tests/Fixture/Blog/``. Fixture Factories ----------------- -Le nombre et la taille de vos fixtures vont croissantes avec la taille votre application. Il est possible qu'à -un certain point, vous ne soyez plus en mesure les maintenir. +Le nombre et la taille de vos fixtures vont croissant avec la taille votre +application. Il est possible qu'à un certain point, vous ne soyez plus en mesure +de les maintenir et de suivre leur contenu. -Le `fixture factories plugin `__ propose une -alternative efficace pour des applications de taille moyenne et plus. +Le `plugin fixture factories `__ propose une +alternative efficace pour des applications de grande taille. -Le plugin utilise le `test suite light plugin `__ -afin de tronquer les tables non vierges avant chaque test. +Le plugin utilise le `plugin test suite light `__ +pour tronquer avant chaque test toutes les tables modifiées. -La commande bake suivante vous assistera pour créer vos factories:: +Cette commande bake vous aidera créer vos factories:: bin/cake bake fixture_factory -h Une fois vos factories `mises en place `__, -vous voilà équipés pour créer vos fixtures de test à vitesse folle. +vous voilà équipés pour créer vos fixtures de test à une vitesse folle. -Les intéractions non nécessaires avec la base de donnée ralentissent les tests, ainsi que votre application. -Il est possible de créer des fixtures sans les insérer. Ceci est utile lorsque vous testez des méthodes -qui n'intéragissent pas avec la base de donnée:: +Les interactions inutiles avec la base de donnée vont ralentir les tests ainsi +que votre application. +Il est possible de créer des fixtures sans les insérer. Ceci est utile lorsque +vous testez des méthodes qui n'interagissent pas avec la base de donnée:: $article = ArticleFactory::make()->getEntity(); @@ -752,20 +759,22 @@ Pour insérer dans la base de donnée:: $article = ArticleFactory::make()->persist(); -En supposant que les articles appartiennent à plusieurs auteurs, il est possible de créer 5 articles ayant chacun -2 auteurs de la manière suivante:: +Les factories peuvent aussi aider à créer des fixtures associées. +En supposant que les *Articles belongs to many Authors*, il est possible de +créer 5 articles ayant chacun 2 auteurs de la manière suivante:: $articles = ArticleFactory::make(5)->with('Authors', 2)->getEntities(); -Notez que bien que les factories ne nécessitent ni la création, ni la déclaration de fixtures, elles sont -parfaitement compatibles avec ces dernières. Pour plus de détails, +Notez que bien que les factories ne nécessitent ni création, ni déclaration de +fixtures. Elles sont toujours parfaitement compatibles avec les fixtures qui +viennent de CakePHP. Pour plus de détails, rendez-vous `ici `_. Charger des Routes dans les Tests --------------------------------- -Si vous testez des mailers, des composants de controllers ou d'autres classes -qui ont besoin de routes et qui résolvent des URLs, vous aurez besoin de charger +Si vous testez des mailers, des components de controllers ou d'autres classes +qui ont besoin de routes et de résoudre des URLs, vous aurez besoin de charger des routes. Pendant le ``setUp()`` d'une classe ou pendant les méthodes de tests individuelles vous pouvez utiliser ``loadRoutes()`` pour vous assurer que les routes de votre application sont chargées:: @@ -778,7 +787,7 @@ routes de votre application sont chargées:: Cette méthode construira une instance de votre ``Application`` et appellera la méthode ``routes()`` dessus. Si votre classe ``Application`` a besoin de -paramètres de constructeur spécialisés, vous pouvez les fournir à +paramètres spécialisés dans le constructeur, vous pouvez les fournir dans ``loadRoutes($constructorArgs)``. Création de routes dans les tests @@ -836,11 +845,11 @@ tests:: // Tester la logique qui nécessite d'avoir chargé Company/Cms. } -Tester les Classes Table -======================== +Tester les Classes De Tables +============================ -Disons que nous avons déjà notre table Articles définie dans -**src/Model/Table/ArticlesTable.php**, qui ressemble à ceci:: +Supposons que nous avons déjà notre table Articles définie dans +**src/Model/Table/ArticlesTable.php**, et qu'elle ressemble à ceci:: namespace App\Model\Table; @@ -859,9 +868,8 @@ Disons que nous avons déjà notre table Articles définie dans } } -Nous voulons maintenant configurer un test qui va tester ce model tout -en utilisant les Fixtures pour garder nos Tests isolés. Créons un fichier -nommé **ArticlesTableTest.php** dans notre répertoire +Nous voulons maintenant configurer un test qui va tester cette classe de table. +Créons un fichier nommé **ArticlesTableTest.php** dans notre répertoire **tests/TestCase/Model/Table**, avec le contenu suivant:: namespace App\Test\TestCase\Model\Table; @@ -871,10 +879,10 @@ nommé **ArticlesTableTest.php** dans notre répertoire class ArticlesTableTest extends TestCase { - protected $fixtures = ['app.articles']; + protected $fixtures = ['app.Articles']; } -Dans notre variable de cas de test ``$fixtures``, nous définissons l'ensemble +Dans la variable ``$fixtures`` de notre cas de test, nous définissons l'ensemble des fixtures que nous utiliserons. Vous devriez vous rappeler d'inclure tous les fixtures sur lesquelles des requêtes vont être lancées. @@ -893,7 +901,7 @@ maintenant à ceci:: class ArticlesTableTest extends TestCase { - protected $fixtures = ['app.articles']; + protected $fixtures = ['app.Articles']; public function setUp(): void { @@ -907,9 +915,9 @@ maintenant à ceci:: $this->assertInstanceOf('Cake\ORM\Query', $query); $result = $query->enableHydration(false)->toArray(); $expected = [ - ['id' => 1, 'title' => 'First Article'], - ['id' => 2, 'title' => 'Second Article'], - ['id' => 3, 'title' => 'Third Article'] + ['id' => 1, 'title' => 'Premier Article'], + ['id' => 2, 'title' => 'Deuxième Article'], + ['id' => 3, 'title' => 'Troisième Article'] ]; $this->assertEquals($expected, $result); @@ -917,16 +925,16 @@ maintenant à ceci:: } Vous pouvez voir que nous avons ajouté une méthode appelée -``testFindPublished()``. Nous commençons par créer une instance de notre model -``Article``, et lançons ensuite notre méthode ``published()``. Dans -``$expected``, nous définissons ce que nous en attendons, ce qui devrait être le -résultat approprié (que nous connaissons depuis que nous avons défini les -enregistrements qui sont remplis initialement dans la table articles). Nous -testons que les résultats correspondent à nos attentes en utilisant la méthode -``assertEquals()``. Regardez la section sur les :ref:`running-tests` pour plus -d'informations sur la façon de lancer les cas de test. +``testFindPublished()``. Nous commençons par créer une instance de notre classe +``ArticleTable``, et lançons ensuite notre méthode ``find('published')``. Dans +``$expected``, nous définissons ce qui devrait être le résultat approprié (que +nous connaissons puisque nous avons défini les enregistrements qui sont remplis +initialement dans la table articles). Nous testons si les résultats +correspondent à nos attentes en utilisant la méthode ``assertEquals()``. +Lisez la section sur les :ref:`running-tests` pour plus d'informations sur la +façon de lancer les cas de test. -En utilisant les fixture factories, le test se présente ainsi:: +En utilisant les fixture factories, le test se présenterait ainsi:: namespace App\Test\TestCase\Model\Table; @@ -954,16 +962,17 @@ En utilisant les fixture factories, le test se présente ainsi:: } } -Aucune fixture n'est déclarée. Les 5 articles créés n'existeront que pour ce test. La méthode statique ``::find()`` -permet d'explorer la base de donnée sans évènements et sans recours à la table ``ArticlesTable``. +Aucune fixture n'a besoin d'être déclarée. Les 5 articles créés n'existeront que +dans ce test. La méthode statique ``::find()`` va requêter la base de donnée +sans utiliser la table ``ArticlesTable`` ni ses évènements. Méthodes de Mocking des Models ------------------------------ -Il y aura des fois où vous voudrez mocker les méthodes sur les models quand vous -les testez. Vous devrez utiliser ``getMockForModel`` pour créer les mocks de -test des models. Cela évite des problèmes avec les propriétés réfléchies que -les mocks normaux ont:: +Il y aura des fois où vous voudrez mocker les méthodes des models quand vous les +testez. Vous devrez utiliser ``getMockForModel`` pour créer des mocks de test +des classes de tables. Cela évite des problèmes avec les propriétés réfléchies +qu'ont les mocks normaux:: public function testSendingEmails(): void { @@ -981,24 +990,23 @@ Dans votre méthode ``tearDown()``, assurez-vous de retirer le mock avec ceci:: .. _integration-testing: -Test d'Intégrations des Controllers +Tests d'Intégration des Controllers =================================== Alors que vous pouvez tester les controllers de la même manière que les Helpers, Models et Components, CakePHP offre une classe spécialisée -``IntegrationTestCase``. L'utilisation de cette classe en tant que classe de -base pour les cas de test de votre controller vous permet de mettre en place des -tests d'intégration pour vos controllers. - -Si vous n'êtes pas familier avec les tests d'intégrations, il s'agit d'une -approche de test qui facilite le test de plusieurs éléments en même temps. Les -fonctionnalités de test d'intégration dans CakePHP simulent une requête HTTP à -traiter par votre application. Par exemple, tester vos controllers impactera -les Models, Components et Helpers qui auraient été invoqués suite à une requête -HTTP. Cela vous permet d'écrire des tests au plus haut niveau de votre -application en ayant un impact sur chacun de ses travaux. - -Disons que vous avez un controller typique ArticlesController, et son model +``IntegrationTestCase``. L'utilisation de ce trait dans vos cas de test de vos +controllers vous offre une interface de test de haut niveau. + +Si vous n'êtes pas familier avec les tests d'intégration, dites-vous qu'il +s'agit d'une approche de test qui permet de test de plusieurs éléments qui +fonctionnent de concert. Les fonctionnalités de test d'intégration dans CakePHP +simulent une requête HTTP à traiter par votre application. Par exemple, tester +vos controllers impactera également vos components, models et helpers qui +auraient été invoqués pour traiter la requête HTTP. Cela vous donne un test de +plus haut niveau sur votre application et tous ses composants. + +Supposons que vous ayez un controller typique ArticlesController, et son model correspondant. Le code du controller ressemble à ceci:: namespace App\Controller; @@ -1019,12 +1027,12 @@ correspondant. Le code du controller ressemble à ceci:: } } if (!empty($short)) { - $result = $this->Article->find('all', [ + $result = $this->Articles->find('all', [ 'fields' => ['id', 'title'] ]) ->all(); } else { - $result = $this->Article->find()->all(); + $result = $this->Articles->find()->all(); } $this->set([ @@ -1035,23 +1043,25 @@ correspondant. Le code du controller ressemble à ceci:: } Créez un fichier nommé **ArticlesControllerTest.php** dans votre répertoire -**tests/TestCase/Controller** et mettez ce qui suit à l'intérieur:: +**tests/TestCase/Controller** et mettez-y ce qui suit:: namespace App\Test\TestCase\Controller; use Cake\TestSuite\IntegrationTestTrait; - use Cake\TestSuite\IntegrationTestCase; + use Cake\TestSuite\TestCase; - class ArticlesControllerTest extends IntegrationTestCase + class ArticlesControllerTest extends TestCase { - protected $fixtures = ['app.articles']; + use IntegrationTestTrait; + + protected $fixtures = ['app.Articles']; public function testIndex(): void { $this->get('/articles'); $this->assertResponseOk(); - // D'autres asserts. + // D'autres assertions. } public function testIndexQueryData(): void @@ -1059,7 +1069,7 @@ Créez un fichier nommé **ArticlesControllerTest.php** dans votre répertoire $this->get('/articles?page=1'); $this->assertResponseOk(); - // D'autres asserts. + // D'autres assertions. } public function testIndexShort(): void @@ -1068,7 +1078,7 @@ Créez un fichier nommé **ArticlesControllerTest.php** dans votre répertoire $this->assertResponseOk(); $this->assertResponseContains('Articles'); - // D'autres asserts. + // D'autres assertions. } public function testIndexPostData(): void @@ -1076,23 +1086,23 @@ Créez un fichier nommé **ArticlesControllerTest.php** dans votre répertoire $data = [ 'user_id' => 1, 'published' => 1, - 'slug' => 'new-article', - 'title' => 'New Article', - 'body' => 'New Body' + 'slug' => 'nouvel-article', + 'title' => 'Nouvel Article', + 'body' => 'Nouveau Contenu' ]; $this->post('/articles', $data); - $this->assertResponseSuccess(); + $this->assertResponseSuccess(); $articles = $this->getTableLocator()->get('Articles'); $query = $articles->find()->where(['title' => $data['title']]); $this->assertEquals(1, $query->count()); } } -Cet exemple montre quelques méthodes d'envoi de requête et quelques -assertions qu'intègre ``IntegrationTestCase``. Avant de pouvoir utiliser les -assertions, vous aurez besoin de simuler une requête. Vous pouvez utiliser -l'une des méthodes suivantes pour simuler une requête: +Cet exemple montre quelques exemples de méthodes qui envoient des requêtes et +quelques assertions fournies par ``IntegrationTestTrait``. Avant de pouvoir +utiliser les assertions, vous aurez besoin de simuler une requête. Vous pouvez +utiliser l'une des méthodes suivantes pour envoyer une requête: * ``get()`` Envoie une requête GET. * ``post()`` Envoie une requête POST. @@ -1103,40 +1113,40 @@ l'une des méthodes suivantes pour simuler une requête: * ``head()`` Envoie une requête HEAD. Toutes les méthodes exceptées ``get()`` et ``delete()`` acceptent un second -paramètre qui vous permet de saisir le corps d'une requête. Après avoir émis -une requête, vous pouvez utiliser les différentes assertions que fournit -``IntegrationTestCase`` ou PHPUnit afin de vous assurer que votre requête -possède de correctes effets secondaires. +paramètre qui vous permet d'envoyer le corps d'une requête. Après avoir émis +une requête, vous pouvez utiliser les différentes assertions fournies par +``IntegrationTestTrait`` ou PHPUnit afin de vous assurer que votre requête a les +effets attendus. Configurer la Requête --------------------- -La classe ``IntegrationTestCase`` intègre de nombreux helpers pour configurer -les requêtes que vous allez envoyer à votre controller:: +Le trait ``IntegrationTestTrait`` comporte de nombreux helpers pour configurer +les requêtes que vous allez envoyer à l'application testée:: // Définit des cookies - $this->cookie('name', 'Uncle Bob'); + $this->cookie('name', 'Oncle Bob'); // Définit des données de session - $this->session(['Auth.User.id', 1]); + $this->session(['Auth.User.id' => 1]); // Configure les en-têtes $this->configRequest([ 'headers' => ['Accept' => 'application/json'] ]); -Les états de ces helpers définis par ces méthodes est remis à zéro dans la +Les états définis par les méthodes de cet utilitaire sont remis à zéro dans la méthode ``tearDown()``. .. _testing-authentication: -Tester des Actions Protégées par AuthComponent ----------------------------------------------- +Tester Des Actions Qui Nécessitent Une Authentification +------------------------------------------------------- Si vous utilisez ``AuthComponent``, vous aurez besoin de simuler les données de session utilisées par AuthComponent pour valider l'identité d'un utilisateur. -Pour ce faire, vous pouvez utiliser les méthodes de helper fournies par -``IntegrationTestCase``. En admettant que vous ayez un ``ArticlesController`` +Pour ce faire, vous pouvez utiliser les méthodes utilitaires fournies par +``IntegrationTestTrait``. En admettant que vous ayez un ``ArticlesController`` qui contient une méthode add, et que cette méthode nécessite une authentification, vous pourriez écrire les tests suivants:: @@ -1166,19 +1176,18 @@ authentification, vous pourriez écrire les tests suivants:: // Autres assertions. } -Test de l'Authentification stateless (sans état) et des APIs +Test de l'Authentification Stateless (sans état) et des APIs ------------------------------------------------------------ -Pour tester les APIs qui utilisent l'authentification stateless, vous pouvez, -comme pour l'authentification basic, configurer la demande de manière à ce -qu'elle injecte des variables d'environnement et des headers (en-têtes), ce qui -permettra de simuler les en-têtes d'une demande d'authentification réelle. +Pour tester les APIs qui utilisent une authentification stateless, telles que +l'authentification Basic, vous pouvez configurer la requête de manière à y +injecter des variables d'environnement et des en-têtes qui vont simuler de +vraies en-têtes d'authentification. -Lorsque vous testez l'authentification simple (Basic) ou de type "Digest", vous -pouvez ajouter les variables d'environnement que PHP crée -` `_ automatiquement. -Ces variables d'environnement utilisées dans l'adaptateur d'authentification sont -décrites dans: ref: `basic-authentication` :: +Lorsque vous testez les authentifications Basic ou Digest, vous pouvez ajouter +les variables d'environnement `créées automatiquement par PHP `_. +Ces variables d'environnement utilisées dans l'adaptateur d'authentification +sont décrites dans: ref: `basic-authentication` :: public function testBasicAuthentication(): void { @@ -1208,15 +1217,15 @@ l'en-tête d'autorisation directement:: $this->assertResponseOk(); } -La clé des en-têtes dans ``configRequest()`` peut être utilisée pour configurer -tout en-tête HTTP supplémentaires nécessaires à une action. +Vous pouvez utiliser la clé ``headers`` dans ``configRequest()`` pour configurer +n'importe quelle autre en-tête HTTP dont vous auriez besoin pour cette action. Tester les Actions Protégées par CsrfComponent ou SecurityComponent ------------------------------------------------------------------- -Quand vous testez les actions protégées par SecurityComponent ou CsrfComponent, +Quand vous testez des actions protégées par CsrfComponent ou SecurityComponent, vous pouvez activer la génération automatique de token pour vous assurer que vos -tests ne vont pas être en échec à cause d'un token non présent:: +tests ne vont pas échoué à cause d'un problème de token:: public function testAdd(): void { @@ -1225,11 +1234,11 @@ tests ne vont pas être en échec à cause d'un token non présent:: $this->post('/posts/add', ['title' => 'News excitante!']); } -Il est aussi important d'activer debug dans les tests qui utilisent les tokens -pour éviter que le SecurityComponent pense que le token debug est utilisé dans -un environnement non-debug. Quand vous testez avec d'autres méthodes comme -``requireSecure()``, vous pouvez utiliser ``configRequest()`` pour définir les -bonnes variables d'environnement:: +Il est aussi important d'activer le débogage dans les tests qui utilisent des +tokens pour éviter que le SecurityComponent ne pense que le token de débogage +est utilisé dans un environnement non-debug. Quand vous faites des tests avec +d'autres méthodes comme ``requireSecure()``, vous pouvez utiliser +``configRequest()`` pour définir les bonnes variables d'environnement:: // Fake out SSL connections. $this->configRequest([ @@ -1246,12 +1255,12 @@ Test d'intégration sur les middlewares PSR-7 Les tests d'intégration peuvent aussi être utilisés pour tester entièrement vos applications PSR-7 et les :doc:`/controllers/middleware`. Par défaut, -``IntegrationTestCase`` détectera automatiquement la présence d'une classe +``IntegrationTestTrait`` détectera automatiquement la présence d'une classe ``App\Application`` et activera automatiquement les tests d'intégration sur votre Application. -Vous pouvez personnaliser le nom de la classe Application utilisé ainsi que les -arguments du contructeur en utilisant la méthode ``configApplication()``:: +Vous pouvez personnaliser le nom de la classe Application utilisée ainsi que les +arguments du contructeur, en utilisant la méthode ``configApplication()``:: public function setUp(): void { @@ -1259,52 +1268,70 @@ arguments du contructeur en utilisant la méthode ``configApplication()``:: } Vous devriez également faire en sorte d'utiliser :ref:`application-bootstrap` -pour charger les plugins qui contiennent des événements et des routes. De cette -manière, vous vous assurez que les événements et les routes seront connectés pour -chacun de vos "test case". +pour charger les plugins qui contiennent des événements ou des routes. De cette +manière, vous vous assurez que les événements et les routes seront connectés +pour tous vos *test cases*. -Tester avec des cookies chiffrés --------------------------------- +Tester avec des Cookies Cryptés +------------------------------- Si vous utilisez le :ref:`encrypted-cookie-middleware` dans votre application, il y a des méthodes pratiques pour définir des cookies chiffrés dans vos *test cases*:: // Définit un cookie en utilisant AES et la clé par défaut. - $this->cookieEncrypted('my_cookie', 'Some secret values'); + $this->cookieEncrypted('mon_cookie', 'Des valeurs secrètes'); - // Partons du principe que cette requête modifie le cookie. + // Partons du principe que cette action modifie le cookie. $this->get('/bookmarks/index'); - $this->assertCookieEncrypted('An updated value', 'my_cookie'); + $this->assertCookieEncrypted('Une nouvelle valeur', 'mon_cookie'); Tester les Messages Flash ------------------------- Si vous souhaitez faire une assertion sur la présence de messages Flash en -session et pas sur le rendu du HTML, vous pouvez utiliser ``enableRetainFlashMessages()`` -dans vos tests pour que les messages Flash soient conservés dans la session -pour que vous puissez écrire vos assertions:: +session et pas sur le rendu du HTML, vous pouvez utiliser +``enableRetainFlashMessages()`` dans vos tests pour que les messages Flash +soient conservés dans la session et que vous puissez ainsi effectuer vos +assertions:: // Active la rétention des messages flash plutôt que leur consommation $this->enableRetainFlashMessages(); $this->get('/bookmarks/delete/9999'); - $this->assertSession('That bookmark does not exist', 'Flash.flash.0.message'); + $this->assertSession("Ce marque-page n'existe pas", 'Flash.flash.0.message'); + + // Vérifie un message flash sous la clé 'flash'. + $this->assertFlashMessage('Marque-page supprimé', 'flash'); + + // Vérifie le deuxième message flash, également sous la clé 'flash'. + $this->assertFlashMessageAt(1, 'Marque-page vraiment supprimé'); -Tester un controller retournant du JSON + // Vérifie un message flash en première position sous la clé 'auth' + $this->assertFlashMessageAt(0, "Vous n'avez pas le droit d'entrer dans ce donjon !", 'auth'); + + // Vérifie qu'un message flash utilise l'élément error + $this->assertFlashElement('Flash/error'); + + // Vérifie l'élément du deuxième message flash + $this->assertFlashElementAt(1, 'Flash/error') + +Tester un Controller Retournant du JSON --------------------------------------- -JSON est un format commun à utiliser lors de la conception de web service. Tester les -points de terminaisons (endpoints) de votre web service est très simple avec CakePHP. -Commençons avec un simple exemple de controller qui renvoie du JSON:: +JSON est un format commun et agréable à utiliser lors de la conception d'un +service web. Avec CakePHP, il est très simple de tester les terminaux de votre +service web. Commençons avec un exemple simple de controller qui renvoie du +JSON:: + use Cake\View\JsonView; + class MarkersController extends AppController { - public function initialize(): void + public function viewClasses(): array { - parent::initialize(); - $this->loadComponent('RequestHandler'); + return [JsonView::class]; } public function view($id) @@ -1320,7 +1347,6 @@ et assurons-nous que le web service répond correctement:: class MarkersControllerTest extends IntegrationTestCase { - public function testGet(): void { $this->configRequest([ @@ -1335,40 +1361,40 @@ et assurons-nous que le web service répond correctement:: ['id' => 1, 'lng' => 66, 'lat' => 45], ]; $expected = json_encode($expected, JSON_PRETTY_PRINT); - $this->assertEquals($expected, $this->_response->body()); + $this->assertEquals($expected, (string)$this->_response->getBody()); } } -Nous utilisons l'option ``JSON_PRETTY_PRINT`` car la vue qui retourne la représentation -JSON intégrée à CakePHP (JsonView) utilise cette option quand le mode ``debug`` est -activé. +Nous utilisons l'option ``JSON_PRETTY_PRINT`` car la vue JsonView intégrée à +CakePHP utilise cette option quand le mode ``debug`` est activé. -Test avec téléchargement de fichiers ------------------------------------- +Test avec Téléversement de Fichiers +----------------------------------- -La simulation du téléchargement de fichiers est simple lorsque vous utilisez le -mode par défaut ":ref:`fichiers téléchargés en tant qu'objets `". -Vous pouvez simplement créer des instances qui implémentent +La simulation d'un téléversement de fichiers est enfantine lorsque vous utilisez +le mode par défaut de +":ref:`téléversement de fichiers en tant qu'objets `". +Vous pouvez créer tout simplement des instances qui implémentent `\\Psr\\Http\\Message\\UploadedFileInterface `__ (l'implémentation par défaut actuellement utilisée par CakePHP est -``\Laminas\Diactoros\UploadedFile``), et les passer dans vos données de demande -de test. Dans l'environnement CLI, ces objets passeront par défaut les contrôles -de validation qui testent si le fichier a été téléchargé via HTTP. Il n'en va pas +``\Laminas\Diactoros\UploadedFile``), et les passer dans les données de vos +requêtes de test. Dans l'environnement CLI, ces objets passeront par défaut les contrôles +de validation qui testent si le fichier a été téléversé via HTTP. Il n'en va pas de même pour les données de type tableau comme celles que l'on trouve dans -``$_FILES``, ce contrôle échouerait. +``$_FILES`` ; ce contrôle échouerait. -Afin de simuler exactement comment les objets de fichiers téléchargés seraient -présents dans une requête normale, vous devez non seulement les passer dans les -données de la requête, mais aussi les passer dans la configuration de la requête -de test via l'option "files". Ce n'est pas techniquement nécessaire, sauf si -votre code accède aux fichiers téléchargés via les méthodes +Afin de simuler exactement la façon dont les objets de fichiers téléversés +seraient présents dans une requête normale, vous devez non seulement les passer +dans les données de la requête, mais aussi les passer dans la configuration de +la requête de test via l'option ``files``. Ce n'est toutefois pas techniquement +nécessaire, sauf si votre code accède aux fichiers téléversés via les méthodes :php:meth:`Cake\\Http\\ServerRequest::getUploadedFile()` ou :php:meth:`Cake\\Http\\ServerRequest::getUploadedFiles()`. Supposons que les articles aient une image d'accroche, et une association -``Articles hasMany Attachments``, le formulaire ressemblerait à quelque chose -comme ceci en conséquence, où un fichier image, et plusieurs fichiers/attaches -seraient acceptés:: +``Articles hasMany Attachments``. Le formulaire ressemblerait à quelque chose +comme ceci en conséquence, où un fichier image et plusieurs fichiers ou pièces +jointes seraient acceptés:: Form->create($article, ['type' => 'file']) ?> Form->control('title') ?> @@ -1380,16 +1406,16 @@ seraient acceptés:: Form->button('Submit') ?> Form->end() ?> -Le test qui simulerait la demande correspondante pourrait ressembler à ceci:: +Le test qui simulerait la requête correspondante pourrait ressembler à ceci:: public function testAddWithUploads(): void { $teaserImage = new \Laminas\Diactoros\UploadedFile( '/path/to/test/file.jpg', // flux ou chemin d'accès au fichier représentant le fichier temporaire - 12345, // la taille des fichiers en octets - \UPLOAD_ERR_OK, // le statut de téléchargement ou d'erreur - 'teaser.jpg', // le nom du fichier tel qu'il a été envoyé par le client - 'image/jpeg' // le mimétisme tel qu'envoyé par le client + 12345, // la taille du fichier en octets + \UPLOAD_ERR_OK, // le statut de téléversement ou d'erreur + 'teaser.jpg', // le nom du fichier tel qu'il aurait été envoyé par le client + 'image/jpeg' // le type mime tel qu'il aurait été envoyé par le client ); $textAttachment = new \Laminas\Diactoros\UploadedFile( @@ -1408,7 +1434,7 @@ Le test qui simulerait la demande correspondante pourrait ressembler à ceci:: 'application/pdf' ); - // Ce sont les données accessibles via `$this->request->getUploadedFile()` + // Voici les données accessibles via `$this->request->getUploadedFile()` // et `$this->request->getUploadedFiles()`. $this->configRequest([ 'files' => [ @@ -1424,7 +1450,7 @@ Le test qui simulerait la demande correspondante pourrait ressembler à ceci:: ], ]); - // Ce sont les données accessibles via `$this->request->getData()`. + // Voici les données accessibles via `$this->request->getData()`. $postData = [ 'title' => 'Nouvel Article', 'teaser_image' => $teaserImage, @@ -1442,7 +1468,7 @@ Le test qui simulerait la demande correspondante pourrait ressembler à ceci:: $this->post('/articles/add', $postData); $this->assertResponseOk(); - $this->assertFlashMessage('L'article a été sauvegardé avec succès'); + $this->assertFlashMessage("L'article a été sauvegardé avec succès"); $this->assertFileExists('/path/to/uploads/teaser.jpg'); $this->assertFileExists('/path/to/uploads/attachment.txt'); $this->assertFileExists('/path/to/uploads/attachment.pdf'); @@ -1450,11 +1476,11 @@ Le test qui simulerait la demande correspondante pourrait ressembler à ceci:: .. tip:: - Si vous configurez la demande de test avec des fichiers, alors elle *doit* + Si vous configurez la requête de test avec des fichiers, alors elle *doit* correspondre à la structure de vos données POST (mais n'inclure que les - objets de fichiers téléchargés)! + objets de fichiers téléversés)! -De même, vous pouvez simuler des `erreurs de téléchargement `_ +De même, vous pouvez simuler des `erreurs de téléversement `_ ou d'autres fichiers invalides qui ne passent pas la validation:: public function testAddWithInvalidUploads(): void @@ -1503,7 +1529,7 @@ ou d'autres fichiers invalides qui ne passent pas la validation:: 'attachments' => [ 0 => [ 'file' => $uploadFailureAttachment, - 'description' => 'Pièce jointe de d'échec du téléchargement', + 'description' => 'Pièce jointe en échec de téléversement', ], 1 => [ 'file' => $invalidTypeAttachment, @@ -1514,7 +1540,7 @@ ou d'autres fichiers invalides qui ne passent pas la validation:: $this->post('/articles/add', $postData); $this->assertResponseOk(); - $this->assertFlashMessage("L'article n'a pas pu être sauvé"); + $this->assertFlashMessage("L'article n'a pas pu être sauvegardé"); $this->assertResponseContains("Une image d'accroche est nécessaire"); $this->assertResponseContains('Dépassement de la taille maximale autorisée des fichiers'); $this->assertResponseContains('Type de fichier non supporté'); @@ -1526,10 +1552,10 @@ ou d'autres fichiers invalides qui ne passent pas la validation:: Désactiver le Middleware de Gestion d'Erreurs dans les Tests ------------------------------------------------------------ -Quand vous debuggez des tests qui échouent car l'application a rencontré des -erreurs, il peut être utile de désactiver temporairement le middleware de gestion -des erreurs pour permettre aux erreurs de remonter. Vous pouvez utiliser la méthode -``disableErrorHandlerMiddleware()`` pour permettre ce comportement:: +Quand vous déboguez des tests qui échouent parce que votre application rencontre +des erreurs, il peut être utile de désactiver temporairement le middleware de +gestion des erreurs pour permettre aux erreurs de remonter. Pour cela, vous +pouvez utiliser la méthode ``disableErrorHandlerMiddleware()``:: public function testGetMissing(): void { @@ -1538,14 +1564,14 @@ des erreurs pour permettre aux erreurs de remonter. Vous pouvez utiliser la mét $this->assertResponseCode(404); } -Dans l'exemple ci-dessus, le test échouera et le message d'exception et le stack-trace -seront affichés à la place de la page d'erreur de l'application. +Dans l'exemple ci-dessus, le test échouera et le message d'exception et la +stack-trace seront affichés à la place de la page d'erreur de l'application. Méthodes d'Assertion -------------------- -La classe ``IntegrationTestCase`` vous fournis de nombreuses méthodes -d'assertions afin de tester plus simplement les réponses. Quelques exemples:: +Le trait ``IntegrationTestTrait`` fournit de nombreuses méthodes +d'assertions afin de tester les réponses plus simplement. Quelques exemples:: // Vérifie un code de réponse 2xx $this->assertResponseOk(); @@ -1581,16 +1607,22 @@ d'assertions afin de tester plus simplement les réponses. Quelques exemples:: $this->assertResponseEmpty(); // Vérifie le contenu de la réponse - $this->assertResponseEquals('Yeah!'); + $this->assertResponseEquals('Ouais !'); + + // Vérifie que le contenu de la réponse n'est pas égal à... + $this->assertResponseNotEquals('Non !'); // Vérifie un contenu partiel de la réponse - $this->assertResponseContains('You won!'); - $this->assertResponseNotContains('You lost!'); + $this->assertResponseContains("C'est gagné !"); + $this->assertResponseNotContains("Encore raté !"); + + // Vérifie un fichier renvoyé + $this->assertFileResponse('/chemin/absolu/vers/le/fichier.ext'); // Vérifie le layout $this->assertLayout('default'); - // Vérifie quel Template a été rendu. + // Vérifie quel Template a été rendu (s'il y en a un) $this->assertTemplate('index'); // Vérifie les données de la session @@ -1617,18 +1649,18 @@ En plus des méthodes d'assertion ci-dessus, vous pouvez également utiliser toutes les assertions de `TestSuite `_ et celles de -`PHPUnit `__. +`PHPUnit `__. Comparer les Résultats du Test avec un Fichier ---------------------------------------------- Pour certains types de test, il peut être plus simple de comparer les résultats -d'un test avec le contenu d'un fichier - par exemple, quand vous testez la -sortie rendue d'une view. +d'un test avec le contenu d'un fichier - par exemple, quand vous testez le +rendu d'une vue. ``StringCompareTrait`` ajoute une méthode d'assertion simple pour cela. -Pour l'utiliser, vous devez inclure un Trait, définir le chemin de base de -comparaison et appeler ``assertSameAsFile``:: +Pour l'utiliser, vous devez inclure le trait, définir le répertoire contenant le +fichier servant de base de comparaison et appeler ``assertSameAsFile``:: use Cake\TestSuite\StringCompareTrait; use Cake\TestSuite\TestCase; @@ -1650,13 +1682,13 @@ comparaison et appeler ``assertSameAsFile``:: } } -L'exemple ci-dessus va comparer ``$result`` au contenu du fichier +Cet exemple va comparer ``$result`` au contenu du fichier ``APP/tests/comparisons/example.php``. -Un mécanisme est fourni pour écrire/mettre à jour les fichiers de test, en +Il existe un mécanisme pour écrire/mettre à jour les fichiers de test, en définissant la variable d'environment ``UPDATE_TEST_COMPARISON_FILES``, ce qui -va créer et/ou mettre à jour les fichiers de comparaison de test au fur et à -mesure où ils sont rendus: +va créer et/ou mettre à jour les fichiers de comparaison de test dès qu'ils +seront référencés: .. code-block:: console @@ -1677,77 +1709,8 @@ mesure où ils sont rendus: # # modified: tests/comparisons/example.php -Tester avec des Cookies Chiffrés --------------------------------- - -Si vous utilisez :php:class:`Cake\\Controller\\Component\\CookieComponent` dans -vos controllers, vos cookies sont probablement chiffrés. Depuis 3.1.7, CakePHP -fournit des méthodes pour intéragir avec les cookies chiffrés dans vos cas de -test:: - - // Définit un cookie en utilisant aes et la clé par défaut. - $this->cookieEncrypted('my_cookie', 'Some secret values'); - - // En supposant que cette action modifie le cookie. - $this->get('/bookmarks/index'); - - $this->assertCookieEncrypted('Une valeur mise à jour', 'my_cookie'); - -Tester un Controller dont la Réponse est au format JSON -------------------------------------------------------- - -JSON est un format sympa et courant à utiliser quand on construit un service -web. Tester les endpoints de votre service web est très simple avec CakePHP. -Commençons par un exemple de controller simple qui répond en JSON:: - - class MarkersController extends AppController - { - public $components = ['RequestHandler']; - - public function view($id) - { - $marker = $this->Markers->get($id); - $this->set([ - '_serialize' => ['marker'], - 'marker' => $marker, - ]); - } - } - -Maintenant créons un fichier -**tests/TestCase/Controller/MarkersControllerTest.php** et assurons-nous que -notre service web retourne une réponse appropriée:: - - class MarkersControllerTest extends IntegrationTestCase - { - - public function testGet() - { - $this->configRequest([ - 'headers' => ['Accept' => 'application/json'] - ]); - $result = $this->get('/markers/view/1.json'); - - // Vérifie que le code de réponse est 200 - $this->assertResponseOk(); - - $expected = [ - ['id' => 1, 'lng' => 66, 'lat' => 45], - ]; - $expected = json_encode($expected, JSON_PRETTY_PRINT); - $this->assertEquals($expected, $this->_response->body()); - } - } - -Nous utilisons l'option ``JSON_PRETTY_PRINT`` comme le fait CakePHP à partir de -la classe JsonView. Ce dernier utilise cette option quand le mode ``debug`` est -activé. Vous pouvez utiliser ceci afin que votre test marche dans les deux cas:: - - json_encode($data, Configure::read('debug') ? JSON_PRETTY_PRINT : 0); - - -Tests d'Intégration de la Console -================================= +Tests d'Intégration en Console +============================== Voir la :ref:`console-integration-testing` pour savoir comment tester les commandes. @@ -1758,27 +1721,33 @@ Mocker les Injections de Dépendances Voir :ref:`mocking-services-in-tests` pour savoir comment remplacer des services injectés avec le conteneur d'injection de dépendances dans vos tests d'intégration. + +Mocker les Réponses du Client HTTP +================================== + +Voir :ref:`httpclient-testing` pour savoir comment créer des mocks de réponses +vers des API externes. Tester les Views ================ -Généralement, la plupart des applications ne va pas directement tester leur -code HTML. Faire ça donne souvent des résultats fragiles, il est difficile de -maintenir les suites de test qui sont sujet à se casser. En écrivant des -tests fonctionnels en utilisant :php:class:`ControllerTestCase`, vous -pouvez inspecter le contenu de la vue rendue en configurant l'option -``return`` à 'view'. Alors qu'il est possible de tester le contenu de la vue -en utilisant ControllerTestCase, un test d'intégration/vue plus robuste -et maintenable peut être effectué en utilisant des outils comme +Généralement, la plupart des applications ne vont pas directement tester leur +code HTML. Le faire donne souvent des suites de tests fragiles, difficiles à +maintenir et sujettes à se casser. En écrivant des tests fonctionnels avec +:php:class:`IntegrationTestTrait`, vous pouvez inspecter le contenu de la vue +rendue en configurant l'option ``return`` à 'view'. Bien qu'il soit possible de +tester le contenu de la vue en utilisant ``IntegrationTestTrait``, il est aussi +possible de réaliser des tests d'intégration/de vue plus robustes plus +maintenables en utilisant des outils comme `Selenium webdriver `_. Tester les Components ===================== -Imaginons que nous avons un component appelé PagematronComponent dans notre -application. Ce component nous aide à paginer la valeur limite à travers tous -les controllers qui l'utilisent. Voici notre exemple de component localisé dans -**src/Controller/Component/PagematronComponent.php**:: +Imaginons que nous ayons dans notre application un component appelé +PagematronComponent. Ce component nous aide à fixer la valeur limite de +pagination dans tous les controllers qui l'utilisent. Voici notre exemple de +component situé dans **src/Controller/Component/PagematronComponent.php**:: class PagematronComponent extends Component { @@ -1793,7 +1762,7 @@ les controllers qui l'utilisent. Voici notre exemple de component localisé dans } } - public function startup(Event $event) + public function startup(EventInterface $event) { $this->setController($event->getSubject()); } @@ -1874,10 +1843,10 @@ dans notre component. Nous créons le fichier Tester les Helpers ================== -Puisqu'un bon nombre de logique se situe dans les classes Helper, il est -important de s'assurer que ces classes sont couvertes par des cas de test. +Puisqu'une bonne quantité de logique se situe dans les classes de helpers, il est +important de s'assurer que ces classes sont couvertes par des tests. -Tout d'abord, nous créons un exemple de helper à tester. +Tout d'abord, créons un exemple de helper à tester. Le ``CurrencyRendererHelper`` va nous aider à afficher les monnaies dans nos vues et pour simplifier, il ne va avoir qu'une méthode ``usd()``:: @@ -1894,11 +1863,11 @@ et pour simplifier, il ne va avoir qu'une méthode ``usd()``:: } } -Ici nous définissons la décimale à 2 après la virgule, le séparateur de -décimal, le séparateur des centaines avec une virgule, et le nombre formaté -avec la chaîne 'USD' en préfixe. +Ici nous configurons deux décimales après la virgule, le séparateur décimal est +un point, le séparateur de milliers est une virgule, et nous plaçons la chaîne +'USD' en préfixe. -Maintenant nous créons nos tests:: +Maintenant, créons nos tests:: // tests/TestCase/View/Helper/CurrencyRendererHelperTest.php @@ -1913,7 +1882,7 @@ Maintenant nous créons nos tests:: public $helper = null; - // Nous instancions notre helper + // Ici nous instancions notre helper public function setUp(): void { parent::setUp(); @@ -1938,15 +1907,14 @@ Maintenant nous créons nos tests:: } } -Ici nous appelons ``usd()`` avec des paramètres différents et disons à test -suite de vérifier si les valeurs retournées sont égales à ce que nous en -attendons. +Ici nous appelons ``usd()`` avec différents paramètres et demandons à notre test +de vérifier si les valeurs retournées sont égales à ce que nous attendons. -Sauvegardons cela et exécutons le test. Vous devriez voir une barre verte et -un message indiquant 1 passé et 4 assertions. +Sauvegardez cela et exécutez le test. Vous devriez voir une barre verte et +un message indiquant 1 test passé et 4 assertions. -Lorsque vous testez un Helper qui utilise d'autres Helpers, assurez-vous de -créer un mock de la méthode ``loadHelpers`` de la classe View. +Lorsque vous testez un Helper qui utilise d'autres helpers, assurez-vous de +mocker la méthode ``loadHelpers`` de la classe View. .. _testing-events: @@ -1954,21 +1922,20 @@ Tester les Events ================= Les :doc:`/core-libraries/events` sont un bon moyen pour découpler le code de -votre application, mais parfois quand nous les testons, nous avons tendance à -tester les événements dans les cas de test qui éxecutent ces événements. C'est +votre application. Mais parfois quand vous les testez, vous aurez tendance à +tester les événements dans les cas de test qui exécutent ces événements. C'est une forme supplémentaire de couplage qui peut être évitée en utilisant à la place ``assertEventFired`` et ``assertEventFiredWith``. -En poursuivant l'exemple sur les Orders, disons que nous avons les tables +En poursuivant l'exemple sur les Orders, supposons que nous avons les tables suivantes:: class OrdersTable extends Table { - public function place($order): bool { if ($this->save($order)) { - // moved cart removal to CartsTable + // la suppression du panier a été déplacée dans CartsTable $event = new Event('Model.Order.afterPlace', $this, [ 'order' => $order ]); @@ -1981,7 +1948,6 @@ suivantes:: class CartsTable extends Table { - public function implementedEvents(): array { return [ @@ -1999,11 +1965,11 @@ suivantes:: .. note:: Pour faire des assertions sur le fait que des événements sont déclenchés, vous devez d'abord activer :ref:`tracking-events` sur le gestionnaire - d'événements pour lequel vous souhaitez faire des asserts. + d'événements sur lequel vous souhaitez faire des assertions. -Pour tester le ``OrdersTable`` du dessus, vous devez activer le tracking dans la -méthode ``setUp()`` puis vérifier par exemple que l'événement a été déclenché, -puis que l'entity ``$order`` a été passée dans les données de l'événement:: +Pour tester la ``OrdersTable`` ci-dessus, nous devons activer le tracking dans +la méthode ``setUp()`` puis vérifier que l'événement a été déclenché, puis que +l'entity ``$order`` a été passée dans les données de l'événement:: namespace App\Test\TestCase\Model\Table; @@ -2013,8 +1979,7 @@ puis que l'entity ``$order`` a été passée dans les données de l'événement: class OrdersTableTest extends TestCase { - - protected $fixtures = ['app.orders']; + protected $fixtures = ['app.Orders']; public function setUp(): void { @@ -2022,8 +1987,8 @@ puis que l'entity ``$order`` a été passée dans les données de l'événement: $this->Orders = $this->getTableLocator()->get('Orders'); - // enable event tracking - $this->Orders->eventManager()->setEventList(new EventList()); + // activer le suivi de la trace des événements + $this->Orders->getEventManager()->setEventList(new EventList()); } public function testPlace(): void @@ -2036,17 +2001,22 @@ puis que l'entity ``$order`` a été passée dans les données de l'événement: $this->assertTrue($this->Orders->place($order)); - $this->assertEventFired('Model.Order.afterPlace', $this->Orders->eventManager()); - $this->assertEventFiredWith('Model.Order.afterPlace', 'order', $order, $this->Orders->eventManager()); + $this->assertEventFired('Model.Order.afterPlace', $this->Orders->getEventManager()); + $this->assertEventFiredWith('Model.Order.afterPlace', 'order', $order, $this->Orders->getEventManager()); } } -Par défaut, l'``EventManager`` global est utilisé pour les assertions, donc -tester les événements globaux ne nécessitent pas de passer le gestionnaire -d'événements:: +Par défaut, les assertions utilisent l'``EventManager`` global, donc il n'est +pas nécessaire de passer le gestionnaire d'événements pour tester les événements +globaux:: + + $this->assertEventFired('Mon.Event.Global'); + $this->assertEventFiredWith('Mon.Event.Global', 'user', 1); - $this->assertEventFired('My.Global.Event'); - $this->assertEventFiredWith('My.Global.Event', 'user', 1); +Testing Email +============= + +Consultez :ref:`email-testing` pour savoir comment tester les emails. Créer des Suites de Test (Test Suites) ====================================== @@ -2069,8 +2039,8 @@ votre application. Un exemple simple serait: Créer des Tests pour les Plugins ================================ -Les Tests pour les plugins sont créés dans leur propre répertoire à -l'intérieur du dossier des plugins:: +Les Tests pour les plugins sont créés dans leur propre répertoire dans le +dossier des plugins:: /src /plugins @@ -2081,11 +2051,10 @@ l'intérieur du dossier des plugins:: Ils fonctionnent comme des tests normaux mais vous devrez vous souvenir d'utiliser les conventions de nommage pour les plugins quand vous importez des -classes. Ceci est un exemple d'un cas de test pour le model ``BlogPost`` à -partir du chapitre des plugins de ce manuel. Une différence par rapport aux -autres test est dans la première ligne où 'Blog.BlogPost' est importé. Vous -devrez aussi préfixer les fixtures de votre plugin avec -``plugin.blog.blog_posts``:: +classes. Ceci est un exemple d'un cas de test pour le model ``BlogPost`` tiré du +chapitre des plugins de ce manuel. Une différence par rapport aux autres tests +se situe dans la première ligne, où on importe 'Blog.BlogPost'. Vous devrez +aussi préfixer les fixtures de votre plugin avec ``plugin.Blog.BlogPosts``:: namespace Blog\Test\TestCase\Model\Table; @@ -2096,7 +2065,7 @@ devrez aussi préfixer les fixtures de votre plugin avec { // Fixtures de plugin se trouvant dans /plugins/Blog/tests/Fixture/ - protected $fixtures = ['plugin.blog.blog_posts']; + protected $fixtures = ['plugin.Blog.BlogPosts']; public function testSomething(): void { @@ -2104,9 +2073,9 @@ devrez aussi préfixer les fixtures de votre plugin avec } } -Si vous voulez utiliser les fixtures de plugin dans les app tests, vous pouvez -y faire référence en utilisant la syntaxe ``plugin.pluginName.fixtureName`` -dans le tableau ``$fixtures``. +Si vous voulez utiliser des fixtures de plugin dans les tests de l'application, +vous pouvez y faire référence en utilisant la syntaxe +``plugin.pluginName.fixtureName`` dans le tableau ``$fixtures``. Avant d'utiliser des fixtures, assurez-vous que le :ref:`listener de fixture ` soit configuré dans @@ -2116,7 +2085,7 @@ Vérifiez que le code suivant est présent dans votre fichier **composer.json**: "autoload-dev": { "psr-4": { - "MyPlugin\\Test\\": "./plugins/MyPlugin/tests" + "MonPlugin\\Test\\": "plugins/MonPlugin/tests" } } @@ -2131,7 +2100,7 @@ Générer des Tests avec Bake Si vous utilisez :doc:`bake ` pour générer votre code, il va également générer le squelette de vos fichiers de tests. Si vous avez besoin de re-générer le squelette de vos fichiers de tests, ou si vous souhaitez -générer le squelette de test pour le code que vous avez écrit, vous pouvez +générer le squelette de test pour du code que vous avez écrit, vous pouvez utiliser ``bake``: .. code-block:: console @@ -2147,9 +2116,14 @@ utiliser ``bake``: #. Behavior #. Helper #. Shell +#. Task +#. ShellHelper #. Cell +#. Form +#. Mailer +#. Command -```` doit être le nom de l'objet dont vous voulez générer le squelette de +Où ```` est le nom de l'objet dont vous voulez générer le squelette de tests. .. meta:: diff --git a/fr/orm/validation.rst b/fr/orm/validation.rst index 22b4f0001b..e5dd3dcb88 100644 --- a/fr/orm/validation.rst +++ b/fr/orm/validation.rst @@ -5,44 +5,46 @@ Avant que vous ne :doc:`sauvegardiez des données` vous voudre probablement vous assurer que les données sont correctes et cohérentes. Dans CakePHP, nous avons deux étapes de validation: -1. Avant que les données de requête ne soient converties en entity, les règles de - validation concernant le type de données et leur format peuvent être appliquées. -2. Avant que les données ne soient sauvegardées, les règles du domaine ou de - l'application peuvent être appliquées. Ces règles aident à garantir que les - données de votre application restent cohérentes. +1. Avant que les données de requête ne soient converties en entities, il est + possible d'appliquer des règles de validation concernant le type de données + et leur format. +2. Avant que les données ne soient sauvegardées, il est possible d'appliquer des + règles du domaine ou de l'application. Ces règles aident à garantir la + cohérence des données de votre application. .. _validating-request-data: Valider les Données Avant de Construire les Entities ---------------------------------------------------- -Durant la transformation des données en entities, vous pouvez valider les -données. La validation des données vous permet de vérifier le type, la forme et -la taille des données. Par défaut les données de la requête seront validées avant -qu'elles ne soient converties en entities. -Si une ou plusieurs règles de validation échouent, l'entity retournée contiendra des -erreurs. Les champs avec des erreurs ne seront pas présents dans l'entity -retournée:: +Vous pouvez valider les données lors de leur transformation en entities. Cette +validation vous permet de vérifier le type, la forme et la taille des données. +Par défaut, les données de la requête seront validées avant d'être converties en +entities. +Si une ou plusieurs règles de validation échouent, l'entity retournée contiendra +des erreurs. Les champs comportant des erreurs ne figureront pas dans l'entity +renvoyée:: $article = $articles->newEntity($this->request->getData()); if ($article->getErrors()) { // La validation de l'entity a échoué. } -Quand vous construisez une entity avec la validation activée, les choses -suivantes vont se produire: +Voici ce qui se passe quand vous construisez une entity et que la validation est +activée: 1. L'objet validator est créé. -2. Les providers de validation ``table`` et ``default`` sont attachés. -3. La méthode de validation nommée est appelée. Par exemple, +2. Les fournisseurs de validation (*providers*) ``table`` et ``default`` sont + attachés. +3. La méthode de validation désignée est appelée. Par exemple ``validationDefault()``. -4. L'event ``Model.buildValidator`` va être déclenché. -5. Les données de la requête vont être validées. -6. Les données de la requête vont être castées en types qui correspondent - aux types de colonne. -7. Les erreurs vont être définies dans l'entity. -8. Les données valides vont être définies dans l'entity, tandis que les champs - qui auront échoué à la validation seront laissés de côté. +4. L'événement ``Model.buildValidator`` est déclenché. +5. Les données de la requête sont validées. +6. Les données de la requête sont castées en types correspondant aux types des + colonnes. +7. Les erreurs sont définies dans l'entity. +8. Les données valides sont définies dans l'entity, tandis que les champs qui + ont échoué à la validation sont laissés de côté. Si vous voulez désactiver la validation lors de la conversion des données de la requête, définissez l'option ``validate`` à false:: @@ -95,7 +97,7 @@ Les méthodes et règles de validation disponibles proviennent de la classe .. note:: Les objets de validation sont principalement destinés à valider les données - provenant de l'utilisateur, par exemple les formulaires ou n'importe quelles + provenant de l'utilisateur, par exemple les formulaires ou d'autres données postées dans la requête. Utiliser un Ensemble de Validation Différent @@ -109,10 +111,10 @@ ensemble de règles de validation que vous souhaitez appliquer:: ['validate' => 'update'] ); -Ce qui précède appellerait la méthode ``validationUpdate()`` sur l'instance -table pour construire les règles requises. Par défaut la méthode -``validationDefault()`` sera utilisée. Un exemple de validator pour -notre table ``articles`` serait:: +Ceci appellerait la méthode ``validationUpdate()`` sur l'instance de table pour +construire les règles requises. Par défaut, c'est la méthode +``validationDefault()`` qui sera utilisée. Voici un exemple de validator pour +notre table ``articles``:: class ArticlesTable extends Table { @@ -193,15 +195,15 @@ Avec cette configuration, lors de l'utilisation de l'ensemble de validation Validation Providers ==================== -Les règles de validation peuvent utiliser des fonctions définies sur n'importe +Les règles de validation peuvent utiliser des fonctions définies par n'importe quel provider connu. Par défaut, CakePHP définit quelques providers: -1. Les méthodes sur la classe de table, ou ses behaviors, sont disponibles sur +1. Les méthodes sur la classe de table, ou ses behaviors, sont disponibles dans le provider ``table``. 2. La classe du cœur :php:class:`~Cake\\Validation\\Validation` est configurée en tant que provider ``default``. -Quand une règle de validation est créée, vous pouvez nommer le provider de cette +En créant une règle de validation, vous pouvez désigner le provider de cette règle. Par exemple, si votre table a une méthode ``isValidRole``, vous pouvez l'utiliser comme une règle de validation:: @@ -210,7 +212,6 @@ l'utiliser comme une règle de validation:: class UsersTable extends Table { - public function validationDefault(Validator $validator): Validator { $validator @@ -241,8 +242,8 @@ Vous pouvez également utiliser des closures en tant que règles de validation:: ]); Les méthodes de validation peuvent renvoyer des messages d'erreur lorsqu'elles échouent. -C'est un moyen simple de créer des messages d'erreur dynamiques basés sur la -valeur fournie. +C'est un moyen simple de créer des messages d'erreur dynamiques basés en +fonction de la valeur fournie. Récupérer des Validators depuis les Tables ========================================== @@ -265,22 +266,21 @@ une instance d'un validator personnalisé, vous pouvez utiliser l'attribut // Dans votre classe de table public function initialize(array $config): void { - $this->_validatorClass = \FullyNamespaced\Custom\Validator::class; + $this->_validatorClass = \FullyNamespaced\Custom\Validator::class; } .. _application-rules: -Appliquer des Règles pour l'Application -======================================= +Appliquer des Règles d'Application +================================== -Tandis qu'une validation basique des données est faite quand :ref:`les données -de la requête sont converties en entities `, de -nombreuses applications ont aussi d'autres validations plus complexes qui -doivent être appliquées seulement après qu'une validation basique a été -achevée. +Au-delà de la validation basique des données qui est lancée quand +:ref:`les données de la requête sont converties en entities `, +de nombreuses applications ont des validations plus complexes qui doivent être +appliquées après la validation basique. -Là où la validation s'assure que le formulaire ou la syntaxe de vos données -sont corrects, les règles s'attellent à comparer les données avec l'état +Là où la validation s'assure que la forme ou la syntaxe de vos données +sont correctes, les règles s'attellent à comparer les données avec l'état existant de votre application et/ou du réseau. Ces types de règles sont souvent appelées 'règles de domaine' ou @@ -290,8 +290,9 @@ quelques exemples de règles de domaine: * S'assurer qu'un email est unique. * Transition d'états ou étapes de flux de travail, par exemple pour mettre à - jour un statut de facture. -* Eviter la modification ou la suppression soft d'articles. + jour le statut d'une facture. +* Eviter la modification d'articles ayant fait l'objet d'une suppression + logique. * Appliquer des limites d'usage, que ce soit en nombre d'appels total ou en nombre d'appels sur une période donnée. @@ -303,9 +304,9 @@ Les règles de domaine sont vérifiées lors de l'appel aux méthodes ``save()`` Créer un Vérificateur de Règles ------------------------------- -Les classes de vérificateur de règles sont généralement définies par la +Les classes de vérificateur de règles (*rules checkers*) sont généralement définies par la méthode ``buildRules()`` dans votre classe de table. Les behaviors et les autres -souscripteurs d'event peuvent utiliser l'event ``Model.buildRules`` pour +souscripteurs d'events peuvent utiliser l'événement ``Model.buildRules`` pour ajouter des règles au vérificateur pour une classe Table donnée:: use Cake\ORM\RulesChecker; @@ -313,7 +314,7 @@ ajouter des règles au vérificateur pour une classe Table donnée:: // Dans une classe de table public function buildRules(RulesChecker $rules): RulesChecker { - // Ajoute une règle qui est appliquée pour la création et la mise à jour d'opérations + // Ajoute une règle qui est appliquée pour les opérations de création et de mise à jour $rules->add(function ($entity, $options) { // Retourne un booléen pour indiquer si succès/échec }, 'ruleName'); @@ -337,8 +338,8 @@ ajouter des règles au vérificateur pour une classe Table donnée:: } Vos fonctions de règles ont pour paramètres l'Entity à vérifier et un tableau -d'options. Le tableau d'options va contenir ``errorField``, ``message`` et -``repository``. L'option ``repository`` va contenir la classe de table à +d'options. Le tableau d'options contiendra ``errorField``, ``message`` et +``repository``. L'option ``repository`` contiendra la classe de table à laquelle les règles sont attachées. Comme les règles acceptent n'importe quel ``callable``, vous pouvez aussi utiliser des fonctions d'instance:: @@ -348,8 +349,8 @@ ou des classes callable:: $rules->addCreate(new IsUnique(['email']), 'uniqueEmail'); -Lors de l'ajout de règles, vous pouvez définir le champ pour lequel la règle -est faite, et le message d'erreur en options:: +Lors de l'ajout de règles, vous pouvez définir en options le champ correspondant +à la règle et le message d'erreur:: $rules->add([$this, 'isValidState'], 'validState', [ 'errorField' => 'status', @@ -361,11 +362,11 @@ l'entity:: $entity->getErrors(); // Contient les messages d'erreur des règles de domaine -Créer des Règles de Champ Unique --------------------------------- +Créer des Règles d'Unicité de Champ +----------------------------------- -Comme les règles uniques sont couramment utilisées, CakePHP inclut une classe -de règle simple qui vous permet de définir des ensembles de champs uniques:: +Les règles d'unicité étant couramment utilisées, CakePHP inclut une classe +simple qui vous permet de définir des ensembles de champs uniques:: use Cake\ORM\Rule\IsUnique; @@ -381,11 +382,11 @@ de règle simple qui vous permet de définir des ensembles de champs uniques:: Quand vous définissez des règles sur des champs de clé étrangère, il est important de se rappeler que seuls les champs listés sont utilisés dans la règle. L'ensemble des règles d'unicité sera trouvé avec ``find('all')``. Cela -signifie que définir ``$user->account->id`` ne va pas déclencher -la règle ci-dessus. +signifie que la règle ci-dessus ne sera pas déclenchée en définissant +``$user->account->id``. -Beaucoup de moteurs de base de données autorisent les valeurs NULL à être -uniques dans les index UNIQUE. Pour simuler cela, définissez les options +De nombreux moteurs de bases de données autorisent plusieurs valeurs NULL dans +les index UNIQUE. Pour simuler ce comportement, définissez l'option ``allowMultipleNulls`` à ``true``:: $rules->add($rules->isUnique( @@ -400,8 +401,8 @@ uniques dans les index UNIQUE. Pour simuler cela, définissez les options Règles de Clés Etrangères ------------------------- -Alors que vous pourriez compter sur les erreurs de la base de données pour -imposer des contraintes, utiliser des règles peut vous aider à fournir une +Bien que vous puissiez vous reposer sur les erreurs de la base de données pour +imposer des contraintes, le fait d'utiliser des règles vous permet de fournir une expérience utilisateur plus sympathique. C'est pour cela que CakePHP inclut une classe de règle ``ExistsIn``:: @@ -411,16 +412,16 @@ une classe de règle ``ExistsIn``:: // Plusieurs clés, utile pour des clés primaires composites. $rules->add($rules->existsIn(['site_id', 'article_id'], 'Articles')); -Les champs dont il faut vérifier l'existence dans la table liée doivent faire -partie de la clé primaire. +Les champs dont vous demandez à vérifier l'existence dans la table +correspondante doivent faire partie de la clé primaire. -Vous pouvez forcer ``existsIn`` à passer quand des parties qui peuvent être -null de votre clé étrangère composite sont nulles:: +Vous pouvez forcer ``existsIn`` à accepter qu'une partie de votre clé étrangère +composite soit null:: - // Exemple: Une clé primaire composée dans NodesTable est (id, site_id). - // Un "Node" peut faire référence à un parent Node mais ce n'est pas + // Exemple: NodesTable contient une clé primaire composite (parent_id, site_id). + // Un "Node" peut faire référence à un Node parent mais ce n'est pas // obligatoire. Dans ce dernier cas, parent_id est null. - // Nous permettons à cette règle de passer, même si les champs qui sont + // Nous autorisons la validation de cette règle même si les champs qui sont // nullables, comme parent_id, sont null: $rules->add($rules->existsIn( ['parent_id', 'site_id'], // Schema: parent_id NULL, site_id NOT NULL @@ -432,9 +433,9 @@ null de votre clé étrangère composite sont nulles:: $rules->add($rules->existsIn(['site_id'], 'Sites')); Dans la majorité des bases de données SQL, les index ``UNIQUE`` sur plusieurs -colonnes permettent à plusieurs valeurs null d'exister car ``NULL`` n'est -pas égal à lui même. Même si autoriser plusieurs valeurs null est le comportement -par défaut de CakePHP, vous pouvez inclure des valeurs null dans vos +colonnes autorisent plusieurs valeurs null car ``NULL`` n'est +pas égal à lui même. Même si CakePHP autorise par défaut plusieurs valeurs null, +vous pouvez inclure les nulls dans vos vérifications d'unicité en utilisant ``allowMultipleNulls``:: // Une seule valeur null peut exister dans `parent_id` et `site_id` @@ -455,10 +456,10 @@ certain nombre de valeurs, vous pouvez utiliser la règle ``validCount()``:: $rules->add($rules->validCount('tags', 5, '<=', 'Vous ne pouvez avoir que 5 tags')); Quand vous définissez des règles qui basées sur un nombre de valeurs, le troisième -paramètre vous permet de définir l'opérateur de comparaison à utiliser. ``==``, -``>=``, ``<=``, ``>``, ``<``, and ``!=`` sont les opérateurs acceptés. Pour vous -assurer qu'un nombre d'une propriété est entre certaines valeurs, utilisez deux -règles:: +paramètre vous permet de définir l'opérateur de comparaison à utiliser. Les +opérateurs acceptés sont ``==``, ``>=``, ``<=``, ``>``, ``<``, et ``!=``. Pour +vérifier que le décompte d'une propriété est entre certaines valeurs, utilisez +deux règles:: // Dans le fichier ArticlesTable.php // Entre 3 et 5 tags @@ -480,7 +481,7 @@ messages d'erreur plus sympathiques quand la contrainte échoue. Cette règle vo permet de vérifier si une association a ou non des enregistrements liés en fonction du mode utilisé:: - // S'assure que chaque commentaire est lié à un Article pendant les mises à jour. + // S'assure que chaque commentaire est lié à un Article lors de sa mise à jour. $rules->addUpdate($rules->isLinkedTo( 'Articles', 'article', @@ -491,13 +492,13 @@ fonction du mode utilisé:: $rules->addDelete($rules->isNotLinkedTo( 'Comments', 'comments', - 'Impossible de supprimer un article qui contient des commentaires.' + 'Impossible de supprimer un article qui contient des commentaires.' )); .. versionadded:: 4.0.0 -Utiliser les Méthodes Entity en tant que Règles ------------------------------------------------ +Utiliser les Méthodes d'Entity en tant que Règles +------------------------------------------------- Vous pouvez utiliser les méthodes d'une entity en tant que règles de domaine:: @@ -508,7 +509,8 @@ Vous pouvez utiliser les méthodes d'une entity en tant que règles de domaine:: Utiliser des Règles Conditionnelles ----------------------------------- -Vous pouvez appliquer des règles sous condition selon les données de l'entity:: +Vous pouvez appliquer des règles conditionnelles en fonction des données de +l'entity:: $rules->add(function ($entity, $options) use($rules) { if ($entity->role == 'admin') { @@ -530,12 +532,12 @@ Messages d'Erreur Dynamiques/Conditionnels Les règles, qu'elles soient :ref:`des callables personnalisés ` ou :ref:`des objets Rule `, peuvent soit retourner -un booléen, indiquant si elles ont réussi, soit retourner une chaîne, qui +un booléen indiquant si elles ont réussi, soit retourner une chaîne qui signifie que la règle a échoué et que la chaîne doit être utilisée comme message d'erreur. Les messages d'erreur possibles définis par l'option ``message`` seront écrasés -par ceux retournés pas la règle:: +par ceux retournés par la règle:: $rules->add( function ($entity, $options) { @@ -564,8 +566,8 @@ par ceux retournés pas la règle:: Notez que pour que le message retourné puisse être utilisé, vous *devez* aussi définir l'option ``errorField``, sinon la règle va se contenter - d'échouer en silence, c'est-à-dire sans message d'erreur inséré dans - l'entity ! + d'échouer silencieusement, c'est-à-dire sans insérer le message d'erreur + dans l'entity ! Créer des Règles Personnalisées Réutilisables --------------------------------------------- @@ -614,7 +616,7 @@ utile de packager ces règles dans des classes réutilisables:: $rules->add(new CustomRule(...), 'ruleName'); -En ajoutant des classes de règle personnalisée, vous pouvez garder votre code +En ajoutant des classes de règles personnalisées, vous pouvez garder votre code DRY et tester vos règles de domaine isolément. Désactiver les Règles @@ -632,11 +634,11 @@ L'ORM de CakePHP est unique dans le sens où il utilise une approche à deux couches pour la validation. La première couche est la validation. Les règles de validation ont pour objet -d'opérer de façon stateless. Elles servent à s'assurer que la forme, les +d'opérer sans état (*stateless*). Elles servent à s'assurer que la forme, les types de données et le format des données sont corrects. La seconde couche est celle des règles d'application. Les règles d'application -sont utiles pour vérifier les propriétés stateful de vos entities. Par exemple, +sont plus appropriés pour vérifier l'état des propriétés de vos entities. Par exemple, les règles de validation peuvent permettre de s'assurer qu'une adresse email est valide, tandis qu'une règle d'application permet de s'assurer que l'adresse email est unique. @@ -666,8 +668,8 @@ défini en utilisant la méthode ``validationMaRegle()``:: return $validator; } -La validation suppose que des chaînes de caractères ou des tableaux -sont passés puisque c'est ce qui est reçu par n'importe quelle requête:: +La validation présuppose que les arguments passés sont des chaînes de caractères +ou des tableaux, puisque c'est ce qui est passé dans n'importe quelle requête:: // Dans src/Model/Table/UsersTable.php public function validatePasswords($validator) @@ -682,8 +684,8 @@ sont passés puisque c'est ce qui est reçu par n'importe quelle requête:: return $validator; } -La validation **n'est pas** déclenchée lorsqu'une propriété est définie -directement sur vos entities:: +La validation **n'est pas** déclenchée lorsque vous définissez directement une +propriété sur vos entities:: $userEntity->email = 'pas un email!!'; $usersTable->save($userEntity); @@ -693,13 +695,13 @@ déclenchée que par les méthodes ``newEntity()`` et ``patchEntity()``. Le seco niveau de validation est conçu pour gérer cette situation. Les règles d'application, comme expliqué précédement, seront vérifiées à chaque -fois que ``save()`` ou ``delete()`` sont appelées:: +appel de ``save()`` ou ``delete()``:: // Dans src/Model/Table/UsersTable.php public function buildRules(RulesChecker $rules): RulesChecker { $rules->add($rules->isUnique(['email'])); - + return $rules; } @@ -707,9 +709,9 @@ fois que ``save()`` ou ``delete()`` sont appelées:: $userEntity->email = 'a@email.en.doublon'; $usersTable->save($userEntity); // Retourne false -Alors que la validation est conçue pour les données provenant directement -d'utilisateurs, les règles d'application sont spécifiques aux transitions de -données générées à l'intérieur de l'application:: +La validation est conçue pour les données provenant directement des +utilisateurs, tandis que les règles d'application sont conçues spécifiquement +pour les transitions de données générées à l'intérieur de l'application:: // Dans src/Model/Table/CommandesTable.php public function buildRules(RulesChecker $rules): RulesChecker @@ -730,7 +732,7 @@ données générées à l'intérieur de l'application:: // Autre part dans le code de l'application $commande->prix = 50; - $commande->livraison = 'free'; + $commande->livraison = 'gratuit'; $commandesTable->save($commande); // Retourne false Utiliser la Validation en tant que Règle d'Application @@ -760,6 +762,7 @@ qui définit des propriétés directement dans des entities:: $rules->add(function($entity) { $data = $entity->extract($this->getSchema()->columns(), true); $validator = $this->getValidator('default'); + $errors = $validator->validate($data, $entity->isNew()); $entity->setErrors($errors); return empty($errors); diff --git a/ja/orm/validation.rst b/ja/orm/validation.rst index 45b21aca5a..326f0c0d83 100644 --- a/ja/orm/validation.rst +++ b/ja/orm/validation.rst @@ -378,7 +378,7 @@ CakePHP は、エンティティーが保存される前に適用される「ル 複合外部キーの null が可能な部分が null の時、 ``existsIn`` が通るように強制することができます。 :: - // 例: NodesTable の複合主キーは (id, site_id) です。 + // 例: NodesTable の複合主キーは (parent_id, site_id) です。 // Node は、親 Node を参照しますが、必須ではありません。参照しない場合、parent_id が null になります。 // たとえ null が可能なフィールド (parent_id のような) が null であっても、このルールが通ることを許可します。 $rules->add($rules->existsIn(