Magento 2 | What is proxies in magento 2.

 

Video link for real example.

Proxies Design
Pattern

Proxies is a powerful and
easy technique to help you overcome a huge problem of Magento 2. Basically, we
can say it is design pattern, which is remove cycle of dependency injection.
However, its role and importance are still underestimated. Therefore, in this
post, I am going to help you to understand what is Magento 2 proxies and
its need in Magento 2.

What are Proxies in Magento 2

Just similar to design patterns which are created to solve a
cycle of dependency problems in the project, proxy design pattern solves a
particular problem. Proxies work as a surrogate which means it acts on behalf
of others, in programming, they are classes which could be used instead of any
other class. More specifically, in Magento 2, proxies are used to replace
resource hungry classes.

However, when replacing like that, an issue happens in Magento 2

In Magento 2 your able use constructor injection pattern to
flexibly manage your class dependencies. However, constructor injection also
means that a chain reaction of object instantiation is often the result when
you create an object. (The original object has dependencies that have
dependencies, and those objects have dependencies, and so on.)

If a
class’s constructor is particularly resource-intensive, this can lead to
unnecessary performance impact when another class depends on it, if the
expensive object does not end up being needed during a particular request. (You
can display a dependency graph of such objects
by enabling profiling.)

As an
example, consider the following two classes:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class SlowLoading
{
    public function __construct()
    {
        // ... Do something resource intensive
    }
 
    public function getValue()
    {
        return 'SlowLoading value';
    }
}
 
class FastLoading
{
    protected $slowLoading;
 
    public function __construct(
        SlowLoading $slowLoading
    ){
        $this->slowLoading = $slowLoading;
    }
 
    public function getFastValue()
    {
        return 'FastLoading value';
    }
 
    public function getSlowValue()
    {
        return $this->slowLoading->getValue();
    }
}

 

 

 





























Assume that class SlowLoading has a
non-trivial performance impact when instantiated (perhaps due to a complex
database query or a call to a third-party web API). Because of the dependency
injection in the constructor of 
FastLoading, this impact is
incurred if 
FastLoading is instantiated. Note, however, that the SlowLoading instance is used only in the method getSlowValue, meaning that the resource cost is unnecessary if this method
is never called on the 
FastLoading object.

Proxies
are generated code

Magento
has a solution for this situation: proxies. Proxies extend
other classes to become lazy-loaded versions of them. That is, a real instance
of the class a proxy extends is created only after one of the class’s methods
is actually called. A proxy implements the same interface as the original class
and so can be used as a dependency anywhere the original class can. Unlike its
parent, a proxy has only one dependency: the object manager.

Proxies
are generated code and therefore do not need to be manually written. (See Code
generation
 for more information.) Simply reference a class in the form 
OriginalClassNameProxy, and the class is generated if it
does not exist.

Using
the preceding example, a proxy can be passed into the constructor arguments
instead of the original class, using DI configuration as follows:

1
2
3
4
5
<type name="FastLoading">
    <arguments>
        <argument name="slowLoading" xsi:type="object">SlowLoadingProxy</argument>
    </arguments>
</type>


With
the proxy used in place of 
SlowLoading, the SlowLoading class will not be instantiated—and therefore, the resource
intensive constructor operations not performed—until the 
SlowLoading object is used (that is, if the getSlowValue method is called).

Because
DI configuration is used to inject a proxy, proxies can be dropped in to
replace their corresponding classes – or proxy replacements removed –
without touching application code.

As a
practical example of a proxy, you can see the StoreManager class and
then see the generated 
StoreManager proxy
class.

The
following excerpt from the Magento code passes the 
storeManager argument as a proxy to the MagentoStoreModelResolverStore class. The StoreManagerInterface model is defined as a proxy
class by the added 
Proxy at the end of the original class in the di.xml file.


1
2
3
4
5
<type name="MagentoStoreModelResolverStore">
    <arguments>
        <argument name="storeManager" xsi:type="object">MagentoStoreModelStoreManagerInterfaceProxy</argument>
    </arguments>
</type>
Puneet Kumar Magento Developer

Magento 2 order missing while place with online payment gateway.

Magento 2 order missing while place with online payment gateway. 

I am getting an issue very long time with online payment gateway. Some orders are not created in backend but payment was successful in payment gateway portal and user is also getting mail of order confirmation with order id. But order is missing in Magento2.3.x-p1 backend. 

Magento 2 sales dashboard screenshot.

Puneet Kumar Magento Developer

Dependency Injection in Magento 2

 Dependency Injection in Magento 2

Magento 2

In Magento 2 Dependency Injection is one of
the most useful design patterns.

We all are aware about class and objects,
class is collection of objects and methods, while object is an instance of
class.

Dependency means the class will use objects to
perform some function. Injection is process of passing required dependency to
that Class / Object. In other words, you can use functions of parent class with
the help of dependency injection to reuse code.

If you have one class (MagentoCustomerBlockForm)
that fetches data from other class (MagentoCustomerModelURL), then it is
conceived that one class has dependency on other class to provide some data or
results.

There are mainly two types of dependency
injection in 
Magento 2 which are as below.

Constructor Injection:

It is the basic type of dependency injection
used in Magento 2, where you just have to add parameter in class constructor to
inject dependency.

In Magento 2, if you want to get current
customer’s data then you can inject dependency of customer session in the
created class easily, there is no need to write whole code to get customer
data.

To understand this in detail, we take an
example. Data block class of custom module to get customer’s data and also it
has used viewHelper dependency to get customer name.

<?php

namespace PkgroupCustomBlockData;

 

class Data extends MagentoFrameworkViewElementTemplate

{

          
protected $_customerSession
;

          
protected $_viewHelper
;

 

        public
function __construct(

            
   MagentoFrameworkViewElementTemplateContext
$context
,

               
MagentoCustomerModelSession $customerSession
,

                MagentoCustomerHelperView
$viewHelper

           
)

        {

                       /* dependency injection of customerSession
and viewHelper for Data class */

                       $this->_customerSession = $customerSession;

                       $this->_viewHelper = $viewHelper;

               parent::__construct($context);

        }

 

        public
function getCustomer()

        {

              return  $this->_customerSession->getData(); // retrive customer
data

 

        }

                public function getName()

               {

                  /* get customer name with the help of
viewHelper object’s dependency injection using

                      customer data*/

                   return $this->_viewHelper->getCustomerName($this->getCustomer());

 

                }

}

?>

Code Sample: Data.php
(Constructor Injection)

In above sample code, a defined Data class for
custom module is created that will fetch data of customer session.

In this example, Data class has used object
customerSession to call the logic of getData() method, where you don’t have to
write separate code to get customer session data.

Method Injection:

When a particular method is dependent on class
then we have to pass that class as dependency in our method. The best example
to understand this concept is Magento Event’s Observer.

To understand this in detail, consider below
code sample of sales_order_place_after event’s OrderNumber Observer.

<?php

namespace PkgroupCustomObserver;

 

use
MagentoFrameworkEventObserverInterface
;

 

class OrderNumber implements ObserverInterface {

 

    /**

     *
@param MagentoFrameworkEventObserver $observer

    
*/

   
public
function execute(MagentoFrameworkEventObserver
$observer
) {

       
$order
= $observer->getEvent()->getOrder();

       
// Do Something Here

    }

}

 

?>

Code Sample:
OrderNumber.php(Method Injection)

In the above sample code,
MagentoFrameworkEventObserver will be served as a Dependency Injection for
OrderNumber class’s execute () method Or we can say that “execute()” method
function of OrderNumber class will be dependent on
MagentoFrameworkEventObserver to get order object $order.

ObjectManager:

The ObjectManager is class which handles
`automatic` Dependency Injection in Magento 2. When any class is constructed,
ObjectManager automatically injects class’s dependencies which is provided
directly (Constructor Injection) and defined in all di.xml files of modules.

The di.xml file defines which dependencies are
injected by ObjectManager which can be better understood with the use of
following di.xml code sample.

