Creating custom Web APIs

Magento comes with a solid number of API methods that we can call. However, sometimes this is not enough, as our business needs dictate additional logic, and we need to be able to add our own methods to the Web API.

The best part of creating our own API's is that we do not have to be concerned about making them REST or SOAP. Magento abstracts this so that our API methods are automatically available for REST and for SOAP calls.

Adding new API's conceptually evolves around two things: defining business logic through various classes, and exposing it via the webapi.xml file. However, as we will soon see, there is a lot of boilerplate to it.

Let's create a miniature module called Foggyline_Slider, on which we will demonstrate create (POST), update (PUT), delete (DELETE), and list (GET) method calls.

Create a module registration file, app/code/Foggyline/Slider/registration.php, with content (partial) as follows:

MagentoFrameworkComponentComponentRegistrar::register(
    MagentoFrameworkComponentComponentRegistrar::MODULE,
    'Foggyline_Slider',
    __DIR__
);

Create a module configuration file, app/code/Foggyline/Slider/etc/module.xml, with content as follows:

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module /etc/module.xsd">
    <module name="Foggyline_Slider" setup_version="1.0.0"/>
</config>

Create an install script where our future models will persist module data. We do so by creating the app/code/Foggyline/Slider/Setup/InstallSchema.php file with content (partial) as follows:

namespace FoggylineSliderSetup;

use MagentoFrameworkSetupInstallSchemaInterface;
use MagentoFrameworkSetupModuleContextInterface;
use MagentoFrameworkSetupSchemaSetupInterface;

class InstallSchema implements InstallSchemaInterface
{
    public function install(SchemaSetupInterface $setup, ModuleContextInterface $context)
    {
        $installer = $setup;
        $installer->startSetup();

        /**
         * Create table 'foggyline_slider_slide'
         */
        $table = $installer->getConnection()
            ->newTable($installer- >getTable('foggyline_slider_slide'))
            ->addColumn(
                'slide_id',
                MagentoFrameworkDBDdlTable::TYPE_INTEGER,
                null,
                ['identity' => true, 'unsigned' => true, 'nullable' => false, 'primary' => true],
                'Slide Id'
            )
            ->addColumn(
                'title',
                MagentoFrameworkDBDdlTable::TYPE_TEXT,
                200,
                [],
                'Title'
            )
            ->setComment('Foggyline Slider Slide');
        $installer->getConnection()->createTable($table);
        ...
        $installer->endSetup();

    }
}

Now we specify the ACL for our resources. Our resources are going to be CRUD actions we do on our module entities. We will structure our module in a way that slide and image are separate entities, where one slide can have multiple image entities linked to it. Thus, we would like to be able to control access to save and delete actions separately for each entity. We do so by defining the app/code/Foggyline/Slider/etc/acl.xml file as follows:

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Acl/etc/ acl.xsd">
    <acl>
        <resources>
            <resource id="Magento_Backend::admin">
                <resource id="Magento_Backend::content">
                    <resource id= "Magento_Backend::content_elements">
                        <resource id="Foggyline_Slider::slider" title="Slider" sortOrder="10">
                            <resource id="Foggyline_Slider::slide" title="Slider Slide" sortOrder="10">
                                <resource id= "Foggyline_Slider::slide_save" title="Save Slide" sortOrder="10" />
                                <resource id="Foggyline_Slider:: slide_delete" title="Delete Slide" sortOrder="20" />
                            </resource>
                            <resource id="Foggyline_Slider::image" title="Slider Image" sortOrder="10">
                                <resource id= "Foggyline_Slider::image_save" title="Save Image" sortOrder="10" />
                                <resource id= "Foggyline_Slider::image_delete" title="Delete Image" sortOrder="20" />
                            </resource>
                        </resource>
                    </resource>
                </resource>
            </resource>
        </resources>
    </acl>
</config>

Now that the ACL has been set, we define our Web API resources within the app/code/Foggyline/Slider/etc/webapi.xml file (partial) as follows:

<routes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation= "urn:magento:module:Magento_Webapi:etc/webapi.xsd">
    <route url="/V1/foggylineSliderSlide/:slideId" method="GET">
        <service class="FoggylineSliderApi SlideRepositoryInterface" method="getById" />
        <resources>
            <resource ref="Foggyline_Slider::slide" />
        </resources>
    </route>
    <route url="/V1/foggylineSliderSlide/search" method="GET">
        <service class="FoggylineSliderApi SlideRepositoryInterface" method="getList" />
        <resources>
            <resource ref="anonymous" />
        </resources>
    </route>
    <route url="/V1/foggylineSliderSlide" method="POST">
        <service class="FoggylineSliderApi SlideRepositoryInterface" method="save" />
        <resources>
            <resource ref="Foggyline_Slider::slide_save" />
        </resources>
    </route>
    <route url="/V1/foggylineSliderSlide/:id" method="PUT">
        <service class="FoggylineSliderApi SlideRepositoryInterface" method="save" />
        <resources>
            <resource ref="Foggyline_Slider::slide_save" />
        </resources>
    </route>
    <route url="/V1/foggylineSliderSlide/:slideId" method="DELETE">
        <service class="FoggylineSliderApi SlideRepositoryInterface" method="deleteById" />
        <resources>
            <resource ref="Foggyline_Slider::slide_delete" />
        </resources>
    </route>
    <route url="/V1/foggylineSliderImage/:imageId" method="GET">
        <service class="FoggylineSliderApi ImageRepositoryInterface" method="getById" />
        <resources>
            <resource ref="Foggyline_Slider::image" />
        </resources>
    </route>
    <route url="/V1/foggylineSliderImage/search" method="GET">
        <service class="FoggylineSliderApi ImageRepositoryInterface" method="getList" />
        <resources>
            <resource ref="Foggyline_Slider::image" />
        </resources>
    </route>
    <route url="/V1/foggylineSliderImage" method="POST">
        <service class="FoggylineSliderApi ImageRepositoryInterface" method="save" />
        <resources>
            <resource ref="Foggyline_Slider::image_save" />
        </resources>
    </route>
    <route url="/V1/foggylineSliderImage/:id" method="PUT">
        <service class="FoggylineSliderApi ImageRepositoryInterface" method="save" />
        <resources>
            <resource ref="Foggyline_Slider::image_save" />
        </resources>
    </route>
    <route url="/V1/foggylineSliderImage/:imageId" method="DELETE">
        <service class="FoggylineSliderApi ImageRepositoryInterface" method="deleteById" />
        <resources>
            <resource ref="Foggyline_Slider::image_delete" />
        </resources>
    </route>
