Search by

liamhackett / valueobjectcompiler

LiamPigInTheCity

Compile strict type, read only value objects from JSON and XML

Package info

github.com/liamh101/value-object-compiler

pkg:composer/liamhackett/valueobjectcompiler

Statistics

Installs: 252

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

0.1.0 2026-09-11 12:09 UTC

README

Source Code Latest Version Software License PHP Version Coverage Status Build

This package takes a source file, such as JSON or XML and creates strict typed, PSR12, readonly Value Objects.

The aim is to slowly support more file formats with the same file output.

Installation

composer require --dev liamhackett/valueobjectcompiler

How To Use

Single file Compiler

vendor/bin/ValueObjectCompiler compile:json {jsonLocation} --outputDir={dir} vendor/bin/ValueObjectCompiler compile:xml {xmlLocation} --outputDir={dir}

Single file compiler will take a JSON or XML file and output its value object representation. By default, it will output in the current Directory, but you can specify your own directory using the outputDir flag.

JSON Example

example.json

{
  "id": 12,
  "name": "Json Object",
  "status": "Published",
  "tags": ["tag1", "tag2", "tag3"],
  "sub_objects": [
    {
      "name": "Hello", 
      "value": 13.132
    },
    {
      "name": "World",
      "value": null
    }
  ]
}

Example.php

<?php

readonly class Example
{
    /**
     * @var string[] $tags
     * @var SubObject[] $subObjects
     */
    public function __construct(
        public int $id,
        public string $name,
        public string $status,
        public array $subObjects,
        public array $tags,
    ) {
    }

    /**
     * @return self[]
     */
    public static function hydrateMany(array $bulkData): array
    {
        $result = [];

        foreach ($bulkData as $data) {
            $result[] = self::hydrate($data);
        }

        return $result;
    }

    public static function hydrate(array $data): self
    {
        if (!isset($data['id'], $data['name'], $data['status'], $data['sub_objects'], $data['tags'])) {
            throw new \RuntimeException('Missing required parameter');
        }
        return new self(
            id: $data['id'],
            name: $data['name'],
            status: $data['status'],
            subObjects: SubObject::hydrateMany($data['sub_objects']),
            tags: $data['tags'],
        );
    }
}

SubObject.php

<?php

readonly class SubObject
{
    public function __construct(
        public string $name,
        public ?float $value = null,
    ) {
    }

    /**
     * @return self[]
     */
    public static function hydrateMany(array $bulkData): array
    {
        $result = [];

        foreach ($bulkData as $data) {
            $result[] = self::hydrate($data);
        }

        return $result;
    }

    public static function hydrate(array $data): self
    {
        if (!isset($data['name'])) {
            throw new \RuntimeException('Missing required parameter');
        }
        return new self(
            name: $data['name'],
            value: $data['value'] ?? null,
        );
    }
}

XML Example

example.xml

<?xml version="1.0" encoding="UTF-8"?>
<catalog>
    <book id="bk101">
        <author>Example Author</author>
        <title>Sample Title</title>
        <genre>Fiction</genre>
        <price>19.99</price>
        <publish_date>2023-01-01</publish_date>
        <description>A brief description of the book.</description>
    </book>
    <book id="bk102">
        <author>Example Author 2</author>
        <title>Another Sample Title</title>
        <genre>Fiction</genre>
        <price>25.99</price>
        <publish_date>2025-01-01</publish_date>
        <description>A brief description of the book.</description>
    </book>
</catalog>

Catalog.php

<?php

readonly class Catalog
{
    /**
     * @param Book[] $book
     */
    public function __construct(
        public array $book,
    ) {
    }

    /**
     * @return self[]
     */
    public static function hydrateMany(array $bulkData): array
    {
        $result = [];

        foreach ($bulkData as $data) {
            $result[] = self::hydrate($data);
        }

        return $result;
    }

    public static function hydrate(\SimpleXMLElement $data): self
    {
        if ((string)$data->book === '') {
            throw new \RuntimeException('Missing required parameter');
        }
        return new self(book: Book::hydrateMany(iterator_to_array($data->book, false)),);
    }
}

Book.php

<?php

readonly class Book
{
    public function __construct(
        public string $author,
        public string $description,
        public string $genre,
        public string $id,
        public float $price,
        public string $publishDate,
        public string $title,
    ) {
    }

    /**
     * @return self[]
     */
    public static function hydrateMany(array $bulkData): array
    {
        $result = [];

        foreach ($bulkData as $data) {
            $result[] = self::hydrate($data);
        }

        return $result;
    }

    public static function hydrate(\SimpleXMLElement $data): self
    {
        if (
            !isset($data['id']) ||
            (string)$data->author === '' ||
            (string)$data->description === '' ||
            (string)$data->genre === '' ||
            (string)$data->price === '' ||
            (string)$data->publish_date === '' ||
            (string)$data->title === ''
        ) {
            throw new \RuntimeException('Missing required parameter');
        }
        return new self(
            author: $data->author->__toString(),
            description: $data->description->__toString(),
            genre: $data->genre->__toString(),
            id: $data['id'],
            price: (float) $data->price->__toString(),
            publishDate: $data->publish_date->__toString(),
            title: $data->title->__toString(),
        );
    }
}