public function testChildResourceObjectIdentifierMappingInCollectionsViaControllerReturn() { $this->setUpAlternateRouter(); $resource = new Resource(); $resource->getEventManager()->attach('fetchAll', function ($e) { return array((object) array('id' => 'luke', 'name' => 'Luke Skywalker'), (object) array('id' => 'leia', 'name' => 'Leia Organa')); }); $controller = new RestController(); $controller->setPluginManager($this->plugins); $controller->setResource($resource); $controller->setRoute('parent/child'); $controller->setIdentifierName('child_id'); $controller->setCollectionName('children'); $r = new ReflectionObject($controller); $m = $r->getMethod('getIdentifier'); $m->setAccessible(true); $uri = 'http://localhost.localdomain/api/parent/anakin/child'; $request = new Request(); $request->setUri($uri); $matches = $this->router->match($request); $this->assertInstanceOf('Zend\\Mvc\\Router\\RouteMatch', $matches); $this->assertEquals('anakin', $matches->getParam('id')); $this->assertNull($matches->getParam('child_id')); $this->assertEquals('parent/child', $matches->getMatchedRouteName()); // Emulate url helper factory and inject route matches $this->helpers->get('url')->setRouteMatch($matches); $result = $controller->getList(); $this->assertInstanceOf('ZF\\Hal\\Collection', $result); // Now, what happens if we render this? $model = new HalJsonModel(); $model->setPayload($result); $json = $this->renderer->render($model); $test = json_decode($json); $this->assertObjectHasAttribute('_links', $test); $this->assertObjectHasAttribute('self', $test->_links); $this->assertObjectHasAttribute('href', $test->_links->self); $this->assertEquals('http://localhost.localdomain/api/parent/anakin/child', $test->_links->self->href); $this->assertObjectHasAttribute('_embedded', $test); $this->assertObjectHasAttribute('children', $test->_embedded); $this->assertInternalType('array', $test->_embedded->children); foreach ($test->_embedded->children as $child) { $this->assertObjectHasAttribute('_links', $child); $this->assertObjectHasAttribute('self', $child->_links); $this->assertObjectHasAttribute('href', $child->_links->self); $this->assertRegexp('#^http://localhost.localdomain/api/parent/anakin/child/[^/]+$#', $child->_links->self->href); } }
/** * Loop through configuration to discover and set controller options. * * @param array $config * @param RestController $controller */ protected function setControllerOptions(array $config, RestController $controller) { foreach ($config as $option => $value) { switch ($option) { case 'collection_http_methods': $controller->setCollectionHttpMethods($value); break; case 'collection_name': $controller->setCollectionName($value); break; case 'collection_query_whitelist': if (is_string($value)) { $value = (array) $value; } if (!is_array($value)) { break; } // Create a listener that checks the query string against // the whitelisted query parameters in order to seed the // collection route options. $whitelist = $value; $controller->getEventManager()->attach('getList.pre', function (Event $e) use($whitelist) { $controller = $e->getTarget(); $resource = $controller->getResource(); if (!$resource instanceof Resource) { // ResourceInterface does not define setQueryParams, so we need // specifically a Resource instance return; } $request = $controller->getRequest(); if (!method_exists($request, 'getQuery')) { return; } $query = $request->getQuery(); $params = new Parameters(array()); foreach ($query as $key => $value) { if (!in_array($key, $whitelist)) { continue; } $params->set($key, $value); } $resource->setQueryParams($params); }); $controller->getEventManager()->attach('getList.post', function (Event $e) use($whitelist) { $controller = $e->getTarget(); $resource = $controller->getResource(); if (!$resource instanceof Resource) { // ResourceInterface does not define setQueryParams, so we need // specifically a Resource instance return; } $collection = $e->getParam('collection'); if (!$collection instanceof Collection) { return; } $params = $resource->getQueryParams()->getArrayCopy(); // Set collection route options with the captured query whitelist, to // ensure paginated links are generated correctly $collection->setCollectionRouteOptions(array('query' => $params)); // If no self link defined, set the options in the collection and return $links = $collection->getLinks(); if (!$links->has('self')) { return; } // If self link is defined, but is not route-based, return $self = $links->get('self'); if (!$self->hasRoute()) { return; } // Otherwise, merge the query string parameters with // the self link's route options $self = $links->get('self'); $options = $self->getRouteOptions(); $self->setRouteOptions(array_merge($options, array('query' => $params))); }); break; case 'entity_http_methods': $controller->setEntityHttpMethods($value); break; /** * The identifierName is a property of the ancestor * and is described by Apigility as route_identifier_name */ /** * The identifierName is a property of the ancestor * and is described by Apigility as route_identifier_name */ case 'route_identifier_name': $controller->setIdentifierName($value); break; case 'page_size': $controller->setPageSize($value); break; case 'page_size_param': $controller->setPageSizeParam($value); break; /** * @todo Remove this by 1.0; BC only, starting in 0.9.0 */ /** * @todo Remove this by 1.0; BC only, starting in 0.9.0 */ case 'resource_http_methods': $controller->setEntityHttpMethods($value); break; case 'route_name': $controller->setRoute($value); break; } } }