</routes>

Notice how each of those service class attributes point to the interface, not the class. This is the way we should build our exposable services, always having an interface definition behind them. As we will soon see, using di.xml, this does not mean Magento will try to create objects from these interfaces directly.

We now create the app/code/Foggyline/Slider/etc/di.xml file with content (partial) as follows:

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation= "urn:magento:framework:ObjectManager/etc/config.xsd">

    <preference for="FoggylineSliderApiDataSlideInterface" type="FoggylineSliderModelSlide"/>

    <preference for="FoggylineSliderApi SlideRepositoryInterface" type= "FoggylineSliderModelSlideRepository"/>
    ...
</config>

What is happening here is that we are telling Magento something like, "hey, whenever you need to pass around an instance that conforms to the FoggylineSliderApiDataSlideInterface interface, preferably use the FoggylineSliderModelSlide class for it."

At this point, we still do not have any of those interfaces or model classes actually created. When creating APIs, we should first start by defining interfaces, and then our models should extend from those interfaces.

Interface FoggylineSliderApiDataSlideInterface is defined within the app/code/Foggyline/Slider/Api/Data/SlideInterface.php file (partial) as follows:

namespace FoggylineSliderApiData;

/**
* @api
*/
interface SlideInterface
{
    const PROPERTY_ID = 'slide_id';
    const PROPERTY_SLIDE_ID = 'slide_id';
    const PROPERTY_TITLE = 'title';

    /**
    * Get Slide entity 'slide_id' property value
    * @return int|null
    */
    public function getId();

    /**
    * Set Slide entity 'slide_id' property value
    * @param int $id
    * @return $this
    */
    public function setId($id);

    /**
    * Get Slide entity 'slide_id' property value
    * @return int|null
    */
    public function getSlideId();

    /**
    * Set Slide entity 'slide_id' property value
    * @param int $slideId
    * @return $this
    */
    public function setSlideId($slideId);

    /**
    * Get Slide entity 'title' property value
    * @return string|null
    */
    public function getTitle();

    /**
    * Set Slide entity 'title' property value
    * @param string $title
    * @return $this
    */
    public function setTitle($title);
}

We are going for ultimate simplification here. Our Slide entity only really has ID and title values. The id and slide_id point to the same field in the database and the implementation of their getters and setters should yield the same results.

Although API/Data/*.php interfaces become blueprint requirements for our data models, we also have Api/*RepositoryInterface.php files. The idea here is to extract create, update, delete, search, and similar data-handling logic away from the data model class into its own class. This way, our model classes become more pure data and business logic classes while the rest of persistence and search-related logic moves into these repository classes.

Our Slide Repository Interface is defined within the app/code/Foggyline/Slider/Api/SlideRepositoryInterface.php file as follows:

namespace FoggylineSliderApi;

/**
* @api
*/
interface SlideRepositoryInterface
{
    /**
    * Retrieve slide entity.
    * @param int $slideId
    * @return FoggylineSliderApiDataSlideInterface
    * @throws MagentoFrameworkExceptionNoSuchEntityException If slide with the specified ID does not exist.
    * @throws MagentoFrameworkExceptionLocalizedException
    */
    public function getById($slideId);

    /**
    * Save slide.
    * @param FoggylineSliderApiDataSlideInterface $slide
    * @return FoggylineSliderApiDataSlideInterface
    * @throws MagentoFrameworkExceptionLocalizedException
    */
    public function save(FoggylineSliderApiDataSlideInterface $slide);

    /**
    * Retrieve slides matching the specified criteria.
    * @param MagentoFrameworkApiSearchCriteriaInterface $searchCriteria
    * @return MagentoFrameworkApiSearchResultsInterface
    * @throws MagentoFrameworkExceptionLocalizedException
    */
    public function getList(MagentoFrameworkApiSearchCriteriaInterface $searchCriteria);

    /**
    * Delete slide by ID.
    * @param int $slideId
    * @return bool true on success
    * @throws MagentoFrameworkExceptionNoSuchEntityException
    * @throws MagentoFrameworkExceptionLocalizedException
    */
    public function deleteById($slideId);
}

With interfaces in place, we can move on to model class. In order to persist and fetch data in a database, our Slide entity really needs three files under the Model directory. These are called data model, resource class, and collection class.

