Create a module

Module is a structural element of Commerce – the whole system is built upon modules. Typically, the first step in creating a customization is building a module.

Who is this video for?

  • Developers

Steps to add a module

  • Create the module folder.
  • Create the etc/module.xml file.
  • Create the registration.php file.
  • Run the bin/magento setup.
  • Upgrade script to install the new module.
  • Check that the module is working.
Transcript
Create a new module. A module is a structural element to 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 will need to conduct the following steps. One, create the module folder. Two, create the etc/module.xml file. Three, create the registration.php file. Four, run the bin/magento setup:upgrade script to install the new module. And five, check that the module is working. Let’s go through each of these steps.
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 folders for the composer installation or in the app/code/magento folder when you’re cloning GitHub. Which of these locations should you choose for your new module? If you built a module for a specific project, it is best to choose the app/code folder and commit to the project’s repository. If you built an extension to be reused, it is better to use composer to create it and put your module in the vendor/ /module folder.
Each module made in the 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 CD to the root directory, mkdir app/code/Learning 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, and module load order. Module name is defined by the folders we just created because Magento 2 class names must follow the folder structure.
As we created the folders Learning FirstUnit, our module name will be Learning FirstUnit. And all classes that belong to this module will begin with Learning FirstUnit. Module version indicates the current version of database schema and data, and is used in upgrading. Magento 2 has install and upgrade scripts in every module. 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 of XML. Module load order. If the module being developed needs to be loaded after an existing module, the module.xml file contains a special declaration for that. In this example, we will load our new module after Magento catalog, and we’ll add our dependency via the sequence.
Enter in Magento catalog as our dependency.
Note that in the XML file we specified, module name equals Learning FirstUnit. Based off of the folders we created, the version equals 0.0.1. The initial version of our module and the dependency is Magento catalog. Next, you need to create the registration.php file and we’ll need to put the following content into it.
The registration.php file 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.
The next step is to run the setup:upgrade command.
Running this command will make your new module active, notifying Magento of its presence.
It should echo a large amount of output. One line of which should be Learning FirstUnit.
Verify that this line of code is there.
Finally, 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 app etc config.php file. It has a list of auto generated modules that are active and you should never change it manually.
Employing these steps, you can successfully create a new module in Magento 2. -

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="Training_Sales">
        <sequence>
            <module name="Magento_Sales"/>
        </sequence>
    </module>
</config>

registration.php

<?php

use Magento\Framework\Component\ComponentRegistrar;

ComponentRegistrar::register(
    ComponentRegistrar::MODULE,
    'Training_Sales',
    __DIR__);

Add a plugin and provide some functionality

The next step is to add some functionality to our basic module. A Plugin is an essential tool that all Adobe Commerce developers use. This video and tutorial help you create a plugin.

Things to remember for Plugins

  • All plugins are declared in di.xml.
  • The plugin requires a unique name
  • disabled and sortOrder are optional
  • Scope of the plugin is set by the folder that it is inside
  • Plugins can be executed before, after or both (around) the method is called
  • Avoid using around plugins. They are tempting to use but often are the wrong choice and will lead to performance issues.

Plugin code samples

Here are the XML and PHP classes used in the tutorial for adding a plugin to the first module

app/code/Training/Sales/etc/adminhtml/di.xml

<?xml version="1.0" ?>
<!--
/**
 * Copyright &copy; Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <!-- A Plugin that executes when the admin user places an order -->
    <type name="Magento\Sales\Model\Order">
        <plugin name="admin-training-sales-add-logging" type="Training\Sales\Plugin\AdminAddLoggingAfterOrderPlacePlugin" disabled="false" sortOrder="0"/>
    </type>
</config>

app/code/Training/Sales/etc/frontend/di.xml

<?xml version="1.0" ?>
<!--
/**
 * Copyright &copy; Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <!-- A plugin that executes when a customer uses the LoginPost controller from the Luma frontend -->
    <type name="Magento\Customer\Controller\Account\LoginPost">
        <plugin name="training-customer-loginpost-plugin"
                type="Training\Sales\Plugin\CustomerLoginPostPlugin" sortOrder="20"/>
    </type>
</config>

app/code/Training/Sales/etc/webapi_rest/di.xml

<?xml version="1.0" ?>
<!--
/**
 * Copyright &copy; Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <!-- A plugin that executes when the REST API is used OR when the Luma frontend places an order -->
    <type name="Magento\Sales\Model\Order">
        <plugin name="rest-training-sales-add-logging" type="Training\Sales\Plugin\RestAddLoggingAfterOrderPlacePlugin"/>
    </type>
</config>

app/code/Training/Sales/Plugin/AdminAddLoggingAfterOrderPlacePlugin.php

<?php

declare(strict_types=1);

namespace Training\Sales\Plugin;

use Magento\Sales\Model\Order;
use Psr\Log\LoggerInterface;

/**
 *
 */
class AdminAddLoggingAfterOrderPlacePlugin
{

    /**
     * @var LoggerInterface
     */
    private LoggerInterface $logger;

    /**
     * @param LoggerInterface $logger
     */
    public function __construct(
        LoggerInterface $logger
    ) {
        $this->logger = $logger;
    }

    /**
     * Add log after an order is placed
     *
     * @param Order $subject
     * @param Order $result
     * @return Order
     */
    public function afterPlace(Order $subject, Order $result): Order
    {
        // Log this admin area order
        $this->logger->notice('An ADMIN User placed an Order - $' . $subject->getBaseTotalDue());
        return $result;
    }
}

app/code/Training/Sales/Plugin/CustomerLoginPostPlugin.php

<?php

declare(strict_types=1);

namespace Training\Sales\Plugin;

use Psr\Log\LoggerInterface;
use Magento\Framework\App\RequestInterface;

/**
 * Introduces Context information for ActionInterface of Customer Action
 */
class CustomerLoginPostPlugin
{

    /**
     * @var LoggerInterface
     */
    private LoggerInterface $logger;

    /**
     * @var RequestInterface
     */
    private RequestInterface $request;

    /**
     * @param LoggerInterface $logger
     * @param RequestInterface $request
     */
    public function __construct(LoggerInterface $logger, RequestInterface $request)
    {
        $this->logger = $logger;
        $this->request = $request;
    }

    /**
     * Simple example of a before Plugin on a public method in a Controller
     */
    public function beforeExecute()
    {
        $login = $this->request->getParam('login');
        $username = $login['username'];
        $this->logger->notice( "Login Post controller was used for " . $username );
    }
}

app/code/Training/Sales/Plugin/RestAddLoggingAfterOrderPlacePlugin.php

<?php

declare(strict_types=1);

namespace Training\Sales\Plugin;

use Magento\Sales\Model\Order;
use Psr\Log\LoggerInterface;

/**
 *
 */
class RestAddLoggingAfterOrderPlacePlugin
{

    /**
     * @var LoggerInterface
     */
    private LoggerInterface $logger;

    /**
     * @param LoggerInterface $logger
     */
    public function __construct(
        LoggerInterface $logger
    ) {
        $this->logger = $logger;
    }

    /**
     * Add log after an order is placed
     *
     * @param Order $subject
     * @param Order $result
     * @return Order
     */
    public function afterPlace(Order $subject, Order $result): Order
    {
        // Log this customer driven order
        $this->logger->notice('A Customer placed an Order for $' . $subject->getBaseTotalDue());
        return $result;
    }
}

Useful resources

recommendation-more-help
3a5f7e19-f383-4af8-8983-d01154c1402f