squirrelphp/entities

View on GitHub
src/Attribute/EntityProcessor.php

Summary

Maintainability
B
6 hrs
Test Coverage
A
100%
<?php

namespace Squirrel\Entities\Attribute;

use Squirrel\Entities\RepositoryConfig;
use Squirrel\Entities\RepositoryConfigInterface;

/**
 * Processes entity classes
 */
class EntityProcessor
{
    /**
     * Processes a class according to its attributes
     *
     * @param object|class-string $class
     */
    public function process(object|string $class): ?RepositoryConfigInterface
    {
        // Get class reflection data
        $reflectionClass = new \ReflectionClass($class);

        $entity = $this->getEntityFromAttribute($reflectionClass);

        // This class was annotated as Entity
        if ($entity instanceof Entity) {
            // Configuration options which need to be populated
            $tableToObjectFields = [];
            $objectToTableFields = [];
            $objectTypes = [];
            $objectTypesNullable = [];
            $autoincrement = '';

            // Go through all public values of the class
            foreach ($reflectionClass->getProperties() as $property) {
                $field = $this->getFieldFromAttribute($property);

                // A Field attribute was found
                if ($field instanceof Field) {
                    $fieldType = $property->getType();

                    // We need property types to know what to cast fields to
                    if ($fieldType === null) {
                        throw new \InvalidArgumentException('No property type for property field ' . $property->getName() . ' in ' . $reflectionClass->getName());
                    }

                    if ($fieldType instanceof \ReflectionUnionType) {
                        throw new \InvalidArgumentException('Union property types are not supported, encountered with property field ' . $property->getName() . ' in ' . $reflectionClass->getName());
                    }

                    // @codeCoverageIgnoreStart
                    if (!$fieldType instanceof \ReflectionNamedType) {
                        throw new \InvalidArgumentException('Property type is not a named type, encountered with property field ' . $property->getName() . ' in ' . $reflectionClass->getName());
                    }
                    // @codeCoverageIgnoreEnd

                    $fieldTypeName = $fieldType->getName();

                    if ($field->isBlob() === true && $fieldTypeName === 'string') {
                        $fieldTypeName = 'blob';
                    } elseif ($field->isBlob() === true) {
                        throw new \InvalidArgumentException('Blob property type set for a non-string property field: ' . $property->getName() . ' in ' . $reflectionClass->getName());
                    }

                    $tableToObjectFields[$field->getName()] = $property->getName();
                    $objectToTableFields[$property->getName()] = $field->getName();
                    $objectTypes[$property->getName()] = $fieldTypeName;
                    $objectTypesNullable[$property->getName()] = $fieldType->allowsNull();

                    if ($field->isAutoincrement() === true) {
                        $autoincrement = $field->getName();
                    }
                }
            }

            // Create new config for a repository
            return new RepositoryConfig(
                $entity->getConnection(),
                $entity->getName(),
                $tableToObjectFields,
                $objectToTableFields,
                $reflectionClass->getName(),
                $objectTypes,
                $objectTypesNullable,
                $autoincrement,
            );
        }

        // No entity found, so no configuration could be generated
        return null;
    }

    private function getEntityFromAttribute(\ReflectionClass $class): ?Entity
    {
        $attributes = $class->getAttributes(Entity::class);

        if (\count($attributes) === 0) {
            return null;
        }

        return $attributes[0]->newInstance();
    }

    private function getFieldFromAttribute(\ReflectionProperty $property): ?Field
    {
        $attributes = $property->getAttributes(Field::class);

        if (\count($attributes) === 0) {
            return null;
        }

        return $attributes[0]->newInstance();
    }
}