The data model class is defined under the app/code/Foggyline/Slider/Model/Slide.php file (partial) as follows:

namespace FoggylineSliderModel;

class Slide extends MagentoFrameworkModelAbstractModel
    implements FoggylineSliderApiDataSlideInterface
{
    /**
    * Initialize Foggyline Slide Model
    *
    * @return void
    */
    protected function _construct()
    {
        /* _init($resourceModel) */
        $this->_init ('FoggylineSliderModelResourceModelSlide');
    }

    /**
    * Get Slide entity 'slide_id' property value
    *
    * @api
    * @return int|null
    */
    public function getId()
    {
        return $this->getData(self::PROPERTY_ID);
    }

    /**
    * Set Slide entity 'slide_id' property value
    *
    * @api
    * @param int $id
    * @return $this
    */
    public function setId($id)
    {
        $this->setData(self::PROPERTY_ID, $id);
        return $this;
    }

    /**
    * Get Slide entity 'slide_id' property value
    *
    * @api
    * @return int|null
    */
    public function getSlideId()
    {
        return $this->getData(self::PROPERTY_SLIDE_ID);
    }

    /**
    * Set Slide entity 'slide_id' property value
    *
    * @api
    * @param int $slideId
    * @return $this
    */
    public function setSlideId($slideId)
    {
        $this->setData(self::PROPERTY_SLIDE_ID, $slideId);
        return $this;
    }

    /**
    * Get Slide entity 'title' property value
    *
    * @api
    * @return string|null
    */
    public function getTitle()
    {
        return $this->getData(self::PROPERTY_TITLE);
    }

    /**
    * Set Slide entity 'title' property value
    *
    * @api
    * @param string $title
    * @return $this
    */
    public function setTitle($title)
    {
        $this->setData(self::PROPERTY_TITLE, $title);
    }
}

Following the model data class is the model resource class, defined in the app/code/Foggyline/Slider/Model/ResourceModel/Slide.php file (partial) as follows:

namespace FoggylineSliderModelResourceModel;

/**
* Foggyline Slide resource
*/
class Slide extends MagentoFrameworkModelResourceModelDbAbstractDb
{
    /**
    * Define main table
    *
    * @return void
    */
    protected function _construct()
    {
        /* _init($mainTable, $idFieldName) */
        $this->_init('foggyline_slider_slide', 'slide_id');
    }
}

Finally, the third bit is the model collection class, defined in the app/code/Foggyline/Slider/Model/ResourceModel/Slide/Collection.php file as follows:

namespace FoggylineSliderModelResourceModelSlide;

/**
* Foggyline slides collection
*/
class Collection extends MagentoFrameworkModelResourceModelDbCollection AbstractCollection
{
    /**
    * Define resource model and model
    *
    * @return void
    */
    protected function _construct()
    {
        /* _init($model, $resourceModel) */
        $this->_init('FoggylineSliderModelSlide', 'FoggylineSliderModelResourceModelSlide');
    }
}

If we were to manually instantiate the model data class now, we would be able to persist the data in the database. To complete the di.xml requirements, we still lack one more final ingredient – the Model/SlideRepository class file.

Let us go and create the app/code/Foggyline/Slider/Model/SlideRepository.php file with content (partial) as follows:

namespace FoggylineSliderModel;

use MagentoFrameworkApiDataObjectHelper;
use MagentoFrameworkApiSearchCriteriaInterface;
use MagentoFrameworkExceptionCouldNotDeleteException;
use MagentoFrameworkExceptionCouldNotSaveException;
use MagentoFrameworkExceptionNoSuchEntityException;
use MagentoFrameworkReflectionDataObjectProcessor;

class SlideRepository implements FoggylineSliderApiSlideRepositoryInterface
{
    /**
    * @var FoggylineSliderModelResourceModelSlide
    */
    protected $resource;

    /**
    * @var FoggylineSliderModelSlideFactory
    */
    protected $slideFactory;

    /**
    * @var FoggylineSliderModelResourceModelSlide CollectionFactory
    */
    protected $slideCollectionFactory;

    /**
    * @var MagentoFrameworkApiSearchResultsInterface
    */
    protected $searchResultsFactory;

    /**
    * @var MagentoFrameworkApiDataObjectHelper
    */
    protected $dataObjectHelper;

    /**
    * @var MagentoFrameworkReflectionDataObjectProcessor
    */
    protected $dataObjectProcessor;

    /**
    * @var FoggylineSliderApiDataSlideInterfaceFactory
    */
    protected $dataSlideFactory;

    /**
    * @param ResourceModelSlide $resource
    * @param SlideFactory $slideFactory
    * @param ResourceModelSlideCollectionFactory $slideCollectionFactory
    * @param MagentoFrameworkApiSearchResultsInterface $searchResultsFactory
    * @param DataObjectHelper $dataObjectHelper
    * @param DataObjectProcessor $dataObjectProcessor
    * @param FoggylineSliderApiDataSlideInterfaceFactory $dataSlideFactory
    */
    public function __construct(
        FoggylineSliderModelResourceModelSlide $resource,
        FoggylineSliderModelSlideFactory $slideFactory,
        FoggylineSliderModelResourceModelSlide CollectionFactory $slideCollectionFactory,
        MagentoFrameworkApiSearchResultsInterface $searchResultsFactory,
        MagentoFrameworkApiDataObjectHelper $dataObjectHelper,
        MagentoFrameworkReflectionDataObjectProcessor $dataObjectProcessor,
        FoggylineSliderApiDataSlideInterfaceFactory $dataSlideFactory

    )
    {
        $this->resource = $resource;
        $this->slideFactory = $slideFactory;
        $this->slideCollectionFactory = $slideCollectionFactory;
        $this->searchResultsFactory = $searchResultsFactory;
        $this->dataObjectHelper = $dataObjectHelper;
        $this->dataObjectProcessor = $dataObjectProcessor;
        $this->dataSlideFactory = $dataSlideFactory;
    }
    ...
}

It might appear that there is a lot going on here, but really we are just passing on some class and interface names to the constructor in order to instantiate the objects we will use across individual service methods defined in the webapi.xml file.

The first service method on our list is getById, defined within SlideRepository.php as follows:

/**
* Retrieve slide entity.
*
* @api
* @param int $slideId
* @return FoggylineSliderApiDataSlideInterface
* @throws MagentoFrameworkExceptionNoSuchEntityException If slide with the specified ID does not exist.
* @throws MagentoFrameworkExceptionLocalizedException
*/
public function getById($slideId)
{
    $slide = $this->slideFactory->create();
    $this->resource->load($slide, $slideId);
    if (!$slide->getId()) {
        throw new NoSuchEntityException(__('Slide with id %1 does not exist.', $slideId));
    }
    return $slide;
}

Then we have the save method, defined within SlideRepository.php as follows:

/**
* Save slide.
*
* @param FoggylineSliderApiDataSlideInterface $slide
* @return FoggylineSliderApiDataSlideInterface
* @throws MagentoFrameworkExceptionLocalizedException
*/
public function save(FoggylineSliderApiDataSlideInterface $slide)
{
    try {
        $this->resource->save($slide);
    } catch (Exception $exception) {
        throw new CouldNotSaveException(__($exception- >getMessage()));
    }
    return $slide;
}

The save method addresses both POST and PUT requests defined in webapi.xml, thus effectively handling the creation of new slides or an update of existing ones.

Going further, we have the getList method, defined within SlideRepository.php as follows:

/**
* Retrieve slides matching the specified criteria.
*
* @param MagentoFrameworkApiSearchCriteriaInterface $searchCriteria
* @return MagentoFrameworkApiSearchResultsInterface
* @throws MagentoFrameworkExceptionLocalizedException
*/
public function getList(MagentoFrameworkApiSearchCriteriaInterface $searchCriteria)
{
    $this->searchResultsFactory->setSearchCriteria ($searchCriteria);

    $collection = $this->slideCollectionFactory->create();

    foreach ($searchCriteria->getFilterGroups() as $filterGroup) {
        foreach ($filterGroup->getFilters() as $filter) {
            $condition = $filter->getConditionType() ?: 'eq';
            $collection->addFieldToFilter($filter->getField(), [$condition => $filter->getValue()]);
        }
    }
    $this->searchResultsFactory->setTotalCount($collection-> getSize());
    $sortOrders = $searchCriteria->getSortOrders();
    if ($sortOrders) {
        foreach ($sortOrders as $sortOrder) {
            $collection->addOrder(
                $sortOrder->getField(),
                (strtoupper($sortOrder->getDirection()) === 'ASC') ? 'ASC' : 'DESC'
            );
        }
    }
    $collection->setCurPage($searchCriteria->getCurrentPage());
    $collection->setPageSize($searchCriteria->getPageSize());
    $slides = [];
    /** @var FoggylineSliderModelSlide $slideModel */
    foreach ($collection as $slideModel) {
        $slideData = $this->dataSlideFactory->create();
        $this->dataObjectHelper->populateWithArray(
            $slideData,
            $slideModel->getData(),
            'FoggylineSliderApiDataSlideInterface'
        );
        $slides[] = $this->dataObjectProcessor-> buildOutputDataArray(
            $slideData,
            'FoggylineSliderApiDataSlideInterface'
        );
    }
    $this->searchResultsFactory->setItems($slides);
    return $this->searchResultsFactory;
}

Finally, we have the deleteById method, defined within SlideRepository.php as follows:

/**
* Delete Slide
*
* @param FoggylineSliderApiDataSlideInterface $slide
* @return bool
* @throws CouldNotDeleteException
*/
public function delete(FoggylineSliderApiDataSlideInterface $slide)
{
    try {
        $this->resource->delete($slide);
    } catch (Exception $exception) {
        throw new CouldNotDeleteException(__($exception-> getMessage()));
    }
    return true;
}

/**
* Delete slide by ID.
*
* @param int $slideId
* @return bool true on success
* @throws MagentoFrameworkExceptionNoSuchEntityException
* @throws MagentoFrameworkExceptionLocalizedException
*/
public function deleteById($slideId)
{
    return $this->delete($this->getById($slideId));
}

Keep in mind that we only covered the Slide entity in the preceding partial code examples, which is enough to progress further with API call examples.

API call examples

Since all of our defined API's are resource protected, we first need to authenticate as the admin user, assuming the admin user has access to all our custom resources that encompass the ones we defined. For simplicity sake, we will use the token-based authentication method, examples of which are given previously in this chapter. Once authenticated, we should have a 32 random characters long token like pk8h93nq9cevaw55bohkjbp0o7kpl4d3, for example.

Once the token key has been obtained, we will test the following API calls using console cURL, PHP cURL, PHP SoapClient, and console SOAP style cURL examples:

  • GET /V1/foggylineSliderSlide/:slideId, calls the getById service method, requires the Foggyline_Slider::slide resource
  • GET /V1/foggylineSliderSlide/search, calls the getList service method, requires the Foggyline_Slider::slide resource
  • POST /V1/foggylineSliderSlide, calls the save service method, requires the Foggyline_Slider::slide_save resource
  • PUT /V1/foggylineSliderSlide/:id, calls the save service method, requires the Foggyline_Slider::slide_save resource
  • DELETE /V1/foggylineSliderSlide/:slideId, calls the deleteById service method, requires the Foggyline_Slider::slide_delete resource

The getById service method call examples

The console cURL style for executing GET /V1/foggylineSliderSlide/:slideId is done as follows:

curl -X GET -H 'Content-type: application/json' 
-H 'Authorization: Bearer pk8h93nq9cevaw55bohkjbp0o7kpl4d3' 
http://magento2.ce/rest/V1/foggylineSliderSlide/1

The PHP cURL style for executing GET /V1/foggylineSliderSlide/:slideId is done as follows:

$ch = curl_init('http://magento2.ce/rest/V1/foggylineSliderSlide/1');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
    'Content-Type: application/json',
    'Authorization: Bearer pk8h93nq9cevaw55bohkjbp0o7kpl4d3'
));

$result = curl_exec($ch);

The response for console and PHP cURL style should be a JSON string similar to the following one:

{"slide_id":1,"title":"Awesome stuff #1"}

The PHP SoapClient style for executing GET /V1/foggylineSliderSlide/:slideId is done as follows:

$request = new SoapClient(
    'http://magento2.ce/index.php/soap/? wsdl&services=foggylineSliderSlideRepositoryV1',
    array(
        'soap_version' => SOAP_1_2,
        'stream_context' => stream_context_create(array(
                'http' => array(
                    'header' => 'Authorization: Bearer pk8h93nq9cevaw55bohkjbp0o7kpl4d3')
            )
        )
    )
);
$response = $request-> foggylineSliderSlideRepositoryV1GetById(array('slideId'=>1));

The response for PHP SoapClient style should be the stdClass PHP object as follows:

object(stdClass)#2 (1) {
    ["result"]=>
    object(stdClass)#3 (2) {
    ["slideId"]=>
    int(1)
    ["title"]=>
    string(16) "Awesome stuff #1"
    }
}

The console SOAP style cURL for executing GET /V1/foggylineSliderSlide/:slideId is done as follows:

curl -X POST 
-H 'Content-Type: application/soap+xml; charset=utf-8; action="foggylineSliderSlideRepositoryV1GetById"' 
-H 'Authorization: Bearer pk8h93nq9cevaw55bohkjbp0o7kpl4d3' 
-d @request.xml 
http://magento2.ce/index.php/soap/default?services=foggyline SliderSlideRepositoryV1

Where request.xml has content as follows:

<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:ns1="http://magento2.ce/index.php/soap/default? services=foggylineSliderSlideRepositoryV1">
    <env:Body>
        <ns1:foggylineSliderSlideRepositoryV1GetByIdRequest>
            <slideId>1</slideId>
        </ns1:foggylineSliderSlideRepositoryV1GetByIdRequest>
    </env:Body>
</env:Envelope>

Notice how we did not really do GET, rather a POST type of request. Also, the URL to which we are pointing our POST is not really the same as with previous requests. This is because Magento SOAP requests are always POST (or PUT) type, as the data is submitted in XML format. XML format in return specifies the service, and the request header action specifies the method to be called on the service.

The response for console SOAP style cURL should be an XML as follows:

<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:ns1="http://magento2.ce/index.php/soap/default? services=foggylineSliderSlideRepositoryV1">
    <env:Body>
        <ns1:foggylineSliderSlideRepositoryV1GetByIdResponse>
            <result>
                <slideId>1</slideId>
                <title>Awesome stuff #1</title>
            </result>
        </ns1:foggylineSliderSlideRepositoryV1GetByIdResponse>
    </env:Body>
</env:Envelope>

The getList service method call examples

The console cURL style for executing GET /V1/foggylineSliderSlide/search is done as follows:

curl -X GET -H 'Content-type: application/json' 
-H 'Authorization: Bearer pk8h93nq9cevaw55bohkjbp0o7kpl4d3' 
"http://magento2.ce/rest/V1/foggylineSliderSlide/search?search_criteria%5Bfilter_groups%5D%5B0%5D%5Bfilters%5D%5B0%5D%5Bfield%5D=title&search_criteria%5Bfilter_groups%5D%5B0%5D%5Bfilters%5D%5B0%5D%5Bvalue%5D=%25some%25&search_criteria%5Bfilter_groups%5D%5B0%5D%5Bfilters%5D%5B0%5D%5Bcondition_type%5D=like&search_criteria%5Bcurrent_page%5D=1&search_criteria%5Bpage_size%5D=10&search_criteria%5Bsort_orders%5D%5B0%5D%5Bfield%5D=slide_id&search_criteria%5Bsort_orders%5D%5B0%5D%5Bdirection%5D=ASC"

The PHP cURL style for executing GET /V1/foggylineSliderSlide/search is done as follows:

$searchCriteriaJSON = '{
  "search_criteria": {
    "filter_groups": [
      {
        "filters": [
          {
            "field": "title",
            "value": "%some%",
            "condition_type": "like"
          }
        ]
      }
    ],
    "current_page": 1,
    "page_size": 10,
    "sort_orders": [
      {
        "field": "slide_id",
        "direction": "ASC"
      }
    ]
  }
}';

