Verification: a143cc29221c9be0

Php array of json objects

Использование ClassTransformer

С его помощью я автоматически привожу данные к нужному мне классу. Рассмотрим более подробно все также на примере создание пользователя. К нам приходит запрос, и данные из него мы должны отправить в метод. В моем случае на Laravel проекте это выглядит вот так:

class UserController extends Controller {
	public function __construct(
      private UserService $userService,
	) {}

	public function createUser(CreateUserRequest $request)
	{
      $dto = ClassTransformer::transform(CreateUserDTO::class, $request);
      $user = $this->userService->create($dto);
      return response(UserResources::make($user));
	}
}
class CreateUserDTO
{
    public string $name;
    public string $email;
    public string $phone;
}

В запросе к нам приходит массив параметров: name, phone и email. Пакет просто смотрит есть ли такие параметры у класса, и, если есть, сохраняет значение. В противном случае просто отсеивает их. На входе transform можно передавать не только массив, это может быть другой object, из которого также будут разобраны нужные параметры.

Но наименования аргументов могут отличаться. Тогда, в созданной нами DTO, мы можем спокойно описать свою реализацию приведения:

class CreateUserDTO
{
    public string $name;
    public string $email;
    public string $phone;
    
    public static function transform(mixed $args):CreateUserDTO
    {
        $dto = new self();
        $dto->name = $args['fullName'];
        $dto->email = $args['mail'];
        $dto->phone = $args['phone'];
        return $dto;
    }
}

Существуют объекты гораздо сложнее, с параметрами определенного класса, либо массивом объектов. Что же с ними? Все просто, указываем параметру в PHPDoc путь к классу и все. В случае массива нужно указать, каких именно объектов этот массив:

class PurchaseDTO
{
    /** @var array $products Product list */
    public array $products;
    
    /** @var \DTO\UserDTO $user */
    public UserDTO $user;
}

Указывая параметру класс, вы можете строить более сложные цепочки связей. Пакет рекурсивно будет проходить по всем типизированным параметрам и приведет их к нужному формату.

Путь к классу обязательно должен быть полным, т.к. иначе гораздо сложнее реализовать нахождение класса для инициализации. Или это вообще будет alias и нужно будет парсить файл, а это уже будет оверхед для такого сервиса.

Что мы получаем?

  • Метод сервиса работает с конкретным набором данным

  • Знаем все параметры, которые есть у объекта

  • Можно задать типизацию каждому параметру

  • Вызов метода становится проще, за счет удаления приведения вручную

  • В IDE работают все подсказки.

Аналоги

Увы, я не нашел подобных решений. Отмечу лишь пакет от Spatie - https://github.com/spatie/data-transfer-object

Он пытает решить ту же проблему используя DTO, но их подход заставляет ваш класс наследоваться от их DTO, и самим вызывать инициализацию, передавая в конструктор набор данных. При такой реализации я не вижу особо профита использования, ведь я так же могу сам прописать new DTO() и заполнить параметры.

Я же, в свою очередь, был вдохновлен методом преобразования из NestJS - plainToClass. Такой подход не заставляет реализовывать свои интерфейсы, что позволяет делать преобразования более гибким, и любой набор данных можно привести к любому классу. Хоть массив данных сразу в ORM модель (если прописаны параметры), но лучше так не надо:)