How to create a basic module in Magento 2

How to create a basic module in Magento 2

We’re going to build a very simple module in Magento 2. When finished, the module’s output will say “Hello world!” in the block content on a custom frontend route.

Prerequisites

Needless to say, you will need the latest Magento 2 version which is currently 2.1. If you need any help with the Magento 2 installation we have a great article regarding this particular topic “How to install Magento 2”.

Before we start a Magento 2 module development, there are two things people often forget and we recommend you to do:

1. Disable Magento cache

Disabling Magento cache during development will save you some time because you won’t need to manually flush the cache every time you make changes to your code.

The easiest way to disable cache is to go to Admin → System → Cache Management → select all cache types and disable them.

2. Put Magento into a developer mode

You should put Magento into a developer mode to ensure that you see all the errors Magento is throwing at you.

In order to do this, open your terminal and go to the Magento 2 root. From there you should run the following command:

php bin/magento deploy:mode:set developer

Creating the module files and folders

Module setup

If you have used the Magento 1 version, you’re used to the term code pools – community, core and local folders which reside in the app/code folder. In Magento 2, there are no more code pools. Modules are grouped by namespace and placed directly in the app/code folder.

So our first step is to create the module folder and necessary files required to register a Magento module.

1. Create the following folders:

  • app/code/Inchoo
  • app/code/Inchoo/Helloworld

The Inchoo folder is the module’s namespace, and Helloworld is the module’s name.

Note: If you don’t have the code folder in your app directory, create it manually.

2. Now that we have a module folder, we need to create a module.xml file in the app/code/Inchoo/Helloworld/etc folder with the following code:

<?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="Inchoo_Helloworld" setup_version="1.0.0">
    </module>
</config>

3. To register the module, create a registration.php file in the app/code/Inchoo/Helloworld folder with the following code:

<?php
 
\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'Inchoo_Helloworld',
    __DIR__
);

4. Open your terminal and go to the Magento 2 root. Run from there the following command:

php bin/magento setup:upgrade

If you want to make sure that the module is installed, you can go to Admin → Stores → Configuration → Advanced → Advanced and check that the module is present in the list or you can open app/etc/config.php and check the array for the ‘Inchoo_Helloworld’ key, whose value should be set to 1.

Creating a controller

1. First we need to define the router. To do this, create a routes.xml file in the app/code/Inchoo/Helloworld/etc/frontend folder with the following code:

<?xml version="1.0"?>
 
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
    <router id="standard">
        <route id="helloworld" frontName="helloworld">
            <module name="Inchoo_Helloworld" />
        </route>
    </router>
</config>

Here we’re defining our frontend router and route with an id “helloworld”.

The frontName attribute is going to be the first part of our URL.

In Magento 2 URL’s are constructed this way:
<frontName>/<controler_folder_name>/<controller_class_name>

So in our example, the final URL will look like this:

helloworld/index/index

2. Now we create the Index.php controller file in the app/code/Inchoo/Helloworld/Controller/Index folder with the following code:

<?php
 
namespace Inchoo\Helloworld\Controller\Index;
 
use Magento\Framework\App\Action\Context;
 
class Index extends \Magento\Framework\App\Action\Action
{
    protected $_resultPageFactory;
 
    public function __construct(Context $context, \Magento\Framework\View\Result\PageFactory $resultPageFactory)
    {
        $this->_resultPageFactory = $resultPageFactory;
        parent::__construct($context);
    }
 
    public function execute()
    {
        $resultPage = $this->_resultPageFactory->create();
        return $resultPage;
    }
}

In Magento 1 each controller can have multiple actions, but in Magento 2 this is not the case. In Magento 2 every action has its own class which implements the execute() method.

Creating a block

We’ll create a simple block class with the getHelloWorldTxt() method which returns the “Hello world” string.

1. Create a Helloworld.php file in the app/code/Inchoo/Helloworld/Block folder with the following code:

<?php
namespace Inchoo\Helloworld\Block;
 
class Helloworld extends \Magento\Framework\View\Element\Template
{
    public function getHelloWorldTxt()
    {
        return 'Hello world!';
    }
}
Creating a layout and template files

In Magento 2, layout files and templates are placed in the view folder inside your module. Inside the view folder, we can have three subfolders: adminhtml, base and frontend.
The adminhtml folder is used for admin, the frontend folder is used for frontend and the base folder is used for both, admin and frontend files.

1. First we will create a helloworld_index_index.xml file in the app/code/Inchoo/Helloworld/view/frontend/layout folder with the following code:

<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../lib/internal/Magento/Framework/View/Layout/etc/page_configuration.xsd" layout="1column">
    <body>
        <referenceContainer name="content">
            <block class="Inchoo\Helloworld\Block\Helloworld" name="helloworld" template="helloworld.phtml" />
        </referenceContainer>
    </body>
</page>

Every page has a layout hand and for our controller action the layout handle is helloworld_index_index. You can create a layout configuration file for every layout handle.

In our layout file we have added a block to the content container and set the template
of our block to helloworld.phtml, which we will create in the next step.

2. Create a helloworld.phtml file in the app/code/Inchoo/Helloworld/view/frontend/templates folder with the following code:

<h1><?php echo $this->getHelloWorldTxt(); ?></h1>

$this variable is refrencing our block class and we are calling the method getHelloWorldTxt() which is returning the string ‘Hello world!’.

And that’s it. Open the /helloworld/index/index URL in your browser and you should get something like this:

helloworld

In case you feel you need some extra help, we can offer you a detailed custom report based on our technical audit – feel free to get in touch and see what we can do for you!

Related Inchoo Services

You made it all the way down here so you must have enjoyed this post! You may also like:

3 best open-source eCommerce platforms in 2021 Zrinka Antolovic
Zrinka Antolovic, | 4

3 best open-source eCommerce platforms in 2021

Magento 2 custom widget Ivan Miskic
Ivan Miskic, | 9

Magento 2 custom widget

File upload in Magento 2 store configuration Luka Rajcevic
Luka Rajcevic, | 7

File upload in Magento 2 store configuration

