m1x0n / opis-json-schema-error-presenter
JSON schema error presenter for opis library
Installs: 699 880
Dependents: 5
Suggesters: 0
Security: 0
Stars: 17
Watchers: 2
Forks: 5
Open Issues: 2
Requires
- php: >=7.1
- ext-json: *
- opis/json-schema: ^1.0
Requires (Dev)
- dg/bypass-finals: ^1.1
- opis/json-schema: ^1.0
- phpstan/phpstan: ^1
- phpunit/phpunit: >=7.1 <10
- squizlabs/php_codesniffer: ^3.4
README
Warning
⚠️This library might become obsolote since new major version (2.0.0) of opis/json-schema already supports Error formating.
Customizable error presenter for json schema validation errors produced by opis/json-schema library: JSON schema implementation.
In other words it's a raw attempt to represent Opis\JsonSchema\ValidationError
collection in
human readable way.
Requirements
- php >= 7.1
- opis/json-schema
Installation
composer require m1x0n/opis-json-schema-error-presenter
Usage example
use Opis\JsonSchema\Schema; use Opis\JsonSchema\ValidationResult; use Opis\JsonSchema\Validator; use OpisErrorPresenter\Contracts\PresentedValidationError; use OpisErrorPresenter\Implementation\MessageFormatterFactory; use OpisErrorPresenter\Implementation\PresentedValidationErrorFactory; use OpisErrorPresenter\Implementation\ValidationErrorPresenter; require __DIR__ . '/../vendor/autoload.php'; $jsonSchema ='{ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "http://example.com/product.schema.json", "title": "Product", "description": "A product from Acme\'s catalog", "type": "object", "properties": { "productId": { "type": "integer", "minimum": 1 }, "productName": { "type": "string", "minLength": 3 }, "price": { "type": "object", "properties": { "amount": { "type": "integer", "minimum": 0, "maximum": 1000 }, "currency": { "type": "string", "enum": ["USD", "EUR", "BTC"] } }, "required": ["amount", "currency"] } }, "required": [ "productId", "productName", "price" ] }'; $data = '{ "productId": "123", "productName": "XX", "price": { "amount": 200, "currency": "GBP" } }'; $data = json_decode($data); $jsonSchema = Schema::fromJsonString($jsonSchema); $validator = new Validator(); // Get all errors. Yeah -1 here. /** @var ValidationResult $result */ $result = $validator->schemaValidation($data, $jsonSchema, -1); // Default strategy is AllErrors $presenter = new ValidationErrorPresenter( new PresentedValidationErrorFactory( new MessageFormatterFactory() ) ); $presented = $presenter->present(...$result->getErrors()); // Inspected presenter error print_r(array_map(static function (PresentedValidationError $error) { return $error->toArray(); }, $presented)); // Json-serializable echo json_encode($presented);
Output result example
Array
(
[0] => Array
(
[keyword] => type
[pointer] => productId
[message] => The attribute expected to be of type 'integer' but 'string' given.
)
[1] => Array
(
[keyword] => minLength
[pointer] => productName
[message] => The attribute length should be at least 3 characters.
)
[2] => Array
(
[keyword] => enum
[pointer] => price/currency
[message] => The attribute must be one of the following values: 'USD', 'EUR', 'BTC'.
)
)
Presenting Strategies
AllErrors
- shows all available presented errorsFirstError
- picks the first of the presented errorsBestMatchError
- evaluates best matching error
In order to specify strategy simply pass selected one to
PresentedValidationErrorFactory
, e.g:
$presenter = new ValidationErrorPresenter( new PresentedValidationErrorFactory( new MessageFormatterFactory() ), new BestMatchError() );
Custom translations
There is a possibility to have custom translations.
Currently there is only DefaultTranslator
which exposes some generic messages like:
The attribute length should be at least 3 characters
In order to replace or extend or come up with new translations MessageTranslator
interface
must be implemented.
For example:
<?php declare(strict_types=1); namespace Acme; use OpisErrorPresenter\Contracts\Keyword; use OpisErrorPresenter\Implementation\MessageFormatterFactory; use OpisErrorPresenter\Implementation\PresentedValidationErrorFactory; use OpisErrorPresenter\Implementation\Translators\DefaultTranslator; use OpisErrorPresenter\Implementation\ValidationErrorPresenter; class InternationalTranslator extends DefaultTranslator { protected $messages = []; private const DEFAULT_MESSAGE = 'The attribute is invalid'; public function __construct() { parent::__construct(); $this->loadMessages(); } public function translate(string $key, array $replacements = [], $locale = null): string { if ($locale && array_key_exists($locale, $this->messages)) { $message = $this->messages[$locale][$key] ?? self::DEFAULT_MESSAGE; return strtr($message, $replacements); } // Fallback on default locale return parent::translate($key, $replacements, $locale); } private function loadMessages(): void { /* Locales structure example: [ 'locale_1' => [ 'keyword' => 'translation_1' ... ], 'locale_2' => [ 'keyword' => 'translation_2' ... ], ... ] */ $this->messages = [ 'de_DE' => [ // The rest of other keywords ... Keyword::MIN_LENGTH => 'Die Attributlänge sollte mindestens betragen: min: Zeichen.' // ... ], 'ru_RU' => [ // ... Keyword::ENUM => 'Длина атрибута должна быть минимум :min: символов.' // .... ] ]; } } // Then configure presenter factory $presenter = new ValidationErrorPresenter( new PresentedValidationErrorFactory( new MessageFormatterFactory(), new InternationalTranslator() ) );
Locale resolving
For better experience with different localizations custom translator could be used alongside with automatic locale resolution.
Currently next locale resolvers are implemented:
NullLocaleResolver
as fallback to generic messages.HttpLocaleResolver
tries to detect locale based onHTTP_ACCEPT_LANGUAGE
header and requiresext-intl
to be installed.
It's also possible to implement custom locale resolver by implementing LocaleResolver
interface.
To set up HttpLocaleResolver
or custom-crafted one:
$presenter = new ValidationErrorPresenter( new PresentedValidationErrorFactory( new MessageFormatterFactory(), new InternationalTranslator(), new HttpLocaleResolver() ) );
Locale support
Currently there is a possibility to use locales defined in php arrays
via ArrayLocaleLoader
. See lang/en.php
.
In order to load more languages ValidationErrorPresenter
might be configured in following way:
$presenter = new ValidationErrorPresenter( new PresentedValidationErrorFactory( new MessageFormatterFactory(), new DefaultTranslator( (new ArrayLocaleLoader())->addPath('de', '/path_to_lang/lang/de.php') ) ) );
However it's possible to load locale strings from anywhere by implementing
LocaleLoader
interface.
Also presenter could be configured with single locale.
For doing that FixedLocaleResolver
will be helpful.
For example:
$presenter = new ValidationErrorPresenter( new PresentedValidationErrorFactory( new MessageFormatterFactory(), new DefaultTranslator( (new ArrayLocaleLoader())->addPath('cs', '../lang') ), new FixedLocaleResolver('cs') ) );
All the configurations might be slightly simplified by using DI-container.