$searchCriteriaQueryString = http_build_query(json_decode($searchCriteriaJSON));

$ch = curl_init('http://magento2.ce/rest/V1/foggylineSliderSlide/ search?' . $searchCriteriaQueryString);
  curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch, CURLOPT_HTTPHEADER, array(
      'Content-Type: application/json',
      'Authorization: Bearer pk8h93nq9cevaw55bohkjbp0o7kpl4d3'
  ));

$result = curl_exec($ch);

The response for console and PHP cURL style should be a JSON string similar to the following one:

{"items":[{"slide_id":2,"title":"Just some other slider"},{"slide_id":1,"title":"Awesome stuff #1"}], "search_criteria":{"filter_groups":[{"filters": [{"field":"title","value":"%some%","condition_type":"like"}]}], "sort_orders":[{"field":"slide_id","direction":"- 1"}],"page_size":10,"current_page":1},"total_count":2}

The PHP SoapClient style for executing GET /V1/foggylineSliderSlide/search is done as follows:

$searchCriteria = [
    'searchCriteria' =>
        [
            'filterGroups' =>
                [
                    [
                        'filters' =>
                            [
                                [
                                    'field' => 'title',
                                    'value' => '%some%',
                                    'condition_type' => 'like',
                                ],
                            ],
                    ],
                ],
            'currentPage' => 1,
            'pageSize' => 10,
            'sort_orders' =>
                [
                    [
                        'field' => 'slide_id',
                        'direction' =>'ASC',
                    ],
                ],
        ],
];

$request = new SoapClient(
    'http://magento2.ce/index.php/soap/?wsdl&services= foggylineSliderSlideRepositoryV1',
    array(
        'soap_version' => SOAP_1_2,
        'trace'=>1,
        'stream_context' => stream_context_create(array(
                'http' => array(
                    'header' => 'Authorization: Bearer pk8h93nq9cevaw55bohkjbp0o7kpl4d3')
            )
        )
    )
);

$response = $request-> foggylineSliderSlideRepositoryV1GetList($searchCriteria);

The response for PHP SoapClient style should be the stdClass PHP object as follows:

object(stdClass)#2 (1) {
  ["result"]=>
  object(stdClass)#3 (3) {
    ["items"]=>
    object(stdClass)#4 (0) {
    }
    ["searchCriteria"]=>
    object(stdClass)#5 (3) {
      ["filterGroups"]=>
      object(stdClass)#6 (1) {
        ["item"]=>
        object(stdClass)#7 (1) {
          ["filters"]=>
          object(stdClass)#8 (1) {
            ["item"]=>
            object(stdClass)#9 (2) {
              ["field"]=>
              string(5) "title"
              ["value"]=>
              string(6) "%some%"
            }
          }
        }
      }
      ["pageSize"]=>
      int(10)
      ["currentPage"]=>
      int(1)
    }
    ["totalCount"]=>
    int(0)
  }
}

The console SOAP style cURL for executing GET /V1/foggylineSliderSlide/search is done as follows:

curl -X POST 
-H 'Content-Type: application/soap+xml; charset=utf-8; action="foggylineSliderSlideRepositoryV1GetList"' 
-H 'Authorization: Bearer pk8h93nq9cevaw55bohkjbp0o7kpl4d3' 
-d @request.xml 
http://magento2.ce/index.php/soap/default?services=foggyline SliderSlideRepositoryV1

Where request.xml has content as follows:

<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:ns1="http://magento2.ce/index.php/soap/default? services=foggylineSliderSlideRepositoryV1">
    <env:Body>
        <ns1:foggylineSliderSlideRepositoryV1GetListRequest>
            <searchCriteria>
                <filterGroups>
                    <item>
                        <filters>
                            <item>
                                <field>title</field>
                                <value>%some%</value>
                            </item>
                        </filters>
                    </item>
                </filterGroups>
                <pageSize>10</pageSize>
                <currentPage>1</currentPage>
            </searchCriteria>
        </ns1:foggylineSliderSlideRepositoryV1GetListRequest>
    </env:Body>
</env:Envelope>

Notice we did not really do GET, rather POST. Also, the URL to which we are pointing our POST is not really the same as with previous requests. This is because Magento SOAP requests are always POST type, as the data is submitted in XML format. XML format in return specifies the service, and the request header action specifies the method to be called on the service.

The response for console SOAP style cURL should be an XML as follows:

<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:ns1="http://magento2.ce/index.php/soap/default? services=foggylineSliderSlideRepositoryV1">
    <env:Body>
        <ns1:foggylineSliderSlideRepositoryV1GetListResponse>
            <result>
                <items/>
                <searchCriteria>
                    <filterGroups>
                        <item>
                            <filters>
                                <item>
                                    <field>title</field>
                                    <value>%some%</value>
                                </item>
                            </filters>
                        </item>
                    </filterGroups>
                    <pageSize>10</pageSize>
                    <currentPage>1</currentPage>
                </searchCriteria>
                <totalCount>0</totalCount>
            </result>
        </ns1:foggylineSliderSlideRepositoryV1GetListResponse>
    </env:Body>
</env:Envelope>

The save (as new) service method call examples

The console cURL style for executing POST /V1/foggylineSliderSlide is done as follows:

curl -X POST -H 'Content-type: application/json' 
-H 'Authorization: Bearer pk8h93nq9cevaw55bohkjbp0o7kpl4d3' 
-d '{"slide": {"title": "API test"}}' 
http://magento2.ce/rest/V1/foggylineSliderSlide/

The PHP cURL style for executing POST /V1/foggylineSliderSlide is done as follows:

$slide = json_encode(['slide'=>['title'=> 'API test']]);

$ch = curl_init('http://magento2.ce/rest/V1/foggylineSliderSlide');
  curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
  curl_setopt($ch, CURLOPT_POSTFIELDS, $slide);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch, CURLOPT_HTTPHEADER, array(
      'Content-Type: application/json',
      'Content-Length: ' . strlen($slide),
      'Authorization: Bearer pk8h93nq9cevaw55bohkjbp0o7kpl4d3'
  ));

$result = curl_exec($ch);

The response for console and PHP cURL style should be a JSON string similar to the following one:

{"slide_id":4,"title":"API test"}

The PHP SoapClient style for executing POST /V1/foggylineSliderSlide is done as follows:

$slide = ['slide'=>['title'=> 'API test']];

$request = new SoapClient(
    'http://magento2.ce/index.php/soap/?wsdl&services= foggylineSliderSlideRepositoryV1',
    array(
        'soap_version' => SOAP_1_2,
        'trace'=>1,
        'stream_context' => stream_context_create(array(
                'http' => array(
                    'header' => 'Authorization: Bearer pk8h93nq9cevaw55bohkjbp0o7kpl4d3')
            )
        )
    )
);

$response = $request-> foggylineSliderSlideRepositoryV1Save($slide);

The response for PHP SoapClient style should be the stdClass PHP object as follows:

object(stdClass)#2 (1) {
  ["result"]=>
  object(stdClass)#3 (2) {
    ["slideId"]=>
    int(6)
    ["title"]=>
    string(8) "API test"
  }
}

The console SOAP style cURL for executing POST /V1/foggylineSliderSlide is done as follows:

curl -X POST 
-H 'Content-Type: application/soap+xml; charset=utf-8; action="foggylineSliderSlideRepositoryV1Save"' 
-H 'Authorization: Bearer pk8h93nq9cevaw55bohkjbp0o7kpl4d3' 
-d @request.xml 
http://magento2.ce/index.php/soap/default?services=foggyline SliderSlideRepositoryV1

Where request.xml has content as follows:

<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:ns1="http://magento2.ce/index.php/soap/default? services=foggylineSliderSlideRepositoryV1">
    <env:Body>
        <ns1:foggylineSliderSlideRepositoryV1SaveRequest>
            <slide>
                <title>API test</title>
            </slide>
        </ns1:foggylineSliderSlideRepositoryV1SaveRequest>
    </env:Body>
</env:Envelope>

The response for console SOAP style cURL should be an XML as follows:

<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:ns1="http://magento2.ce/index.php/soap/default? services=foggylineSliderSlideRepositoryV1">
    <env:Body>
        <ns1:foggylineSliderSlideRepositoryV1SaveResponse>
            <result>
                <slideId>8</slideId>
                <title>API test</title>
            </result>
        </ns1:foggylineSliderSlideRepositoryV1SaveResponse>
    </env:Body>
</env:Envelope>

The save (as update) service method call examples

The console cURL style for executing PUT /V1/foggylineSliderSlide/:id is done as follows:

curl -X PUT -H 'Content-type: application/json' 
-H 'Authorization: Bearer pk8h93nq9cevaw55bohkjbp0o7kpl4d3' 
-d '{"slide": {"slide_id": 2, "title": "API update test"}}' 
http://magento2.ce/rest/V1/foggylineSliderSlide/2

The PHP cURL style for executing PUT /V1/foggylineSliderSlide/:id is done as follows:

$slideId = 2;
$slide = json_encode(['slide'=>['slide_id'=> $slideId, 'title'=> 'API update test']]);

$ch = curl_init('http://magento2.ce/rest/V1/foggylineSliderSlide/' . $slideId);
  curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
  curl_setopt($ch, CURLOPT_POSTFIELDS, $slide);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch, CURLOPT_HTTPHEADER, array(
      'Content-Type: application/json',
      'Content-Length: ' . strlen($slide),
      'Authorization: Bearer pk8h93nq9cevaw55bohkjbp0o7kpl4d3'
  ));

$result = curl_exec($ch);

The response for console and PHP cURL style should be a JSON string similar to the following one:

{"id":2,"slide_id":2,"title":"API update test"}

The PHP SoapClient style for executing PUT /V1/foggylineSliderSlide/:id is done as follows:

$slideId = 2;
$slide = ['slide'=>['slideId'=> $slideId, 'title'=> 'API update test']];

$request = new SoapClient(
    'http://magento2.ce/index.php/soap/?wsdl&services= foggylineSliderSlideRepositoryV1',
    array(
        'soap_version' => SOAP_1_2,
        'trace'=>1,
        'stream_context' => stream_context_create(array(
                'http' => array(
                    'header' => 'Authorization: Bearer pk8h93nq9cevaw55bohkjbp0o7kpl4d3')
            )
        )
    )
);

$response = $request-> foggylineSliderSlideRepositoryV1Save($slide);

The response for PHP SoapClient style should be the stdClass PHP object as follows:

object(stdClass)#2 (1) {
  ["result"]=>
  object(stdClass)#3 (2) {
    ["slideId"]=>
    int(2)
    ["title"]=>
    string(15) "API update test"
  }
}

The console SOAP style cURL for executing PUT /V1/foggylineSliderSlide/:id is done as follows:

curl -X PUT 
-H 'Content-Type: application/soap+xml; charset=utf-8; action="foggylineSliderSlideRepositoryV1Save"' 
-H 'Authorization: Bearer pk8h93nq9cevaw55bohkjbp0o7kpl4d3' 
-d @request.xml 
http://magento2.ce/index.php/soap/default?services= foggylineSliderSlideRepositoryV1

Where request.xml has content as follows:

<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:ns1="http://magento2.ce/index.php/soap/default? services=foggylineSliderSlideRepositoryV1">
    <env:Body>
        <ns1:foggylineSliderSlideRepositoryV1SaveRequest>
            <slide>
                <slideId>2</slideId>
                <title>API update test</title>
            </slide>
        </ns1:foggylineSliderSlideRepositoryV1SaveRequest>
    </env:Body>
</env:Envelope>

The response for console SOAP style cURL should be an XML as follows:

<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:ns1="http://magento2.ce/index.php/soap/default? services=foggylineSliderSlideRepositoryV1">
    <env:Body>
        <ns1:foggylineSliderSlideRepositoryV1SaveResponse>
            <result>
                <slideId>2</slideId>
                <title>API update test</title>
            </result>
        </ns1:foggylineSliderSlideRepositoryV1SaveResponse>
    </env:Body>
</env:Envelope>

The deleteById service method call examples

The console cURL style for executing DELETE /V1/foggylineSliderSlide/:slideId is done as follows:

curl -X DELETE -H 'Content-type: application/json' 
-H 'Authorization: Bearer pk8h93nq9cevaw55bohkjbp0o7kpl4d3' 
http://magento2.ce/rest/V1/foggylineSliderSlide/3

The PHP cURL style for executing DELETE /V1/foggylineSliderSlide/:slideId is done as follows:

$slideId = 4;

$ch = curl_init('http://magento2.ce/rest/V1/foggylineSliderSlide/' . $slideId);
  curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch, CURLOPT_HTTPHEADER, array(
      'Content-Type: application/json',
      'Authorization: Bearer pk8h93nq9cevaw55bohkjbp0o7kpl4d3'
  ));

$result = curl_exec($ch);

The response for console and PHP cURL style should be a JSON string similar to the following one:

true

The PHP SoapClient style for executing DELETE /V1/foggylineSliderSlide/:slideId is done as follows:

$slideId = 2;

$request = new SoapClient(
    'http://magento2.ce/index.php/soap/?wsdl&services= foggylineSliderSlideRepositoryV1',
    array(
        'soap_version' => SOAP_1_2,
        'trace'=>1,
        'stream_context' => stream_context_create(array(
                'http' => array(
                    'header' => 'Authorization: Bearer pk8h93nq9cevaw55bohkjbp0o7kpl4d3')
            )
        )
    )
);

$response = $request-> foggylineSliderSlideRepositoryV1DeleteById(array('slideId'=> $slideId));

The response for PHP SoapClient style should be the stdClass PHP object as follows:

object(stdClass)#2 (1) {
  ["result"]=>
  bool(true)
}

The console SOAP style cURL for executing DELETE /V1/foggylineSliderSlide/:slideId is done as follows:

curl -X POST 
-H 'Content-Type: application/soap+xml; charset=utf-8; action="foggylineSliderSlideRepositoryV1DeleteById"' 
-H 'Authorization: Bearer pk8h93nq9cevaw55bohkjbp0o7kpl4d3' 
-d @request.xml 
http://magento2.ce/index.php/soap/default?services= foggylineSliderSlideRepositoryV1

Where request.xml has content as follows:

<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:ns1="http://magento2.ce/index.php/soap/default? services=foggylineSliderSlideRepositoryV1">
    <env:Body>
        <ns1:foggylineSliderSlideRepositoryV1DeleteByIdRequest>
            <slideId>5</slideId>
        </ns1:foggylineSliderSlideRepositoryV1DeleteByIdRequest>
    </env:Body>
</env:Envelope>

The response for console SOAP style cURL should be an XML as follows:

<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:ns1="http://magento2.ce/index.php/soap/default? services=foggylineSliderSlideRepositoryV1">
    <env:Body>
        <ns1:foggylineSliderSlideRepositoryV1DeleteByIdResponse>
            <result>true</result>
        </ns1:foggylineSliderSlideRepositoryV1DeleteByIdResponse>
    </env:Body>
</env:Envelope>

The preceding API call examples cover all of our custom-defined APIs for the Slide entity.

Looking back at the $searchCriteria variable, we used the GET type of HTTP method, passing the entire variable as a query string. If we think about it, we could have specified POST during the Web API resource definition and packed the content of the $searchCriteria variable into the request body. Although the GET method approach might look a bit dirtier, imagine if we assigned the anonymous or self role to the resource: we would be able to simply open a lengthy URL in the browser and have the search results. Think of a possible widget use, where a widget would simply do an AJAX request to the URL and fetch the results for guests or the customer.

The full module source code can be found here: https://github.com/ajzele/B05032-Foggyline_Slider. Aside from the Slide entity, the full module code includes the Image entity as well. Since each slide can contain multiple images, we can further test the Image API calls analogous to the preceding calls.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.140.108