Schema: Validación de datos
Una biblioteca práctica para la validación y normalización de estructuras de datos con respecto a un esquema dado con una API inteligente y fácil de entender.
Instalación:
Uso básico
En la variable $schema
tenemos un esquema de validación (qué significa esto exactamente y cómo crearlo lo
diremos más adelante) y en la variable $data
tenemos una estructura de datos que queremos validar y normalizar.
Pueden ser, por ejemplo, datos enviados por el usuario a través de una API, fichero de configuración, etc.
De esta tarea se encarga la clase Nette\Schema\Processor, que procesa la entrada y devuelve los datos normalizados o lanza una excepción Nette\Schema\ValidationException en caso de error.
El método $e->getMessages()
devuelve un array con todas las cadenas de mensajes y
$e->getMessageObjects()
devuelve todos los mensajes como objetos Nette\Schema\Message.
Definición del esquema
Y ahora vamos a crear un esquema. La clase Nette\Schema\Expect se utiliza para definirlo, en realidad
definimos las expectativas de cómo deben ser los datos. Digamos que los datos de entrada deben ser una estructura (por ejemplo un
array) que contenga elementos processRefund
de tipo bool y refundAmount
de tipo int.
Creemos que la definición del esquema parece clara, incluso si la ves por primera vez.
Enviemos los siguientes datos para su validación:
La salida, es decir, el valor $normalized
, es el objeto stdClass
. Si queremos que la salida sea un
array, añadimos un cast al esquema Expect::structure([...])->castTo('array')
.
Todos los elementos de la estructura son opcionales y tienen un valor por defecto null
. Ejemplo:
El hecho de que el valor por defecto sea null
no significa que se aceptaría en los datos de entrada
'processRefund' => null
. No, la entrada debe ser booleana, es decir, sólo true
o false
.
Tendríamos que permitir explícitamente null
a través de Expect::bool()->nullable()
.
Un elemento puede hacerse obligatorio mediante Expect::bool()->required()
. Cambiamos el valor por defecto a
false
utilizando Expect::bool()->default(false)
o en breve utilizando
Expect::bool(false)
.
¿Y si quisiéramos aceptar 1
and 0
además de booleanos? Entonces enumeramos los valores permitidos,
que también normalizaremos a booleanos:
Ahora ya sabes lo básico de cómo se define el esquema y cómo se comportan los elementos individuales de la estructura. Ahora mostraremos qué otros elementos se pueden utilizar para definir un esquema.
Tipos de datos: type()
Todos los tipos de datos estándar de PHP pueden ser listados en el esquema:
Y luego todos los tipos soportados por los Validadores a
través de Expect::type('scalar')
o abreviado Expect::scalar()
. También se aceptan nombres de clases
o interfaces, por ejemplo Expect::type('AddressEntity')
.
También se puede utilizar la notación de unión:
El valor por defecto es siempre null
excepto para array
y list
, donde es un array
vacío. (Una lista es una matriz indexada en orden ascendente de claves numéricas a partir de cero, es decir, una matriz no
asociativa).
Matriz de valores: arrayOf() listOf()
El array es una estructura demasiado general, es más útil especificar exactamente qué elementos puede contener. Por ejemplo, un array cuyos elementos sólo pueden ser cadenas:
El segundo parámetro puede utilizarse para especificar claves (desde la versión 1.2):
La lista es una matriz indexada:
El parámetro también puede ser un esquema, por lo que podemos escribir:
El valor por defecto es un array vacío. Si especifica el valor por defecto, se fusionará con los datos pasados. Esto puede
desactivarse utilizando mergeDefaults(false)
(desde la versión 1.1).
Enumeración: anyOf()
anyOf()
es un conjunto de valores o esquemas que un valor puede ser. A continuación se muestra cómo escribir
una matriz de elementos que pueden ser 'a'
, true
, o null
:
Los elementos de la enumeración también pueden ser esquemas:
El método anyOf()
acepta variantes como parámetros individuales, no como array. Para pasarle un array de
valores, utilice el operador de desempaquetado anyOf(...$variants)
.
El valor por defecto es null
. Utilice el método firstIsDefault()
para que el primer elemento sea el
valor predeterminado:
Estructuras
Las estructuras son objetos con claves definidas. Cada uno de estos pares clave ⇒ valor se denomina „propiedad“:
Las estructuras aceptan matrices y objetos y devuelven objetos stdClass
.
Por defecto, todas las propiedades son opcionales y tienen un valor por defecto de null
. Puede definir propiedades
obligatorias utilizando required()
:
Si no desea mostrar propiedades con un valor predeterminado, utilice skipDefaults()
:
Aunque null
es el valor por defecto de la propiedad optional
, no está permitido en los datos de
entrada (el valor debe ser una cadena). Las propiedades que aceptan null
se definen utilizando
nullable()
:
El array de todas las propiedades de la estructura es devuelto por el método getShape()
.
Por defecto, no puede haber elementos adicionales en los datos de entrada:
Lo cual podemos cambiar con otherItems()
. Como parámetro, especificaremos el esquema para cada
elemento extra:
Puede crear una nueva estructura derivando de otra mediante extend()
:
Matriz
Un array con claves definidas. Se aplican las mismas reglas que para las estructuras.
También puedes definir una matriz indexada, conocida como tupla:
Depreciaciones
Puedes eliminar una propiedad utilizando el método deprecated([string $message])
método. Los avisos de
desaprobación son devueltos por $processor->getWarnings()
:
Rangos: min() max()
Utilice min()
y max()
para limitar el número de elementos de las matrices:
Para cadenas, limita su longitud:
Para los números, limita su valor:
Por supuesto, es posible mencionar sólo min()
, o sólo max()
:
Expresiones regulares: pattern()
Utilizando pattern()
, puede especificar una expresión regular con la que debe coincidir toda la cadena de
entrada (es decir, como si estuviera envuelta en caracteres ^
a $
):
Aserciones personalizadas: assert()
Puede añadir cualquier otra restricción utilizando assert(callable $fn)
.
O:
Puede añadir su propia descripción para cada aserción. Formará parte del mensaje de error.
El método puede llamarse repetidamente para añadir múltiples restricciones. Puede entremezclarse con llamadas a
transform()
y castTo()
.
Transformación: transform()
Los datos validados correctamente pueden modificarse mediante una función personalizada:
El método puede llamarse repetidamente para añadir múltiples transformaciones. Puede entremezclarse con llamadas a
assert()
y castTo()
. Las operaciones se ejecutarán en el orden en que se declaren:
El método transform()
puede transformar y validar el valor simultáneamente. Esto suele ser más sencillo y menos
redundante que encadenar transform()
y assert()
. Para ello, la función recibe un objeto Context con un método addError()
, que puede
utilizarse para añadir información sobre problemas de validación:
Casting: castTo()
Los datos validados correctamente pueden ser emitidos:
Además de los tipos nativos de PHP, también se puede hacer cast a clases. Distingue si se trata de una clase simple sin constructor o de una clase con constructor. Si la clase no tiene constructor, se crea una instancia de la misma y todos los elementos de la estructura se escriben en sus propiedades:
Si la clase tiene un constructor, los elementos de la estructura se pasan como parámetros con nombre al constructor:
El moldeado combinado con un parámetro escalar crea un objeto y pasa el valor como único parámetro al constructor:
Normalización: before()
Antes de la validación propiamente dicha, los datos pueden normalizarse utilizando el método before()
. Como
ejemplo, tengamos un elemento que debe ser un array de cadenas (ej. ['a', 'b', 'c']
), pero que recibe la entrada en
forma de cadena a b c
:
Asignación a objetos: from()
Se puede generar esquema de estructura a partir de la clase. Ejemplo:
También se admiten clases anónimas:
Dado que la información obtenida de la definición de la clase puede no ser suficiente, puede añadir un esquema personalizado para los elementos con el segundo parámetro: