Una alternativa al uso del validador Callback
es escribir nuestra propia clase
validadora, implementando la interfaz ValidatorInterface
. Y luego, este validador
se puede usar en los formulario de nuestra aplicación web.
Para demostrar como crear nuestro propio validador, escribiremos la clase
PhoneValidator
, encapsulando el algoritmo de validación que usamos en el ejemplo
del validador Callback
.
Como podemos recordar, la clase concreta base para todos los validadores estándares es la clase
AbstractValidator
. Por analogía, también derivaremos nuestro validador personalizadoPhoneValidator
de esta clase base.
Planeamos tener los siguientes métodos en nuestra clase validadora PhoneValidator
(ver tabla 9.15):
Nombre del método | Descripción |
---|---|
__construct($options) |
Constructor. Acepta un argumento opcional, $options , que es necesario para colocar las opciones del validador. |
setFormat($format) |
Coloca la opción: formato de número de teléfono. |
getFormat() |
Regresa la opción del formato de número de telefónico. |
isValid($value) |
Regresa true cuando el valor es un número de teléfono valido, de lo contrario regresa false . |
getMessages() |
Si la validación falla, el método regresa un arreglo de mensajes de error. |
Para la clase PhoneValidator
, tendremos tres posibles mensajes de error:
Para comenzar, creamos el archivo PhoneValidator.php en la carpeta Validator que está dentro de la carpeta fuente del módulo 38. Colocamos el siguiente código dentro del archivo:
38) La clase PhoneValidator
se puede considerar como
un modelo de servicio, porque su objetivo es procesar
datos y no guardarlos. Por convención, guardamos el
validador personalizado dentro de la carpeta Validator
.
<?php
namespace Application\Validator;
use Zend\Validator\AbstractValidator;
// This validator class is designed for checking a phone number for
// conformance to the local or to the international format.
class PhoneValidator extends AbstractValidator
{
// Phone format constants.
const PHONE_FORMAT_LOCAL = 'local'; // Local phone format.
const PHONE_FORMAT_INTL = 'intl'; // International phone format.
// Available validator options.
protected $options = [
'format' => self::PHONE_FORMAT_INTL
];
// Validation failure message IDs.
const NOT_SCALAR = 'notScalar';
const INVALID_FORMAT_INTL = 'invalidFormatIntl';
const INVALID_FORMAT_LOCAL = 'invalidFormatLocal';
// Validation failure messages.
protected $messageTemplates = [
self::NOT_SCALAR => "The phone number must be a scalar value",
self::INVALID_FORMAT_INTL => "The phone number must be in international format",
self::INVALID_FORMAT_LOCAL => "The phone number must be in local format",
];
// Constructor.
public function __construct($options = null)
{
// Set filter options (if provided).
if(is_array($options)) {
if(isset($options['format']))
$this->setFormat($options['format']);
}
// Call the parent class constructor.
parent::__construct($options);
}
// Sets phone format.
public function setFormat($format)
{
// Check input argument.
if($format!=self::PHONE_FORMAT_LOCAL &&
$format!=self::PHONE_FORMAT_INTL) {
throw new \Exception('Invalid format argument passed.');
}
$this->options['format'] = $format;
}
// Validates a phone number.
public function isValid($value)
{
if(!is_scalar($value)) {
$this->error(self::NOT_SCALAR);
return false; // Phone number must be a scalar.
}
// Convert the value to string.
$value = (string)$value;
$format = $this->options['format'];
// Determine the correct length and pattern of the phone number,
// depending on the format.
if($format == self::PHONE_FORMAT_INTL) {
$correctLength = 16;
$pattern = '/^\d \(\d{3}\) \d{3}-\d{4}$/';
} else { // self::PHONE_FORMAT_LOCAL
$correctLength = 8;
$pattern = '/^\d{3}-\d{4}$/';
}
// First check phone number length
$isValid = false;
if(strlen($value)==$correctLength) {
// Check if the value matches the pattern.
if(preg_match($pattern, $value))
$isValid = true;
}
// If there was an error, set error message.
if(!$isValid) {
if($format==self::PHONE_FORMAT_INTL)
$this->error(self::INVALID_FORMAT_INTL);
else
$this->error(self::INVALID_FORMAT_LOCAL);
}
// Return validation result.
return $isValid;
}
}
En la línea 2, podemos ver que la clase validadora pertenece al espacio de nombres
Application\Validator
.
En la línea 8, definimos la clase PhoneValidator
. Derivamos nuestra clase validadora
de la clase base AbstractValidator
para reusar la funcionalidad que provee. La
línea 4 contiene el alias para la clase AbstractValidator
.
Entre las líneas 11-12, por conveniencia, definimos las constantes para el formato
de número de teléfono (PHONE_FORMAT_INTL
para el formato internacional y
PHONE_FORMAT_LOCAL
para el formato local). Estas son equivalentes respectivamente
a las cadenas de caracteres «intl» y «local».
En las líneas 15-17, definimos la variable privada $options
que es un arreglo
que tiene una sola llave llamada «format». Esta llave contendrá la opción del
formato de número de teléfono para nuestro validador.
En las líneas 20-22, definimos los identificadores de mensajes de error. Tenemos
tres identificadores (NOT_SCALAR
, INVALID_FORMAT_INTL
e INVALID_FORMAT_LOCAL
),
porque nuestro validador puede generar tres diferentes mensajes de error. Estos
identificadores se usan para que la maquina, y no los humanos, distingan los
diferentes mensajes de error.
Entre las líneas 25-29, tenemos la variable de tipo arreglo $messageTemplates
que contiene la asociación entre los identificadores de mensajes de error anteriores
y su representación textual. Los mensajes textuales se mostrarán a los humanos.
En las líneas 32-43, tenemos al método constructor que toma a la variable $options
como único argumento. Cuando construimos el validador manualmente, podemos omitir
este parámetro. Pero, cuando el validador es construido por la clase fábrica,
la fábrica pasará las opciones al constructor del validador a través de este
argumento.
En las líneas 46-55, tenemos el método setFormat()
que permite colocar el formato
de teléfono actual.
En las líneas 58-98, tenemos al método isValid()
. Este método encapsula el algoritmo
que revisa el número de teléfono. Toma a la variable $value
como parámetro,
ejecuta la comparación con la expresión regular y regresa true
en caso de éxito.
En caso de falla, el método isValid()
regresa el booleano false
y la lista de
errores, esta se pueden recuperar con el método getMessages()
.
Notemos que no se define el método
getMessages()
en nuestra clasePhoneValidator
. Esto es porque heredamos este método de la clase baseAbstractValidator
. Dentro de nuestro métodoisValid()
, para generar mensajes de error, usamos el método protegidoerror()
que provee la clase base (líneas 61, 91, 93).
The
PhoneValidator
is only for demonstration of how to write custom validators in ZF3. Implementing a validator that will work correctly against all possible phone numbers in the world is beyond the scope of this book. If you'd like to use this validator in a real-life app, you will definitely need to improve it. For example, take a look at thelibphonenumber
PHP library from Google.
Cuando la clase validadora PhoneValidator
está lista, podemos fácilmente comenzar
a usarla en el formulario de contacto (o en otro formulario) de la siguiente manera.
Se asume que llamamos el siguiente código dentro del método ContactForm::addInputFilter()
:
$inputFilter->add([
'name' => 'phone',
'required' => true,
'validators' => [
[
[
'name' => PhoneValidator::class,
'options' => [
'format' => PhoneValidator::PHONE_FORMAT_INTL
]
],
],
// ...
],
// ...
]);
Podemos ver al validador PhoneValidator
funcionando en la aplicación de ejemplo
Form Demo que está disponible junto a este libro. Abrimos la página
«http://localhost/contactus» en nuestro navegador web. Si ingresamos algún número
de teléfono en un formato incorrecto, el validador mostrara un error (ver figura 9.3).
Si lo deseamos, podemos usar la clase PhoneValidator
fuera de los formularios
como se muestra en el código de ejemplo más abajo:
<?php
use Application\Validator\PhoneValidator;
// Create PhoneValidator validator
$validator = new PhoneValidator();
// Configure the validator.
$validator->setFormat(PhoneValidator::PHONE_FORMAT_INTL);
// Validate a phone number
$isValid = $validator->isValid('1 (234) 567-8901'); // Returns true.
$isValid2 = $validator->isValid('12345678901'); // Returns false.
if(!$isValid2) {
// Get validation errors.
$errors = $validator->getMessages();
}