Принудительная структура массива в PHP с использованием ArrayObject?

У меня есть набор данных (~ 30 свойств, каждый из которых имеет свой собственный массив значений), которые я хочу передать различным классам в PHP, и я также хочу усилить структуру массива данных. Несколько классов будут ожидать, что эта структура будет последовательной.

Из-за этих фактов я не могу полагаться на стандартный массив, поэтому я решил передать объект. Я посмотрел на ArrayObject, и хотя он позволяет мне устанавливать / получать, как если бы класс был массивом, я ничего не видел насчет усиления структуры.

Существует ли существующий стандартный класс, который может обрабатывать принудительное применение его структуры, подобной массиву, и при этом обрабатываться как массив, например, в основном ArrayObject + принудительное применение?

Пример структуры массива:

$item_type_props = array(
'phone'     => array('speed' => 1000, 'self_label' => false, 'support_poe' => true, 'bluetooth' => false),
'pbx'       => array('acd_support' => true, 'max_conn' => 300, max_phones => 600),
'adapter'   => array('fxo' => 4, 'fxs' => 0, 't1_e1_pri' => 0),
etc...
);

Я знаю, что каждое свойство в массиве может быть его собственным классом и применять свои собственные поля через конструктор и set / get, но потом у меня вдруг появляется ~ 30 классов, которые являются не чем иным, как набором атрибутов, и это кажется несколько чрезмерным для простого хранения данных.


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

1

Решение

Несмотря на то, что вы можете использовать свою собственную, я рекомендую вам использовать существующую реализацию валидации. Например, Symfony\Validator позволяет определить вложенные структуры и требования на каждом уровне:

use Symfony\Component\Validator\Validation;
use Symfony\Component\Validator\Constraints as Assert;

$validator = Validation::createValidator();

$constraint = new Assert\Collection(array(
// the keys correspond to the keys in the input array
'name' => new Assert\Collection(array(
'first_name' => new Assert\Length(array('min' => 101)),
'last_name' => new Assert\Length(array('min' => 1)),
)),
'email' => new Assert\Email(),
'simple' => new Assert\Length(array('min' => 102)),
'gender' => new Assert\Choice(array(3, 4)),
'file' => new Assert\File(),
'password' => new Assert\Length(array('min' => 60)),
));

$violations = $validator->validate($input, $constraint);

Это позволяет вам нажать детали как чтобы перейти на другой (уже протестированный) уровень, позволяя вашему коду сосредоточиться на Зачем ему нужны эти данные. Для случая Symfony вы можете использовать array в качестве механизма хранения и использовать конструкцию, в которой брандмауэры не проверяются на соответствие проверенным данным.


Один способ, которым мы могли бы сделать это, — это обозначение. Представьте, что мы реализовали метод, возможно, с использованием валидатора Symfony, чтобы вернуть проверенный массив. Мы можем использовать венгерскую нотацию, чтобы указать, что наша структура прошла проверку и является «безопасной»:

<?php
$vInput = validate($_GET); // Hungarian notation: any variable beginning with "v" is "validated" and safe to use

function foobar(array $vInput) { ... }

В то время как это эффективно, это не очень хорошо для технического обслуживания в долгосрочной перспективе. Итак, вы можете рассмотреть объектную оболочку, которая позволяет использовать систему типов:

use Symfony\Component\Validator\Validation;
use Symfony\Component\Validator\Constraints as Assert;

class ValidatedArray extends \ArrayObject {
public function construct($input = [], $flags = 0, $iterator_class = 'ArrayIterator') {
$violations = Validation::createValidator()->validate($array, $this->constraints());
// throw exception if any violations
parent::__construct($input, $flags, $iterator_class);
}

public function __offsetSet($index, $value) {
$constraints = $this->constraints()[$index]; // specific constraints to this index
$violations = Validation::createValidator()->validate($array, $constraints);
// throw exception on violations
parent::__offsetSet($index, $value);
}

public function constraints() {
return new Assert\Collection(...);
}
}

