How do you debug RPC service failing to produce HAL JSON output?

I’m trying to set up an RPC service , but I simply can’t get the metadata_map to work.

I have set up the metadata_map to the best of my abilities in the api-tools-hal section of module.config.php, but the output is limited to object data, but no _links section is displayed containing the HAL self and href keys.

In order to debug this I have tried attaching the HAL events and see the data going in and out of the different events, but the only event I can hook onto successfully is the renderEntity event.

How on earth are you supposed to debug this? No error messages, no warnings, no nothing…

You just have to look at it in the clouds!

Well, I was hoping for something a bit more specific. :slight_smile:

Welcome to the forums :slight_smile:


Have you tried xdebug?
No clouds involved, pinky promise ;D

Hi @jAsgeir , perhaps more details or showing some code would help to get the context, but from what I can understand you are looking for something like this.

        /** @var \Laminas\ApiTools\Hal\Plugin\Hal $halPlugin*/
        $halPlugin = $this->getPluginManager()->get('Hal');

        return new ViewModel([
            //  'payload' => $halPlugin->createEntity($entity)
            'payload' => $halPlugin->createCollection($collection)
        ]);

I am trying to hooking up the Hal events from Module.php, onBootstrap function, but as soon as I try to grab the Hal helper (), I render the Admin UI “Unable to fetch the rpc services”:
public function onBootstrap($e)
{
$app = $e->getTarget();
$services = $app->getServiceManager();
$helpers = $services->get(‘ViewHelperManager’);
$hal = $helpers->get(‘Hal’);

public function onBootstrap($e)
{
    $app = $e->getTarget();
    $services = $app->getServiceManager();
    $helpers = $services->get(‘ViewHelperManager’);
    $hal = $helpers->get(‘Hal’);

It’s not in the Module.php onBootstrap function, but in your RPC Controller Action (extends AbstractActionController).

here a simple example.

<?php
namespace Sports\V1\Rpc\PlayersByTeam;

use Laminas\ApiTools\ContentNegotiation\ViewModel;
use Laminas\Mvc\Controller\AbstractActionController;
use Sports\V1\Rest\Player\PlayerTable;

class ProfileToInvoiceController extends AbstractActionController
{
    /**
     * @var PlayerTable $playerTable
     */
    private PlayerTable $playerTable;
    /**
     * @param PlayerTable $playerTable
     */
    public function __construct(
        PlayerTable $playerTable
    )
    {
        $this->playerTable = $playerTable;
    }

    public function playersByTeamAction()
    {
        $team = $this->queryParam("team");
        /** @var \Sports\V1\Rest\Player\PlayerCollection $playerCollection */
        $playerCollection = $this->playerTable->fetchCollection(['team'=> $team]);

        /** @var \Laminas\ApiTools\Hal\Plugin\Hal $halPlugin*/
        $halPlugin = $this->getPluginManager()->get('Hal');

        return new ViewModel([
            'payload' => $halPlugin->createCollection($playerCollection)
        ]);
     }
}

Thnx, jpoitras.

I was initially working with the Laminas Api Tools framework, but I’m now using the MVC framework. Thus I have been using the Hal-plugin, and after reading some code in the plugin I found out how to fill the _embedded and _links elements with sensible data.

So far, I’m adding the self links to the elements being displayed by way of the entityRender event, since I would like to include the self link in all my entities:

    public function onBootstrap($e)
    {
        $app = $e->getTarget();
        $services = $app->getServiceManager();
        $helpers  = $services->get('ViewHelperManager');
        $hal      = $helpers->get('Hal');
        $hal->getEventManager()->attach('renderEntity', [$this, 'onRenderEntity']);
    }

    public function onRenderEntity($e)
    {
        $halEntity = $e->getParam('entity');
        $entity = $halEntity->getEntity();
        $entityClassname = get_class($entity);
        $aClassnameElements = preg_split('#\\\\#', $entityClassname);
        $classname = strtolower(array_pop($aClassnameElements));
        $classpath = implode("\\", $aClassnameElements);
        if ($classpath == "SPModel")
        {
            $routename = 'home/' . $classname;
            $routeidentifier = $classname . "_id"; 
            $halEntity->getLinks()->add(\Laminas\ApiTools\Hal\Link\Link::factory([
                'rel' => 'self',
                'route' => [
                    'name' => $routename,
                    'params' => [
                        $routeidentifier => $entity->getId()
                        ] 
                    ],
                ])
            );
        } else {
            return;
        }
    }

I am using the ArraySerializable hydrator, and the most sensible way of adding _embedded entities I have found to be to add the entities directly in my model, in the getArrayCopy() hydrating function:

    public function getArrayCopy()
    {
        $aValues = $this->toArray();

        $criteria = new Criteria();
        $criteria->add("translation.type_id", [1, 2], Criteria::EQUAL);

        $propelTranslations = $this->getTranslations($criteria);
        $aTranslations = [];
        foreach ($propelTranslations as $propelTranslation)
        {
            $aTranslations[] = new \Laminas\ApiTools\Hal\Entity($propelTranslation, $propelTranslation->getId());
        }
        $halTranslations = new \Laminas\ApiTools\Hal\Collection($aTranslations);
        $aValues['Translations'] = $halTranslations;

        $propelTips = $this->getTipss();
        $aTips = [];
        foreach ($propelTips as $propelTip)
        {
            $aTips[] = new \Laminas\ApiTools\Hal\Entity($propelTip, $propelTip->getId());
        }
        $halTips = new \Laminas\ApiTools\Hal\Collection($aTips);
        $aValues['Tips'] = $halTips;

        return $aValues;
    }

If this is the most sensible way of doing things is an open question, and I’d appreciate any comments or advice on the matter… :slight_smile: