How to create a repository interface in Magento 2?

First of all, if you want to add a repository to your store and make it a part of the REST API, you need to create repository and data model interfaces.

Warning: the code below is provided as an example of the Magento 2 repository only and requires further customization before implementation.

→ Need a hand with custom development? Our specialists are ready to help

Magento 2 difference between a factory and a repository

Factories are service classes that instantiate non-injective classes, that is, models that represent the database entity. They create an abstraction layer between ObjectManager and the business code.

The repository object is responsible for reading and writing information about your object to the object store.

Magento repository and data model interfaces creation

To do this, you need to create two folders in your module Api/Data/. We need to create a PHP interface in the API folder. You must use interface suffix for Magento 2. We will name our file Api/AmastyRepositoryInterface.

Repositories frequently use the methods getById, save, delete, and getList, but you can add any other.

<?php

namespace Amasty\Example\Api;

use Magento\Framework\Api\SearchCriteriaInterface;
use Amasty\Example\Api\Data\AmastyInterface;

interface AmastyRepositoryInterface
{
/**
* @param int $id
* @return \Amasty\Example\Api\Data\AmastyInterface
* @throws \Magento\Framework\Exception\NoSuchEntityException
*/
public function getById($id);

/**
* @param \Amasty\Example\Api\Data\AmastyInterface $amasty
* @return \Amasty\Example\Api\Data\AmastyInterface
*/
public function save(AmastyInterface $amasty);

/**
* @param \Amasty\Example\Api\Data\AmastyInterface $amasty
* @return void
*/
public function delete(AmastyInterface $amasty);

/**
* @param \Magento\Framework\Api\SearchCriteriaInterface $searchCriteria
* @return \Amasty\Example\Api\Data\AmastySearchResultInterface
*/
public function getList(SearchCriteriaInterface $searchCriteria);
}

! Don’t forget to use annotation for every method.

The DTO (Data Transfer Object interface)

Next, you need to create the Data Transfer Object interface. If a method extracts or sends back an array, the type of the items should be defined in the annotation, followed by []. For example: int[]

<?php

namespace Amasty\Example\Api\Data;

use Magento\Framework\Api\ExtensibleDataInterface;

interface AmastyInterface extends ExtensibleDataInterface
{
    /**
     * @return int
     */
    public function getId();

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

    /**
     * @return string
     */
    public function getName();

    /**
    * @param string $name
    * @return void
    */
    public function setName($name);

    /**
     * @return \Amasty\Example\Api\Data\IngredientInterface[]
     */
    public function getIngredients();

    /**
     * @param \Amasty\Example\Api\Data\ObjectInterface[] $ingredients
     * @return void
     */
    public function setIngredients(array $ingredients);

    /**
     * @return string[]
     */
    public function getImageUrls();

    /**
     * @param string[] $urls
     * @return void
    */
    public function setImageUrls(array $urls);

     /**
      * @return \Amasty\Example\Api\Data\AmastyExtensionInterface|null
      */
    public function getExtensionAttributes();

    /**
     * @param \Amasty\Example\Api\Data\AmastyExtensionInterface $extensionAttributes
     * @return void
     */
    public function setExtensionAttributes(AmastyExtensionInterface $extensionAttributes);
}

Search results interface

To set the right work of getList method, we need to specify SearchResultsInterface instance. Here is the code example:

<?php
 
namespace Amasty\Example\Api\Data;

use Magento\Framework\Api\SearchResultsInterface;

interface AmastySearchResultInterface extends SearchResultsInterface
{
    /**
     * @return \Amasty\Example\Api\Data\AmastyInterface[]
     */
    public function getItems();

    /**
     * @param \Amasty\Example\Api\Data\AmastyInterface[] $items
     * @return void
     */
    public function setItems(array $items);
}

As a result, you will have these interfaces:

  • \Amasty\Example\Api\AmastyRepositoryInterface
  • \Amasty\Example\Api\Data\AmastyInterface
  • \Amasty\Example\Api\Data\AmastySearchResultInterface

The repository interface of your Magento 2 extends nothing, the AmastyInterface extends the \Magento\Framework\Api\ExtensibleDataInterface, and the AmastySearchResultInterface extends the \Magento\Framework\Api\SearchResultsInterface.

Create the Magento 2 repository and implement a data model

First of all let’s see how our AmastyFactory will use getById(), save() and delete() methods.

It is inserted into the repository as an argument. Check the example below:

public function getById($id)
{
    $amasty = $this->amastyFactory->create();
    $amasty>getResource()->load($amasty, $id);
    if (! $amasty>getId()) {
       throw new NoSuchEntityException(__('Unable to find amasty with ID "%1"', $id));
    }
    return $amasty;
}

public function save(AmastyInterface $amasty)
{
    $amasty>getResource()->save($amasty);
    return $amasty;
}

public function delete(AmastyInterface $amasty)
{
    $amasty>getResource()->delete($amasty);
}

And the last difficult part is getList() method settings. It has to convert the SearchCriteria conditions into method calls on the collection. Usually, you can configure it like this:

<?php

namespace Amasty\Example\Model;

use Amasty\Example\Api\AmastyRepositoryInterface;
use Amasty\Example\Api\Data\AmastyInterface;
use Amasty\Example\Api\Data\AmastySearchResultInterface;
use Amasty\Example\Api\Data\AmastySearchResultInterfaceFactory;
use Amasty\Example\Model\ResourceModel\Amasty\CollectionFactory as AmastyCollectionFactory;
use Amasty\Example\Model\ResourceModel\Amasty\Collection;
use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface;
use Magento\Framework\Api\SearchCriteriaInterface;
use Magento\Framework\Api\SortOrder;
use Magento\Framework\Exception\NoSuchEntityException;

class AmastyRepository implements AmastyRepositoryInterface
{
    /**
     * @var AmastyFactory
     */
    private $amastyFactory;

    /**
     * @var AmastyCollectionFactory
     */
    private $amastyCollectionFactory;

    /**
     * @var AmastySearchResultInterfaceFactory
     */
    private $searchResultFactory;
    /**
     * @var CollectionProcessorInterface
     */
    private $collectionProcessor;

    public function __construct(
        AmastyFactory $amastyFactory,
        AmastyCollectionFactory $amastyCollectionFactory,
        AmastySearchResultInterfaceFactory $amastySearchResultInterfaceFactory,
        CollectionProcessorInterface $collectionProcessor
    ) {
        $this->amastyFactory = $amastyFactory;
        $this->amastyCollectionFactory = $amastyCollectionFactory;
        $this->searchResultFactory = $amastySearchResultInterfaceFactory;
       $this->collectionProcessor = collectionProcessor;
    }

    // ... getById, save and delete methods listed above ...

    public function getList(SearchCriteriaInterface $searchCriteria)
    {
       $collection = $this->collectionFactory->create();
       $this->collectionProcessor->process($searchCriteria, ($collection);
       $searchResults = $this->searchResultFactory->create();

       $searchResults->setSearchCriteria($searchCriteria);
       $searchResults->setItems($collection->getItems());

       return $searchResults;
    }
}

Don’t forget that FilterGroup must be connected by using an OR operator. And different filter groups should be linked with AND operator.

The DTO (Data Transfer Object)

The DTO interface extends the Magento\Framework\Api\ExtensibleDataInterface. That means that the model should extend Magento\Framework\Model\AbstractExtensibleModel.

But usually, if you wish, you can continue to use the ORM model base class Magento\Framework\Model\AbstractModel.

In our example, we will use Magento 2 DTO to extend the Interface:

<?php

namespace Amasty\Example\Model;

use Magento\Framework\Model\AbstractExtensibleModel;
use Amasty\Example\Api\Data\AmastyExtensionInterface;
use Amasty\Example\Api\Data\AmastyInterface;

class Amasty extends AbstractExtensibleModel implements AmastyInterface
{
    const NAME = 'name';
    const INGREDIENTS = 'ingredients';
    const IMAGE_URLS = 'image_urls';

    protected function _construct()
    {
        $this->_init(ResourceModel\Amasty::class);
    }
    /**
     * @inheritDoc
     */
    public function getName()
    {
        return $this->_getData(self::NAME);
    }
    /**
     * @inheritDoc
     */
    public function setName($name)
    {
        $this->setData(self::NAME);
    }
    /**
     * @inheritDoc
     */
    public function getIngredients()
    {
        return $this->_getData(self::INGREDIENTS);
    }
    /**
     * @inheritDoc
     */
    public function setIngredients(array $ingredients)
    {
        $this->setData(self::INGREDIENTS, $ingredients);
    }
    /**
     * @inheritDoc
     */
    public function getImageUrls()
    {
        $this->_getData(self::IMAGE_URLS);
    }
    /**
     * @inheritDoc
     */
    public function setImageUrls(array $urls)
    {
        $this->setData(self::IMAGE_URLS, $urls);
     }
    /**
     * @inheritDoc
     */
    public function getExtensionAttributes()
    {
         return $this->_getExtensionAttributes();
    }
    /**
     * @inheritDoc
     */
    public function setExtensionAttributes(AmastyExtensionInterface $extensionAttributes)
    {
        $this->_setExtensionAttributes($extensionAttributes);
    }
}

The search result

This Magento SearchResultsInterface can acquire its functionality from a framework class. Here is how you can specify it:

<?php
 
namespace Amasty\Example\Model;

use Magento\Framework\Api\SearchResults;
use Amasty\Example\Api\Data\AmastySearchResultInterface;

class AmastySearchResult extends SearchResults implements AmastySearchResultInterface
{

}

The object manager

The last point we need to set to make all this stuff working is the ObjectManager preferences.

To do this, create etc/di.xml and use the following code:

<?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=" Amasty\Example\Api\AmastyRepositoryInterface" type=" 
Amasty\Example\Model\AmastyRepository"/>
    <preference for=" Amasty\Example\Api\Data\AmastyInterface" type=" 
Amasty\Example\Model\Amasty"/>
    <preference for=" Amasty\Example\Api\Data\AmastySearchResultInterface" type="
Magento\Framework\Api\SearchResults"/>
</config>

API connection

To define the created repository as an API resource design an etc/webapi.xml file:

<?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/amasty_example/:id">
        <service class="Amasty\Example\Api\AmastyRepositoryInterface" method="getById"/>
        <resources>
            <resource ref="anonymous"/>
        </resources>
    </route>
    <route method="GET" url="/V1/amasty_example">
        <service class="Amasty\Example\Api\AmastyRepositoryInterface" method="getList"/>
        <resources>
            <resource ref="amasty_example"/>
        </resources>
    </route>
    <route method="POST" url="/V1/amasty_example">
        <service class="Amasty\Example\Api\AmastyRepositoryInterface" method="save"/>
        <resources>
            <resource ref="anonymous"/>
        </resources>
    </route>
    <route method="PUT" url="/V1/amasty_example">
        <service class="Amasty\Example\Api\AmastyRepositoryInterface" method="save"/>
        <resources>
            <resource ref="anonymous"/>
        </resources>
    </route>
    <route method="DELETE" url="/V1/amasty_example">
        <service class="Amasty\Example\Api\AmastyRepositoryInterface" method="delete"/>
        <resources>
            <resource ref="anonymous"/>
        </resources>
    </route>
</routes>

These settings are suitable not only as REST endpoints but also as a part of the SOAP API.

Here are some notes that you should pay attention to:

  • When you specify the route method, the placeholder should match the name of the argument, otherwise, it just won’t be working.
  • In our example, we have used anonymous accessibility, i.e the resource is public. You can set the self permission to provide access to logged-in customers only.

In case you know a few in programming and need to solve a problem fast - contact our software development specialists. We provide full-cycle development, turn-key website development, mobile application development, and any coding for Magento extending.

 

How can we help you?

Didn’t you find the answer to your question? We are always happy to help you out.