Magento2 – CRUD – Module Development Series Part3

In this blog post we will see how to setup magento models and do database related operations.

To setup models in magento we need to add many files. The fastest way to do this is using code generate tools. You can find many tools online for magento2 code generation, but the one i used for this blog is this

To install this tool, go to your magento2 root folder and run this command

curl -LO http://pestle.pulsestorm.net/pestle.phar
chmod +x pestle.phar

Next run this code

./pestle.phar generate_crud_model Excellence_Hello Test

This should generate all the required files for a model


Creating: /var/www/html/magento2/app/code/Excellence/Hello/Model/TestInterface.php
Creating: /var/www/html/magento2/app/code/Excellence/Hello/Model/ResourceModel/Test/Collection.php
Creating: /var/www/html/magento2/app/code/Excellence/Hello/Model/ResourceModel/Test.php
Creating: /var/www/html/magento2/app/code/Excellence/Hello/Model/Test.php
Creating: /var/www/html/magento2/app/code/Excellence/Hello/Setup/InstallSchema.php
Creating: /var/www/html/magento2/app/code/Excellence/Hello/Setup/InstallData.php

Lets look at the files in details

Setup Script

The file Excellence/Hello/Setup/InstallSchema.php contains code creating your database table. This is executed only one time during module installation. The contents for the file are

<?php
namespace Excellence\Hello\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();
        //START: install stuff
        //END:   install stuff
        
//START table setup
$table = $installer->getConnection()->newTable(
            $installer->getTable('excellence_hello_test')
    )->addColumn(
            'excellence_hello_test_id',
            \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER,
            null,
            [ 'identity' => true, 'nullable' => false, 'primary' => true, 'unsigned' => true, ],
            'Entity ID'
        )->addColumn(
            'title',
            \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
            255,
            [ 'nullable' => false, ],
            'Demo Title'
        )->addColumn(
            'creation_time',
            \Magento\Framework\DB\Ddl\Table::TYPE_TIMESTAMP,
            null,
            [ 'nullable' => false, 'default' => \Magento\Framework\DB\Ddl\Table::TIMESTAMP_INIT, ],
            'Creation Time'
        )->addColumn(
            'update_time',
            \Magento\Framework\DB\Ddl\Table::TYPE_TIMESTAMP,
            null,
            [ 'nullable' => false, 'default' => \Magento\Framework\DB\Ddl\Table::TIMESTAMP_INIT_UPDATE, ],
            'Modification Time'
        )->addColumn(
            'is_active',
            \Magento\Framework\DB\Ddl\Table::TYPE_SMALLINT,
            null,
            [ 'nullable' => false, 'default' => '1', ],
            'Is Active'
        );
$installer->getConnection()->createTable($table);
//END   table setup
$installer->endSetup();
    }
}

looking at the file contents, its clear how the table is created. You can also edit this to make a table according to your needs.

The file Excellence/Hello/Setup/InstallData.php is for setting initial data for the module, but we will discuss that in detail later.

At this point, if you look at your database tables you will not the see the table “excellence_hello_test” in database. The reason is, we have previously already setup the model and version 0.0.1 is already enabled in magento. So magento won’t run the setup scripts since as per magento the module is already installed.

To fix, see the table ‘setup_module’ in your magento2 database. It should have an entry for your module ‘Excellence_Hello’.
Delete that entry from table and run the command

bin/magento setup:upgrade

This should run the setup scripts again and create your table.

Model File

The model file is located at ‘Excellence/Hello/Model/Test.php’
The code in the file is

<?php
namespace Excellence\Hello\Model;
class Test extends \Magento\Framework\Model\AbstractModel implements TestInterface, \Magento\Framework\DataObject\IdentityInterface
{
    const CACHE_TAG = 'excellence_hello_test';

    protected function _construct()
    {
        $this->_init('Excellence\Hello\Model\ResourceModel\Test');
    }

    public function getIdentities()
    {
        return [self::CACHE_TAG . '_' . $this->getId()];
    }
}

The CACHE_TAG is important in magento2 for models. We will see later in detail what is it, just remember it should be unique for each model.

To use models in blocks we need to injected it. Magento2 we always we need to dependency injection, never should be we directly create block instance using “new” or “Mage::getModel”

Let’s see how to use the model in our block
In our block Excellence\Hello\Block\Main we will add this code

<?php
namespace Excellence\Hello\Block;
 
