# Validation

<div id="introduction"></div>

## Introduction

Nylo v7 fournit un systeme de validation construit autour de la classe `FormValidator`. Vous pouvez valider des donnees dans les pages en utilisant la methode `check()`, ou utiliser `FormValidator` directement pour la validation autonome et au niveau des champs.

``` dart
// Valider des donnees dans une NyPage/NyState avec check()
check((validate) {
  validate.that('user@example.com').email();
  validate.that('Anthony')
              .capitalized()
              .maxLength(50);
}, onSuccess: () {
  print("All validations passed!");
});

// FormValidator avec des champs de formulaire
Field.text("Email", validator: FormValidator.email())
```

<div id="validating-data"></div>

## Valider des donnees avec check()

Dans n'importe quel `NyPage`, utilisez la methode `check()` pour valider des donnees. Elle accepte un callback qui recoit une liste de validateurs. Utilisez `.that()` pour ajouter des donnees et chainer les regles :

``` dart
class _RegisterPageState extends NyPage<RegisterPage> {

  void handleForm() {
    check((validate) {
      validate.that(_emailController.text, label: "Email").email();
      validate.that(_passwordController.text, label: "Password")
          .notEmpty()
          .password(strength: 2);
    }, onSuccess: () {
      // Toutes les validations ont reussi
      _submitForm();
    }, onValidationError: (FormValidationResponseBag bag) {
      // La validation a echoue
      print(bag.firstErrorMessage);
    });
  }
}
```

### Fonctionnement de check()

1. `check()` cree une `List<FormValidator>` vide
2. Votre callback utilise `.that(data)` pour ajouter un nouveau `FormValidator` avec des donnees a la liste
3. Chaque `.that()` retourne un `FormValidator` sur lequel vous pouvez chainer des regles
4. Apres le callback, chaque validateur de la liste est verifie
5. Les resultats sont collectes dans un `FormValidationResponseBag`

### Valider plusieurs champs

``` dart
check((validate) {
  validate.that(_nameController.text, label: "Name").notEmpty().capitalized();
  validate.that(_emailController.text, label: "Email").email();
  validate.that(_phoneController.text, label: "Phone").phoneNumberUs();
  validate.that(_ageController.text, label: "Age").numeric().minValue(18);
}, onSuccess: () {
  _submitForm();
});
```

Le parametre optionnel `label` definit un nom lisible utilise dans les messages d'erreur (par exemple, "The Email must be a valid email address.").

<div id="validation-results"></div>

## Resultats de validation

La methode `check()` retourne un `FormValidationResponseBag` (une `List<FormValidationResult>`), que vous pouvez aussi inspecter directement :

``` dart
FormValidationResponseBag bag = check((validate) {
  validate.that(_emailController.text, label: "Email").email();
  validate.that(_passwordController.text, label: "Password")
      .password(strength: 1);
});

if (bag.isValid) {
  print("All valid!");
} else {
  // Obtenir le premier message d'erreur
  String? error = bag.firstErrorMessage;
  print(error);

  // Obtenir tous les resultats echoues
  List<FormValidationResult> errors = bag.validationErrors;

  // Obtenir tous les resultats reussis
  List<FormValidationResult> successes = bag.validationSuccess;
}
```

### FormValidationResult

Chaque `FormValidationResult` represente le resultat de la validation d'une seule valeur :

``` dart
FormValidator validator = FormValidator(data: "test@email.com")
    .email()
    .maxLength(50);

FormValidationResult result = validator.check();

if (result.isValid) {
  print("Valid!");
} else {
  // Premier message d'erreur
  String? message = result.getFirstErrorMessage();

  // Tous les messages d'erreur
  List<String> messages = result.errorMessages();

  // Reponses d'erreur
  List<FormValidationError> errors = result.errorResponses;
}
```

<div id="using-form-validator"></div>

## Utiliser FormValidator

`FormValidator` peut etre utilise de maniere autonome ou avec des champs de formulaire.

### Utilisation autonome

