# Generate templated Link with Zend Expressive HAL

**URL:** <https://discourse.laminas.dev/t/generate-templated-link-with-zend-expressive-hal/935>\
**Category:** Mezzio\
**Tags:** zend-expressive-hal\
**Created:** [February 6, 2019, 8:54pm UTC](https://discourse.laminas.dev/t/generate-templated-link-with-zend-expressive-hal/935 "2019-02-06T20:54:11Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![DominicDetta](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/dominicdetta/32/285_2.png) [@DominicDetta](https://discourse.laminas.dev/u/DominicDetta)\
**Post date:** [February 6, 2019, 8:54pm UTC](https://discourse.laminas.dev/t/generate-templated-link-with-zend-expressive-hal/935/1 "2019-02-06T20:54:11Z")

</div>

As the title say I’m tryng to generate a **templated** Link as defined by the [PSR-13](https://www.php-fig.org/psr/psr-13/#14-link-templates) and documented in [zend expressive hal](https://docs.zendframework.com/zend-expressive-hal/links-and-resources/#links).

My router is the `FastRoute`.

This is my actual routing configuration in the `config/routes.php` file:

```php
    $app->get('/api/test/{id:\d+}',App\Handler\TestHandler::class,'api.test.get');

```

The code I use to generate the link:

```php
/* Code */
 $linkGenerator=$this->resourceGenerator
        ->getLinkGenerator();
$linkGenerator->templatedFromRoute(
                    'test',
                    $request,
                    'api.test.get'
                );
/* Code */

```

The problem is that the code continue to throw an error with the following message:

```php
Zend\Expressive\Router\Exception\RuntimeException raised in file D:\Sites\expressive-test\vendor\zendframework\zend-expressive-fastroute\src\FastRouteRouter.php line 299:
Message: Route `api.test.get` expects at least parameter values for [id], but received []

```

The json response that I expect is something similar to this:

```auto
{
    "id": 1,
    "name": "Test",
    "_links": {
        "self": {
            "href": "http://localhost/expressive-test/public/api/workflow-resources/1"
        },
        "test": {
            "href": "http://localhost/expressive-test/public/api/test/{id}",
            "templated": true
        }
    }
}

```

---

<div class="post-metadata">

**Author:** ![ocramius](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/ocramius/32/23_2.png) [@ocramius](https://discourse.laminas.dev/u/ocramius)\
**Post date:** [February 9, 2019, 3:46pm UTC](https://discourse.laminas.dev/t/generate-templated-link-with-zend-expressive-hal/935/2 "2019-02-09T15:46:33Z")

</div>

Does it by chance work if you use {id} instead of {id:\d+}?

> The json response that I expect is something similar to this

Where would the “1” come from?

---

<div class="post-metadata">

**Author:** ![DominicDetta](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/dominicdetta/32/285_2.png) [@DominicDetta](https://discourse.laminas.dev/u/DominicDetta)\
**Post date:** [February 11, 2019, 9:17am UTC](https://discourse.laminas.dev/t/generate-templated-link-with-zend-expressive-hal/935/3 "2019-02-11T09:17:41Z")

</div>

I already tried to remove the constraints and to rename the placeholder and it didn’t solve the error.  
Maybe it’s a problem related to FastRoute, I will try with ZendRouter.

---

<div class="post-metadata">

**Author:** ![DominicDetta](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/dominicdetta/32/285_2.png) [@DominicDetta](https://discourse.laminas.dev/u/DominicDetta)\
**Post date:** [February 12, 2019, 7:45pm UTC](https://discourse.laminas.dev/t/generate-templated-link-with-zend-expressive-hal/935/4 "2019-02-12T19:45:17Z")

</div>

Even with the Zend Router the problem persists.

The `routes.php` file contains the following with FastRoute:

```php
return function (Application $app, MiddlewareFactory $factory, ContainerInterface $container) : void {
    $app->get('/api/test',App\Handler\TestHandler::class,'api.test.list');
    $app->post('/api/test',App\Handler\TestHandler::class,'api.test.create');
    $app->get('/api/test/{id}',App\Handler\TestHandler::class,'api.test.get');
    $app->get('/api/workflow-resources/{id} ',Workflow\Handler\GetWorkflowResource::class,'api.workflowresources.get');

};

```

The code of GetWorkflowResourceHandler:

```php
<?php

declare(strict_types=1);

namespace Workflow\Handler;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Zend\Expressive\Hal\HalResponseFactory;
use Zend\Expressive\Hal\ResourceGenerator;
use Zend\Expressive\Hal\LinkGenerator;
use Zend\Hydrator\ObjectPropertyHydrator;
use Workflow\Entity\WorkflowResource;

class GetWorkflowResource implements RequestHandlerInterface
{

    private $configJson;

    /** @var ResourceGenerator */
    private $resourceGenerator;

    /** @var HalResponseFactory */
    private $responseFactory;

    private $linkGenerator;

    /**
     * 
     */
    public function __construct(
        $configJson,
        ResourceGenerator $resourceGenerator,
        HalResponseFactory $responseFactory,
        LinkGenerator $linkGenerator
    ){

        $this->configJson=$configJson;
        $this->resourceGenerator = $resourceGenerator;
        $this->responseFactory = $responseFactory;
        $this->linkGenerator=$linkGenerator;
    }

/**
     * {@inheritDoc}
     */
    public function handle(ServerRequestInterface $request) : ResponseInterface
    {
        $workflowResourceJson=array_shift($this->configJson);

        $hydrator=new ObjectPropertyHydrator();
        $workflowResource=$hydrator->hydrate($workflowResourceJson,new WorkflowResource());

        $resource = $this->resourceGenerator->fromObject($workflowResource, $request);
        //$linkGenerator = $this->resourceGenerator->getLinkGenerator();

        $linkGenerator=$this->linkGenerator;
        $actionLink=$linkGenerator->templatedFromRoute(
                    'test',
                    $request,
                   'api.test.get',
                    [],
                    []
                );
        $actionLink= $actionLink->withAttribute('method','GET');
        $resource=$resource->withLink($actionLink);

        return $this->responseFactory->createResponse($request, $resource);
    }

}

```

What I tried is to compose and use the class `LinkGenerator` instead of retrieving it directly from the `$this->resourceGenerator->getLinkGenerator()` but again the error appears.

Can anyone help me or show me a code where a templated link is generated with the zend expressive hal component?

---

<div class="post-metadata">

**Author:** ![DominicDetta](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/dominicdetta/32/285_2.png) [@DominicDetta](https://discourse.laminas.dev/u/DominicDetta)\
**Post date:** [February 13, 2019, 2:12pm UTC](https://discourse.laminas.dev/t/generate-templated-link-with-zend-expressive-hal/935/5 "2019-02-13T14:12:47Z")

</div>

Also I searched the [test](https://github.com/zendframework/zend-expressive-hal/blob/4fd30329f61ebd7f33dfaa798195ef45861f477e/test/LinkGeneratorTest.php) developed for this feature and it seems to me that it is missing a specific case where some route parameters exist in the URI of the request.

---

<div class="post-metadata">

**Author:** ![froschdesign](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/froschdesign/32/38_2.png) [@froschdesign](https://discourse.laminas.dev/u/froschdesign)\
**Post date:** [February 13, 2019, 2:54pm UTC](https://discourse.laminas.dev/t/generate-templated-link-with-zend-expressive-hal/935/6 "2019-02-13T14:54:23Z")

</div>

The error message that is thrown is correct, because the url helpers (used by the url generator) can not create the url without all required parameters. But if you add the missing parameter, you still do not get the right link with the placeholder. The url helpers return the fully generated link.

And the documentation does not say that you’re getting a templated link with placeholders.

> `fromRoute()` will generate a non-templated `Link` instance, while `templatedFromRoute()` generates a templated instance.

_Templated instance_ of the `Link` class, not templated link or URI which can be used in an output.  
Unfortunately very misleading – in my opinion!

**Conclusion** : It looks like this feature is not implemented and should be reported at the issue tracker.

---

<div class="post-metadata">

**Author:** ![matthew](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/matthew/32/14_2.png) [@matthew](https://discourse.laminas.dev/u/matthew)\
**Post date:** [February 13, 2019, 3:04pm UTC](https://discourse.laminas.dev/t/generate-templated-link-with-zend-expressive-hal/935/7 "2019-02-13T15:04:46Z")

</div>

Well, it was _meant_ to be implemented… but clearly I didn’t do any integration tests. So, yes, please, report it in the tracker; ideally submit a PR with a failing test case.

---

<div class="post-metadata">

**Author:** ![froschdesign](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/froschdesign/32/38_2.png) [@froschdesign](https://discourse.laminas.dev/u/froschdesign)\
**Post date:** [February 13, 2019, 3:06pm UTC](https://discourse.laminas.dev/t/generate-templated-link-with-zend-expressive-hal/935/8 "2019-02-13T15:06:37Z")

</div>

@matthew  
Thanks for the quick feedback and the clarification!

---

<div class="post-metadata">

**Author:** ![DominicDetta](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/dominicdetta/32/285_2.png) [@DominicDetta](https://discourse.laminas.dev/u/DominicDetta)\
**Post date:** [February 19, 2019, 8:19pm UTC](https://discourse.laminas.dev/t/generate-templated-link-with-zend-expressive-hal/935/9 "2019-02-19T20:19:50Z")

</div>

Ok thanks for the info. I will try to create a PR even if I dont know exactly where to start.
