acme_tracking:AC, vous enregistrez la logique de validation dans la Factory de validation. Elle peut alors être utilisée depuis les form requests de l’application consommatrice avec la même syntaxe que les règles intégrées.
Cette page a été vérifiée sur l’implémentation officielle de Laravel Framework v13.40.0. Elle complète les exemples de base de Règles de validation personnalisées et traite de l’enregistrement, de la traduction et de la durée de vie de l’état lorsque vous maintenez ces règles comme API publique d’un package.
Choisir entre ceci et les objets de règle
Les objetsValidationRule présentés dans la documentation officielle peuvent aussi être utilisés dans un package. Si la possibilité de spécifier la règle sous forme de chaîne n’est pas en soi une exigence, envisagez un objet capable de recevoir des arguments typés via son constructeur.
Même si la méthode porte le même nom
validate(), le contrat d’appel est différent. Si vous passez directement une classe implémentant ValidationRule comme chaîne de classe à Factory::extend(), le type du troisième argument et la valeur de retour ne correspondent pas.
Séparer enregistrement, création et validation
La méthodeextend() de la façade Validator est un appel vers Illuminate\Validation\Factory. Il s’agit d’un objet distinct de l’instance Illuminate\Validation\Validator qui valide les entrées.
Le diagramme montre le chemin suivi lorsqu’une règle sous forme de chaîne est exécutée. Les cas où elle est ignorée, par exemple en l’absence de saisie ou avec nullable, sont décrits plus loin.
La Factory est enregistrée comme singleton sous la clé validator, mais le Validator retourné par l’implémentation standard de make() est créé à neuf à chaque appel. Un enregistrement ultérieur dans la Factory ne met pas à jour le tableau d’extensions des Validator déjà créés.
Enregistrer une règle propre au package
À titre d’exemple, nous validons un code de suivi fictif composé d’un préfixe en majuscules, d’un tiret et de 8 chiffres. Il s’agit d’un format d’exemple commeAC-00000001, et non de la spécification de numéros d’un transporteur réel.
Nous supposons que l’espace de noms du package Acme\Courier\ correspond à src/ via le PSR-4 de Composer, et qu’un Service Provider classique non différé est chargé. Pour le chargement des providers, consultez Découverte automatique des packages.
Séparer la logique de validation dans une classe
Préparez danssrc/Validation/TrackingCode.php un handler pour la règle sous forme de chaîne. Cette classe n’implémente pas l’interface ValidationRule.
acme_tracking:AC, le tableau de paramètres est ['AC']. La règle vérifie elle-même le type de la valeur, afin de ne pas provoquer d’erreur de type même lorsqu’elle est utilisée seule.
Un préfixe invalide ou un nombre de paramètres incorrect lève une exception, car il s’agit non pas d’une erreur de saisie de l’utilisateur mais d’une erreur de configuration dans la définition de la règle. Lorsque la règle est ignorée pour une valeur vide, cette vérification de configuration n’est pas exécutée non plus ; il faut donc aussi des tests avec une valeur non vide valide.
Enregistrer après la résolution de la Factory
Depuis la méthodeboot() de src/CourierServiceProvider.php, enregistrez la règle et le traitement de remplacement du message.
callAfterResolving() attend la création de la Factory si elle n’est pas encore résolue, et enregistre immédiatement si elle l’est déjà. Le hook lui-même est détaillé dans Hooks après résolution des services de package.
La chaîne de classe TrackingCode::class.'@validate' résout la classe via le conteneur au moment de la validation, puis appelle la méthode. Même avec seulement le nom de la classe sans @validate, le nom de méthode par défaut est validate. Si le constructeur a des dépendances, liez-les de façon à ce que le conteneur puisse les résoudre.
Cet exemple n’enregistre pas le handler comme singleton et ne comporte aucune propriété stockant les entrées. Les données de validation sont manipulées via les arguments, et la conception évite de capturer un état propre à la requête dans les closures conservées par la Factory.
Utiliser la règle depuis l’application consommatrice
bail arrête les règles suivantes dès qu’une règle précédente de cet attribut échoue. Les rôles sont séparés : required pour le caractère obligatoire, acme_tracking pour le format.
Uniformisez le nom public avec un snake case unique comme acme_tracking. Un enregistrement portant le même nom pour le même type d’extension est écrasé par le dernier. Par ailleurs, lorsqu’une méthode intégrée comme validateRequired() existe, c’est elle qui est appelée directement : une extension portant le même nom ne peut donc pas remplacer une validation intégrée.
Préserver les messages d’erreur de l’utilisateur
Le message d’une règle sous forme de chaîne classique est sélectionné globalement selon l’ordre de priorité suivant. Les branches particulières comme les règles de taille ne sont pas traitées ici.
Le replacer personnalisé est appelé après que Laravel a remplacé
:attribute et les autres placeholders. Le replacer reçoit cinq arguments : le message sélectionné, le nom de l’attribut, le nom de la règle, le tableau de paramètres et le Validator. L’exemple précédent omet le cinquième argument, qu’il n’utilise pas.
Pour définir un message par défaut en japonais côté application, ajoutez acme_tracking au tableau retourné par le fichier existant lang/ja/validation.php. L’exemple ci-dessous montre l’entrée à ajouter ; il ne supprime pas les autres messages intégrés.
__('...') au moment de l’enregistrement et stockez le résultat comme fallback, la chaîne de la locale alors active reste dans la Factory. Dans une application qui change de locale, utilisez la traduction au moment de la génération de l’erreur.
Utiliser les traductions avec espace de noms du package
En appelantloadTranslationsFrom(__DIR__.'/../lang', 'acme-courier') dans boot(), vous pouvez référencer le fichier lang/ja/validation.php du package, entre autres, via acme-courier::validation.tracking_code.
Cependant, passer simplement une clé de traduction comme troisième argument de extend() ne la traduit pas automatiquement. La Factory la conserve comme une simple chaîne fallback. En remplaçant les appels extend() et replacer() du hook précédent par l’enregistrement suivant, vous pouvez récupérer la traduction du package uniquement lorsque le fallback est sélectionné.
tracking_code dans le validation.php de chaque locale du package. Pour le japonais, vous pouvez utiliser le même texte que l’entrée acme_tracking précédente.
Comme le replacer récupère ici une nouvelle chaîne de traduction, :attribute est aussi passé explicitement à la traduction. Le remplacement standard de Laravel n’est pas réappliqué à cette valeur de retour. Si un message personnalisé de l’utilisateur est sélectionné, son texte est conservé. Pour la surcharge des traductions distribuées, consultez Surcharge et mise à jour des traductions de package.
Comprendre les conditions d’exécution avec Implicit et nullable
extendImplicit() enregistre une règle qui devient candidate à la validation même pour un attribut absent ou une chaîne vide. Cela ne signifie ni « faire échouer systématiquement » ni « ignorer toutes les autres conditions de saut ».
Le tableau compare les conditions d’exécution de chaque règle. Les cas où le traitement s’arrête plus tôt à cause de
bail, de l’exclusion d’attributs ou de stopOnFirstFailure() sont à part. Pour les entrées HTTP, une chaîne vide peut être convertie en null par un middleware ; testez donc séparément la chaîne vide et null.
Pour la règle de format présentée ici, la combinaison d’un extend() classique et de required suffit. Passer une règle déjà publiée en Implicit peut faire échouer des entrées qui n’étaient pas validées auparavant ; traitez donc ce changement comme une modification de compatibilité.
Résoudre les références dans les tableaux avec les règles Dependent
Pour une règle qui prend en paramètre le nom d’un autre champ, vous pouvez utiliserextendDependent(). Lors de la validation de items.*.confirmed_tracking, le paramètre items.*.tracking est converti vers la même ligne, par exemple items.0.tracking. Ce remplacement n’est pas effectué avec un extend() classique.
Voici une règle de correspondance destinée à illustrer ce mécanisme. Pour une simple vérification d’égalité, une application réelle peut utiliser la règle intégrée same. Considérez-la comme un exemple à adapter pour des validations inter-champs propres à votre package.
Ajoutez-la à la Factory reçue depuis le hook.
getData() du quatrième argument correspond aux données soumises à la validation, et pas uniquement aux données validées. Définissez les règles nécessaires à la fois sur le champ référençant et sur le champ référencé.
De plus, une règle Dependent ne devient pas Implicit pour autant. Spécifiez séparément le caractère obligatoire pour les valeurs absentes ou vides. L’implémentation applique aux paramètres un échappement des points et le remplacement de * ; évitez donc une API qui mélange des chaînes arbitraires autres que des noms de champs.
Tester de l’enregistrement à la validation dans une application
Dans une application Laravel 13 qui charge le package et son Service Provider, créeztests/Feature/TrackingRuleTest.php. En plus des tests unitaires de la classe, vérifiez l’enregistrement et le traitement des messages via la Factory réelle.
Points à vérifier pour la maintenance à long terme
- Documenter comme API publique le nom de la règle, la signification et le nombre de paramètres, le caractère obligatoire et les types de valeurs acceptés.
- Tester les paramètres invalides, les valeurs absentes, les chaînes vides et
null, ainsi que les combinaisons avecnullable,sometimesetbail. - Vérifier les
messages()de l’utilisateur, les noms d’affichage des attributs, le changement de locale et la surcharge des traductions du package. - Pour les règles Dependent, vérifier avec plusieurs lignes et des tableaux imbriqués qu’aucune valeur d’une autre ligne n’est référencée.
- Vérifier l’enregistrement sur une Factory non résolue et déjà résolue, sans s’attendre à ce qu’il soit appliqué rétroactivement aux Validator déjà créés.
- Éviter les collisions de noms avec d’autres packages et ne pas mélanger un même nom entre enregistrements classique, Implicit et Dependent.
- Pour appliquer des changements aux workers de longue durée, redémarrer les processus, et ne pas laisser d’entrées ni d’informations utilisateur dans des handlers partagés.
Sources primaires consultées
- Documentation officielle de Laravel : règles personnalisées et règles Implicit
- Documentation officielle de Laravel : Service Provider et traductions des packages
- Factory : make, extend, extendImplicit, extendDependent, replacer
- Validator : conditions d’exécution, enregistrement des extensions, résolution des classes
- ValidationRuleParser : analyse des règles sous forme de chaîne
- FormatsMessages : priorité et remplacement des messages
- ValidationServiceProvider : enregistrement de validator en singleton
- ServiceProvider : callAfterResolving
- Tests officiels : transfert des extensions et du fallback depuis la Factory
- Tests officiels : règles en chaîne, classe, Implicit et Dependent