Атрибуты
Speca использует PHP-атрибуты для управления правилами инстанцирования и сериализации полей объектов. Рассмотрим существующие атрибуты с позиции их целевого назначения.
Вспомогательная декларация
Set
Декларирует, что свойство является массивом элементов типа.
Аргументы
of
указывает на целевой тип элемента массива. Если этот тип -Data
, следующие аргументы не требуются.parser
является аналогом единичного атрибутаParseBy
для элемента массива.serializer
является аналогом единичного атрибутаSerializeBy
для элемента массива.
Пример
Рассмотрим декларирование набора элементов для класса, не являющегося Data:
class CompositionTag {
public function __construct(
public string $id,
public string $name
)
{}
}
class Composition extends Data {
public function __construct(
#[Set(of: CompositionTag::class,
parser: CompositionTagParser::class,
serializer: CompositionTagSerializer::class)]
public array $tags
)
{}
}
class CompositionTagParser implements Transformer {
public function transform(mixed $value, Property $property): mixed {
return new CompositionTag(
id: $value['id'],
name: $value['name']
);
}
}
class CompositionTagSerializer implements Transformer {
public function transform(mixed $value, Property $property): mixed {
//Просто убедимся..
assert($value instanceof CompositionTag);
//И вернем массив
return [
'id' => $value->id,
'name' => $value->name
];
}
}
Много бойлерплейта, не так ли? Давайте упростим!
class CompositionTag extends Data {
public function __construct(
public string $id,
public string $name
)
{}
}
class Composition extends Data {
public function __construct(
#[Set(of: CompositionTag::class)]
public array $tags
)
{}
}
Это все - достаточно было использовать Data
в классе CompositionTag
. Благодаря этому массив будет собираться автоматически по тем правилам, которые опишет класс CompositionTag
.
Парсинг
ParseBy
Назначает парсер свойств.
Пример
use Looqey\Speca\Data;
use Looqey\Speca\Attributes\ParseBy;
class User extends Data {
public function __construct(
#[ParseBy(NameArrayToStringParser::class)]
public string $name;
) 8
{}
}
use Looqey\Speca\Contracts\Transformer;
use Looqey\Speca\Core\Property;
class NameArrayToStringParser implements Transformer {
public function transform(mixed $value, Property $property): mixed {
return $value['first_name'] . ' ' . $value['surname'];
}
}
$data = [
'name' => [
'first_name' => 'John',
'surname' => 'Doe'
]
];
$it = User::from($data);
// $it->name = 'John Doe'
Частные случаи
Использование с конструктором без продвижения свойства
Пользовательский парсер будет применен к свойству ДО передачи его конструктору:
class User extends Data {
#[ParseBy(NameArrayToStringParser::class)]
public string $name;
public function __construct(string $name) {
// В $name мы получим "John Doe"!
$this->name = 'rough '.$name;
}
}
ParseFrom
Устанавливает для поля возможные источники для парсинга. Например:
use Looqey\Speca\Data;
use Looqey\Attributes\ParseFrom;
class Painting extends Data {
public function __construct(
#[ParseFrom('technique_description', 'techniqueDescription', 'technique')]
public string $techniqueDescription;
)
{}
}
При инстанцировании объекта методом from
Speca возьмет первый существующий во входном наборе данных параметр (fallback-схема). При использовании этого атрибута на поле его изначальное имя не учитывается (не берется для того чтобы искать значение в источнике данных)
$data = [
//Это поле будет использовано для заполнения techniqueDescription по умолчанию
'technique_description' => 'Sfumato creates soft transitions, layered glazes add depth, light shapes form.',
//Если бы предыдущего поля не было - было бы использовано это поле
'techniqueDescription' => 'Fine hatching adds texture, layered washes create depth, contrast defines form.',
//..аналогично предыдущему полю
'technique' => 'Glazes build depth, soft blending smooths edges, light and shadow shape form.',
];
$it = Painting::from($data);
Взаимодействие
Возможно использовать атрибуты совместно. Происходить это будет по схеме:
- Получение действительного значения поля (
ParseFrom
) - Парсинг значения поля (
ParseTo
)
Сериализация
SerializeBy
Назначает сериализатор свойств
use Looqey\Speca\Data;
class User extends Data {
public function __construct(
#[SerializeBy(NameSerializer::class)]
public string $name
)
{}
}
use Looqey\Speca\Contracts\Transformer;
use Looqey\Speca\Core\Property;
class NameSerializer implements Transformer {
public function transform(mixed $value, Property $property): mixed {
$parts = explode(' ', $value);
return ['first' => $parts[0], 'last' => $parts[1] ?? ''];
}
}
$data = [
'name' => 'John Doe'
];
$user = User::from($data);
$output = $user->toArray();
// $output['name'] будет массивом с ключами "first" и "last"