Skip to content

A validation library for PHP that uses the notification pattern

License

Notifications You must be signed in to change notification settings

eugene-borovov/validation

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Validation

A validation library for PHP that uses the notification pattern.

Latest Version on Packagist Software License Build Status Coverage Status Quality Score Total Downloads

Table of contents

Requirements

  • PHP 7.2+ or 8.0+

Installation

composer require selective/validation

Usage

A notification is a collection of errors

In order to use a notification, you have to create the ValidationResult object. A ValidationResult can be really simple:

<?php

use Selective\Validation\Exception\ValidationException;
use Selective\Validation\ValidationResult;

$validationResult = new ValidationResult();

if (empty($data['username'])) {
    $validationResult->addError('username', 'Input required');
}

You can now test the ValidationResult and throw an exception if it contains errors.

<?php
if ($validationResult->fails()) {
    throw new ValidationException('Please check your input', $validationResult);
}

Validating form data

Login example:

<?php

use Selective\Validation\Exception\ValidationException;
use Selective\Validation\ValidationResult;

// ...

// Get all POST values
$data = (array)$request->getParsedBody();

$validation = new ValidationResult();

// Validate username
if (empty($data['username'])) {
    $validation->addError('username', 'Input required');
}

// Validate password
if (empty($data['password'])) {
    $validation->addError('password', 'Input required');
}

// Check validation result
if ($validation->fails()) {
    // Trigger error response (see validation middleware)
    throw new ValidationException('Please check your input', $validation);
}

Validating JSON

Validating a JSON request works like validating form data, because in PHP it's just an array from the request object.

<?php
use Selective\Validation\ValidationResult;

// Fetch json data from request as array
$jsonData = (array)$request->getParsedBody();

$validation = new ValidationResult();

// ...

if ($validation->fails()) {
    throw new ValidationException('Please check your input', $validation);
}

In vanilla PHP you can fetch the JSON request as follows:

<?php

$jsonData = (array)json_decode(file_get_contents('php://input'), true);

// ...

Regex

The Selective\Validation\Regex\ValidationRegex class allows you to validate if a given string conforms a defined regular expression.

Example usage:

use Selective\Validation\Factory\CakeValidationFactory;
use Selective\Validation\Regex\ValidationRegex;
// ...

$data = [ /* ... */ ];

$validationFactory = new CakeValidationFactory();
$validator = $validationFactory->createValidator();

$validator
    ->regex('id', ValidationRegex::ID, 'Invalid')
    ->regex('country', ValidationRegex::COUNTRY_ISO_2, 'Invalid country')
    ->regex('date_of_birth', ValidationRegex::DATE_DMY, 'Invalid date format');

Middleware

The ValidationExceptionMiddleware PSR-15 middleware catches all exceptions and converts it into a nice JSON response.

Slim 4 integration

Insert a container definition for ValidationExceptionMiddleware::class:

<?php

use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseFactoryInterface;
use Selective\Validation\Encoder\JsonEncoder;
use Selective\Validation\Middleware\ValidationExceptionMiddleware;
use Selective\Validation\Transformer\ErrorDetailsResultTransformer;
use Slim\App;
use Slim\Factory\AppFactory;
// ...

return [
    ValidationExceptionMiddleware::class => function (ContainerInterface $container) {
        $factory = $container->get(ResponseFactoryInterface::class);

        return new ValidationExceptionMiddleware(
            $factory, 
            new ErrorDetailsResultTransformer(), 
            new JsonEncoder()
        );
    },

    ResponseFactoryInterface::class => function (ContainerInterface $container) {
        $app = $container->get(App::class);

        return $app->getResponseFactory();
    },

    App::class => function (ContainerInterface $container) {
        AppFactory::setContainer($container);

        return AppFactory::create();
    },

    // ...

];

Add the ValidationExceptionMiddleware into your middleware stack:

<?php

use Selective\Validation\Middleware\ValidationExceptionMiddleware;
use Slim\Factory\AppFactory;

require_once __DIR__ . '/../vendor/autoload.php';

$app = AppFactory::create();

// ...

$app->add(ValidationExceptionMiddleware::class);

// ...

$app->run();

Usage in Slim

<?php

use Selective\Validation\ValidationException;
use Selective\Validation\ValidationResult;

$validation = new ValidationResult();

// Validate username
if (empty($data->username)) {
    $validation->addError('username', 'Input required');
}

// Check validation result
if ($validation->fails()) {
    // Trigger the validation middleware
    throw new ValidationException('Please check your input', $validation);
}

Validators

You can combine this library with a validator that is doing the actual validation of your input data.

The converter pattern makes it easy to map instances of one class into instances of another class.

CakePHP Validator

The cakephp/validation library provides features to build validators that can validate arbitrary arrays of data with ease.

Installation

composer require cakephp/validation

Usage

The Cake\Validation\Validator::validate() method returns a non-empty array when there are validation failures. The list of errors then can be converted into a ValidationResult using the Selective\Validation\Factory\CakeValidationFactory or Selective\Validation\Converter\CakeValidationConverter.

For example, if you want to validate a login form you could do the following:

use Selective\Validation\Factory\CakeValidationFactory;
use Selective\Validation\Exception\ValidationException;
// ...

// Within the Action class: fetch the request data, e.g. from a JSON request
$data = (array)$request->getParsedBody();

// Within the Application Service class: Do the validation
$validationFactory = new CakeValidationFactory();
$validator = $validationFactory->createValidator();

$validator
    ->notEmptyString('username', 'Input required')
    ->notEmptyString('password', 'Input required');

$validationResult = $validationFactory->createValidationResult(
    $validator->validate($data)
);

if ($validationResult->fails()) {
    throw new ValidationException('Please check your input', $validationResult);
}

Please note: The CakeValidationFactory should be injected via constructor.

Read more: https://odan.github.io/2020/10/18/slim4-cakephp-validation.html

Symfony Validator

Installation

composer require symfony/validator

Usage

<?php

use Selective\Validation\Converter\SymfonyValidationConverter;
use Selective\Validation\Exception\ValidationException;
use Selective\Validation\Regex\ValidationRegex;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Validation;

// Note: This is just an example. Don't use the $request object within the domain layer.
$formData = (array)$request->getParsedBody();

// ...

// Create a Symfony validator instance
$validator = Validation::createValidator();

// Add rules
$constraint = new Assert\Collection(
    [
        'first_name' => new Assert\NotBlank(['message' => 'Input required']),
        'last_name' => new Assert\NotBlank(['message' => 'Input required']),
        'mobile' => new Assert\Optional(
            [
                new Assert\Regex(
                    [
                        'pattern' => ValidationRegex::PHONE_NUMBER,
                        'message' => 'Invalid',
                    ]
                ),
            ]
        ),
        'comment' => new Assert\Optional(
            [
                new Assert\Length(['max' => 255, 'maxMessage' => 'Too long']),
            ]
        ),
        'email' => new Assert\Optional(
            [
                new Assert\Email(
                    [
                        'message' => 'Invalid',
                    ]
                ),
            ]
        )
    ]
);

$constraint->missingFieldsMessage = 'Input required';
$violations = $validator->validate($formData, $constraint);

// Convert symfony violations to ValidationResult
$validationResult = SymfonyValidationConverter::createValidationResult($violations);

// Optional: Do more complex validation and append it to the validation result
if ($this->existsUsername($formData['username'])) {
    $validationResult->addError('username', 'Username is already taken');
}

if ($validationResult->fails()) {
    throw new ValidationException('Please check your input', $validationResult);
}

Transformer

If you want to implement a custom response data structure, you can implement a custom transformer against the \Selective\Validation\Transformer\ResultTransformerInterface interface.

Example

<?php

namespace App\Transformer;

use Selective\Validation\Exception\ValidationException;
use Selective\Validation\Transformer\ResultTransformerInterface;
use Selective\Validation\ValidationResult;

final class MyValidationTransformer implements ResultTransformerInterface
{
    public function transform(
        ValidationResult $validationResult, 
        ValidationException $exception = null
    ): array {
        // Implement your own data structure for the response
        // ...

        return [];
    }
}

License

The MIT License (MIT). Please see License File for more information.

About

A validation library for PHP that uses the notification pattern

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • PHP 100.0%