Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFC]: Process parent properties in the ReflectionHydrator. #114

Open
ianef opened this issue Sep 28, 2023 · 4 comments
Open

[RFC]: Process parent properties in the ReflectionHydrator. #114

ianef opened this issue Sep 28, 2023 · 4 comments
Labels

Comments

@ianef
Copy link

ianef commented Sep 28, 2023

RFC

Q A
Proposed Version(s) next minor
BC Break? No

Provide a means to hydrate private properties of parent classes.

Background

Currently the hydrator gets the class properties using ReflectionClass. This is fine if the class does not inherit from other classes, or if all the properties are public. However if the properties are private then only the instantiated class properties are enumerated. For Doctrine entities that are associations, and other cases using inherited getters and private properties, Doctrine returns a proxied class and not the original entity. When the hydrator enumerates the properties of the class it just gets the properties of the proxy and not the base entity.

Considerations

There will be no impact on current use of the hydrator as fetching parent properties is optional.

Proposal(s)

    /**
     * Extract values from an object
     *
     * {@inheritDoc}
     */
    public function extract(object $object, bool $includeParentProperties = false): array
    {
        $result = [];
        foreach (static::getReflProperties($object, $includeParentProperties) as $property) {
            $propertyName = $this->extractName($property->getName(), $object);
            if (! $this->getCompositeFilter()->filter($propertyName)) {
                continue;
            }

            $value                 = $property->getValue($object);
            $result[$propertyName] = $this->extractValue($propertyName, $value, $object);
        }

        return $result;
    }

    /**
     * Hydrate $object with the provided $data.
     *
     * {@inheritDoc}
     */
    public function hydrate(array $data, object $object, bool $includeParentProperties = false)
    {
        $reflProperties = static::getReflProperties($object, $includeParentProperties);
        foreach ($data as $key => $value) {
            $name = $this->hydrateName($key, $data);
            if (isset($reflProperties[$name])) {
                $reflProperties[$name]->setValue($object, $this->hydrateValue($name, $value, $data));
            }
        }
        return $object;
    }

    /**
     * Get a reflection properties for an object.
     * If $includeParentProperties is true, return return all parent properties as well.
     *
     * @return ReflectionProperty[]
     */
    protected static function getReflProperties(object $input, bool $includeParentProperties): array
    {
        $class = get_class($input);

        if (isset(static::$reflProperties[$class])) {
            return static::$reflProperties[$class];
        }

        static::$reflProperties[$class] = [];
        $reflClass = new ReflectionClass($class);

        do {
            foreach ($reflClass->getProperties() as $property) {
                $property->setAccessible(true);
                static::$reflProperties[$class][$property->getName()] = $property;
            }
        } while ($includeParentProperties === true && ($reflClass = $reflClass->getParentClass()) !== false);

        return static::$reflProperties[$class];
    }
@ianef ianef added the RFC label Sep 28, 2023
ianef added a commit to ianef/laminas-hydrator that referenced this issue Oct 1, 2023
ianef added a commit to ianef/laminas-hydrator that referenced this issue Oct 1, 2023
ianef added a commit to ianef/laminas-hydrator that referenced this issue Oct 3, 2023
@froschdesign
Copy link
Member

@ianef

For Doctrine entities…

Have you checked the Doctrine integration project for hydrators?

@ianef
Copy link
Author

ianef commented Oct 4, 2023

@froschdesign Many thanks for pointing me to that, I've not seen that before and I'll try some tests with it. I assume that it deals with proxied entities but I'll find out soon enough.

I think my PR is still valid for the ReflectionHydrator.

@froschdesign
Copy link
Member

@ianef
Copy link
Author

ianef commented Oct 4, 2023

That's interesting @froschdesign, but my hydrator is used in Symfony projects and based on the Laminas hydrator as it's parent. I use it to hydrate model classes from Request objects (form data) which usually contain Doctrine entities and other DTOs.

I need t look at the Laminas framework in details to see what it offers.

ianef added a commit to ianef/laminas-hydrator that referenced this issue Oct 10, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants