5

I'm attempting to write a child route for an Apigility Service, and everything routes fine until it comes time to render the _self link..

GET http://host/api/service/parameter/gui/page

{
    "status": 500,
    "title": "Unexpected error",
    "describedBy": "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html",
    "detail": "Missing parameter \"parameter\"",
    "details": {
        "code": 0,
        "message": "Missing parameter \"parameter\"",
        "trace": "#0 /vagrant/vendor/zendframework/zendframework/library/Zend/Mvc/Router/Http/Segment.php(313): Zend\\Mvc\\Router\\Http\\Segment->buildPath(Array, Array, true, true, Array)\n#1 /vagrant/vendor/zendframework/zendframework/library/Zend/Mvc/Router/Http/Segment.php(409): Zend\\Mvc\\Router\\Http\\Segment->buildPath(Array, Array, false, true, Array)\n#2 /vagrant/vendor/zendframework/zendframework/library/Zend/Mvc/Router/Http/Part.php(197): Zend\\Mvc\\Router\\Http\\Segment->assemble(Array, Array)\n#3 /vagrant/vendor/zendframework/zendframework/library/Zend/Mvc/Router/Http/TreeRouteStack.php(351): Zend\\Mvc\\Router\\Http\\Part->assemble(Array, Array)\n#4 /vagrant/vendor/zendframework/zendframework/library/Zend/View/Helper/Url.php(100): Zend\\Mvc\\Router\\Http\\TreeRouteStack->assemble(Array, Array)\n#5 [internal function]: Zend\\View\\Helper\\Url->__invoke('parent.rest.s...', Array, Array, true)\n#6 /vagrant/vendor/zfcampus/zf-hal/src/Plugin/Hal.php(610): call_user_func(Object(Zend\\View\\Helper\\Url), 'parent.rest.s...', Array, Array, true)\n#7 /vagrant/vendor/zfcampus/zf-hal/src/Plugin/Hal.php(634): ZF\\Hal\\Plugin\\Hal->fromLink(Object(ZF\\Hal\\Link\\Link))\n#8 /vagrant/vendor/zfcampus/zf-hal/src/Plugin/Hal.php(667): ZF\\Hal\\Plugin\\Hal->fromLinkCollection(Object(ZF\\Hal\\Link\\LinkCollection))\n#9 /vagrant/vendor/zfcampus/zf-hal/src/Plugin/Hal.php(525): ZF\\Hal\\Plugin\\Hal->fromResource(Object(ZF\\Hal\\Entity))\n#10 /vagrant/vendor/zfcampus/zf-hal/src/View/HalJsonRenderer.php(117): ZF\\Hal\\Plugin\\Hal->renderEntity(Object(ZF\\Hal\\Entity))\n#11 /vagrant/vendor/zendframework/zendframework/library/Zend/View/View.php(205): ZF\\Hal\\View\\HalJsonRenderer->render(Object(ZF\\Hal\\View\\HalJsonModel))\n#12 /vagrant/vendor/zendframework/zendframework/library/Zend/Mvc/View/Http/DefaultRenderingStrategy.php(102): Zend\\View\\View->render(Object(ZF\\Hal\\View\\HalJsonModel))\n#13 [internal function]: Zend\\Mvc\\View\\Http\\DefaultRenderingStrategy->render(Object(Zend\\Mvc\\MvcEvent))\n#14 /vagrant/vendor/zendframework/zendframework/library/Zend/EventManager/EventManager.php(468): call_user_func(Array, Object(Zend\\Mvc\\MvcEvent))\n#15 /vagrant/vendor/zendframework/zendframework/library/Zend/EventManager/EventManager.php(207): Zend\\EventManager\\EventManager->triggerListeners('render', Object(Zend\\Mvc\\MvcEvent), Array)\n#16 /vagrant/vendor/zendframework/zendframework/library/Zend/Mvc/Application.php(352): Zend\\EventManager\\EventManager->trigger('render', Object(Zend\\Mvc\\MvcEvent))\n#17 /vagrant/vendor/zendframework/zendframework/library/Zend/Mvc/Application.php(327): Zend\\Mvc\\Application->completeRequest(Object(Zend\\Mvc\\MvcEvent))\n#18 /vagrant/public/index.php(38): Zend\\Mvc\\Application->run()\n#19 {main}"
    }
}

Looking at the ZF2 MVC documentation, I kind of realize I need to setup a metadata_map entry for my parameter.. but no examples for Apigility exist. I've read hard coding didn't work, even so, this parameter is dynamic and it's unclear how to do that call properly from module.config.php

'zf-hal' => array(
    'metadata_map' => array(
        'Parent\\V1\\Rest\\Service\\ServiceEntity' => array(
            'entity_identifier_name' => 'parameter',
            'route_name' => 'parent.rest.service',
            'route_identifier_name' => 'parameter',
            'hydrator' => 'Zend\\Stdlib\\Hydrator\\ObjectProperty',
        ),
        'Parent\\V1\\Rest\\Service\\ServiceCollection' => array(
            'entity_identifier_name' => 'parameter',
            'route_name' => 'parent.rest.service',
            'route_identifier_name' => 'parameter',
            'is_collection' => true,
        ),
        'Parent\\V1\\Rest\\Gui\\GuiEntity' => array(
            'entity_identifier_name' => 'page',
            'route_name' => 'parent.rest.service/gui',
            'route_identifier_name' => 'gui_page',
            'route_params' => array('parameter'),   #<------- unknown setting area, needs to get the parent route identifier somehow...
            'hydrator' => 'Zend\\Stdlib\\Hydrator\\ObjectProperty',
        ),
        'Parent\\V1\\Rest\\Gui\\GuiCollection' => array(
            'entity_identifier_name' => 'page',
            'route_name' => 'parent.rest.service/gui',
            'route_identifier_name' => 'gui_page',
            'route_params' => array('parameter'),
            'is_collection' => true,
        ),
    ),
),

edit : adding the router configuration

 'router' => array(
        'routes' => array(
            'parent.rest.service' => array(
                'type' => 'Segment',
                'options' => array(
                    'route' => '/api/service[/:parameter]',
                    'defaults' => array(
                        'controller' => 'Parent\\V1\\Rest\\Service\\Controller',
                    ),
                ),
                'may_terminate' => true,
                'child_routes' => array(
                    'gui' => array(
                        'type' => 'Segment',
                        'options' => array(
                            'route' => '/gui[/:page]',
                            'defaults' => array(
                                'controller' => 'Parent\\V1\\Rest\\Gui\\Controller',
                            ),
                        ),
                    )
                ),
            ),
        ),
    ),
Wilt
  • 41,477
  • 12
  • 152
  • 203
Erik
  • 2,782
  • 3
  • 34
  • 64

1 Answers1

1

I have some experience with Apigility, but not with your kind of configuration. As far as I know it indeed means that the Hal-plugin cannot find a value for your the route_identifier called parameter and because of this it cannot render a self link.

In some of your metadata_map configs (nr 1+2) it seems like it should get the value from the resource: There is a route with route name:

'route_name' => 'parent.rest.service',

To properly render this route we need a route identifier named parameter:

'route_identifier_name' => 'parameter',

To get the value for this route identifier we need the property named parameter from the entity:

'entity_identifier_name' => 'parameter',

Most likely the entity should have data in an array and that array should hold a key named 'parameter' then it will try to get the value that goes with that key.


In another metadata_map config you set an array of 'static' route params like this:

'route_params' => array('parameter'),

This is incomplete, because you set a route parameter array always with a key and a value. So this should be something like this:

'route_params' => array('parameter' => 'my_value'),

Where my_value can be a string an integer etc.

I hope this insight can help you a bit. I don't seem to totally see where else it could go wrong.

Wilt
  • 41,477
  • 12
  • 152
  • 203
  • if the parameter value is dynamic.. I'm at a loss as to how to grab it... in this case it's a user's name – Erik Jul 12 '14 at 23:44
  • @Erik If it is the users name and the name of the parameter in the entity is `userName`, you can then set the entity_identifier_name accordingly. – Wilt Jul 28 '14 at 14:16
  • I renamed it to `parameter` in this question .. for generality purposes.. but I have the correct name in my deployment. – Erik Jul 28 '14 at 17:22
  • and here guys, it's how easy is to pass a parameter to this router. Amazing! – funder7 Jan 27 '22 at 17:51