Примеры использования и лучшие практики
Лучшие практики
1. Использование конструктора с продвижением свойств
Лучшей практикой является использование конструктора для объявления публичных свойств через продвижение. Это упрощает создание объекта и делает код более чистым и понятным.
Когда мы объявляем свойства в корне класса и полагаемся только на метод from()
, мы теряем гибкость и прямой контроль над созданием объекта. Такое решение может выглядеть привлекательным из-за простоты, но оно ограничивает нас в возможности напрямую работать с объектом через конструктор.
Пример плохой практики:
class User extends Data {
public string $name;
public int $age;
}
$data = [
'name' => 'John Doe',
'age' => '30'
];
// Создание объекта через from
$user = User::from($data);
Здесь мы теряем возможность явно контролировать создание объекта через конструктор, что может привести к путанице, если понадобятся дополнительные действия при создании объекта, такие как валидация или преобразование данных.
Пример хорошей практики:
class User extends Data {
public function __construct(
public string $name,
public int $age
) {}
}
$data = [
'name' => 'John Doe',
'age' => 30
];
// Создание объекта с использованием конструктора
$user = new User(name: $data['name'], age: $data['age']);
Здесь мы сохраняем прямой контроль над процессом создания объекта и можем легко управлять поведением объекта при его создании.
2. Совмещение атрибутов для одного поля
Можно комбинировать несколько атрибутов для одного поля. Это даёт мощные возможности для преобразования данных, особенно в случаях, когда нужно проводить несколько этапов обработки.
Пример: если нам нужно парсить поле user
из user_id
и использовать парсер, который по этому ID находит объект пользователя, мы можем применить атрибуты ParseFrom
и ParseBy
вместе.
Пример:
use Looqey\Speca\Data;
use Looqey\Speca\Attributes\ParseBy;
use Looqey\Speca\Attributes\ParseFrom;
use Looqey\Speca\Contracts\Transformer;
class User extends Data {
public function __construct(
#[ParseBy(UserIdToUserParser::class)]
#[ParseFrom('user_id')]
public User $user
) {}
}
class UserIdToUserParser implements Transformer {
public function transformer(mixed $value, Property $property): mixed {
// Ищем пользователя по ID
return User::findById($value);
}
}
$data = [
'user_id' => '12345'
];
$user = User::from($data);
Здесь сначала атрибут ParseFrom
указывает, что данные для поля user приходят из поля user_id
, а атрибут ParseBy
использует парсер, который находит пользователя по этому ID. Это позволяет комбинировать правила парсинга и при этом сохранять логику гибкой и масштабируемой.
3. Использование кэширования для повторяющихся запросов
Если атрибуты или парсеры могут повторно использовать данные (например, пользователь с одинаковым user_id
), стоит рассмотреть внедрение кэширования для таких данных на уровне входных данных. Например, можно использовать хранилище уровня запроса для того, чтобы один раз загрузить объект пользователя по ID и вернуть его при следующем запросе, значительно ускоряя обработку.
Пример:
use Looqey\Speca\Data;
use Looqey\Speca\Attributes\ParseBy;
use Looqey\Speca\Attributes\ParseFrom;
use Looqey\Speca\Contracts\Transformer;
class User extends Data {
public function __construct(
#[ParseBy(UserIdToUserParser::class)]
#[ParseFrom('user_id')]
public User $user
) {}
}
class UserIdToUserParser implements Transformer {
public function transform(mixed $value, Property $property): mixed {
// Ищем пользователя по ID
return User::findById($value);
}
}
$data = [
'user_id' => '12345'
];
$user = User::from($data);
Здесь сначала атрибут ParseFrom
указывает, что данные для поля user
приходят из поля user_id
, а атрибут ParseBy
использует парсер, который находит пользователя по этому ID. Это позволяет комбинировать правила парсинга и при этом сохранять логику гибкой и масштабируемой.
3. Использование кэширования для повторяющихся запросов
Если атрибуты или парсеры могут повторно использовать данные (например, пользователь с одинаковым user_id
), стоит рассмотреть внедрение кэширования для таких данных на уровне входных данных. Например, можно использовать хранилище уровня запроса для того, чтобы один раз загрузить объект пользователя по ID и вернуть его при следующем запросе (при конструировании другого Data-объекта), значительно ускоряя обработку.
Пример:
class UserIdToUserParser implements Transformer {
private static array $cache = [];
public function transform(mixed $value, Property $property): mixed {
// Если объект уже в кэше, возвращаем его
if (isset(self::$cache[$value])) {
return self::$cache[$value];
}
// Если объекта нет в кэше, загружаем его и сохраняем в кэш
$user = User::findById($value);
self::$cache[$value] = $user;
return $user;
}
}
Этот подход будет эффективен при работе с данными, которые часто повторяются, и поможет избежать лишних запросов или операций.
4. Управление включениями и исключениями
Когда речь идет о "тяжелых" или ленивых данных, важно эффективно управлять включениями и исключениями полей в сериализации. Использование методов include
и exclude
помогает контролировать, какие данные включать в вывод, что позволяет экономить ресурсы.
Пример:
$user = new User('John Doe', 30);
$user->include('profile'); // Включаем профиль в вывод
$user->exclude('password'); // Исключаем пароль из вывода
$data = $user->toArray();
Этот метод позволяет вам исключать или включать данные по мере необходимости, что идеально подходит для работы с большими или чувствительными данными.