Kończymy walidator, tak aby wszystko robił za nas automatycznie. Do dzieła.

Nasze formData i formRules:

<?php 

$form_rules =  [
    'email' => ['required', 'email'],
    'password' => ['required'],
    'confirmPassword' => ['required', 'match:password'],
    'name' => ['unique:people.name', 'maxLength:20'],
    'country' => ['in:USA,Poland'],
    'age' => ['min:18']
];


$form_data = [ 
    'email' => 'john.doe@gmail.com',
    'password' => md5('helloworld'),
    'confirmPassword' => md5('helloworld'),
    'name' => 'Janet',
    'country' => 'USA',
    'age' => 19
];

Poniżej interfejs i klasy z lekcji poprzednich:


interface RuleInterface
{
  public function validate(array $data, string $field, array $params): bool;

  public function getMessage(array $data, string $field, array $params): string;
}

class RequiredRule implements RuleInterface
{
 //(...)
}

class EmailRule implements RuleInterface
{
  //(...)
}

class UrlRule implements RuleInterface
{
  //(...)
}

class MatchRule implements RuleInterface
{
  //(...)
}

class ConfigINI {
//(...)
}

class PDOConnection {
//(...)
}

class UniqueRule implements RuleInterface
{
   //(...)
}

class InRule implements RuleInterface
{
  //(...)
}

class MinRule implements RuleInterface
{
 //(...)
}

class LengthMaxRule implements RuleInterface
{
  //(...)
}

Zrobimy sobie autoloading już niedługo. Na razie jednak piszmy naszą klasę:

class Validator
{
  private array $rules = [];

  public function add(string $alias, RuleInterface $rule)
  {
    $this->rules[$alias] = $rule;
    return $this;
  }

  public function validate(array $formData, array $fields)
  {
    $errors = [];

    if(count($errors))
      print_r($errors);
    
  }
}

Metodę validate jeszcze sobie naprawimy, na razie jednak utwórzmy walidator:

$validator = new Validator;

$validator->add('required', new RequiredRule)
->add('email', new EmailRule)
->add('min', new MinRule)
->add('in', new InRule)
->add('match', new MatchRule)
->add('maxLength', new LengthMaxRule)
->add('unique', new UniqueRule);

$validator->validate($form_data, $form_rules);

Fluent-interface (return this) to świetna sprawa, możemy metody wywoływać łańcuchowo. Teraz tylko naprawmy validate:

class Validator
{
  private array $rules = [];

  //(...)

  public function validate(array $formData, array $fields)
  {
    $errors = [];

    foreach ($fields as $fieldName => $rules) {
      foreach ($rules as $rule) {
        $ruleParams = [];

        if (str_contains($rule, ':')) {
          [$rule, $ruleParams] = explode(':', $rule);
          $ruleParams = explode(',', $ruleParams);
        }

        $ruleValidator = $this->rules[$rule];

        if ($ruleValidator->validate($formData, $fieldName, $ruleParams)) {
          continue;
        }

        $errors[$fieldName][] = $ruleValidator->getMessage(
          $formData,
          $fieldName,
          $ruleParams
        );
      }
    }

    if (count($errors)) {
      print_r($errors);
    }
  }
}

Nie różni się to nic od parsowania z lekcji poprzedniej, tylko tym razem używamy danych z form_rules zamias je wyświetlać.

Jeżeli nie widzimy nic to znaczy, że nasze dane przechodzą walidację.

Popsujmy coś – u mnie jest to zamiana 'Janet’ na 'Jane’:


$form_rules =  [
    'email' => ['required', 'email'],
    'password' => ['required'],
    'confirmPassword' => ['required', 'match:password'],
    'name' => ['unique:people.name', 'maxLength:20'],
    'country' => ['in:USA,Poland'],
    'age' => ['min:18']
];


$form_data = [ 
    'email' => 'john.doe@gmail.com',
    'password' => md5('helloworld'),
    'confirmPassword' => md5('helloworld'),
    'name' => 'Jane',
    'country' => 'USA',
    'age' => 19
];


//(...)

$validator = new Validator;

//(...)

$validator->validate($form_data, $form_rules);
//Array ( [name] => Array ( [0] => Field people.name already in use! ) )

Jedyne co nam zostało to errory nie wyświetlać jako print_r, ale w prawdziwy Error wrzucać i obsługiwać ich wyświetlanie, ale tym zajmiemy się później.