0

I have implemented the Magento 2 Service Contracts, everything is working as expected, I can use my repository's getById and getList in Controller but as we know we can use Service Contracts for RestAPI so I have implemented API using webapi.xml but it is not working.

My RepositoryInterface Vendor/Module/Api/TestRepositoryInterface.php

<?php
namespace Vendor\Module\Api;

use \Vendor\Module\Api\Data\TestInterface;
use \Magento\Framework\Api\SearchCriteriaInterface;

interface TestRepositoryInterface
{
    /**
     * @api
     * @param \Vendor\Module\Api\Data\TestInterface $Test
     * @return \Vendor\Module\Api\Data\TestInterface
     */
    public function save(TestInterface $Test);

    /**
     * @api
     * @param \Vendor\Module\Api\Data\TestInterface $Test
     * @return \Vendor\Module\Api\Data\TestInterface
     */
    public function delete(TestInterface $Test);

    /**
     * @api
     * @param \Vendor\Module\Api\Data\TestInterface $id
     * @return void
     */
    public function deleteById($id);

    /**
     * @api
     * @param int $id
     * @return \Vendor\Module\Api\Data\TestInterface
     * @throws \Magento\Framework\Exception\NoSuchEntityException
     */
    public function getById($id);

    /**
     * @api
     * @param \Magento\Framework\Api\SearchCriteriaInterface $criteria
     * @return \Vendor\Module\Api\Data\TestSearchResultInterface
     */
    public function getList(SearchCriteriaInterface $criteria);
}

My Repository Vendor/Module/Model/TestRepository.php

<?php
namespace Vendor\Module\Model;

use \Vendor\Module\Api\Data\TestInterface;
use \Vendor\Module\Model\ResourceModel\Test as ObjectResourceModel;
use \Magento\Framework\Api\SearchCriteriaInterface;
use \Magento\Framework\Exception\CouldNotSaveException;
use \Magento\Framework\Exception\NoSuchEntityException;
use \Magento\Framework\Exception\CouldNotDeleteException;
use \Magento\Framework\Api\SearchResultsInterfaceFactory;

class TestRepository implements \Vendor\Module\Api\TestRepositoryInterface
{
    protected $objectFactory;

    protected $objectResourceModel;

    protected $collectionFactory;

    protected $searchResultsFactory;

    public function __construct(
        \Vendor\Module\Model\TestFactory $objectFactory,
        ObjectResourceModel $objectResourceModel,
        \Vendor\Module\Model\ResourceModel\Test\CollectionFactory $collectionFactory,
        \Magento\Framework\Api\SearchResultsInterfaceFactory $searchResultsFactory
    ) {
        $this->objectFactory        = $objectFactory;
        $this->objectResourceModel  = $objectResourceModel;
        $this->collectionFactory    = $collectionFactory;
        $this->searchResultsFactory = $searchResultsFactory;
    }

    public function save(TestInterface $object)
    {
        try {
            $this->objectResourceModel->save($object);
        } catch (\Exception $e) {
            throw new CouldNotSaveException(__($e->getMessage()));
        }
        return $object;
    }

    /**
     * @inheritdoc
     */
    public function getById($id)
    {
        $object = $this->objectFactory->create();
        $this->objectResourceModel->load($object, $id);
        if (!$object->getId()) {
            throw new NoSuchEntityException(__('Object with id "%1" does not exist.', $id));
        }
        return $object;
    }

    public function delete(TestInterface $object)
    {
        try {
            $this->objectResourceModel->delete($object);
        } catch (\Exception $exception) {
            throw new CouldNotDeleteException(__($exception->getMessage()));
        }
        return true;
    }

    public function deleteById($id)
    {
        return $this->delete($this->getById($id));
    }

    /**
     * @inheritdoc
     */
    public function getList(SearchCriteriaInterface $criteria)
    {
        $searchResults = $this->searchResultsFactory->create();
        $searchResults->setSearchCriteria($criteria);
        $collection = $this->collectionFactory->create();
        foreach ($criteria->getFilterGroups() as $filterGroup) {
            $fields = [];
            $conditions = [];
            foreach ($filterGroup->getFilters() as $filter) {
                $condition = $filter->getConditionType() ? $filter->getConditionType() : 'eq';
                $fields[] = $filter->getField();
                $conditions[] = [$condition => $filter->getValue()];
            }
            if ($fields) {
                $collection->addFieldToFilter($fields, $conditions);
            }
        }
        $searchResults->setTotalCount($collection->getSize());
        $sortOrders = $criteria->getSortOrders();
        if ($sortOrders) {
            /** @var SortOrder $sortOrder */
            foreach ($sortOrders as $sortOrder) {
                $collection->addOrder(
                    $sortOrder->getField(),
                    ($sortOrder->getDirection() == SortOrder::SORT_ASC) ? 'ASC' : 'DESC'
                );
            }
        }
        $collection->setCurPage($criteria->getCurrentPage());
        $collection->setPageSize($criteria->getPageSize());
        $objects = [];
        foreach ($collection as $objectModel) {
            $objects[] = $objectModel;
        }
        $searchResults->setItems($objects);
        return $searchResults;
    }

}

My DI Vendor/Module/etc/di.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <preference for="Vendor\Module\Api\TestRepositoryInterface" type="Vendor\Module\Model\TestRepository" />
    <preference for="Vendor\Module\Api\Data\TestInterface" type="Vendor\Module\Model\Test" />
    <preference for="Vendor\Module\Api\Data\TestSearchResultInterface" type="Vendor\Module\Model\TestSearchResult" />
</config>

My WebAPI Vendor/Module/etc/webapi.xml