<? 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=“MagentoCustomerApiCustomerNameGenerationInterface”

                type=“MagentoCustomerHelperView” />

</config>

Code Sample: di.xml

According to the above code sample di.xml of
customer module has specified dependency of class “MagentoCustomerHelperView”
on Interface “CustomerNameGenerationInterface”. Preferences defines which class
will be used to instantiate an object.

Consider Constructor Injection Code Sample, in
which “ObjectManager” directly injects dependency of “viewHelper”, which will
use “MagentoCustomerApiCustomerNameGenerationInterface” to instantiate “viewHelper”
object according to above di.xml code sample.

Dependency Injection Object Types:

There are mainly two types of Objects in
Dependency Injection:

Injectable:

Injectable objects are singleton objects which
use “di.xml’s” configuration for Dependency Injection. Constructor Injection is
one of the examples of Injectable Objects.

Newable:

It can be also known as non-injectable
objects. These objects are used when we will require input from user or
database.

For Example, we cannot directly depend on
model like MagentoCustomerModel because it cannot be used without “customer_id”
or we have to specify customer object and we cannot specify such data in
constructor because it can’t be injectable.

In Magento 2, we can overcome this problem
with the use of factories. Factories are classes used to instantiate
non-injectable classes, so it can be called as service classes.

Models that represent a database entity is
non-injectable class and that can be instantiate using factories. Factory
classes provide one layer of abstraction to combine ObjectManager and business
code. Injectable objects depend on factories to get new able objects.

Newable objects are obtained by creating new
class instance when required with the use of create () method of factory class.
There is no need to define factory class because they are automatically
generated by ObjectManager when we define factory in class constructor.

For Example, we can directly use customer
model’s factory to get specific customer’s data or we can retrieve collection
of customer’s data from customer model object.

<?php

namespace PkgroupCustomBlock;

 

class CustomerData extends MagentoFrameworkViewElementTemplate

{

   
protected $customerFactory
;

 

    /**

     *
Construct

     *

     *
@param MagentoFrameworkViewElementTemplateContext $context

     *
@param MagentoCustomerModelCustomerFactory $customerSession

     *
@param array $data

    
*/

   
public
function __construct(

       
MagentoFrameworkViewElementTemplateContext $context
,

       
MagentoCustomerModelCustomerFactory $customerFactory
,

       
array $data
= []

    ) {

       
parent
::__construct($context, $data);

       
$this
->customerFactory = $customerFactory; // non-injectable objects used factory

    }

 

   
public
function CustomerData()

    {

       
$id
= 10 // Customer Id

       
/* create new
instance of customer model class using injectable object */

       
$customerModel
= $this->customerFactory->create()->load($id);

       
/*call this method
to get customer email from customer model object*/

       
$customerEmail
= $customerModel->getEmail();

       
/* retrive
collection of customer model  */

       
$collection
= $customerModel->getCollection();

    }

}

?>

Code Sample:
CustomerData.php (Use of Factories)

Another use of ObjectManager is that we can
directly use any model class, interfaces in templates, block or constructor.

To understand this, consider following example
of sample template code.

<?php

    
$ObjectManager
= MagentoFrameworkAppObjectManager::getInstance(); // get ObjectManager
instance

    
$customerid
= 14;

     /* create new instance of customer model
class using ObjectManager which is same as $customerModel  of above code sample */

    
$customer
= $ObjectManager->create(‘MagentoCustomerModelCustomer’)->load($customerid);

    
echo $customer
->getEmail(); // 

?>

Code Sample:
custom.phtml (Use Of ObjectManager In Template)

In above code sample, we have used
ObjectManager dependency to call customer model and the code written will also
give customer’s email from customer’s object.

Both the code (CustomerData.php and
custom.phtml) will give customer’s email but it gets differentiated because
Magento 2 supports automatic dependency injection as specified. So, when we use
factories, we don’t need to take ObjectManager dependencies, Magento 2 will
automatically inject it using customer factory; but in templates, Magento 2
does not support automatic dependency injection and we have to use
ObjectManager to get customer’s data directly.

Using ObjectManager
directly in template is not recommended because it defeats the purpose of
dependency injection and if we don’t use ObjectManager directly then our code
will be clearer to understand dependency by exploring constructors.


—————————–
Puneet Kumar Magento Developer

Composer and CLI php version change

 Compoer 

composer self-update --1

CLI php version
sudo update-alternatives --set php /usr/bin/php7.2

Puneet Kumar Magento Developer

Magento 2.4.x install | getting error Invalid credentials for ‘xxxproject-community-edition/magento-project-community-edition-2.4.2.0.zip’, aborting.

 I am trying to install Magento 2.4.x via composer but it giving below error. Have any one idea. 

Invalid credentials for ‘https://repo.magento.com/archives/magento/project-community-edition/magento-project-community-edition-2.4.2.0.zip’, aborting.

_____________________________________________________________________________________

Puneet Kumar Magento Developer

How to upgrade or downgrade install a specific version of Composer?

 Assuming a regular composer installation, to rollback to version 1 of composer, you simply execute:

composer self-update --1
When you want to go back to version 2 (which you should, after updating or 
removing the incompatible plugins):
composer self-update --2

Puneet Kumar Magento Developer

How to use magento 1 customer password in magento 2

 Magento 1 use MD5 hash to encrypt the password and Magento 2 use SHA-256.

In Magento 1, they use Mage_Core_Model_Encryption class with following functions.

1
2
3
4
5
6
7
8
public function getHash($password, $salt = false)
{
    if (is_integer($salt)) {
        $salt = $this>_helper>getRandomString($salt);
    }
    return $salt === false ? $this>hash($password) : $this>hash($salt . $password) . ‘:’ . $salt;
}
 

1
2
3
4
5
public function hash($data)
{
    return md5($data);
}
 

Magento 1 generate hash by md5(salt + password) and save in database with 1 colon like $password-hash : $salt.

Magento 2 has changed logic and written in vendor/magento/framework/Encryption/Encryptor.php

Magento 2 generate hash like hash(‘sha256’, $salt . $password); and save with 2 colons in database like

$password-hash : $salt: $version

You have to override Encryptor class via di.xml with some private functions in your module.

/**

 * Class Encryptor provides basic logic for hashing strings and encrypting/decrypting misc data

 */

class Encryptor extends MagentoFrameworkEncryptionEncryptor

{

/**

* @var array map of hash versions

*/

private $hashVersionMap = [

self::HASH_VERSION_MD5 => ‘md5’,

self::HASH_VERSION_SHA256 => ‘sha256’

];

/**

* @var array map of password hash

*/

private $passwordHashMap = [

self::PASSWORD_HASH => ”,

self::PASSWORD_SALT => ”,

self::PASSWORD_VERSION => self::HASH_VERSION_LATEST

];

/**

* @param string $hash

* @return array

*/

private function explodePasswordHash($hash)

{

$explodedPassword = explode(self::DELIMITER, $hash, 3);

foreach ($this->passwordHashMap as $key => $defaultValue) {

$this->passwordHashMap[$key] = (isset($explodedPassword[$key])) ? $explodedPassword[$key] : $defaultValue;

}

return $this->passwordHashMap;

}

/**

* @return string

*/

private function getPasswordHash()

{

return (string)$this->passwordHashMap[self::PASSWORD_HASH];

}

/**

* @return string

*/

private function getPasswordSalt()

{

return (string)$this->passwordHashMap[self::PASSWORD_SALT];

}

/**

* @return array

*/

private function getPasswordVersion()

{

return array_map(‘intval’, explode(self::DELIMITER, $this->passwordHashMap[self::PASSWORD_VERSION]));

}

    /**

     * @inheritdoc

     */

    public function isValidHash($password, $hash)

