Альтернативой использованию валидатора Callback
является написание своего собственного
класса валидатора, реализующего интерфейс ValidatorInterface
. Этот валидатор затем может
быть использован в формах вашего веб-приложения.
Чтобы продемонстрировать создание своего валидатора, мы напишем класс PhoneValidator
,
инкапсулирующий алгоритм валидации номера, который мы использовали в предыдущем примере.
Как вы возможно помните, базовым классом для всех стандартных валидаторов является класс
AbstractValidator
. По аналогии мы также будем наследовать наш валидаторPhoneValidator
от этого базового класса.
Мы планируем иметь следующие методы в классе валидатора PhoneValidator
(см. таблицу 9.15):
Имя метода | Описание |
---|---|
__construct($options) |
Конструктор - принимает опциональный аргумент $options , который нужен для того, чтобы сразу задать опции валидатора. |
setFormat($format) |
Задает опцию формата номера. |
getFormat() |
Возвращает опцию формата номера. |
isValid($value) |
Возвращает true , если значение - действительный телефонный номер; иначе возвращает false . |
getMessages() |
При неудачной валидации этот метод вернет массив сообщений об ошибках. |
Для PhoneValidator
у нас будет три возможных сообщения об ошибках:
Сперва создайте файл PhoneValidator.php в каталоге Validator под корневым каталогом модуля 38. Поместите в этот файл следующий код:
38) Класс PhoneValidator
можно считать моделью сервиса, так как его задачей является
обработка данных, а не их хранение. Все пользовательские валидаторы принято хранить под каталогом Validator
.
<?php
namespace Application\Validator;
use Zend\Validator\AbstractValidator;
// Этот класс валидатора предназначен для проверки телефонного номера на
// соответствие локальному или международному формату.
class PhoneValidator extends AbstractValidator
{
// Константы формата номера.
const PHONE_FORMAT_LOCAL = 'local'; // Локальный формат номера.
const PHONE_FORMAT_INTL = 'intl'; // Международный формат номера.
// Доступные опции валидатора.
protected $options = [
'format' => self::PHONE_FORMAT_INTL
];
// ID сообщений об ошибках валидации.
const NOT_SCALAR = 'notScalar';
const INVALID_FORMAT_INTL = 'invalidFormatIntl';
const INVALID_FORMAT_LOCAL = 'invalidFormatLocal';
// Сообщения об ошибках валидации.
protected $messageTemplates = [
self::NOT_SCALAR => "Номер телефона должен быть скалярным значением",
self::INVALID_FORMAT_INTL => "Номер телефона должен быть в международном формате",
self::INVALID_FORMAT_LOCAL => "Номер телефона должен быть в локальном формате",
];
// Конструктор.
public function __construct($options = null)
{
// Задаем опции валидатора (если они предоставлены).
if(is_array($options)) {
if(isset($options['format']))
$this->setFormat($options['format']);
}
// Вызываем конструктор родительского класса.
parent::__construct($options);
}
// Задаем формат номера.
public function setFormat($format)
{
// Проверяем входной аргумент.
if($format!=self::PHONE_FORMAT_LOCAL &&
$format!=self::PHONE_FORMAT_INTL) {
throw new \Exception('Invalid format argument passed.');
}
$this->options['format'] = $format;
}
// Валидируем номер телефона.
public function isValid($value)
{
if(!is_scalar($value)) {
$this->error(self::NOT_SCALAR);
return false; // Номер должен быть скалярной величиной.
}
// Преобразуем значение в строку.
$value = (string)$value;
$format = $this->options['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}$/';
}
// Сперва проверяем длину телефонного номера
$isValid = false;
if(strlen($value)==$correctLength) {
// Проверяем, соответствует ли значение шаблону
if(preg_match($pattern, $value))
$isValid = true;
}
// Если была ошибка, задаем сообщение об ошибке
if(!$isValid) {
if($format==self::PHONE_FORMAT_INTL)
$this->error(self::INVALID_FORMAT_INTL);
else
$this->error(self::INVALID_FORMAT_LOCAL);
}
// Возвращаем результат валидации.
return $isValid;
}
}
Как видите из строки 2, класс валидатора содержится в пространстве имен Application\Validator
.
В строке 8 мы определяем класс PhoneValidator
. Мы наследуем наш валидатор от базового класса
AbstractValidator
для повторного использования предоставляемой им функциональности. Строка 4
содержит псевдоним для класса AbstractValidator
.
В строках 11-12 мы для удобства определяем константы форматов номера (PHONE_FORMAT_INTL
для
международного формата и PHONE_FORMAT_LOCAL
для локального). Эти константы - эквиваленты
строк "intl" и "local" соответственно.
В строках 15-17 мы определяем private-переменную $options
, которая является массивом, имеющим
один единственный ключ под названием "format". Этот ключ будет содержать опцию формата номера для
нашего валидатора.
В строках 20-22 мы определяем идентификаторы сообщений об ошибках. В нашем случае идентификаторов три
(NOT_SCALAR
, INVALID_FORMAT_INTL
и INVALID_FORMAT_LOCAL
), так как наш валидатор может генерировать
три разных сообщения об ошибках. Эти идентификаторы предназначены для распознавания различных сообщений
машиной, а не человеком.
В строках 25-29 находится переменная массива, которая содержит соответствия между идентификаторами сообщений об ошибках и их текстовыми представлениями. Текстовые сообщения предназначены для показа человеку.
В строках 32-43 находится метод конструктора, который принимает один аргумент $options
.
При создании валидатора вручную, этот параметр можно пропустить. Однако, когда валидатор создается
классом фабрики, фабрика будет передавать опции валидатора его конструктору через этот аргумент.
В строках 46-55 находится метод setFormat()
, позволяющий задать текущий формат номера.
Строки 58-98 содержат метод isValid()
. Этот метод инкапсулирует алгоритм проверки телефонного
номера. Он принимает параметр $value
, осуществляет сопоставление с регулярным выражением и
возвращает true
при удачном исходе.
В случае неудачной валидации метод isValid()
возвращает булевое false
, а список ошибок можно
извлечь методом getMessages()
.
Вы могли заметить, что мы не определили метод
getMessages()
в нашем классеPhoneValidator
. Это потому, что мы наследовали этот метод от базового классаAbstractValidator
. Внутри методаisValid()
, для генерации сообщений об ошибках, мы также использовали предоставляемый базовым классом protected-методerror()
(строки 61, 91, 93).
PhoneValidator
приведен только для демонстрации того, как писать собственные валидаторы в ZF3. Реализация валидатора, который бы правильно обрабатывал все возможные номера телефонов в мире находится за пределами обсуждения данной книги. Если вы захотите использовать данный валидатор в своем приложении, вам определенно придется улучшить его. Например, взгляните на PHP библиотекуlibphonenumber
от Google.
Как только класс валидатора PhoneValidator
будет готов, вы легко можете начать его использовать в
форме обратной связи (или других формам) как показано ниже. Предполагается, что вы вызываете
следующий код внутри метода ContactForm::addInputFilter()
:
$inputFilter->add([
'name' => 'phone',
'required' => true,
'validators' => [
[
[
'name' => PhoneValidator::class,
'options' => [
'format' => PhoneValidator::PHONE_FORMAT_INTL
]
],
],
// ...
],
// ...
]);
Вы можете посмотреть, как работает валидатор PhoneValidator
в примере Form Demo - приложении, которое идет
вместе с этой книгой. Откройте страницу "http://localhost/contactus" в своем браузере. Если вы введете
какой-либо телефонный номер в некорректном формате, валидатор отобразит ошибку (см. рисунок 9.3).
Если хотите, можете использовать PhoneValidator
вне форм, как показано во фрагменте кода ниже:
<?php
use Application\Validator\PhoneValidator;
// Создаем валидатор PhoneValidator
$validator = new PhoneValidator();
// Настраиваем валидатор.
$validator->setFormat(PhoneValidator::PHONE_FORMAT_INTL);
// Валидируем номер телефона
$isValid = $validator->isValid('1 (234) 567-8901'); // Возвращает true.
$isValid2 = $validator->isValid('12345678901'); // Возвращает false.
if(!$isValid2) {
// Получаем ошибки валидации.
$errors = $validator->getMessages();
}