<?xml version="1.0"?>
<routes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Webapi:etc/webapi.xsd">
    <route method="GET" url="/V1/testing/:id">
        <service class="Vendor\Module\Api\TestRepositoryInterface" method="getById"/>
        <resources>
            <resource ref="anonymous"/>
        </resources>
    </route>
    <route method="GET" url="/V1/testing">
        <service class="Vendor\Module\Api\TestRepositoryInterface" method="getList"/>
        <resources>
            <resource ref="anonymous"/>
        </resources>
    </route>
</routes>

If I access test.local/rest/V1/testing/1 I end up with the following result (nothing),

enter image description here

If I pass non-existent id it throws an exception or if I use die() in the getById method it works but somehow not returning actual data.

Whereas if I access test.local/rest/V1/testing it gives me the following result, even though I am using <resource ref="anonymouns"/>

enter image description here

When I created an integration (although it shouldn't be required) to access test.local/rest/V1/testing I encountered the following error.

enter image description here

Above error is obvious because the getList has mandatory parameter $criteria but I don't know how to pass because $criteria param has \Magento\Framework\Api\SearchCriteriaInterface type.

Remember integration is not working with test.local/rest/V1/testing/1 either, it gives me nothing as above relevant screenshot.

What I am missing? Definitely, I am doing something wrong/silly but what it is? I would like to make it work.

test.local/rest/V1/testing/1 for single record from the database table test.local/rest/V1/testing for all records of the table

I didn't implement other method under webapi.xml because I have stuck at the start of the API journey :)

Please HELP (help-flag)

Update


Thanks @raumatbel for the correction, there was a typo in webapi.xml it was anonymouns instead of anonymous, silly isn't it? :)

I am searching using REST endpoints with the following URL

test.local/rest/V1/testing?criteria[filter_groups][0][filters][0][field]=customer_name&criteria[filter_groups][0][filters][0][value]=%na%&criteria[filter_groups][0][filters][0][condition_type]=like

enter image description here

total_count is 1 but items node is empty.

No results in either URL test.local/rest/V1/testing/1 or test.local/rest/V1/testing both are empty with no errors.

  • If you use the method getById() in your code of Magento, does it work? can you review your model and resource model? – raumatbel Oct 14 '18 at 19:49
  • Yes it is working – Adnan Oct 15 '18 at 20:36
  • Can you share the code of your custom module in a .zip or .tar? If I download and install it, i can debug and find a solution fastly. – raumatbel Oct 16 '18 at 15:50
  • 1
    Sorry, I have not checked yet, but tomorrow I think that I will can check it. Sorry – raumatbel Oct 23 '18 at 1:46
2

You have a problem is the name of resource in your webapi.xml for the method getList()

You have written anonymouns instead of anonymous

If you want use this api, you must do it as indicated by Magento in your doc. https://devdocs.magento.com/guides/v2.2/rest/performing-searches.html

The problem is in your custom model and interface. The webapi retrieve the information if you have defined it in your custom model.

Ad/Shipping/Api/Data/QuoteInterface.php

<?php
namespace Ad\Shipping\Api\Data;

interface QuoteInterface
{
    /**
     * Retrieve the id
     *
     * @return int
     */
    public function getId();

    /**
     * Set id
     *
     * @param int $id
     * @return $this
     */
    public function setId($id);

    /**
     * Retrieve the product id
     *
     * @return int
     */
    public function getProductId();

    /**
     * Set product id
     *
     * @param int $productId
     * @return $this
     */
    public function setProductId($productId);

    /**
     * Retrieve the customerName
     *
     * @return string
     */
    public function getCustomerName();

    /**
     * Set customer name
     *
     * @param string $customerName
     * @return $this
     */
    public function setCustomerName($customerName);

    ...
}

Ad/Shipping/Model/Quote.php

<?php

namespace Ad\Shipping\Model;

class Quote extends \Magento\Framework\Model\AbstractModel implements
    \Ad\Shipping\Api\Data\QuoteInterface
{
    protected function _construct()
    {
        $this->_init('Ad\Shipping\Model\ResourceModel\Quote');
    }

    public function getProductId()
    {
        return $this->_getData('product_id');
    }

    public function setProductId($productId)
    {
        $this->setData('product_id', $productId);
    }

    public function getCustomerName()
    {
        return $this->_getData('customer_name');
    }

    public function setCustomerName($customerName)
    {
        $this->setData('customer_name', $customerName);
    }

    ...
}

You need add the remaining data (customer_email, customer_comments, date_added, date_updated)

You can read my post blog about this. https://www.interactiv4.com/blog-es/nueva-entidad-en-magento-2/

The post is in Spanish, I had a English version, but it is not updated and published, I have pending to do it.

  • Thank you very much, as I said, must be some silly mistake +1, I have updated my question after correction and implementation of search criteria (please see my question). Also, I am wondering why test.local/rest/V1/testing/1 is not returning any result. – Adnan Oct 15 '18 at 20:52
  • thanks for your answer, yes it worked, but I am confused, isn't the get and set is part of AbstractModel? – Adnan Oct 23 '18 at 23:48
  • 1
    Abstract model have the methods getData() and setData(), but if you don't specify the methods in your interface and in your implementation, your api doesn't know what fields must return. alanstorm.com/magento_2_crud_models_for_database_access you can read this paragraph. "While not strictly necessary, the model specific TodoItemInterface interface plays an important role when it comes time to exporting CRUD models to Magento’s new service contracts based API. The model specific interfaces for Magento CRUD models will determine which class methods are available via the Magento API." – raumatbel Oct 24 '18 at 8:14
  • Ahan, "The model specific interfaces for Magento CRUD models will determine which class methods are available via the Magento API." ... right ... Thank you very much @raumatbel :) – Adnan Oct 24 '18 at 18:27

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.