    {

        $this->explodePasswordHash($hash);

        

        $hashs = explode(“:”, $hash);

        if(count($hashs) == 2){

        $password = md5($this->getPasswordSalt() . $password);

        }

        else{

        foreach ($this->getPasswordVersion() as $hashVersion) {

        $password = $this->hash($this->getPasswordSalt() . $password, $hashVersion);

        }

        }

        

        //print $password . ” “. $this->getPasswordHash(); die;


        return Security::compareStrings(

            $password,

            $this->getPasswordHash()

        );

    }

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
/**
* Class Encryptor provides basic logic for hashing strings and encrypting/decrypting misc data
*/
class Encryptor extends MagentoFrameworkEncryptionEncryptor
{
/**
* @var array map of hash versions
*/
private $hashVersionMap = [
self::HASH_VERSION_MD5 => ‘md5’,
self::HASH_VERSION_SHA256 => ‘sha256’
];
/**
* @var array map of password hash
*/
private $passwordHashMap = [
self::PASSWORD_HASH => ,
self::PASSWORD_SALT => ,
self::PASSWORD_VERSION => self::HASH_VERSION_LATEST
];
/**
* @param string $hash
* @return array
*/
private function explodePasswordHash($hash)
{
$explodedPassword = explode(self::DELIMITER, $hash, 3);
foreach ($this>passwordHashMap as $key => $defaultValue) {
$this>passwordHashMap[$key] = (isset($explodedPassword[$key])) ? $explodedPassword[$key] : $defaultValue;
}
return $this>passwordHashMap;
}
/**
* @return string
*/
private function getPasswordHash()
{
return (string)$this>passwordHashMap[self::PASSWORD_HASH];
}
/**
* @return string
*/
private function getPasswordSalt()
{
return (string)$this>passwordHashMap[self::PASSWORD_SALT];
}
/**
* @return array
*/
private function getPasswordVersion()
{
return array_map(‘intval’, explode(self::DELIMITER, $this>passwordHashMap[self::PASSWORD_VERSION]));
}
    /**
     * @inheritdoc
     */
    public function isValidHash($password, $hash)
    {
        $this>explodePasswordHash($hash);
        
        $hashs = explode(“:”, $hash);
        if(count($hashs) == 2){
         $password = md5($this>getPasswordSalt() . $password);
        }
        else{
         foreach ($this>getPasswordVersion() as $hashVersion) {
         $password = $this>hash($this>getPasswordSalt() . $password, $hashVersion);
         }
        }
        
        //print $password . ” “. $this->getPasswordHash(); die;
 
        return Security::compareStrings(
            $password,
            $this>getPasswordHash()
        );
    }
}
 

 

Now Magento 1 user will able to login their old password. New customers password logic will remain same.

Puneet Kumar Magento Developer

Magento 2 : How do create Setup/InstallSchema.php?

Install Script: InstallSchema & InstallData

The InstallSchema and InstallData classes will be run during the module install.
The InstallSchema setup script in magento 2 will be use to change the database schema (create or change database table). This’s the setup script to create the m2commerce_adminlog_post table:
File: app/code/M2commerce/Adminlog/Setup/InstallSchema.php
<?php

namespace M2commerceAdminlogSetup;

class InstallSchema implements MagentoFrameworkSetupInstallSchemaInterface
{

 public function install(MagentoFrameworkSetupSchemaSetupInterface $setup, MagentoFrameworkSetupModuleContextInterface $context)
 {
  $installer = $setup;
  $installer->startSetup();
  if (!$installer->tableExists('m2commerce_adminlog_post')) {
   $table = $installer->getConnection()->newTable(
    $installer->getTable('mageplaza_helloworld_post')
   )
    ->addColumn(
     'post_id',
     MagentoFrameworkDBDdlTable::TYPE_INTEGER,
     null,
     [
      'identity' => true,
      'nullable' => false,
      'primary'  => true,
      'unsigned' => true,
     ],
     'Post ID'
    )
    ->addColumn(
     'name',
     MagentoFrameworkDBDdlTable::TYPE_TEXT,
     255,
     ['nullable => false'],
     'Post Name'
    )
    ->addColumn(
     'url_key',
     MagentoFrameworkDBDdlTable::TYPE_TEXT,
     255,
     [],
     'Post URL Key'
    )
    ->addColumn(
     'post_content',
     MagentoFrameworkDBDdlTable::TYPE_TEXT,
     '64k',
     [],
     'Post Post Content'
    )
    ->addColumn(
     'tags',
     MagentoFrameworkDBDdlTable::TYPE_TEXT,
     255,
     [],
     'Post Tags'
    )
    ->addColumn(
     'status',
     MagentoFrameworkDBDdlTable::TYPE_INTEGER,
     1,
     [],
     'Post Status'
    )
    ->addColumn(
     'featured_image',
     MagentoFrameworkDBDdlTable::TYPE_TEXT,
     255,
     [],
     'Post Featured Image'
    )
    ->addColumn(
     'created_at',
     MagentoFrameworkDBDdlTable::TYPE_TIMESTAMP,
     null,
     ['nullable' => false, 'default' => MagentoFrameworkDBDdlTable::TIMESTAMP_INIT],
     'Created At'
    )->addColumn(
     'updated_at',
     MagentoFrameworkDBDdlTable::TYPE_TIMESTAMP,
     null,
     ['nullable' => false, 'default' => MagentoFrameworkDBDdlTable::TIMESTAMP_INIT_UPDATE],
     'Updated At')
    ->setComment('Post Table');
   $installer->getConnection()->createTable($table);

   $installer->getConnection()->addIndex(
    $installer->getTable('mageplaza_helloworld_post'),
    $setup->getIdxName(
     $installer->getTable('mageplaza_helloworld_post'),
     ['name', 'url_key', 'post_content', 'tags', 'featured_image'],
     MagentoFrameworkDBAdapterAdapterInterface::INDEX_TYPE_FULLTEXT
    ),
    ['name', 'url_key', 'post_content', 'tags', 'featured_image'],
    MagentoFrameworkDBAdapterAdapterInterface::INDEX_TYPE_FULLTEXT
   );
  }
  $installer->endSetup();
 }
}
Looking into this file we will see:
The class must extend MagentoFrameworkSetupInstallSchemaInterface
The class must have install() method with 2 arguments SchemaSetupInterface and ModuleContextInterface. The SchemaSetupInterface is the setup object which provide many function to interact with database server. The ModuleContextInterface has only 1 method getVersion() which will return the current version of your module.
In the example above, we create a table named m2commerce_adminlog_post with columns: post_id, name, post_content, created_at ....
InstallData will be run after the InstallSchema class to add data to the database table.
File: app/code/M2commerce/Adminlog/Setup/InstallData.php
<?php

namespace m2commerceAdminlogSetup;

use MagentoFrameworkSetupInstallDataInterface;
use MagentoFrameworkSetupModuleContextInterface;
use MagentoFrameworkSetupModuleDataSetupInterface;

class InstallData implements InstallDataInterface
{
 protected $_postFactory;

 public function __construct(M2commerceAdminlogModelPostFactory $postFactory)
 {
  $this->_postFactory = $postFactory;
 }

 public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
 {
  $data = [
   'name'         => "How to Create SQL Setup Script in Magento 2",
   'post_content' => "In this article, we will find out how to install and upgrade sql script for module in Magento 2. When you install or upgrade a module, you may need to change the database structure or add some new data for current table. To do this, Magento 2 provide you some classes which you can do all of them.",
   'url_key'      => '/magento-2-module-development/magento-2-how-to-create-sql-setup-script.html',
   'tags'         => 'magento 2,m2commerce helloworld',
   'status'       => 1
  ];
  $post = $this->_postFactory->create();
  $post->addData($data)->save();
 }
}
This class will have the same concept as InstallSchema.

Upgrade Script: UpgradeSchema & UpgradeData

The both of this files will run when the module is installed or upgraded. This classes is difference with the Install classes because they will run every time the module upgrade. So we will need to check the attribute setup_version in module.xml at app/code/M2commerce/Adminlog/etc/ and separate the script by each version.
In this example, we will change the attrubute setup_version to 1.2.0
File: app/code/M2commerce/Adminlog/etc/module.xml
Contents would be:
<?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="M2commerce_Adminlog" setup_version="1.2.0">
    </module>
</config>

Upgrade Schema:

File: app/code/M2commerce/Adminlog/Setup/UpgradeSchema.php
<?php
namespace M2commerceAdminlogSetup;

use MagentoFrameworkSetupUpgradeSchemaInterface;
use MagentoFrameworkSetupSchemaSetupInterface;
use MagentoFrameworkSetupModuleContextInterface;

class UpgradeSchema implements UpgradeSchemaInterface
{
 public function upgrade( SchemaSetupInterface $setup, ModuleContextInterface $context ) {
  $installer = $setup;

  $installer->startSetup();

  if(version_compare($context->getVersion(), '1.2.0', '<')) {
   $installer->getConnection()->addColumn(
    $installer->getTable( 'mageplaza_helloworld_post' ),
    'test',
    [
     'type' => MagentoFrameworkDBDdlTable::TYPE_DECIMAL,
     'nullable' => true,
     'length' => '12,4',
     'comment' => 'test',
     'after' => 'status'
    ]
   );
  }



  $installer->endSetup();
 }
}

In this class, we use the upgrade() method which will be run every time the module is upgraded. We also have to compare the version to add the script for each version.

Upgrade Data:

This will same with the UpgradeSchema class
File: app/code/M2commerce/Adminlog/Setup/UpgradeData.php
<?php

namespace M2commerceAdminlogSetup;

use MagentoFrameworkSetupUpgradeDataInterface;
use MagentoFrameworkSetupModuleDataSetupInterface;
use MagentoFrameworkSetupModuleContextInterface;

class UpgradeData implements UpgradeDataInterface
{
 protected $_postFactory;

 public function __construct(M2commerceAdminlogModelPostFactory $postFactory)
 {
  $this->_postFactory = $postFactory;
 }

