Empty embedded items in RPC Service using HAL response

To share I am using RPC service and I returning HAL response. Following listCustomerOrdersAction() is the action method from Controller.

public function listCustomerOrdersAction()
    {
        $customerId = (int)$this->params()->fromRoute('customer_id',-1);
        $query = $this->listCustomerOrdersService->getQuery($customerId);

        $adaptor = new DoctrineAdapter(new ORMPaginator($query, false));
        $ordersCollection = new OrdersCollection($adaptor);

        return new ViewModel([
            'payload' => $this->getPluginManager()->get('Hal')->createCollection($ordersCollection)
        ]);

    }

But I am getting empty embedded items in the HAL response.

Please refer to the output below:
{
    "_links": {
        "self": {
            "href": "http://localhost/v1/orders?page=1"
        },
        "first": {
            "href": "http://localhost/v1/orders"
        },
        "last": {
            "href": "http://localhost/v1/orders?page=86"
        },
        "next": {
            "href": "http://localhost/v1/orders?page=2"
        }
    },
    "_embedded": {
        "items": [
            [],
            [],
            [],
            [],
            [],
            [],
.......

The service code where I am formulating the query is:

 public function getQuery($customerId)
    {
        $queryBuilder = $this->entityManager->createQueryBuilder();

        $queryBuilder->select('x')
            ->from(Orders::class, 'x')
            ->where('x.customersId = ?1')
            ->setParameter(1,(int)$customerId);

        return $queryBuilder->getQuery();
    }

In the above code I assume embedded items are populated in the above service code behind the scene. May be my assumption is wrong and my understanding is not right, please guide me how do I achieve the desired result. My desired result is I want to have embedded items full of OrderEntity data.

I do not use the API Tools, so my help is only rudimentary.


It looks like some mapping are missing or did not work:

https://api-tools.getlaminas.org/documentation/recipes/hal-from-rpc#the-payload

As @froschdesign said, it looks like you 're missing the metadata configuration in the metadata_map configuration key for api_tools_hal. Therefore the HAL implementation tries a few things.

  1. Extraction by a configured entity hydrator
    If a hydrator was configured for the orders entity, it will be used to get the data out of your entity. If no hydrator was set, we 'll go to the next step.
    I would always recommend you a hydrator to hydrate and extract the data from your entity. Laminas has a very good hydrator implementation that allows you to do some pretty cool things, like naming or hydrator strategies. Normally properties of an entity are written in the camel case notation. If you want to have your JSON output in underscore notation, naming strategies are highly recommended. Hydrators make sure that you only put out the things that you want to put out.

  2. Implementation of the JsonSerializable interface
    If your entity implements the JsonSerializable interface it will be used to extract the data from your entity. If no hydrator was defined for the entity, the implementation of the JsonSerializable interface will be checked. If this interface was not implemented we 'll come to the next step.

  3. Instance of ArrayObject
    If your entity is an instance of ArrayObject, HAL will try to use the ArrayObject::getArrayCopy() method. This would return an array of the properties of your entity.

  4. Use of get_object_vars()
    If none of the first three steps matches, HAL tries to get the entity properties with get_object_vars(). This can lead to some ugly side effects. If your entity properties are written as protected or private properties, it would return an empty array.

I have a suspicion about your issue. Your entity is not in the hal metadata map, does not implement the JsonSerializable interface and is not an instance of ArrayObject and all your entity properties are at least protected or even worse: private and the extraction leads into an empty array.

1 Like

Thanks! Meanwhile following is the snippet:
I have the hydtrator. Since I am using Code-connected the resource file has the required logic. I am no longer using the controller now just to keep the things simple. Please see the snippet and guide me what am I missing here.
I have a question - there are a number of files generated; what is the purpose of the files - CustomerOrdersEntity and CustomerOrdersCollection?

I was not using the right code in generated Resource files in code-connected configuration for the REST service. The code snippet that worked for fetchAll() is as follows:

public function fetchAll($params = [])
{
   $customerId = $params["customer_id"];
   $orders = $this->entityManager->getRepository(Orders::class)->findByCustomersId($customerId);
   $hydrator = new DoctrineHydrator($this->entityManager);

   $customerOrders = [];
   foreach ($orders as $order){
       //extract array from each order object
       $customerOrders[]  = $hydrator->extract($order);
   }

   return new CustomerOrdersCollection(new Adapter\ArrayAdapter($customerOrders));
}

May be it come help someone in need.