class Main extends \Magento\Framework\View\Element\Template
{    
    protected $_testFactory;
    public function __construct(
        \Magento\Framework\View\Element\Template\Context $context,
        \Excellence\Hello\Model\TestFactory $testFactory
    )
    {
        $this->_testFactory = $testFactory;
        parent::__construct($context);
    }
    protected function _prepareLayout()
    {
 	$test = $this->_testFactory->create();
        $test->setTitle('Test Title');
        $test->save();
        $this->setTestModel($test);
    }
}

You notice that the class \Excellence\Hello\Model\TestFactory doesn’t exist. We have not created it. Factory are special kind of class in magento2. Any class with name Factory in it, magento will auto generate it and place that in var/generation/vendor/module/model folders

In our template file “content.phtml” we will write the code

<h1><?php echo __('Model Saved With Entity ID %1',$block->getTestModel()->getData('excellence_hello_test_id')); ?></h1>

When you open the url, it will do the database entry and show the ID.

Magento2 models are pretty much same as magento1 models, so function like this load(), delete(), etc all work

        $test = $this->_testFactory->create();
        $test->setTitle('Test Title');
        $test->save();

        $test->load(2);  
        print_r($test->getData());
       
        $test->delete(2);

Collection

The collection is located at ‘Excellence\Hello\Model\ResourceModel\Test\Collection.php’ with the code

<?php
namespace Excellence\Hello\Model\ResourceModel\Test;
class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection
{
    protected function _construct()
    {
        $this->_init('Excellence\Hello\Model\Test','Excellence\Hello\Model\ResourceModel\Test');
    }
}

We can do standard collection operations same as magento1 like

        $test = $this->_testFactory->create();
        $collection = $test->getCollection();
        foreach($collection as $row){
           print_r($row->getData());
        }

Resource Model

Resource Model’s are the place where actual sql queries get executed. Model file contain overall database logic, but resource file do the actual sql operations.
e.g In your model file you can write this code

<?php
namespace Excellence\Hello\Model;
class Test extends \Magento\Framework\Model\AbstractModel implements TestInterface, \Magento\Framework\DataObject\IdentityInterface
{
    const CACHE_TAG = 'excellence_hello_test';

    protected function _construct()
    {
        $this->_init('Excellence\Hello\Model\ResourceModel\Test');
    }

    public function getIdentities()
    {
        return [self::CACHE_TAG . '_' . $this->getId()];
    }
    public function loadByTitle($title){
    	if(!$title){
    		$title = $this->getTitle();
    		//random data logic. can be much more complex.
    		//this is just example
    	}
    	$id = $this->getResource()->loadByTitle($title);
    	return $this->load($id);
    }
}

and resource model will have this code

<?php
namespace Excellence\Hello\Model\ResourceModel;
class Test extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
{
    protected function _construct()
    {
        $this->_init('excellence_hello_test','excellence_hello_test_id');
    }
    public function loadByTitle($title){
    	$table = $this->getMainTable(); 
    	$where = $this->getConnection()->quoteInto("title = ?", $title); 
    	$sql = $this->getConnection()->select()->from($table,array('excellence_hello_test_id'))->where($where); 
    	$id = $this->getConnection()->fetchOne($sql); 
    	return $id; 
    }
}

and in our block file we call the model function

<?php
namespace Excellence\Hello\Block;
 
class Main extends \Magento\Framework\View\Element\Template
{    
    protected $_testFactory;
    public function __construct(
        \Magento\Framework\View\Element\Template\Context $context,
        \Excellence\Hello\Model\TestFactory $testFactory
    )
    {
        $this->_testFactory = $testFactory;
        parent::__construct($context);
    }
    protected function _prepareLayout()
    {
 		$test = $this->_testFactory->create();
        $test->loadByTitle('Test Title');
        $this->setTestModel($test);
        
    }
}

This is an important design pattern in magento, model should have the database logic and resource model the actual sql operations.

  • anggakesuma

    Fatal error: Call to undefined function PulsestormPestleRunnermain() in /var/www/html/prosehat/pestle.phar on line 7

    I got that error when using pastle.phar, can you help me ? thanks

  • gaurav agrawal

    I am getting a blank page when I access the block page through browser. I have followed your articles to develop this module. My module was working fine if I remove the model Factory from block file

  • Dan Dodd

    Thanks for the working examples.
    A note for others:
    If I refresh the page I expected it to add another record, it doesn’t with the Page cache on.
    If Page cache is off, it does add another record every time I refresh.