 public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
 {
  if (version_compare($context->getVersion(), '1.2.0', '<')) {
   $data = [
    'name'         => "Magento 2 Events",
    'post_content' => "This article will talk about Events List in Magento 2. As you know, Magento 2 is using the events driven architecture which will help too much to extend the Magento functionality. We can understand this event as a kind of flag that rises when a specific situation happens. We will use an example module Mageplaza_HelloWorld to exercise this lesson.",
    'url_key'      => '/magento-2-module-development/magento-2-events.html',
    'tags'         => 'magento 2,mageplaza helloworld',
    'status'       => 1
   ];
   $post = $this->_postFactory->create();
   $post->addData($data)->save();
  }
 }
}

Recurring Script

The recurring script is a script which will be run after the module setup script every time the command line php bin/magento setup:upgrade run.
This script will be defined same as InstallSchema class but difference in name of the class. The example for this class you can see in vendor/magento/module-indexer/Setup/Recurring.php

Uninstall Script

Magento 2 provide us the uninstall module feature which will remove all of the table, data like it hadn’t installed yet. This’s the example for this class:
File: app/code/M2commerce/Adminlog/Setup/Uninstall.php
<?php
namespace MageplazaHelloWorldSetup;

use MagentoFrameworkSetupUninstallInterface;
use MagentoFrameworkSetupSchemaSetupInterface;
use MagentoFrameworkSetupModuleContextInterface;

class Uninstall implements UninstallInterface
{
 public function uninstall(SchemaSetupInterface $setup, ModuleContextInterface $context)
 {
  $installer = $setup;
  $installer->startSetup();

  $installer->getConnection()->dropTable($installer->getTable('m2commerce_adminlog_post'));

  $installer->endSetup();
 }
}
Puneet Kumar Magento Developer

Magento 2.x | Steps for create a new module

Module is a structural element of Magento 2 – the whole system is built upon modules. Typically, the first step in creating a customization is building a module.
To create a module, you have to complete the following high-level steps:
  1. Create the module folder e.g VendorName/ModuleName.
  2. Create the VendorName/ModuleName/etc/module.xml file.
  3. Create the VendorName/ModuleName/registration.php file.
  4. Run the bin/magento setup:upgrade script to install the new module.
  5. Check that the module is working.
Let’s go through each of these steps in detail.

Create the module folder

There are two possible locations for modules in Magento 2: the app/code folder and the vendor folder

Depending on how Magento 2 has been installed, core modules can either be located in the vendor/magento/magento-*folders (for composer installation) or in the app/code/Magento/ folder (for cloning GitHub).

Which of these locations should you choose for your new module?

If you build a module for a specific project, it is best to choose the app/code folder and commit to the project’s repository.
If you build an extension to be reused, it is better to use composer to create it, and put your module in the vendor/<YOUR_VENDOR>/module-something folder.
Each module name in Magento 2 consists of two parts – the vendor and the module itself. In other words, modules are grouped into vendors, so you need to define the vendor and module names. For this example, let’s name the vendor “Learning” and the module “FirstUnit”.
Let’s create the folder app/code/Learning and inside this folder place another folder: FirstUnit. If you’re using the command line, the code would be:
  1. cd to the root folder
  2. mkdir app/code/Learning
  3. mkdir app/code/Learning/FirstUnit

Make sure you have permission to create files and folders in your installation

Next, you need to create an etc/module.xml file. This file is required for the module to exist.
This file contains the following information:
  • Module name
  • Module version
  • Dependencies
Module name is defined by the folders we just created, because in Magento 2, class names must follow the folder structure. Because we created the folders Learning/FirstUnit, our module name will be Learning_FirstUnit and all classes that belong to this module will begin with LearningFirstUnit – for example: LearningFirstUnitObserverTest.
Module version indicates the current version of the database schema and data, and is used in upgrading. For example, assume you decide to modify a table’s schema in your module. How can you be sure that this change will happen on all instances where the code is deployed? Altering the database by direct SQL queries won’t work. Instead, Magento 2 has install and upgrade scripts in every module (optionally). These scripts contain commands to modify the database schema or data. To track whether to execute a script or not, Magento 2 uses module versions. Every time you implement a new database change, you implement a new version of a module and change the corresponding module.xml. Magento saves the current module’s version in a database, and if the database value and the one in the module.xml do not match, it will execute the upgrade code.
Dependencies. If one module depends on another, the module.xml file will have a special declaration that defines a list of modules that the current module depends on. For this example, we will make our module dependent on Magento_Catalog.
Using the following command-line code, create the folder app/code/Learning/FirstUnit/etc:
mkdir app/code/Learning/FirstUnit/etc

Then put the following code into it:
1
2
3
4
5
6
<?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="Learning_FirstUnit" setup_version="0.0.1"> <sequence>
<module name="Magento_Catalog"/> </sequence>
    </module>
</config>

Note that in the XML file we specified:
  • Module name: Learning_FirstUnit (based on the folders we created)
  • Version: 0.0.1 (initial version of our module)
  • Dependency: Magento_Catalog. We could have multiple dependencies. In this case, we would put <module name=”..” /> nodes under the sequence node.

Create the registration.php file

Each module must have this file, which tells Magento how to locate the module. Continuing our example, create the file app/code/Learning/FirstUnit/registration.php. Then put the following content into it:
1
2
3
4
<?php MagentoFrameworkComponentComponentRegistrar::register(
MagentoFrameworkComponentComponentRegistrar::MODULE, 'Learning_FirstUnit',
__DIR__
);

The registration.php is a standardized file that follows the same pattern for all modules.
The only thing that varies is the module name, which in our case is Learning_FirstUnit.

Run the “setup:upgrade” command

Running this command makes your new module active, notifying Magento of its presence.
php bin/magento setup:upgrade

It should echo a large amount of output, one line of which should be Learning_FirstUnit. Verify that this line of code is there.

Check that the new module is active

So far, we haven’t added any useful code to our module – it is still empty (and therefore invisible). In order to verify that it has been recognized, check the file app/etc/config.php. It has a list of auto-generated modules that are active.
Never change this list manually!
grep Learning_FirstUnit app/etc/config.php

Employing these steps, you can successfully create a new module in Magento 2.
Puneet Kumar Magento Developer