zfcampus / zf-hal

BSD 3-Clause "New" or "Revised" License
30 stars 49 forks source link

Hydration in combination with an abstract factory doesn't work #171

Open kschroeer opened 5 years ago

kschroeer commented 5 years ago

I've a problem to use the zf-hal hydrator in combination with an abstract factory. This is my module configuration:

public function getConfig()
{
    return [
        'hydrators' => [
            'abstract_factories' => [
                AbstractHydratorFactory::class,
            ]
        ],
        'service_manager' => [
            'factories' => [
                ModuleOptions::class => ModuleOptionsFactory::class,
            ],
        ],
        'zf-hal' => [
            'renderer' => [
                'default_hydrator' => 'reflection'
            ],
        ]
    ];
}

My abstract factory looks like this:

class AbstractHydratorFactory implements AbstractFactoryInterface
{
    public function canCreate(ContainerInterface $container, $requestedName)
    {
        $moduleOptions = $container->get(ModuleOptions::class);
        $configuration = $moduleOptions->getClass();
        return isset($configuration[$requestedName]['property_name_translation']);
    }

    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
    {
        $moduleOptions = $container->get(ModuleOptions::class);
        $configuration = $moduleOptions->getClass();
        $hydrator = $container->get($moduleOptions->getHydrator());
        $hydrator->setNamingStrategy(
            new ArrayMapNamingStrategy(
                $configuration[$requestedName]['property_name_translation']
            )
        );
        return $hydrator;
    }
}

To test my module I created some unit tests. One of them is:

class HalEntityHydratorTest extends TestCase
{
    protected $moduleLoader;

    protected function setUp()
    {
        $this->moduleLoader = new ModuleLoader([
            'modules' => [
                'Zend\Hydrator',
                'Zend\Router',
                'ZF\Hal',
                'MyHalHydratorModule',
                'MyHalHydratorModuleTest\Integration\Hydrator\HalEntityHydratorTest',
            ],
            'module_listener_options' => [],
        ]);

        $this->moduleLoader->getApplication()->bootstrap();
    }

    public function testHalRendererWithHalEntities()
    {
        $halPlugin = $this->moduleLoader->getServiceManager()->get('ViewHelperManager')->get('Hal');

        $rootTestEntity = new RootTestEntity();
        $childTestEntity = new ChildTestEntity();

        $rootTestEntity->setChildEntity(new Entity($childTestEntity));
        $rootTestEntity->setUnkownChildEntity(new Entity(new UnkownChildTestEntity()));

        $expectedArray = [
            '_embedded' => [
                'phpunit:test-entity' => [
                    '_links' => [],
                ],
                'unkownChildEntity' => [
                    'unkownChildTestProperty' => 'phpunit',
                    '_links' => [],
                ],
            ],
            '_links' => [],
        ];

        $this->assertSame($expectedArray, $halPlugin->renderEntity(new Entity($rootTestEntity)));
    }
}

These are my test entities:

class ChildTestEntity
{
}

class UnkownChildTestEntity
{
    protected $unkownChildTestProperty = 'phpunit';
}

class RootTestEntity
{
    protected $childEntity;
    protected $unkownChildEntity;

    public function setUnkownChildEntity($unkownChildEntity)
    {
        $this->unkownChildEntity = $unkownChildEntity;
    }

    public function setChildEntity($childEntity)
    {
        $this->childEntity = $childEntity;
    }
}

And then it could be good to know what my test module configuration looks like:

public function getConfig()
{
    return [
        'zf-hal' => [
            'metadata_map' => [
                ChildTestEntity::class => [
                    'force_self_link' => false,
                ],
                UnkownChildTestEntity::class => [
                    'force_self_link' => false,
                ],
            ],
        ],
        'my-hal-hydrator-module' => [
            'class' => [
                RootTestEntity::class => [
                    'property_name_translation' => [
                        'childEntity' => 'phpunit:test-entity',
                    ],
                ],
            ],
        ],
    ];
}

Ok, enough sourcecode. What happens now?
I run my test suite and the test above fails because of the arrays are different. That's why the first key of the result array is still 'childEntity' and not 'phpunit:test-entity' as expected.
So I think the property replacement has not take place but I don't know why.

weierophinney commented 5 years ago

Can you show us the ModuleOptions class, please?

kschroeer commented 5 years ago

No problem, here's the class:

use Zend\Stdlib\AbstractOptions;

class ModuleOptions extends AbstractOptions
{
    protected $hydrator = 'reflection';
    protected $class = [];

    public function getClass()
    {
        return $this->class;
    }

    public function setClass(array $class)
    {
        $this->class = $class;
        return $this;
    }

    public function getHydrator()
    {
        return $this->hydrator;
    }
}

And the corresponding factory:

use Interop\Container\ContainerInterface;
use Zend\ServiceManager\Factory\FactoryInterface;

class ModuleOptionsFactory implements FactoryInterface
{
    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
    {
        $config = $container->get('config');

        $moduleConfig = [];
        if (isset($config['my-hal-hydrator-module'])) {
            $moduleConfig = $config['my-hal-hydrator-module'];
        }

        return new ModuleOptions($moduleConfig);
    }
}
weierophinney commented 4 years ago

This repository has been closed and moved to laminas-api-tools/api-tools-hal; a new issue has been opened at https://github.com/laminas-api-tools/api-tools-hal/issues/3.