``` dart
// Utilisation d'un constructeur nomme
FormValidator validator = FormValidator.email();
FormValidationResult result = validator.check("user@example.com");

if (result.isValid) {
  print("Email is valid");
} else {
  String? errorMessage = result.getFirstErrorMessage();
  print("Error: $errorMessage");
}
```

### Avec des donnees dans le constructeur

``` dart
FormValidator validator = FormValidator(data: "user@example.com", attribute: "Email")
    .email()
    .maxLength(50);

FormValidationResult result = validator.check();
print(result.isValid); // true
```

<div id="form-validator-named-constructors"></div>

## Constructeurs nommes de FormValidator

Nylo v7 fournit des constructeurs nommes pour les validations courantes :

``` dart
// Validation d'email
FormValidator.email(message: "Please enter a valid email")

// Validation de mot de passe (force 1 ou 2)
FormValidator.password(strength: 1)
FormValidator.password(strength: 2, message: "Password too weak")

// Numeros de telephone
FormValidator.phoneNumberUk()
FormValidator.phoneNumberUs()

// Validation d'URL
FormValidator.url()

// Contraintes de longueur
FormValidator.minLength(5, message: "Too short")
FormValidator.maxLength(100, message: "Too long")

// Contraintes de valeur
FormValidator.minValue(18, message: "Must be 18+")
FormValidator.maxValue(100)

// Contraintes de taille (pour listes/chaines)
FormValidator.minSize(2, message: "Select at least 2")
FormValidator.maxSize(5)

// Non vide
FormValidator.notEmpty(message: "This field is required")

// Contient des valeurs
FormValidator.contains(['option1', 'option2'])

// Commence/se termine par
FormValidator.beginsWith("https://")
FormValidator.endsWith(".com")

// Verifications booleennes
FormValidator.booleanTrue(message: "Must accept terms")
FormValidator.booleanFalse()

// Numerique
FormValidator.numeric()

// Validations de dates
FormValidator.date()
FormValidator.dateInPast()
FormValidator.dateInFuture()
FormValidator.dateAgeIsOlder(18, message: "Must be 18+")
FormValidator.dateAgeIsYounger(65)

// Casse du texte
FormValidator.uppercase()
FormValidator.lowercase()
FormValidator.capitalized()

// Formats de localisation
FormValidator.zipcodeUs()
FormValidator.postcodeUk()

// Motif regex
FormValidator.regex(r'^[A-Z]{3}\d{4}$', message: "Invalid format")

// Validation personnalisee
FormValidator.custom(
  message: "Invalid value",
  validate: (data) => data != null,
)
```

<div id="chaining-validation-rules"></div>

## Chainer les regles de validation

Chainez plusieurs regles de maniere fluide en utilisant les methodes d'instance. Chaque methode retourne le `FormValidator`, vous permettant d'accumuler les regles :

``` dart
FormValidator validator = FormValidator()
    .notEmpty()
    .email()
    .maxLength(50);

FormValidationResult result = validator.check("user@example.com");

if (!result.isValid) {
  List<String> errors = result.errorMessages();
  print(errors);
}
```

Tous les constructeurs nommes ont des methodes d'instance chainables correspondantes :

``` dart
FormValidator()
    .notEmpty(message: "Required")
    .email(message: "Invalid email")
    .minLength(5, message: "Too short")
    .maxLength(100, message: "Too long")
    .beginsWith("user", message: "Must start with 'user'")
    .lowercase(message: "Must be lowercase")
```

<div id="nullable-fields"></div>

## Champs nullables

Utilisez `.nullable()` pour marquer un validateur comme optionnel. Lorsqu'il est nullable, une valeur nulle ou vide passe automatiquement la validation -- toutes les autres regles ne sont appliquees que si la valeur est presente.

``` dart
// Le surnom est optionnel, mais s'il est fourni il doit avoir 3-20 caracteres
Field.text(
  "Nickname",
  validator: FormValidator()
      .minLength(3)
      .maxLength(20)
      .nullable(),
)
```