$input = new ValidatedArray($_REQUEST); // first time validation
$input['foo'] = 'bar'; // also subject to validation

Возможно, вы захотите сделать эту реализацию abstract базовый класс, с конкретными потомками, реализующими constraints метод для предоставления конкретных ограничений на сам объект массива. Это обеспечивает гибкий способ получения выделенных объектов передачи данных.

2

Другие решения

Вообще говоря, я бы сказал, что — если вы не передаете данные в другой контекст, например, javascript — приложение PHP должно быть красиво организовано в классах PHP. Это просто самый простой способ обеспечить соблюдение структуры. Вы правы, это может привести к довольно простым DTO с кучей геттеров и сеттеров, но это наверняка побьет проверку структур массива. В вашем случае в массиве также есть отношения, иначе было бы бессмысленно объединять их в массив.

Используя PHP7, вы можете четко определить сигнатуру метода и применить типы, например,

public function setSomething(string $myValue)
{
$this->something = $myValue;
}

То же самое с типами возврата:

public function myActionMethod(array $options): ActionRespsonse
{
// Do something
}

Если у вас есть более сложные типы данных, я бы порекомендовал использовать объекты значения. Это не более чем простые классы PHP, которые представляют более сложное значение. Например, номер телефона:

public function setPhoneNumber(PhoneNumber $phoneNumber)
{
$this->phoneNumber = $phoneNumber;
}

Здесь PhoneNumber — это объект-значение, который фактически сам по себе является крошечным классом, который обеспечивает его использование:

class PhoneNumber {
private $phoneNumber;

public __construct(string $phoneNumber) {
if (strlen($phoneNumber) != 10) {
throw new \Exception('Not a valid phone number');
}

$this->phoneNumber = $phoneNumber;
}
}

В этом случае проверка также может быть связана с ответом @bishop, поскольку вы можете использовать существующий Валидатор, чтобы помочь вам. Вы можете найти пример объекта-значения телефонного номера здесь (просто погуглил): Пример объекта номера телефонного номера

У меня есть ощущение, что вы можете конвертировать ваши данные PHP в массив по другой причине? Например, взаимодействовать с базой данных или передавать ее в другой контекст, такой как Javascript?

В этом случае, когда у вас есть четко определенные DTO и VO, вы можете рассмотреть их сериализацию, например. в / из JSON. Для этого вы можете использовать библиотеки Symfony, как описано здесь: Symfony Serializer

Если вам действительно нужны массивы, вы также можете подумать о том, чтобы объединить их с сущностями, используя библиотеку Marco Pivetta (ocramius), которая в значительной степени является авторитетом в области гидратации — подход, широко используемый в Doctrine: Окрамиус Гидратор

Множество вариантов, а !?

Честно говоря, хотя, IMHO, как правило, должен быть довольно хороший аргумент для передачи этих сложных массивов, так как массивы очень мало поддерживают в функциональности. Кроме того, ваш код с большой вероятностью и очень быстро станет трудным для чтения и обслуживания, поскольку в любой точке, где будет происходить какая-либо модификация массива или использование его элементов данных, вам потребуется реализовать все виды проверок. или выполните проверку, как описано в @bishop. Я бы не позволил чему-то подобному существовать в кодах наших приложений …

Итак, подытожим: если у вас есть строго определенный набор объектов PHP с хорошо разработанным конструктором, свойствами, геттерами, сеттерами, конструктором, отношениями, методами действий и, кроме того, каким-то механизмом сериализации, вы в хорошей форме. Также другие разработчики будут «вынуждены» работать с объектами и предоставленным интерфейсом (методами и ВО), что гарантирует определенную степень удобства и качества.

Кстати, вы могли бы подумать об изменении мыслей Мартина Фаулера об этих вещах: Мартин Фаулер: DTO Мартин Фаулер: В.О. Мартин Фаулер: DTO2

2

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector