I am new to magento2. How to implement this task I don't know. Could you please help me?
3 Answers
Repository Model
Repositories give service requestors the ability to perform create, read, update, and delete (CRUD) operations on entities or a list of entities. A repository is an example of a service contract, and its implementation is part of the domain layer.
Service Contracts
Service contracts are agreements between two parties, a service provider and a service customer. The contract defines the services that the consumer will get, along with some additional information.
Magento Service Contracts
In magento service contracts are simply interfaces and classes that protect the data integrity and hide the business logic. This allows the customers to use these contracts freely and at the same time, allows the service to evolve without affecting the users.
The service layer has two different interface types. They are Data interfaces and Service interfaces.
Data Interfaces Data interfaces are objects that preserve data integrity by using the following patterns:
- They’re read-only, since they only define constants and getters. Getter functions can contain no parameters.
- A getter function can only return a simple object type (string, integer, Boolean), a simple type array, and another data interface.
- Mixed types can’t be returned by getter functions.
- Data entity builders are the only way to populate and modify data interfaces.
Service Interfaces Service interfaces provide a set of public methods that a client can use. There are three service interfaces subtypes:
- Repository Interfaces
- Management Interfaces
- Metadata Interfaces
Here we only focus on the Repository Interfaces. This interface helps us to write methods to communicate with the database.
Repository Interfaces
Repository interfaces ensure that a user can access persistent data entities. For example, persistent data entities within the Customer Module are Consumer, Address, and Group. This gives us three different interfaces:
- CustomerRepositoryInterface
- AddressRepositoryInterface
- GroupRepositoryInterface
The methods that these interfaces have are:
- Save – If there’s no ID, creates a new record, and updates what’s existing if there is one.
- Get – Looks for the IDs in the database and returns a certain data entity interface.
- GetList – Finds all data entities that correspond with the search criteria, then gives access to the matches by returning the search result interface.
- Delete – Deletes the selected entity
- DeleteById – Deletes the entity when you only have its key.
Steps to use Repository Model
- Before creating repository for our model, we have to define
di.xml
at the pathVendorName\ModuleName\etc\di.xml
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<preference for="VendorName\ModuleName\Api\CarsRepositoryInterface" type="VendorName\ModuleName\Model\CarsRepository" />
<preference for="VendorName\ModuleName\Api\Data\CarsInterface" type="VendorName\ModuleName\Model\Cars" />
</config>
- First we have to create a data interface that holds all the columns in our table. Go on and create
VendorName\ModuleName\Api\Data\CarsInterface.php
file.
<?php
namespace VendorName\ModuleName\Api\Data;
/**
* Interface CarsInterface
*
* @api
*/
interface CarsInterface
{
/**
* @return int
*/
public function getId();
/**
* @param int $car_id
* @return $this
*/
public function setId($car_id);
/**
* @return string
*/
public function getTitle();
/**
* @param string $title
* @return $this
*/
public function setTitle($title);
/**
* @return string
*/
public function getDescription();
/**
* @param string $description
* @return $this
*/
public function setDescription($description);
/**
* @param $updateTime
* @return $this
*/
public function setUpdateTime($updateTime);
}
- Now that we have created the data interface, we have to create a repository interface which contains all the methods to perform CRUD in magento. Go on and create
VendorName\ModuleName\Api\CarsRepositoryInterface.php
file.
<?php
namespace VendorName\ModuleName\Api;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Exception\NoSuchEntityException;
use VendorName\ModuleName\Api\Data\CarsInterface;
use Magento\Framework\Api\SearchCriteriaInterface;
/**
* Interface CarsRepositoryInterface
*
* @api
*/
interface CarsRepositoryInterface
{
/**
* Create or update a Car.
*
* @param CarsInterface $car
* @return CarsInterface
*/
public function save(CarsInterface $car);
/**
* Get a Car by Id
*
* @param int $id
* @return CarsInterface
* @throws NoSuchEntityException If Car with the specified ID does not exist.
* @throws LocalizedException
*/
public function getById($id);
/**
* Retrieve Cars which match a specified criteria.
*
* @param SearchCriteriaInterface $criteria
*/
public function getList(SearchCriteriaInterface $criteria);
/**
* Delete a Car
*
* @param CarsInterface $car
* @return CarsInterface
* @throws NoSuchEntityException If Car with the specified ID does not exist.
* @throws LocalizedException
*/
public function delete(CarsInterface $car);
/**
* Delete a Car by Id
*
* @param int $id
* @return CarsInterface
* @throws NoSuchEntityException If car with the specified ID does not exist.
* @throws LocalizedException
*/
public function deleteById($id);
}
This concludes the implementation of repository intereface in magento.
Then we have to create corresponding model system to accompany the service contracts by Magento.
Model System
Models in Magento are an inherent part of the MVC (Model-View-Controller) architecture. Models are used to do data operations, namely Create, Read, Update and Delete, on a database. Magento’s “Model system” is divided into three parts - models, resource models, and collections.
Model:
Models are like a black box which provides a layer of abstraction on top of the resource models. The fetching, extraction, and manipulation of data occur through models. As a rule of thumb, every entity we create (i.e. every table we create in our database) should have its own model class. Every model extends the Magento\Framework\Model\AbstractModelclass
, which inherits the \Magento\Framework\DataObjectclass
, hence, we can call the setDataand getData functions(refer magic method __call()
) on our model, to get or set the data of a model respectively.
Create a php file named Cars.php in the folder VendorName\Modulename\Model
.
The contents of the file are:
<?php
namespace VendorName\Modulename\Model;
use Magento\Framework\DataObject\IdentityInterface;
use Magento\Framework\Model\AbstractModel;
use VendorName\Modulename\Api\Data\CarsInterface;
use VendorName\Modulename\Model\ResourceModel\Cars as ResourceModel;
/**
* Class Cars
*/
class Cars extends AbstractModel implements
CarsInterface,
IdentityInterface
{
const CACHE_TAG = 'cars_table';
/**
* Init
*/
protected function _construct() // phpcs:ignore PSR2.Methods.MethodDeclaration
{
$this->_init(ResourceModel::class);
}
/**
* @inheritDoc
*/
public function getIdentities()
{
return [self::CACHE_TAG . '_' . $this->getId()];
}
public function getId()
{
return $this->getData('cars_id');
}
public function setId($car_id)
{
return $this->setData('cars_id', $car_id);
}
public function getTitle()
{
return $this->getData('title');
}
public function setTitle($title)
{
return $this->setData('title', $title);
}
public function getDescription()
{
return $this->getData('description');
}
public function setDescription($description)
{
return $this->setData('description', $description);
}
public function setUpdateTime($updateTime)
{
return $this->setData('update_time', $updateTime);
}
}
The Cars class only has one method, _construct()
, when we call the _init()
method, and pass the resource model’s name as its parameter. Rest of the methods are implemented from the CarsInterface. Now we will see about resource model.
Resource model:
All of the actual database operations are executed by the resource model. Every model must have a resource model, since all of the methods of a resource model expects a model as its first parameter. All resource models must extend the
Magento\Framework\Model\ResourceModel\Db\AbstractDb
class.
Create a php file named Cars.php
in the folder VendorName\Modulename\Model\ResourceModel
The contents of the file are:
<?php
namespace VendorName\Modulename\Model\ResourceModel;
/**
* Class Cars
*/
class Cars extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
{
/**
* Init
*/
protected function _construct() // phpcs:ignore PSR2.Methods.MethodDeclaration
{
$this->_init('cars_table', 'cars_id');
}
}
Here we specify the table name and a primary key of the table. Since our table name is cars_table and the primary key is cars_id we pass them both as the parameter.
Collection:
Collections are used when we want to fetch multiple rows from our table. Collections can be used when we want to,
- Fetch multiple rows from a table
- Join tables with our primary table
- Select specific columns
- Apply a WHERE clause to our query
- Use GROUP BY or ORDER BY in our query
Create a php file named Collection.php
in the folder
VendorName\Modulename\Model\ResourceModel\Cars
The contents of the file are.
<?php
namespace VendorName\Modulename\Model\ResourceModel\Cars;
use VendorName\Modulename\Model\Cars as Model;
use VendorName\Modulename\Model\ResourceModel\Cars as ResourceModel;
/**
* Class Collection
*/
class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection
{
/**
* Init
*/
protected function _construct() // phpcs:ignore PSR2.Methods.MethodDeclaration
{
$this->_init(Model::class, ResourceModel::class);
}
}
Our collection has a single method _construct()
which calls the _init()
method that takes our Model and ResourceModel classes as the parameters.
Now that we have created the model system in magento we have one more step to start using this model system in magento. We have to create a class that implements the repsository interface that was created earlier. Go on and create VendorName\Modulename\Model\CarsRepository.php
file.
<?php
namespace VendorName\Modulename\Model;
use Magento\Framework\Api\SearchCriteriaInterface;
use Magento\Framework\Api\SearchResultsInterfaceFactory;
use Magento\Framework\Exception\CouldNotDeleteException;
use Magento\Framework\Exception\CouldNotSaveException;
use Magento\Framework\Exception\NoSuchEntityException;
use VendorName\Modulename\Api\CarsRepositoryInterface;
use VendorName\Modulename\Api\Data\CarsInterface;
use VendorName\Modulename\Model\CarsFactory;
use VendorName\Modulename\Model\ResourceModel\Cars as ObjectResourceModel;
use VendorName\Modulename\Model\ResourceModel\Cars\CollectionFactory;
/**
* Class CarsRepository
*/
class CarsRepository implements CarsRepositoryInterface
{
protected $objectFactory;
protected $objectResourceModel;
protected $collectionFactory;
protected $searchResultsFactory;
/**
* CarsRepository constructor.
*
* @param CarsFactory $objectFactory
* @param ObjectResourceModel $objectResourceModel
* @param CollectionFactory $collectionFactory
* @param SearchResultsInterfaceFactory $searchResultsFactory
*/
public function __construct(
CarsFactory $objectFactory,
ObjectResourceModel $objectResourceModel,
CollectionFactory $collectionFactory,
SearchResultsInterfaceFactory $searchResultsFactory
) {
$this->objectFactory = $objectFactory;
$this->objectResourceModel = $objectResourceModel;
$this->collectionFactory = $collectionFactory;
$this->searchResultsFactory = $searchResultsFactory;
}
/**
* @inheritDoc
*
* @throws CouldNotSaveException
*/
public function save(CarsInterface $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;
}
/**
* @inheritDoc
*/
public function delete(CarsInterface $object)
{
try {
$this->objectResourceModel->delete($object);
} catch (\Exception $exception) {
throw new CouldNotDeleteException(__($exception->getMessage()));
}
return true;
}
/**
* @inheritDoc
*/
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;
}
}
Now all we have to do is use this repository class in our module front-end and perform CRUD operation.
Controller for CRUD
Bellow is the code for controller for the CRUD operation.
<?php
namespace VendorName\Modulename\Controller\Index;
use Magento\Framework\App\Action\Action;
use Magento\Framework\App\Action\Context;
use Magento\Framework\Exception\CouldNotSaveException;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\View\Result\PageFactory;
use VendorName\Modulename\Api\CarsRepositoryInterface;
use VendorName\Modulename\Api\Data\CarsInterface
class Index extends Action
{
protected $_pageFactory;
protected $_carsRepository;
protected $_carsModel;
public function __construct(
Context $context,
PageFactory $pageFactory,
CarsRepositoryInterface $carsRepository,
CarsInterface $carsInterface
) {
$this->_pageFactory = $pageFactory;
$this->_carsRepository=$carsRepository;
$this->_carsModel = $carsInterface;
return parent::__construct($context);
}
public function execute()
{
echo "Hello World!!!";
//Create a record.
$this->_carsModel->setTitle("Title");
$this->_carsModel->setDescriptions("Description");
try {
$this->_carsRepository->save($this->_carsModel);
} catch (CouldNotSaveException $e) {
echo $e->getMessage();
}
//Read a record
try {
$car = $this->_carsRepository->getById("19");
echo "Car id = " . $car->getId() . "<br>";
echo "Car Title = " . $car->getTitle();
} catch (NoSuchEntityException $e) {
echo "No such entity exception - " . $e->getMessage();
} catch (LocalizedException $e) {
echo "Localized Exception" . $e->getMessage();
}
//Update a record
try {
$car = $this->_carsRepository->getById("21");
echo "Car id = " . $car->getId() . "<br>";
echo "Car Title = " . $car->getTitle();
$car->setTitle("This is the updated title");
$car->setDescription("This is the updated Description");
$this->_carsRepository->save($car);
} catch (NoSuchEntityException $e) {
echo "No such entity exception - " . $e->getMessage();
} catch (LocalizedException $e) {
echo "Localized Exception" . $e->getMessage();
}
//Delete a record
try {
$this->_carsRepository->deleteById("20");
echo "Deleted the record with id = 20" . "<br>" . "Go to database and check.";
} catch (NoSuchEntityException $e) {
echo "No such entity exception - " . $e->getMessage();
} catch (LocalizedException $e) {
echo "Localized Exception" . $e->getMessage();
}
exit;
}
}
You should inject the VendorName\Modulename\Api\CarsRepositoryInterface and VendorName\Modulename\Api\Data\CarsInterface in the constructor of the block and use the CRUD operations in the block.
If you want to know how to use the getList() method, just mention that in the comment.
Hope this helps.
Comment if there is any spelling mistakes or continuity error in the documentation.
-
-
If you are looking for a tool to auto generate I would recommend pestle by alanstorm. Mar 18, 2020 at 11:10
-
Hello could you please share that how could we get listing on frontend using interfaces Jun 21, 2021 at 9:13
There is a tool available to generate module CRUD boilerplate.
Github Link to download module creator for magento2
Clone this tool in your webroot directory or just download and extract and run in your browser it will give you GUI to generate module.
Also there is some online websites to do the same.
Download and unzip inside your app/code folder to get all the readymade code from this github solution.
https://github.com/birenk/magento2-crud-module
This solution is working, and will help you in learning crud code and it is having both frontend and adminside crud. Please let me know in comments if you have questions. If this is helpful, please appreciate me by upvoting my answer. Thanks.