An open API service indexing awesome lists of open source software.

https://github.com/m1x0n/opis-error-presenter

JSON-schema error presenter for opis/json-schema library
https://github.com/m1x0n/opis-error-presenter

json-schema opis php

Last synced: 5 months ago
JSON representation

JSON-schema error presenter for opis/json-schema library

Awesome Lists containing this project

README

          

opis-json-schema-error-presenter ![Build Status](https://github.com/m1x0n/opis-error-presenter/workflows/Test/badge.svg)
=====

### Warning
⚠️This library might become obsolote since new major version (2.0.0) of [opis/json-schema](https://github.com/opis/json-schema/releases/tag/2.0.0) already supports [Error formating](https://opis.io/json-schema/2.x/php-error-formatter.html).

Customizable error presenter for json schema validation errors produced
by [opis/json-schema](https://github.com/opis/json-schema) library: [JSON schema](http://json-schema.org/) 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

```bash
composer require m1x0n/opis-json-schema-error-presenter
```

### Usage example
```php
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 errors
- `FirstError` - picks the first of the presented errors
- `BestMatchError` - evaluates best matching error

In order to specify strategy simply pass selected one to
`PresentedValidationErrorFactory`, e.g:

```php
$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
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 on `HTTP_ACCEPT_LANGUAGE` header and requires `ext-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:
```php
$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:

```php
$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:
```php
$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.