Synergy of proxy object of doctrine and zend expressive hal

zend-expressive-hal

#1

Hi, I’m working with doctrine and zend expressive hal, but there is a problem when I try to generate the response of an object using the method fromObject of the class Zend\Expressive\Hal\ResourceGenerator.
The problem here is that the object is a proxy object of doctrine and consequently the namespace is not the real one and it isn’t found in the Zend\Expressive\Hal\Metadata\MetadataMap.

One way to solve the problem would be to implement a custom class Zend\Expressive\Hal\ResourceGenerator overwriting the method fromObject in this way:

<?php

use Psr\Container\ContainerInterface;
use Psr\Http\Message\ServerRequestInterface;

class ProxyDoctrineResourceGenerator
{
    /** same code **/

    /**
     * @param object $instance An object of any type; the type will be checked
     *     against types registered in the metadata map.
     * @param ServerRequestInterface $request
     */
    public function fromObject($instance, ServerRequestInterface $request) : HalResource
    {
        if (! is_object($instance)) {
            throw Exception\InvalidObjectException::forNonObject($instance);
        }


        //$class = get_class($instance);
        // Use the static method of doctrine class utils to retrieve the real namespace of the class
        $class=\Doctrine\Common\Util\ClassUtils::getClass($instance);

        if (! $this->metadataMap->has($class)) {
            throw Exception\InvalidObjectException::forUnknownType($class);
        }

        $metadata = $this->metadataMap->get($class);
        $metadataType = get_class($metadata);

        if (! isset($this->strategies[$metadataType])) {
            throw Exception\UnknownMetadataTypeException::forMetadata($metadata);
        }

        $strategy = $this->strategies[$metadataType];
        return $strategy->createResource(
            $instance,
            $metadata,
            $this,
            $request
        );
    }
}

We need to re-implement even the Zend\Expressive\Hal\ResourceGeneratorFactory according with our new custom class:

<?php

class ProxyDoctrineResourceGeneratorFactory
{
    public function __invoke(ContainerInterface $container) : ResourceGenerator
    {
        $generator = new ProxyDoctrineResourceGenerator(
            $container->get(Metadata\MetadataMap::class),
            $container->get(HydratorPluginManager::class),
            $container->get(LinkGenerator::class)
        );

        $this->injectStrategies($container, $generator);

        return $generator;
    }
}

As you can see I don’t like very match this solution, mostly because I can’t extend the Zend\Expressive\Hal\ResourceGenerator or Zend\Expressive\Hal\ResourceGeneratorFactory and I would need to always look if any changes has been made to these classes… (I can extend but it would be useless because the properties are private and not protected and I would in any case rewriting al methods which depends on these private properties.).

Has someone ever had the same problem?


#2

Temporarily I defined in the annotations of the entity to retrieve the associated class in EAGER mode in this way:

	/**
	 * @ORM\ManyToOne(targetEntity="Entity\User", inversedBy="identities",fetch="EAGER")
	 * @ORM\JoinColumn(name="user_id", referencedColumnName="user_id")
	 * @SWG\Property
	 */
	protected $user;

In this way the proxy object isn’t created, but I don’t like and it’s not the right way to solve the problem…


#3

Terrible idea to use EAGER.

Instead, why not wrap the ZF service with something that understands proxy classes?


#4

I know, can you explain more in detail what you mean by wrapping the ZF service?


#5

Specifically talking about the $this->metadataMap bit: that can simply be decorated with a layer that checks doctrine’s class metadata.

Something like this (note: I didn’t read the original code/interfaces, I’m making this up to show how it’s supposed to be done):

final class DoctrineMetadataMap implements MetadataMap {

private $next;

private $metadata;

public function __construct(MetadataMap $next, ClassMetadataFactory $metadata) {

$this->next = $next;

$this->metadata = $metadata;

}

public function has(string $className) : bool {

return $this->next->has($this->metadata->get($className)->getName());

}

public function get(string $className) {
return $this->next->get($this->metadata->get($className)->getName());

}
}


#6

As always you are right, I was struggling to find a solution inside the Zend\Expressive\Hal\ResourceGenerator for this line of code $class = get_class($instance);when actually I can apply the logic after with my MetadataMap service.

Thanks