champ-unique-cakephp

CakePHP 3 : Vérifier que la valeur d’un champ est unique

Avec CakePHP, vous pouvez valider les données avant insertion. Cela consiste à préciser le type de chaque champ, vous pouvez même préciser un certain formatage mais rien ne permettant de spécifier que la valeur d’un champ doit être unique.

Voici comment faire.

En tout cas je n’ai rien trouvé dans la documentation à ce propos, si vous avez une solution n’hésitez pas à m’en informer dans les commentaires ou a m’interpeller sur Twitter.

Le système de validation de CakePHP

Présenté sous la forme de package, l’API de validation (ou objet Validator) inclut dans cakePHP permet de valider les données. Il peut notamment être utilisé dans le cadre d’une insertion en base de données : Avant insertion les champs indiqués sont vérifiés, ce qui permet de limiter le nombre de contrôle à effectuer manuellement. Ce type de contrôle étant assez ennuyeux à mettre en place et assez rébarbatif tout en excluant pas les risques d’erreur.

De manière non exhaustive voici ce que permet cette API :

  • Indiquez quels champs sont requis
  • Longueur minimale d’une chaîne de caractères
  • Longueur maximale d’une chaîne de caractères
  • Que le champ doit être de type :
    • date
    • décimale
    • URL
  • Que le fichier est d’un certain type (extension)
  • etc….

C’est assez complet, pour en savoir plus rendez-vous sur la documentation officielle de cette API : Class Validation.

Utilisation des Validators sur CakePHP

Voici un exemple d’utilisation d’un validator, il s’agit là de valider l’enregistrement d’un article en base de données :

<?php

# Fichier src/Model/Table/ArticleTable.php

namespace App\Model\Table;

use Cake\ORM\Table;
use Cake\Validation\Validator;

class ArticleTable extends Table
{

public function validationDefault(Validator $validator)
{

return $validator
->requirePresence('title')
->notEmpty('title', 'Remplissez ce champ')
->add('title', [
'length' => [
'rule' => ['minLength', 10],
'message' => 'Les titres doivent comporter au moins 10 caractères',
]
])
->allowEmpty('published')
->add('published', 'boolean', [
'rule' => 'boolean'
])
->requirePresence('body')
->add('body', 'length', [
'rule' => ['minLength', 50],
'message' => 'Les articles doivent avoir un corps substantiel.'
]);
}

}

Quelle est la règle pour dire que la valeur d’un champ doit être unique ?

Venons-en enfin au sujet de cet article. Je n’ai pas trouvé dans la documentation de ce framework permettant de mettre en place ce type de règle.

Mais heureusement vous pouvez ajouter des règles personnalisées. Ces règles « customs », vont appeler la méthode d’une classe (que vous aurez passé en paramètres), lors de l’appel de cette méthode la valeur du champ à vérifier sera passé en paramètre. La méthode appelé devra retourné true si le test est OK, false dans le cas contraire.

J’ai conscience que ma dernière phrase est très chiante à lire, donc voici un exemple, ici on veut que le titre de l’article soit unique.

On ajoute donc cette règle

->add('login', 'custom', [
 'rule' => [$this, 'isTitleUnique'],
 'message' => __("Merci d'utiliser un titre unique")
 ])

Donc voilà on ajoute une règle qui appellera la méthode isTitleUnique de la classe courante…. ce qui veut dire qu’il faut écrire cette méthode :

public function isTitleUnique($data) {
 
 $query = $this->find('all')->where(['Articles.title' => $data]);
 
 return $query->count() == 0;
 }

Trop dur….

 

Voici ma classe ArticleTable finale

<?php

# Fichier src/Model/Table/ArticleTable.php

namespace App\Model\Table;

use Cake\ORM\Table;
use Cake\Validation\Validator;

class ArticleTable extends Table
{

public function validationDefault(Validator $validator)
{

return $validator
->requirePresence('title')
->notEmpty('title', 'Remplissez ce champ')
->add('title', [
'length' => [
'rule' => ['minLength', 10],
'message' => 'Les titres doivent comporter au moins 10 caractères',
]
])
->add('login', 'custom', [
 'rule' => [$this, 'isTitleUnique'],
 'message' => __("Merci d'utiliser un titre unique")
 ])
->allowEmpty('published')
->add('published', 'boolean', [
'rule' => 'boolean'
])
->requirePresence('body')
->add('body', 'length', [
'rule' => ['minLength', 50],
'message' => 'Les articles doivent avoir un corps substantiel.'
]);
}

public function isTitleUnique($data) {
 
 $query = $this->find('all')->where(['Articles.title' => $data]);
 
 return $query->count() == 0;
 }

}

Si vous avez des remarques ou des questions, les commentaires sont là pour ça.