A free and open-source book on ZF3 for beginners


8.7. Написание своего собственного фильтра

Альтернативой использования фильтра Callback является написание своего собственного класса фильтра, реализующего интерфейс FilterInterface. Фильтр затем можно будет использовать в формах вашего веб-приложения (или, при желании, вне форм).

Чтобы продемонстрировать создание своего фильтра, мы напишем класс PhoneFilter, инкаспулирующий алгоритм фильтрации номера, который мы использовали в примере фильтра Callback.

Как вы возможно помните, базовым классом для всех стандартных фильтров является класс AbstractFilter. По аналогии мы также будем наследовать наш фильтр PhoneFilter от этого базового класса.

Мы планируем иметь следующие методы в классе фильтра PhoneFilter (см. таблицу 8.18):

Таблица 8.18. Public-методы PhoneFilter
Имя метода Описание
__construct($options) Конструктор - принимает опциональный аргумент $options, который нужен для того, чтобы сразу задать опции фильтра.
setFormat($format) Задает опцию формата номера.
getFormat() Возвращает опцию формата номера.
filter($value) Запускает фильтр.

Сперва создайте файл PhoneFilter.php в каталоге Filter под корневым каталогом модуля 28. Поместите в этот файл следующий код:

28) Класс PhoneFilter можно считать моделью сервиса, так как его задачей является обработка данных, а не их хранение. Все пользовательские фильтры принято хранить под каталогом Filter.

<?php
namespace Application\Filter;

use Zend\Filter\AbstractFilter;

// Этот класс фильтра предназначен для преобразования произвольного номера телефона в 
// локальный или международный формат.
class PhoneFilter extends AbstractFilter 
{    
  // Константы форматов номера.
  const PHONE_FORMAT_LOCAL = 'local'; // Local phone format 
  const PHONE_FORMAT_INTL  = 'intl';  // International phone format 
    
  // Доступные опции фильтра.
  protected $options = [
    'format' => self::PHONE_FORMAT_INTL
  ];
    
  // Конструктор.
  public function __construct($options = null) 
  {     
    // Задает опции фильтра (если они предоставлены).
    if(is_array($options)) {
            
      if(isset($options['format']))
        $this->setFormat($options['format']);
    }
  }
    
  // Задает формат номера.
  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 getFormat() 
  {
    return $this->format;
  }  
	
  // Фильтрует телефонный номер.
  public function filter($value) 
  {                
    if(!is_scalar($value)) {
      // Возвращаем нескалярное значение неотфильтрованным.
      return $value;
    }
            
    $value = (string)$value;
        
    if(strlen($value)==0) {
      // Возвращаем пустое значение неотфильтрованным.
      return $value;
    }
        
    // Сперва удаляем все нецифровые символы.
    $digits = preg_replace('#[^0-9]#', '', $value);
        
    $format = $this->options['format'];
        
    if($format == self::PHONE_FORMAT_INTL) {            
      // Дополняем нулями, если число цифр некорректно.
      $digits = str_pad($digits, 11, "0", STR_PAD_LEFT);

      // Добавляем скобки, пробелы и тире.
      $phoneNumber = substr($digits, 0, 1) . ' (' . 
                     substr($digits, 1, 3) . ') ' .
                     substr($digits, 4, 3) . '-' . 
                     substr($digits, 7, 4);
    } else { // self::PHONE_FORMAT_LOCAL
      // Дополняем нулями, если число цифр некорректно
      $digits = str_pad($digits, 7, "0", STR_PAD_LEFT);

      // Добавляем тире.
      $phoneNumber = substr($digits, 0, 3) . '-'. substr($digits, 3, 4);
    }
        
    return $phoneNumber;                
  }    
}

Как видите из строки 2 класс фильтра содержится в пространстве имен Application\Filter.

В строке 8 мы определяем класс PhoneFilter. Мы наследуем наш фильтр от базового класса AbstractFilter для повторного использования предоставляемой им функциональности. Строка 4 содержит псевдоним для класса AbstractFilter.

В строках 11-12 мы для удобства определяем константы форматов номера (PHONE_FORMAT_INTL для международного формата и PHONE_FORMAT_LOCAL для локального). Эти константы - эквиваленты строк "intl" и "local" соответственно.

В строках 15-17 мы определяем private-переменную $options, которая является массивом, имеющим один единственный ключ под названием "format". Этот ключ будет содержать опцию формата номера для нашего фильтра.

В строках 20-28 находится метод конструктора, который принимает один аргумент $options. При создании фильтра вручную, этот параметр можно пропустить. Однако, когда фильтр создается классом фабрики, фабрика будет передавать опции фильтра его конструктору через этот аргумент.

В строках 31-40 и 43-46 находятся методы setFormat() и getFormat(), позволяющие соответственно задать и извлечь текущий формат номера.

В строках 49-86 находится метод filter(). Этот метод инкапсулирует алгоритм фильтрации телефонного номера. Он принимает параметр $value, преобразовывает его с учетом выбранного формата номера и возвращает отформатированный номер телефона.

8.7.1. Использование класса PhoneFilter

Как только класс фильтра PhoneFilter будет готов, вы легко можете начать его использовать в форме обратной связи (или других формам) как показано ниже. Предполагается, что вы вызываете следующий код внутри метода ContactForm::addInputFilter():

$inputFilter->add([
      'name'     => 'phone',
      'required' => true,                
      'filters'  => [                    				
        [
          'name' => PhoneFilter::class,
          'options' => [
            'format' => PhoneFilter::PHONE_FORMAT_INTL
          ]
        ],
        // ...
      ],                
      // ...
    ]);

Вы можете посмотреть, как работает фильтр PhoneFilter в примере Form Demo - приложении, которое идет вместе с этой книгой. Откройте страницу "http://localhost/contactus" в своем браузере. Если вы введете какой-либо телефонный номер в некорректном формате, фильтр исправит номер.

Если хотите, можете использовать PhoneFilter вне форм, как показано во фрагменте кода ниже:

<?php 
use Application\Filter\PhoneFilter;

// Создаем фильтр PhoneFilter.
$filter = new PhoneFilter();

// Настраиваем фильтр.
$filter->setFormat(PhoneFilter::PHONE_FORMAT_INTL);

// Фильтруем строку.
$filteredValue = $filter->filter('12345678901');

// Ожидаемый вывод фильтра - '1 (234) 567-8901'.

Top