Models in Magento are an inherent part of the MVC (Model-View-Controller) architecture something every Adobe Commerce developer works with regularly. 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. In this tutorial, we will be discussing each component individually, and then use all three to make a simple module.
If you wanna follow along, first you’ll have to create a simple hello world module, and then create a table called “my_cars” containing the columns “car_id”, “manufacturer”, and “model”, using an Install Schema setup script.
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\AbstractModel
class, which inherits the \Magento\Framework\DataObject
class, hence, we can call the setData
and getData
functions on our model, to get or set the data of a model respectively. To understand how data objects work, you can read the tutorial on data objects here.
app/code/Jayanka/HelloWorld/Model/Car.php
<?php namespace Jayanka\HelloWorld\Model; use Magento\Framework\Model\AbstractModel; use Jayanka\HelloWorld\Model\ResourceModel\Car as ResourceModel; class Car extends AbstractModel { protected function _construct() { $this->_init(ResourceModel::class); } }
The Car
class only has one method, _construct()
, when we call the _init()method
, and pass the resource model’s name as its parameter. But what is a 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\AbstractDbclass.
app/code/Jayanka/HelloWorld/Model/ResourceModel/Car.php
<?php namespace Jayanka\HelloWorld\Model\ResourceModel; use Magento\Framework\Model\ResourceModel\Db\AbstractDb; class Car extends AbstractDb { protected function _construct() { $this->_init('my_cars', 'car_id'); } }
As we can see, the <Carclass>
here also has one method, <__construct>
, where we call the <_initmethod>
, and pass two parameters to it. The name of the table in the database, and the name of the primary column in that table.
Collection
Collections are used when we want to fetch multiple rows from our table. Meaning collections are a group of models. 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
app/code/Jayanka/HelloWorld/Model/ResourceModel/Car/Collection.php
<?php namespace Jayanka\HelloWorld\Model\ResourceModel\Car; use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection; use Jayanka\HelloWorld\Model\Car as Model; use Jayanka\HelloWorld\Model\ResourceModel\Car as ResourceModel; class Collection extends AbstractCollection { protected function _construct() { $this->_init(Model::class, ResourceModel::class); } }
All collections must inherit the <Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection>
class. Our collection class also has one method, the <_construct>
, where we call the <_init>
method and pass two arguments, the name of the model class, and the resource model class.
Using our Model
Now that our Model, ResourceModel, and Collection classes are ready, let’s use them.
app/code/Jayanka/HelloWorld/Block/Hello.php
<?php /** * * @package magento2 * @author Jayanka Ghosh * @license https://opensource.org/licenses/OSL-3.0 Open Software License v. 3.0 (OSL-3.0) * @link http://jayanka.me/ */ namespace Jayanka\HelloWorld\Block; use Magento\Framework\View\Element\Template; use Jayanka\HelloWorld\Model\ResourceModel\Car\Collection; class Hello extends Template { /** * @var Collection */ private $collection; /** * Hello constructor. * @param Template\Context $context * @param Collection $collection * @param array $data */ public function __construct( Template\Context $context, Collection $collection, array $data = [] ) { parent::__construct($context, $data); $this->collection = $collection; } public function getAllCars() { return $this->collection; } public function getAddCarPostUrl() { return $this->getUrl('helloworld/car/add'); } }
and in our template file we add a form to add new cars, and a table, listing all the existing cars in the database
app/code/Jayanka/HelloWorld/view/frontend/templates/hello.phtml
<?php /* @var Jayanka\HelloWorld\Block\Hello $block */ $cars = $block->getAllCars(); ?> <form method="post" action="<?= $block->getAddCarPostUrl() ?>"> <fieldset> <div> <label for="manufacturer"><?= __("Manufacturer") ?></label> <input type="text" name="manufacturer" id="manufacturer" /> </div> <div> <label for="model"><?= __("Model") ?> <input type="text" name="model" id="model" /> </div> <input type="submit" value="<?= __("Add Car") ?>" /> </fieldset> </form> <hr /> <?php if (count($cars) > 0): ?> <table border="1"> <tr> <th><?= __("ID") ?></th> <th><?= __("Manufacturer") ?> <?= __("Model") ?></th> </tr> <?php foreach ($cars as $car): ?> <tr> <td><?= $car->getId() ?> <td><?= $car->getManufacturer() ?> <td><?= $car->getModel() ?> </tr> <?php endforeach; ?> </table> <?php else: ?> <h1><?= __("You have no cars yet :(") ?></h1> <?php endif; ?>
Now finally we have to make a controller to save the car information submitted from the form
app/code/Jayanka/HelloWorld/Controller/Car/Add.php
<?php /** * * @package magento2 * @author Codilar Technologies * @license https://opensource.org/licenses/OSL-3.0 Open Software License v. 3.0 (OSL-3.0) * @link https://www.codilar.com/ */ namespace Jayanka\HelloWorld\Controller\Car; use Magento\Framework\App\Action\Action; use Magento\Framework\App\Action\Context; use Magento\Framework\App\ResponseInterface; use Jayanka\HelloWorld\Model\Car; use Jayanka\HelloWorld\Model\ResourceModel\Car as CarResource; class Add extends Action { /** * @var Car */ private $car; /** * @var CarResource */ private $carResource; /** * Add constructor. * @param Context $context * @param Car $car * @param CarResource $carResource */ public function __construct( Context $context, Car $car, CarResource $carResource ) { parent::__construct($context); $this->car = $car; $this->carResource = $carResource; } /** * Execute action based on request and return result * * Note: Request will be added as operation argument in future * * @return \Magento\Framework\Controller\ResultInterface|ResponseInterface * @throws \Magento\Framework\Exception\NotFoundException */ public function execute() { /* Get the post data */ $data = $this->getRequest()->getParams(); /* Set the data in the model */ $carModel = $this->car; $carModel->setData($data); try { /* Use the resource model to save the model in the DB */ $this->carResource->save($carModel); $this->messageManager->addSuccessMessage("Car saved successfully!"); } catch (\Exception $exception) { $this->messageManager->addErrorMessage(__("Error saving car")); } /* Redirect back to cars page */ $redirect = $this->resultRedirectFactory->create(); $redirect->setPath('helloworld'); return $redirect; } }
Now, when we open our favourite browser, and hit the URL http://mywebsite.com/helloworld/, we should be able to see our car list.
That’s all there is to know about the basics of models in Magento. If you want us to make a tutorial blog post about a particular topic comment below.
Comments
how can we insert data on specific a column, suppose I have a column car_id that is autoincremented and a manufacture date with default value timestamp and I only want to insert data in car_model.
Refer this class app/code/Codilar/HelloWorld/Controller/Car/Add.php
In the execute() after getting the params value, add you manufacture_date by using this code
/* Get the post data */
$data = $this->getRequest()->getParams();
/* Set the data in the model */
$carModel = $this->car;
$data['manufacture_date'] = $manufacture_date; // set your data;
$carModel->setData($data);