All of the functionality in Magento2 is built around its API capabilities, so any task that is possible via the application should be possible via its REST and SOAP APIs. Typically, when building projects using Magento2, we are not using it in isolation, more often than not we are using it for its e-commerce functionality alone and leaving the CMS based functionality to a different system that is more suited to the task. There are many benefits to this solution and at some point, we may use Magento as backend only and interact with it entirely via its API.

Magento2 is stable now, but there are still some fairly significant issues open to be resolved. The Magento2 team recommend using the API interfaces where possible during development to give yourself the best chance of future proofing your work through version upgrades and alterations within a system that is very actively developed.

The Problem

During a recent project, we needed to build a menu from the Magento2 category structure - a task that is very straight forward within Magento2, and that you would expect the REST API to facilitate “out of the box”. However, the reality is that while a category tree can be retrieved with a GET request to “/rest/V1/categories”, it does not include all of the information required to replicate a menu in an external system - for example, there is no URL included! Here is a sample output that Magento calls the “Category Tree:

Screen Shot 2017-08-22 at 11.38.20

This is likely because the purpose of the endpoint is to provide a list of categories to be queried individually with GET requests to “/rest/V1/categories/{category_id}”, which will then give you access to a richer dataset:

Screen Shot 2017-08-22 at 11.40.21

However, for our use-case we just want to build a simple menu so we don’t want to have to make individual requests for each category!

The Solution

We decided that the best course of action was to extend the existing API functionality to include the URL in the “category tree”. We do not want to directly alter the existing API endpoint at “GET /rest/V1/categories” as we don’t want to impinge upon other functionality relying on the existing endpoint, so we will create a new endpoint at “GET /rest/V1/ad/categories”.

Fundamentally, the logic that we require to achieve this functionality is extremely simple as we are not writing any new logic as such, we are just giving the existing category tree a new interface, which exposes the already existing URL property - but only when it is accessed via our new endpoint.

To achieve this, we start by creating a new package within “app/code” called “ApiExtensions”:

Screen Shot 2017-08-22 at 11.40.51

You can find information on creating and registering a boilerplate module here if required: http://devdocs.magento.com/guides/v2.2/extension-dev-guide/build/create_component.html

ADApiExtensionsModelWebServiceRepository.php

The WebServiceRepository class actually returns the category tree data, it uses the CategoryManagementInterface to get an instance of the CategoryManagementRepository that the core /rest/V1/categories uses to create the category tree. It passes any parameters through the to underlying logic without modification:

Screen Shot 2017-08-22 at 11.41.42

A particularly interesting part of this class is the DocBlock associated with the categories method:

Screen Shot 2017-08-22 at 11.42.17

This is telling Magento to inherit the DocBlock from its interface (ADApiExtensionsApiWebServiceRepositoryInterface). This may seem unimportant, but the DocBlocks are actually crucial as they are parsed and tell Magento what type of object to return.

ADApiExtensionsApiWebServiceRepositoryInterface.php

The purpose of the WebServiceRepositoryInterface class is to define the methods that the WebServiceRepository must implement, at the moment we are only implementing one method but this could be extended for other extensions that you may require. The DocBlock associated to the categories method tells Magento that it should return an instance of “ADApiExtensionsApiDataCategoryTreeInterface”. This is important and it is the key modification that we are making.

Screen Shot 2017-08-22 at 11.42.49

ADApiExtensionsApiDataCategoryTreeInterface.php

The core CategoryManagementRepository::getTree method returns an instance of “MagentoCatalogApiDataCategoryTreeInterface”, upon inspection you will see that this interface includes “getter and setter” methods for the various properties that are returned in the JSON from the API request. It does not however contain declarations for getUrl or setUrl, as the API is returning an instance of “MagentoCatalogModelCategory” (which includes a getUrl method), this core interface is essentially acting as a filter to exclude the URL from being returned in the response.

For this reason, we will create a new interface replicating the core interface and we will add the missing method declarations (I’ve removed the core methods here for brevity):

Screen Shot 2017-08-22 at 11.43.25

Hooking it all up

So now that the nuts and bolts are in place, we have to add some configuration to make Magento aware that we are adding a new API method and tell it what to do when it receives a request for the endpoint. This requires the following files to be created:

Screen Shot 2017-08-22 at 11.44.08

webapi.xml

Here we are declaring the request URL and method that will be used to access the newly created method and pointing those requests to the WebServiceRepositoryInterface::categories method, which will resolve the WebServiceRepository model via dependency injection as defined in di.xml

di.xml

Screen Shot 2017-08-22 at 11.44.36

If you require more guidance on using dependency injection in Magento2, you might like to work through the information here: http://devdocs.magento.com/guides/v2.0/extension-dev-guide/depend-inj.html

Once all of this is in place, the module should be enabled and the cache flushed to ensure that your configuration is loaded:

Screen Shot 2017-08-22 at 11.45.22

Then make a curl request to your new endpoint:

Screen Shot 2017-08-22 at 11.45.54

Summary

In this demonstration we have created a truly useful API extension by manipulating the configuration of the application, most API extensions that you wish to implement will involve more custom logic than this example - however by understanding the steps that we have taken and the importance of the little details of the implementation you should be able to extend the API quickly and sympathetically without causing regression issues in your codebase. 

Need help with your Magento website? Leave your details below and we’ll be in touch

  • This field is for validation purposes and should be left unchanged.