Размер шрифта:
«Runn Me!» — говорит нам очередной фреймворк* на PHP. А слышится «Throw Me!». Часть 2
Play

«Runn Me!» — говорит нам очередной фреймворк* на PHP. А слышится «Throw Me!». Часть 2

Не прошло и недели с момента «безумного успеха» (тут мнения немного расходятся, конечно. ) первой части нашего повествования, как пришло время выпустить вторую.

Сегодня мы продолжаем путешествие в бездонную глубину библиотеки runn/core будущего фреймворка «Runn Me!». Под катом нам встретятся следующие обитатели бездны:

  • Концепция «мультиисключения» и ее реализация в библиотеке
  • Понятие объекта с внутренней валидацией и эталонная реализация такого объекта
  • Немного заглянем в мир валидаторов и санитайзеров (подробный рассказ о них будет позже)
  • Рассмотрим реализацию объекта с обязательными полями
  • Фирменное наименование: Runn Me!
  • Вендор: Runn
  • GitHub:github.com/RunnMe
  • Composer:packagist.org/packages/runn

Предыдущие серии:

Мультиисключение

В начале был интерфейс Throwable.

Хитрая штука. Напрямую реализовать нельзя, как и многие интерфейсы из стандартной библиотеки PHP, зато любой объект, реализующий этот интерфейс, получает особую суперспособность: его можно «выбросить» (throw). Впрочем, слово «любой» тут явное преувеличение: всё, что нам доступно — это унаследовать собственный класс от библиотечного же \Exception.

Именно с такого наследования и начинается работа с исключениями в «Runn Me!»:

Имплементация интерфейса JsonSerializable неспроста добавлена сразу же в Runn\Core\Exception: это явный задел на будущее, на то светлое будущее, когда мы с вами научимся исключения ловить в наших middleware, упаковывать в JSON и отдавать в ответе клиенту.

Однако «Runn Me!» не так скучен, как может показаться при перечитывании листингов. Совсем рядом с классом Exception в пространстве имён Runn\Core притаился еще один, почти незаметный класс Runn\Core\Exceptions. Что же это такое?

  • Типизированная коллекция (см. предыдущую статью)
  • Тип элементов этой коллекции — Throwable
  • И при этом она сама является Throwable!

Где применяются мультиисключения?

Самое, пожалуй, «яркое» применение этого паттерна — механизм валидации, предусмотренный в стандартных объектах «Runn Me!».

Рассмотрим подробно, шаг за шагом, как же это устроено.

Методы подключения валидации

Внутри стандартного объекта (а точнее — любого наследника HasInnerValidationInterface, если вы не слишком отступили от эталонной реализации, описанной в StdGetSetWValidateSanitizeTrait) вы можете определить методы подключения валидации.

Название такого метода должно начинаться со слова «validate», за которым следует имя ключа, значение для которого мы будем валидировать, с первой большой буквы. Например:

В данном коде показано, что мы создали два метода для подключения валидации validateFoo() и validateBar(). Первый из них будет автоматически вызван ДО присваивания какого-либо значения свойству $obj->foo, второй же, соответственно, до фактического изменения $obj->baz.

Возможны пять вариантов «поведения» метода подключения валидации:

  1. Метод возвращает false
  2. Метод выбрасывает единичное исключение
  3. Метод выбрасывает мультиисключение
  4. Метод является генератором исключений
  5. И, наконец, метод просто вернул что-то, что не является false

В первом случае просто ничего не происходит, присваивание «молча» отменяется, свойство не получает новое значение.

В пятом случае присваивание нового значения свойству также «молча» происходит, не вызывая никаких побочных эффектов.

Чуть сложнее варианты №№ 2, 3 и 4. Все они ведут к отмене присваивания. Но чтобы точно понять, для чего они предназначены, мы с вами пойдем дальше.

Методы массового присваивания (заполнения)

С целью наиболее полно использовать потенциал методов подключения валидации в трейте StdGetSetWValidateSanitizeTrait переопределен важный метод merge() (который, в том числе, используется и в конструкторе класса Std).

Показать его работу лучше всего на примере:

Теперь, когда мы с вами определили все возможные правила валидации всеми возможными способами, давайте попробуем создать объект, который намеренно нарушит все эти правила:

Что же произошло?

Для начала метод merge() готовит пустую коллекцию класса Exceptions для будущих ошибок валидации. Затем для каждого ключа вызывается, если он существует, метод подключения валидации.

2. Метод подключения валидации выбросил одиночное исключение: оно добавляется в коллекцию. 3. Метод выбросил мультиисключение: оно объединяется с коллекцией. 4. Метод является генератором: всё, что он сгенерирует, являющееся Throwable, будет добавлено в коллекцию.

Если после всех этих операций коллекция ошибок валидации оказалась непустой — она выбрасывается. Дальше уже вам надлежит ее где-то поймать и решить, что делать со всеми этими ошибками.

Методы подключения санитайзинга

Ну, тут рассказ будет не таким захватывающим, как про валидацию. Всё просто:

Определили метод, он получает на вход то значение, которое вы намереваетесь присвоить свойству, то, что возвращает — будет действительно присвоено. Банально, но полезно.

Небольшой анонс

Разумеется, «магическими» методами в стандарных объектах тема валидации и санитации не исчерпывается. Одна из следующих статей будет целиком посвящена библиотеке runn/validation, которая сейчас готовится к публикации.

И, наконец, обещанные обязательные поля

Важнейшая вещь, скажу я вам. Особенно когда мы перейдем к теме комплексных (очень хочется произнести это слово с ударением на «е»: «комплЕксных») объектов. Но и без них можно всё понять:

Как вы видите, здесь используется всё тот же уже знакомый механизм мультиисключения для оповещения нас о том, что некоторые обязательные поля оказались неустановленными в конструкторе объекта. Кстати, если там возникнут ошибки валидации — мы их тоже увидим! Всё в той же коллекции $errors.

📎📎📎📎📎📎📎📎📎📎