Sans `.nullable()`, un champ vide echouerait a la regle `minLength`. Avec `.nullable()`, laisser le champ vide passe la validation.

Ceci est utile pour les champs de profil optionnels, les coordonnees secondaires ou tout champ qui n'est valide que lorsque l'utilisateur le remplit. Voir la [documentation des formulaires](/docs/7.x/forms) pour savoir comment `nullable()` s'integre avec les widgets `Field`.

<div id="custom-validation"></div>

## Validation personnalisee

### Regle personnalisee (en ligne)

Utilisez `.custom()` pour ajouter une logique de validation en ligne :

``` dart
FormValidator.custom(
  message: "Username already taken",
  validate: (data) {
    // Retourner true si valide, false si invalide
    return !_takenUsernames.contains(data);
  },
)
```

Ou chainez-la avec d'autres regles :

``` dart
FormValidator()
    .notEmpty()
    .custom(
      message: "Must start with a letter",
      validate: (data) => RegExp(r'^[a-zA-Z]').hasMatch(data.toString()),
    )
```

### Validation d'egalite

Verifiez si une valeur correspond a une autre :

``` dart
FormValidator()
    .notEmpty()
    .equals(
      _passwordController.text,
      message: "Passwords must match",
    )
```

<div id="form-validator-with-fields"></div>

## Utiliser FormValidator avec les champs

`FormValidator` s'integre avec les widgets `Field` dans les formulaires. Passez un validateur au parametre `validator` :

``` dart
class RegisterForm extends NyFormData {
  RegisterForm({String? name}) : super(name ?? "register");

  @override
  fields() => [
        Field.text(
          "Name",
          autofocus: true,
          validator: FormValidator.notEmpty(),
        ),
        Field.email("Email", validator: FormValidator.email()),
        Field.password(
          "Password",
          validator: FormValidator.password(strength: 1),
        ),
      ];
}
```

Vous pouvez egalement utiliser des validateurs chaines avec les champs :

``` dart
Field.text(
  "Username",
  validator: FormValidator()
      .notEmpty(message: "Username is required")
      .minLength(3, message: "At least 3 characters")
      .maxLength(20, message: "At most 20 characters"),
)

Field.slider(
  "Rating",
  validator: FormValidator.minValue(4, message: "Rating must be at least 4"),
)
```

<div id="validation-rules"></div>

## Regles de validation disponibles

Toutes les regles disponibles pour `FormValidator`, a la fois en tant que constructeurs nommes et methodes chainables :

