How to create CRUD Models in Magento 2

26 Nov 2020
Models are an important part of the MVC model. In Magento 2, Models is responsible for working with the database, and is divided into three parts: Model, Resource Model and Collection. They are used to work with CRUD operations (create, read, update, delete) without writing any more SQL code.

Step 1: Setup Script

Firstly, we need to create a database table for our CRUD models. To do this, you insert the setup file:

app/code/Mageworld/HelloWorld/Setup/InstallSchema.php

This file will only be executed once when the module is installed. Let's set the content for this file to create the table above: 

<?php

namespace Mageworld\HelloWorld\Setup;


class InstallSchema implements \Magento\Framework\Setup\InstallSchemaInterface

{


public function install(\Magento\Framework\Setup\SchemaSetupInterface $setup, \Magento\Framework\Setup\ModuleContextInterface $context)

{

$installer = $setup;

$installer->startSetup();

if (!$installer->tableExists('mageworld_helloworld_post')) {

$table = $installer->getConnection()->newTable(

$installer->getTable('mageworld_helloworld_post')

)

->addColumn(

'post_id',

\Magento\Framework\DB\Ddl\Table::TYPE_INTEGER,

null,

[

'identity' => true,

'nullable' => false,

'primary'  => true,

'unsigned' => true,

],

'Post ID'

)

->addColumn(

'name',

\Magento\Framework\DB\Ddl\Table::TYPE_TEXT,

255,

['nullable => false'],

'Post Name'

)

->addColumn(

'content',

\Magento\Framework\DB\Ddl\Table::TYPE_TEXT,

'64k',

[],

'Post Content'

)

->addColumn(

'status',

\Magento\Framework\DB\Ddl\Table::TYPE_INTEGER,

1,

[],

'Post Status'

)

->addColumn(

'created_at',

\Magento\Framework\DB\Ddl\Table::TYPE_TIMESTAMP,

null,

['nullable' => false, 'default' => \Magento\Framework\DB\Ddl\Table::TIMESTAMP_INIT],

'Created At'


->setComment('Post Table');

$installer->getConnection()->createTable($table);


$installer->getConnection()->addIndex(

$installer->getTable('mageworld_helloworld_post'),

$setup->getIdxName(

$installer->getTable('mageworld_helloworld_post'),

['name','content'],

\Magento\Framework\DB\Adapter\AdapterInterface::INDEX_TYPE_FULLTEXT

),

['name',''content'],

\Magento\Framework\DB\Adapter\AdapterInterface::INDEX_TYPE_FULLTEXT

);

}

$installer->endSetup();

}

}

 

This content is showing how the table was created, you can edit it to make your own table. If you installed the module before, you will need to upgrade the module and write the table create code to the UpgradeSchema.php in that folder, and change attribute setup_version greater than the current setup version in module.xml at app/code/Mageworld/HelloWorld/etc/module.xml.

Contents would be:

File: app/code/Mageworld/HelloWorld/etc/module.xml


<?xml version="1.0"?>

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

    <module name="Mageworld_HelloWorld" setup_version="1.1.0">

    </module>

</config>

In module.xml file, we changed the attribute to 1.1.0 greater than setup_version in How to Create Module in Magento 2

File: app/code/Mageworld/HelloWorld/Setup/UpgradeSchema.php

<?php

namespace Mageworld\HelloWorld\Setup;


use Magento\Framework\Setup\UpgradeSchemaInterface;

use Magento\Framework\Setup\SchemaSetupInterface;

use Magento\Framework\Setup\ModuleContextInterface;


class UpgradeSchema implements UpgradeSchemaInterface

{

public function upgrade( SchemaSetupInterface $setup, ModuleContextInterface $context ) {

$installer = $setup;


$installer->startSetup();


if(version_compare($context->getVersion(), '1.1.0', '<')) {

if (!$installer->tableExists('mageworld_helloworld_post')) {

$table = $installer->getConnection()->newTable(

$installer->getTable('mageworld_helloworld_post')

)

->addColumn(

'post_id',

\Magento\Framework\DB\Ddl\Table::TYPE_INTEGER,

null,

[

'identity' => true,

'nullable' => false,

'primary'  => true,

'unsigned' => true,

],

'Post ID'

)

->addColumn(

'name',

\Magento\Framework\DB\Ddl\Table::TYPE_TEXT,

255,

['nullable => false'],

'Post Name'

)

->addColumn(

'content',

\Magento\Framework\DB\Ddl\Table::TYPE_TEXT,

'64k',

[],

'Post Content'

)

->addColumn(

'status',

\Magento\Framework\DB\Ddl\Table::TYPE_INTEGER,

1,

[],

'Post Status'

)

->addColumn(

'created_at',

\Magento\Framework\DB\Ddl\Table::TYPE_TIMESTAMP,

null,

['nullable' => false, 'default' => \Magento\Framework\DB\Ddl\Table::TIMESTAMP_INIT],

'Created At'

) ->setComment('Post Table');

$installer->getConnection()->createTable($table);


$installer->getConnection()->addIndex(

$installer->getTable('mageworld_helloworld_post'),

$setup->getIdxName(

$installer->getTable('mageworld_helloworld_post'),

['name','content'],

\Magento\Framework\DB\Adapter\AdapterInterface::INDEX_TYPE_FULLTEXT

),

['name','content'],

\Magento\Framework\DB\Adapter\AdapterInterface::INDEX_TYPE_FULLTEXT

);

}

}


$installer->endSetup();

}

}

 

After this, please run this command line:

php bin/magento setup:upgrade

When you run upgrade completed, please continue run deploy like this

php bin/magento setup:static-content:deploy

Now checking your database, you will see a table with name mageworld_helloworld_post and above columns. If this table is not created, it may be because you ran the above command line before you add content to InstallSchema.php. To fix this, you need to remove the information that lets Magento know your module has installed in the system. Please open the table ‘setup_module’, find and remove a row that has module equals to mageworld_helloworld_post. After this, run the command again to install the table.

This InstallSchema.php is used to create database structure. If you want to install the data to the table which you was created, you need to use InstallData.php file:

app/code/Mageworld/HelloWorld/Setup/InstallData.php

Please take a look in some InstallData file in Magento to know how to use it. That's some file you can see:

- vendor/magento/module-tax/Setup/InstallData.php

- vendor/magento/module-customer/Setup/InstallData.php

- vendor/magento/module-catalog/Setup/InstallData.php

 

As I said above, those install files will be used for the first time to install the module. If you want to change the database when upgrading the module, please try to use UpgradeSchema.php and UpgradeData.php.

Step 2: Create Model

Now we will create the model file:

app/code/Mageworld/HelloWorld/Model/Post.php

And this is the content of that file:

<?php

namespace Mageworld\HelloWorld\Model;

class Post extends \Magento\Framework\Model\AbstractModel implements \Magento\Framework\DataObject\IdentityInterface

{

const CACHE_TAG = 'mageworld_helloworld_post';


protected $_cacheTag = 'mageworld_helloworld_post';


protected $_eventPrefix = 'mageworld_helloworld_post';


protected function _construct()

{

$this->_init('Mageworld\HelloWorld\Model\ResourceModel\Post');

}


public function getIdentities()

{

return [self::CACHE_TAG . '_' . $this->getId()];

}


public function getDefaultValues()

{

$values = [];


return $values;

}

}

 

This model class will extend the AbstractModel class Magento\Framework\Model\AbstractModel and implements \Magento\Framework\DataObject\IdentityInterface. The IdentityInterface will force Model class to define the getIdentities() method which will return a unique id for the model. You must only use this interface if your model requires cache clear after database operation and render information to the frontend page.

The _construct() method will be called whenever a model is instantiated. Every CRUD model has to use the _construct() method to call _init() method. This _init() method will define the resource model which will actually fetch the information from the database. As above, we define the resource model Mageworld\Post\Model\ResourceModel\Post The last thing about model is some variable which you should you in your model:

  • $_eventPrefix - a prefix for events to be triggered

  • $_eventObject - a object name when access in event

  • $_cacheTag - a unique identifier for use within caching

Step 3: Create Resource Model

As you know, the model file contains overall database logic, it does not execute sql queries. The resource model will do that. Now we will create the Resource Model for this table: app/code/Mageworld/HelloWorld/Model/ResourceModel/Post.php

Content for this file: 

<?php

namespace Mageworld\HelloWorld\Model\ResourceModel;



class Post extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb

{

public function __construct(

\Magento\Framework\Model\ResourceModel\Db\Context $context

)

{

parent::__construct($context);

}

protected function _construct()

{

$this->_init('mageworld_helloworld_post', 'post_id');

}

}

 

Every CRUD resource model in Magento must extend the abstract class \Magento\Framework\Model\ResourceModel\Db\AbstractDb which contains the functions for fetching information from the database.

Like model class, this resource model class will have required method _construct(). This method will call _init() function to define the table name and primary key for that table. In this example, we have table mageworld_helloworld_post and the primary key post_id.

Step 4: Create Resource Model Collection - Get Model Collection

The collection model is considered a resource model which allows us to filter and fetch a collection table data. The collection model will be placed in:

app/code/Mageworld/HelloWorld/Model/ResourceModel/Post/Collection.php

The content for this file:

<?php

namespace Mageworld\HelloWorld\Model\ResourceModel\Post;


class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection

{

protected $_idFieldName = 'post_id';

protected $_eventPrefix = 'mageworld_helloworld_post_collection';

protected $_eventObject = 'post_collection';


/**

* Define resource model

*

* @return void

*/

protected function _construct()

{

$this->_init('Mageworld\HelloWorld\Model\Post', 'Mageworld\HelloWorld\Model\ResourceModel\Post');

}


}

 

Step 5: Factory Object

Just now, we created the model, resource model and collection for the table helloworld_post. And how to use them?

As you know in OOP, a factory method will be used to instantiate an object and in Magento, the Factory Object does the same thing. To use the model we add Factory, for example: PostFactory. You don't need to create a PostFactory class, as Magento will automatically create it for you and place it in the generated folder.

Create file: Mageworld/HelloWorld/Controller/Post/Index.php

<?php

namespace Mageworld\HelloWorld\Controller\Index;


class Index extends \Magento\Framework\App\Action\Action

{

protected $_pageFactory;


protected $_postFactory;


public function __construct(

\Magento\Framework\App\Action\Context $context,

\Magento\Framework\View\Result\PageFactory $pageFactory,

\Mageworld\HelloWorld\Model\PostFactory $postFactory

)

{

$this->_pageFactory = $pageFactory;

$this->_postFactory = $postFactory;

return parent::__construct($context);

}


public function execute()

{

$post = $this->_postFactory->create();

$collection = $post->getCollection();

foreach($collection as $item){

echo "<pre>";

print_r($item->getData());

echo "</pre>";

}

exit();

return $this->_pageFactory->create();

}

}

 

This is the data in the table helloworld_post

 

And this is the result