apie/storage-metadata

Composer package of the apie library: storage metadata

dev-main 2025-03-18 22:52 UTC

This package is auto-updated.

Last update: 2025-03-18 22:52:33 UTC


README

storage-metadata

Latest Stable Version Total Downloads Latest Unstable Version License PHP Composer

PHP Composer

This package is part of the Apie library. The code is maintained in a monorepo, so PR's need to be sent to the monorepo

Documentation

This package is used to convert an Apie domain object to a storage DTO that can be used by data mapper ORM's. The creation of a storage DTO can be done by hand or created automatically by the package apie/storage-metadata-builder.

Usage

The simplest usage is using the simple static create method. In case you want to customize it you can create the object yourself.

use Apie\Core\FileStorage\FileStorageFactory;
use Apie\StorageMetadata\DomainToStorageConverter;

DomainToStorageConverter::create(FileStorageFactory::create());

A file storage service is required to store properties with typehints of UploadedFileInterface or StoredFile, since you do not want to store your files in the database (this is still possible with InlineStorage, but that should only be used for testing).

An Indexer service can also be provided for how indexing search indexes and columns.

You need a domain object and a storage DTO. For example this could be how your storage DTO looks like for an Address object that is part of an User object:

use Apie\StorageMetadata\Attributes\ParentAttribute;
use Apie\StorageMetadata\Attributes\PropertyAttribute;
use Apie\StorageMetadata\Interfaces\StorageDtoInterface;
use ReflectionClass;

class AddressStorage implements StorageDtoInterface
{
    public static function getClassReference(): ReflectionClass
    {
        return new ReflectionClass(Address::class);
    }

    #[ParentAttribute]
    public User $parent;
    public function __construct(
        #[PropertyAttribute('street')]
        public ?string $apieStreet,
        #[PropertyAttribute('streetNumber')]
        public ?string $apieStreetNumber,
        #[PropertyAttribute('zipcode')]
        public ?string $apieZipcode,
        #[PropertyAttribute('city')]
        public ?string $apieCity,
        #[PropertyAttribute('manual')]
        public ?bool $apieManual,
    ) {
    }
}

Then you can convert a domain object to a storage object or vice versa. It's also possible to inject in existing records.

use Apie\Core\FileStorage\FileStorageFactory;
use Apie\StorageMetadata\DomainToStorageConverter;

$converter = DomainToStorageConverter::create(FileStorageFactory::create());
$domainObject = $converter->createDomainObject(
    new AddressStorage(
        'Evergreen Terrace',
        '742',
        '11111',
        'Springfield',
        false
    )
);

This example creates a Address domain object with street, zipcode, city and manual property filled in.

Polymorphic resources

Polymorphic resources are mapped as well, but you need to provide a second argument to PropertyAttribute to define the mapped class. Basically if the property is not mapped to this domain object null will be stored in the column.

use Apie\Core\Utils\EntityUtils;
use Apie\StorageMetadata\Attributes\DiscriminatorMappingAttribute;
use Apie\StorageMetadata\Attributes\GetMethodAttribute;
use Apie\StorageMetadata\Attributes\PropertyAttribute;
use Apie\StorageMetadata\Interfaces\StorageClassInstantiatorInterface;

class AnimalStorage implements StorageClassInstantiatorInterface
{
    public function __construct(
        #[DiscriminatorMappingAttribute]
        private array $discriminatorMapping,
        #[GetMethodAttribute('getId')]
        private string $id,
        #[PropertyAttribute('id')]
        private ?string $apieId,
        #[PropertyAttribute('hasMilk', Cow::class)]
        private ?bool $apieHasMilk = null,
        #[PropertyAttribute('starving', Elephant::class)]
        private ?bool $apieStarving = null,
        #[PropertyAttribute('poisonous', Fish::class)]
        private ?bool $apiePoisonous = null
    ) {
    }

    public static function getClassReference(): ReflectionClass
    {
        return new ReflectionClass(Animal::class);
    }

    public function createDomainObject(ReflectionClass $class): object
    {
        $class = EntityUtils::findClass($this->discriminatorMapping, $class);
        assert(null !== $class);
        return $class->newInstanceWithoutConstructor();
    }
}

This example also shows how to make the domain object first as you can not instantiate a base abstract class like without knowing how to map it (this works with the DiscriminatorMappingAttribute attribute).