| Regle | Constructeur nomme | Methode chainable | Description |
|-------|-------------------|------------------|-------------|
| Email | `FormValidator.email()` | `.email()` | Valide le format d'email |
| Password | `FormValidator.password(strength: 1)` | `.password(strength: 1)` | Force 1 : 8+ caracteres, 1 majuscule, 1 chiffre. Force 2 : + 1 caractere special |
| Not Empty | `FormValidator.notEmpty()` | `.notEmpty()` | Ne peut pas etre vide |
| Min Length | `FormValidator.minLength(5)` | `.minLength(5)` | Longueur minimale de la chaine |
| Max Length | `FormValidator.maxLength(100)` | `.maxLength(100)` | Longueur maximale de la chaine |
| Min Value | `FormValidator.minValue(18)` | `.minValue(18)` | Valeur numerique minimale (fonctionne aussi sur la longueur des chaines, listes, maps) |
| Max Value | `FormValidator.maxValue(100)` | `.maxValue(100)` | Valeur numerique maximale |
| Min Size | `FormValidator.minSize(2)` | `.minSize(2)` | Taille minimale pour les listes/chaines |
| Max Size | `FormValidator.maxSize(5)` | `.maxSize(5)` | Taille maximale pour les listes/chaines |
| Contains | `FormValidator.contains(['a', 'b'])` | `.contains(['a', 'b'])` | La valeur doit contenir l'une des valeurs donnees |
| Begins With | `FormValidator.beginsWith("https://")` | `.beginsWith("https://")` | La chaine doit commencer par le prefixe |
| Ends With | `FormValidator.endsWith(".com")` | `.endsWith(".com")` | La chaine doit se terminer par le suffixe |
| URL | `FormValidator.url()` | `.url()` | Valide le format d'URL |
| Numeric | `FormValidator.numeric()` | `.numeric()` | Doit etre un nombre |
| Boolean True | `FormValidator.booleanTrue()` | `.booleanTrue()` | Doit etre `true` |
| Boolean False | `FormValidator.booleanFalse()` | `.booleanFalse()` | Doit etre `false` |
| Date | `FormValidator.date()` | `.date()` | Doit etre une date valide |
| Date In Past | `FormValidator.dateInPast()` | `.dateInPast()` | La date doit etre dans le passe |
| Date In Future | `FormValidator.dateInFuture()` | `.dateInFuture()` | La date doit etre dans le futur |
| Age Is Older | `FormValidator.dateAgeIsOlder(18)` | `.dateAgeIsOlder(18)` | L'age doit etre superieur a N |
| Age Is Younger | `FormValidator.dateAgeIsYounger(65)` | `.dateAgeIsYounger(65)` | L'age doit etre inferieur a N |
| Capitalized | `FormValidator.capitalized()` | `.capitalized()` | La premiere lettre doit etre en majuscule |
| Lowercase | `FormValidator.lowercase()` | `.lowercase()` | Tous les caracteres doivent etre en minuscules |
| Uppercase | `FormValidator.uppercase()` | `.uppercase()` | Tous les caracteres doivent etre en majuscules |
| Phone US | `FormValidator.phoneNumberUs()` | `.phoneNumberUs()` | Format de numero de telephone americain |
| Phone UK | `FormValidator.phoneNumberUk()` | `.phoneNumberUk()` | Format de numero de telephone britannique |
| Zipcode US | `FormValidator.zipcodeUs()` | `.zipcodeUs()` | Format de code postal americain |
| Postcode UK | `FormValidator.postcodeUk()` | `.postcodeUk()` | Format de code postal britannique |
| Regex | `FormValidator.regex(r'pattern')` | `.regex(r'pattern')` | Doit correspondre au motif regex |
| Equals | — | `.equals(otherValue)` | Doit etre egal a une autre valeur |
| Custom | `FormValidator.custom(validate: fn)` | `.custom(validate: fn)` | Fonction de validation personnalisee |
| Nullable | — | `.nullable()` | Les valeurs nulles ou vides passent automatiquement ; les regles ne s'appliquent que si une valeur est presente |

Toutes les regles acceptent un parametre optionnel `message` pour personnaliser le message d'erreur.

<div id="creating-custom-validation-rules"></div>

## Creer des regles de validation personnalisees

Pour creer une regle de validation reutilisable, etendez la classe `FormRule` :

``` dart
class FormRuleUsername extends FormRule {
  @override
  String? rule = "username";

  @override
  String? message = "The {{attribute}} must be a valid username.";

  FormRuleUsername({String? message}) {
    if (message != null) {
      this.message = message;
    }
  }

  @override
  bool validate(data) {
    if (data is! String) return false;
    // Nom d'utilisateur : alphanumerique, tirets bas, 3-20 caracteres
    return RegExp(r'^[a-zA-Z0-9_]{3,20}$').hasMatch(data);
  }
}
```

Utilisez `{{attribute}}` comme espace reserve dans le `message` — il sera remplace par le label du champ au moment de l'execution.

### Utiliser un FormRule personnalise

Ajoutez votre regle personnalisee a un `FormValidator` en utilisant `FormValidator.rule()` :

``` dart
FormValidator validator = FormValidator.rule([
  FormRuleNotEmpty(),
  FormRuleUsername(),
]);

FormValidationResult result = validator.check("my_username");
```

Ou utilisez la methode `.custom()` pour des regles ponctuelles sans creer de classe :

``` dart
FormValidator()
    .notEmpty()
    .custom(
      message: "Username must start with a letter",
      validate: (data) => RegExp(r'^[a-zA-Z]').hasMatch(data.toString()),
    )
```