103 comments

  1. The issue that throws 404 is in the controller index(/app/code/Inchoo/Helloworld/Controller/Index/Index.php)
    the one who made the tutorial instead of the below:

    "namespace Inchoo\Helloworld\Controller\Index;

    Uses the below which leads the controller to miss an important piece of code:

    "namespace Inchoo\Helloworld\Index;"

    And that’s why you get 404 instead of Hello World, i change it to the correct namespace
    and it is working without giving me 404

  2. Is it possible to nest modules?

    My idea is to put all my carriers together inside, say:

    app/code/company/carriers/usps
    app/code/company/carriers/fedex
    app/code/company/carriers/xpo

    Is that even possible?

  3. 1 exception(s):
    Exception #0 (UnexpectedValueException): Setup version for module ‘Inchoo_Helloworld’ is not specified

    Exception #0 (UnexpectedValueException): Setup version for module ‘Inchoo_Helloworld’ is not specified

  4. 1 exception(s):
    Exception #0 (UnexpectedValueException): Setup version for module ‘Inchoo_Helloworld’ is not specified

    Exception #0 (UnexpectedValueException): Setup version for module ‘Inchoo_Helloworld’ is not specified
    #0 /home/krazybee/stack-7.0.31-1/apache2/htdocs/krazybee/vendor/magento/framework/Module/DbVersionInfo.php(54): Magento\Framework\Module\DbVersionInfo->isModuleVersionEqual(‘Inchoo_Hellowor…’, false)
    #1 /home/krazybee/stack-7.0.31-1/apache2/htdocs/krazybee/vendor/magento/framework/Module/DbVersionInfo.php(77): Magento\Framework\Module\DbVersionInfo->isSchemaUpToDate(‘Inchoo_Hellowor…’)
    #2 /home/krazybee/stack-7.0.31-1/apache2/htdocs/krazybee/vendor/magento/framework/Module/Plugin/DbStatusValidator.php(120): Magento\Framework\Module\DbVersionInfo->getDbVersionErrors()
    #3 /home/krazybee/stack-7.0.31-1/apache2/htdocs/krazybee/vendor/magento/framework/Module/Plugin/DbStatusValidator.php(53): Magento\Framework\Module\Plugin\DbStatusValidator->getGroupedDbVersionErrors()
    #4 /home/krazybee/stack-7.0.31-1/apache2/htdocs/krazybee/vendor/magento/framework/Interception/Interceptor.php(121): Magento\Framework\Module\Plugin\DbStatusValidator->beforeDispatch(Object(Magento\Framework\App\FrontController\Interceptor), Object(Magento\Framework\App\Request\Http))
    #5 /home/krazybee/stack-7.0.31-1/apache2/htdocs/krazybee/vendor/magento/module-page-cache/Model/App/FrontController/BuiltinPlugin.php(69): Magento\Framework\App\FrontController\Interceptor->Magento\Framework\Interception\{closure}(Object(Magento\Framework\App\Request\Http))
    #6 /home/krazybee/stack-7.0.31-1/apache2/htdocs/krazybee/vendor/magento/framework/Interception/Interceptor.php(135): Magento\PageCache\Model\App\FrontController\BuiltinPlugin->aroundDispatch(Object(Magento\Framework\App\FrontController\Interceptor), Object(Closure), Object(Magento\Framework\App\Request\Http))
    #7 /home/krazybee/stack-7.0.31-1/apache2/htdocs/krazybee/vendor/magento/framework/Interception/Interceptor.php(153): Magento\Framework\App\FrontController\Interceptor->Magento\Framework\Interception\{closure}(Object(Magento\Framework\App\Request\Http))
    #8 /home/krazybee/stack-7.0.31-1/apache2/htdocs/krazybee/generated/code/Magento/Framework/App/FrontController/Interceptor.php(26): Magento\Framework\App\FrontController\Interceptor->___callPlugins(‘dispatch’, Array, Array)
    #9 /home/krazybee/stack-7.0.31-1/apache2/htdocs/krazybee/vendor/magento/framework/App/Http.php(135): Magento\Framework\App\FrontController\Interceptor->dispatch(Object(Magento\Framework\App\Request\Http))
    #10 /home/krazybee/stack-7.0.31-1/apache2/htdocs/krazybee/generated/code/Magento/Framework/App/Http/Interceptor.php(24): Magento\Framework\App\Http->launch()
    #11 /home/krazybee/stack-7.0.31-1/apache2/htdocs/krazybee/vendor/magento/framework/App/Bootstrap.php(257): Magento\Framework\App\Http\Interceptor->launch()
    #12 /home/krazybee/stack-7.0.31-1/apache2/htdocs/krazybee/index.php(39): Magento\Framework\App\Bootstrap->run(Object(Magento\Framework\App\Http\Interceptor))
    #13 {main}is not specified

    i am getting the error
    any can help me?

  5. 1 exception(s):
    Exception #0 (UnexpectedValueException): Setup version for module ‘Inchoo_Helloworld’ is not specified

    i am getting the error
    any can help me?

  6. To avoid 404 Error use HelloWorld instead of Helloworld. In above tutorial, their are many typo mistakes. In some places Helloworld is used, so please correct it.

  7. Hi,
    I am running Magento 2.1.2 on local machine.I trying execute the page but get this error:
    1 exception(s):
    Exception #0 (Magento\Framework\Exception\LocalizedException): Invalid XML in file /var/www/html/magento/app/code/Inchoo/Helloworld/etc/frontend/routes.xml:
    Premature end of data in tag config line 3
    Line: 9

    Exception #0 (Magento\Framework\Exception\LocalizedException): Invalid XML in file /var/www/html/magento/app/code/Inchoo/Helloworld/etc/frontend/routes.xml:
    Premature end of data in tag config line 3
    Line: 9

    #0 /var/www/html/magento/vendor/magento/framework/Config/Reader/Filesystem.php(127): Magento\Framework\Config\Reader\Filesystem->_readFiles(Object(Magento\Framework\Config\FileIterator))
    #1 /var/www/html/magento/var/generation/Magento/Framework/App/Route/Config/Reader/Proxy.php(95): Magento\Framework\Config\Reader\Filesystem->read(‘frontend’)
    #2 /var/www/html/magento/vendor/magento/framework/App/Route/Config.php(82): Magento\Framework\App\Route\Config\Reader\Proxy->read(‘frontend’)
    #3 /var/www/html/magento/vendor/magento/framework/App/Route/Config.php(98): Magento\Framework\App\Route\Config->_getRoutes(‘frontend’)
    #4 /var/www/html/magento/vendor/magento/framework/Url.php(600): Magento\Framework\App\Route\Config->getRouteFrontName(‘helloworld’, ‘frontend’)
    #5 /var/www/html/magento/vendor/magento/framework/Url.php(446): Magento\Framework\Url->_getRouteFrontName()
    #6 /var/www/html/magento/vendor/magento/framework/Url.php(726): Magento\Framework\Url->getBaseUrl(Array)
    #7 /var/www/html/magento/vendor/magento/framework/Url.php(895): Magento\Framework\Url->getRouteUrl(‘helloworld/inde…’, Array)
    #8 /var/www/html/magento/vendor/magento/framework/Url.php(846): Magento\Framework\Url->createUrl(‘helloworld/inde…’, Array)
    #9 /var/www/html/magento/vendor/magento/module-store/App/FrontController/Plugin/RequestPreprocessor.php(78): Magento\Framework\Url->getUrl(‘helloworld/inde…’, Array)
    #10 /var/www/html/magento/vendor/magento/framework/Interception/Chain/Chain.php(67): Magento\Store\App\FrontController\Plugin\RequestPreprocessor->aroundDispatch(Object(Magento\Framework\App\FrontController\Interceptor), Object(Closure), Object(Magento\Framework\App\Request\Http))
    #11 /var/www/html/magento/vendor/magento/framework/Interception/Chain/Chain.php(63): Magento\Framework\Interception\Chain\Chain->invokeNext(‘Magento\\Framewo…’, ‘dispatch’, Object(Magento\Framework\App\FrontController\Interceptor), Array, ‘install’)
    #12 /var/www/html/magento/vendor/magento/framework/Module/Plugin/DbStatusValidator.php(69): Magento\Framework\Interception\Chain\Chain->Magento\Framework\Interception\Chain\{closure}(Object(Magento\Framework\App\Request\Http))
    #13 /var/www/html/magento/vendor/magento/framework/Interception/Chain/Chain.php(67): Magento\Framework\Module\Plugin\DbStatusValidator->aroundDispatch(Object(Magento\Framework\App\FrontController\Interceptor), Object(Closure), Object(Magento\Framework\App\Request\Http))
    #14 /var/www/html/magento/vendor/magento/framework/Interception/Chain/Chain.php(63): Magento\Framework\Interception\Chain\Chain->invokeNext(‘Magento\\Framewo…’, ‘dispatch’, Object(Magento\Framework\App\FrontController\Interceptor), Array, ‘front-controlle…’)
    #15 /var/www/html/magento/vendor/magento/module-page-cache/Model/App/FrontController/VarnishPlugin.php(55): Magento\Framework\Interception\Chain\Chain->Magento\Framework\Interception\Chain\{closure}(Object(Magento\Framework\App\Request\Http))
    #16 /var/www/html/magento/vendor/magento/framework/Interception/Chain/Chain.php(67): Magento\PageCache\Model\App\FrontController\VarnishPlugin->aroundDispatch(Object(Magento\Framework\App\FrontController\Interceptor), Object(Closure), Object(Magento\Framework\App\Request\Http))
    #17 /var/www/html/magento/vendor/magento/framework/Interception/Interceptor.php(138): Magento\Framework\Interception\Chain\Chain->invokeNext(‘Magento\\Framewo…’, ‘dispatch’, Object(Magento\Framework\App\FrontController\Interceptor), Array, ‘front-controlle…’)
    #18 /var/www/html/magento/vendor/magento/module-page-cache/Model/App/FrontController/BuiltinPlugin.php(68): Magento\Framework\App\FrontController\Interceptor->Magento\Framework\Interception\{closure}(Object(Magento\Framework\App\Request\Http))
    #19 /var/www/html/magento/vendor/magento/framework/Interception/Interceptor.php(142): Magento\PageCache\Model\App\FrontController\BuiltinPlugin->aroundDispatch(Object(Magento\Framework\App\FrontController\Interceptor), Object(Closure), Object(Magento\Framework\App\Request\Http))
    #20 /var/www/html/magento/var/generation/Magento/Framework/App/FrontController/Interceptor.php(26): Magento\Framework\App\FrontController\Interceptor->___callPlugins(‘dispatch’, Array, Array)
    #21 /var/www/html/magento/vendor/magento/framework/App/Http.php(135): Magento\Framework\App\FrontController\Interceptor->dispatch(Object(Magento\Framework\App\Request\Http))
    #22 /var/www/html/magento/vendor/magento/framework/App/Bootstrap.php(258): Magento\Framework\App\Http->launch()
    #23 /var/www/html/magento/index.php(39): Magento\Framework\App\Bootstrap->run(Object(Magento\Framework\App\Http))
    #24 {main}

    Can anyone please help me?

  8. I’m using Magento 2.2.2 on my local environment, and all worked very well, thank you! This post helped me a lot!

  9. when I run below command:

    php bin/magento setup:upgrade

    I got below reply,
    SQLSTATE[HY000] [2002] No such file or directory

    Also my php.ini files details as below,
    Loaded Configuration File /Applications/MAMP/bin/php/php7.1.8/conf/php.ini
    Scan this dir for additional .ini files (none)

    can you please help me in this?

    1. please run this command after run upgrade command
      php bin/magento setup:static-content:deploy
      php bin/magento indexer:reindex

  10. Hi,
    I am running Magento 2.2.0 on local machine on windows. All things are working fine. I would like to mention here SPECIALLY that into the folder structure I DO NOT HAVE the “code” folder inside “app” as mentioned into the steps to create a custom module.
    Can you please guide me where to put my custom module code? I already tried putting that into
    ..magento_root…/vendor/magento/…my_pkg…/…my_module…
    and
    …magento_root…/vendor/…my_pkg…/…my_module…
    but nothing works.
    As mentioned after certain steps to run the commands through command prompt like “setup:upgrade” and tested the config.php but didnt find my module listed there (ideally it should be with the value 1).

    Please help me. Thanks in advance.

  11. it’s worth leaving a github code sample of the module created here to eliminate room for errors, also specify what minor version of Magento this tutorial applies to, since Magento 2.2 is different from Magento 2.1 this won’t work on 2.1.

  12. when i try this code is shows 404 error and i want only Magento2 module code on Magento2 module link not even Magento 1.X module link now what can i do

  13. I trying execute the page but get this error:
    Fatal error: Uncaught TypeError: Argument 1 passed to Inchoo\Helloworld\Controller\Index\Index::__construct() must be an instance of Magento\Framework\App\Action\Context, instance of Magento\Framework\ObjectManager\ObjectManager given, called in /var/www/mage2/vendor/magento/framework/ObjectManager/Factory/AbstractFactory.php on line 93 and defined in /var/www/mage2/app/code/Inchoo/Helloworld/Controller/Index/Index.php on line 17

    What happend? Help me please…. Thanks!

    1. try first manually remove all files of var/generation directory but this example not worked… after try execute two commands:
      # php bin/magento setup:di:compile
      # php bin/magento setup:static-content:deploy

      Ommit the steps 2 and 3 of the Prerequisites… sorry…

      Now Work it!

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <blockquote cite=""> <code> <del datetime=""> <em> <s> <strike> <strong>. You may use following syntax for source code: <pre><code>$current = "Inchoo";</code></pre>.

Tell us about your project

Drop us a line. We'd love to know more about your project.