4

I'm trying to return a collection of the products that meets an input criteria using Magento api2. Everything is working as expected other than how Magento formats the final JSON.

What i basically want to do is to return a json with two keys, one is the totalRecords specifying the number of items without the sorting (so I can handle the paging on the client side), and the items as the actual products. So I made the below tries:

1. First Try

The problem I faced here is that i found Magento for some reason always return the first item in the Items key, so I get the right totalRecords and the right first item, but i'm not sure what kind of post processing Magento does on the output to stripe the other elements of the array.

protected function _retrieveCollection()
{

      $store_id =  $this->getRequest()->getParam('store_id');
      $page_size = $this->getRequest()->getParam('page_size');
      $page = $this->getRequest()->getParam('page');

      /** @var $collection Mage_Catalog_Model_Resource_Product_Collection */
      $collection = Mage::getResourceModel('catalog/product_collection');
      $collection->setStoreId($store_id);
      $collection->addAttributeToSelect(array_keys(
        $this->getAvailableAttributes($this->getUserType(), Mage_Api2_Model_Resource::OPERATION_ATTRIBUTE_READ)
      ));
      $collection->addFinalPrice();

      if ($page_size) $collection->setPageSize($page_size);
      if ($page) $collection->setCurPage($page);

      $this->_applyCategoryFilter($collection);
      $this->_applyCollectionModifiers($collection);
      $products = $collection->load()->toArray();

      return array(
          'items' => array_values($products),
          'totalRecords' => $collection->getSize()
        );
}

2. Second Try

The second try was simply using the Varien_Data_Collection. It returns all the items, however the totalRecords will be incorrect, since im just copying the results from the products collection to this new collection, which is obviously not the right approach.

protected function _retrieveCollection()
{

      $store_id =  $this->getRequest()->getParam('store_id');
      $page_size = $this->getRequest()->getParam('page_size');
      $page = $this->getRequest()->getParam('page');

      /** @var $collection Mage_Catalog_Model_Resource_Product_Collection */
      $collection = Mage::getResourceModel('catalog/product_collection');
      $collection->setStoreId($store_id);
      $collection->addAttributeToSelect(array_keys(
        $this->getAvailableAttributes($this->getUserType(), Mage_Api2_Model_Resource::OPERATION_ATTRIBUTE_READ)
      ));
      $collection->addFinalPrice();

      if ($page_size) $collection->setPageSize($page_size);
      if ($page) $collection->setCurPage($page);

      $this->_applyCategoryFilter($collection);
      $this->_applyCollectionModifiers($collection);
      $products = $collection->load()->toArray();

      $varien_collection = new Varien_Data_Collection();
      foreach ($products as $key => $value) {
          $varien_object = new Varien_Object();
          $varien_object->setData($value);
          $varien_collection->addItem($varien_object);
      }

      return $varien_collection;
}

I think my first try was using the right approach, however i have no clue on why Magento is stripping the other items in the Items array.


Update #1

After a small discussion with @DigitalPianism, I was thinking there is a problem with the JSON breaking the Magento formatting, however when i try returning something as simple as this:

      return array(
          'items' => array(array('r' => 'q'), array('b' => 'f')),
          'totalRecords' => $collection->getSize()
        );

only the first item is returned as well, and with the lack of documentation i have no idea why the first array is only returned.

Update #2

Problem is in nested array, outlined here https://magento.stackexchange.com/a/33012/13103, not sure how i should return it now :(

Update #3

So digging in how Magento formats the output, i opened /app/code/core/Mage/Api2/Model/Resource.php as suggested by @DigtialPianisim, to line 243 (Magento 1.9), specifically this block:

        case self::ACTION_TYPE_COLLECTION . self::OPERATION_RETRIEVE:
            $this->_errorIfMethodNotExist('_retrieveCollection');
            $retrievedData = $this->_retrieveCollection();
            $filteredData  = $this->getFilter()->collectionOut($retrievedData);
            $this->_render($filteredData);
            break;

The problem is what this line does to the output $this->getFilter()->collectionOut($retrievedData);, if i just replace the $filteredData with the $retrievedData everything would work as expected, but i cant just replace the code base.

  • Any reason why you are applying the setPageSize and setCurPage methods whereas those methods are already being applied in the _applyCollectionModifiers method ? – Raphael at Digital Pianism Mar 8 '16 at 11:28
  • @DigitalPianism not really, i removed the setPageSize and setCurPage now but im still getting one results, the thing is when logging array_values($products) im getting all the products printed, so the problem is most likely to be on how Magento formats the array_values($products) and the entire JSON – Yehia A.Salam Mar 9 '16 at 9:18
  • What if you try to use Mage::helper('core')->jsonEncode(array_values($products)) ? Are you able to retrieve a JSON that contains every product ids ? – Raphael at Digital Pianism Mar 9 '16 at 9:27
  • 1
    @DigitalPianism actually all items are now returned, but they are returned as a single escaped string, not as JSON. So I suspected maybe the an item in $products array is breaking the magento formatter. I'm sharing the json results of the first try here link – Yehia A.Salam Mar 9 '16 at 10:10
  • 1
    @DigitalPianism please check my updated question, we're getting closer – Yehia A.Salam Mar 9 '16 at 11:04
3

Okay so after a couple of hours of debugging i came up with the right approach. Magento can not work with nested arrays in the return _retrieveCollection function, this is how the collectionOut function is written. So the best way to deal with this is to return the resulting array of the collection and set the total count in the header, something like this:

      $products = $collection->load()->toArray();

      $this->getResponse()
            ->setHeader('X-total-count', $collection->getSize() , true);

      return array_values($products); // or just $products

Using Varien_Data_Collection is not the right approach even if we were getting the right results, it causes an error as shown in the system.log

2016-03-09T23:28:51+00:00 ERR (3): Recoverable Error: Argument 1 passed to Mage_Api2_Model_Acl_Filter::out() must be of the type array, object given, called in C:\stage\appfactory-magento\app\code\core\Mage\Api2\Model\Resource.php on line 240 and defined  in C:\stage\appfactory-magento\app\code\core\Mage\Api2\Model\Acl\Filter.php on line 194

It was showing the right results because the error was recoverable and the collection has a built-in toJSON function that returns the totalRecords in the fields, it was called by Zend_json:encode when rendering the response (https://stackoverflow.com/a/8388637/226256)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.