News in Nette Schema 1.2
Nette Schema is the youngest addition to the Nette family. The library was originally created for the needs of Nette DI, ie to validate and normalize the input configuration files and to inform about possible errors.
Nette Schema was an attempt to find a comprehensible and economical language for describing data structures. The first version covered all the needs of Nette DI and further developments then addressed the requirements arising from new usages.
Let's recall how Schema is used. For example, we will create
a schema, where the input should be an array (or an object with public
properties, in the terminology Schema it is a structure), with a mandatory
element foo
of type bool
and an optional numeric
bar
and string baz
:
use Nette\Schema\Expect;
$schema = Expect::structure([
'foo' => Expect::bool()->required(),
'bar' => Expect::int()->default(123),
'baz' => Expect::string(),
]);
We validate the input $data
:
$processor = new Nette\Schema\Processor;
$normalized = $processor->process($schema, $data);
// in case of error it throws Nette\Schema\ValidationException
Inside structure()
, unless otherwise stated, each element:
- is optional
- has a default value of
null
(resp.[]
in the case of array)
Default value of null
does not mean that null
it
also accepted – this would have to be stated in the definition, for example
Expect::int()->nullable()
.
So for input $data = ['foo' => true]
it returns object
{foo: true, bar: 123, baz: null}
, see demo. So we can access
$normalized->bar
without worrying that the property would be
undefined.
Default structure values
If the element accepts null
and at the same time has a default
value of null
, it is not possible to distinguish the default value
from the passed value. If necessary, the default values can be completely
omitted since version 1.2 using skipDefaults()
:
$schema = Expect::structure([
...
])->skipDefaults();
So for input $data = ['foo' => true]
it returns object
{foo: true}
.
Mandatory / optional elements
As mentioned, only elements marked as required()
are mandatory.
But what if the structure is deeper? Is element bar
mandatory?
$schema = Expect::structure([
'foo' => Expect::int(),
'inner' => Expect::structure([
'bar' => Expect::string()->required(),
]),
]);
In this case, the Schema tries to behave in such a way that it does not
surprise the programmer, so that the element bar
is mandatory,
although strictly technically the element inner
would also have to
be marked as mandatory. The Schema makes it mandatory implicitly. See demo.
The structure can now be marked as truly optional using
requred(false)
.
$schema = Expect::structure([
'foo' => Expect::int(),
'inner' => Expect::structure([
'bar' => Expect::string()->required(),
])->required(false),
]);
In this case, the data may not contain a key inner
, but if it
does, a subkey bar
must be present.
Merging elements in arrays
One of the things that arose from the behavior of Nette DI, but proved
surprising for users of a separate Schema, was the merging of default and passed
values in the case of arrays, ie Expect::array()
,
list()
, etc. In Nette Schema 2, this behavior will be removed, it
can now be turned off:
Expect::list([1, 2, 3])->mergeDefaults(false)
arrayOf() and keys
The arrayOf
describes what elements an array can contain. For
example, an array whose elements can only be strings is written as
Expect::arrayOf('string')
.
It is now possible to specify the key type with the second parameter:
$schema = Expect::arrayOf('string', 'int');
Default value of anyOf()
It is now easier to define the default value of enumeration
anyOf()
by declaring the first item as the default:
Expect::anyOf(
Expect::string()->default('def'),
false
)->firstIsDefault();
The default value of such an element is 'def'
.
Richer error messages
If the data does not pass validation, the Nette\Schema\ValidationException
exception is thrown. Its method getMessages()
returns an array of
all error messages.
New is the method getMessageObjects()
, which returns array of
errors as Nette\Schema\Message
objects. They broke the report message into basic information:
$messages = $e->getMessageObjects();
$message = $messages[0];
$message->message // message text with %placeholders% for variables
$message->code // error code, one of the constants in the Message class
$message->path // a arrays referring to the element to which the error relates
$message->variables // variables to replace %placeholders%
This makes it easy to translate messages into other languages.
Deprecated
And finally. Elements can be referred to as deprecated with an optional error
message. The warning list then returns getWarnings()
.
use Nette\Schema\Expect;
$schema = Expect::structure([
'foo' => Expect::bool()->deprecated('Use `bar` instead'),
'bar' => Expect::bool(),
]);
$normalized = $processor->process($schema, $data);
$warnings = $processor->getWarnings(); // returns an array of strings
Sign in to submit a comment