Custom modules are where your custom controllers, blocks, associated templates, layouts and logic will exist. They are located in app/code/<Namespace>/<Module_Name>
. These custom modules could allow you to create new pages for your store via controllers, serve as API endpoints, or simply insert some data into a database. Any custom logic you wish to create must be in a custom module. You should NEVER directly modify the core Magento files in /vendor/Magento
. If you are looking to create a custom theme, then check out our custom theme tutorial!
Want someone else to handle all this Magento work for you? Contact the Richmond, Virginia Magento experts at Ameronix.
Disabling the Cache
Up until now we have simply been clearing the cache each time we make a change. You can continue to do this, however disabling/re-enabling the cache is simple. Go to the admin panel, and navigate to System -> Cache Management, select all, and disable them. This will drastically increase page load times but you will no longer have to clear your cache after every change.
Directory Structure
First we want to create a namespace folder for our modules to exist in, so we will create a Training folder in app/code (Note: Your Magento installation may lack an app/code folder, simply create one). This Training folder holds our custom modules, so inside of Training directory create a HelloWorld folder. Our goal for this article is to create a custom controller, which will use a custom block to render ‘Hello World’ to the screen.
Module Setup
We first need to setup a few boilerplate elements of a custom module. First, create an etc folder containing a module.xml file.
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="Training_HelloWorld" setup_version="0.0.1">
<sequence>
<module name="Magento_Catalog"/>
</sequence>
</module>
</config>
Key Information:
name=”Training_HelloWorld”
: comes from our folder structure. In Magento, you can think of most underscores as a / in the directory structure.setup_version=”0.0.1”
: is the current version of our module. If we later wish to make changes to our database structure or other things, we can use upgrade scripts to handle any changes/migrations and Magento will automatically find the appropriately versioned upgrade scripts by comparing the version number in our etc/module.xml to the version number stored in the database in the setup_module table.<sequence>...</sequence>
: defines any dependencies for our module. Since we are giving a dependency of Magento_Catalog, our module will be loaded after Magento_Catalog is loaded.
Next, just as we did for our theme are need to create a registration.php file in HelloWorld so that Magento is able to locate our module.
HelloWorld/registration.php
<?php \Magento\Framework\Component\ComponentRegistrar::register(
\Magento\Framework\Component\ComponentRegistrar::MODULE, 'Training_HelloWorld',
__DIR__
);
As you can see this is very similar to our registration.php for our theme, the only difference being ComponentRegistrar::MODULE
instead of ComponentRegistrar::THEME
and of course the module name. All registration.php files in Magento follow this pattern. Even though our module isn’t currently doing anything, let’s go ahead and install it so any future changes will take effect.
Anytime you create a new module, you need to run php bin/magento setup:upgrade
. You can also use this command to cause Magento to run any upgrade scripts if you have upped the version number of a module. After this command runs, we can check to see if it was successfully installed by looking at app/etc/config.php
. You should never modify this file manually, but it has a list of all currently active modules. cat app/etc/config.php | grep Training_HelloWorld
should return 'Training_HelloWorld' => 1
. If it did, congratulations you just created your first custom module! If not, make sure you created the app/code/Training/HelloWorld/registration.php
and HelloWorld/etc/module.xml
files and then re-run the setup:upgrade
command.
Custom Controller
Our module is currently installed, but isn’t actually doing anything which isn’t very exciting. We will make a custom controller to handle displaying our custom page. We first need to specify some routes so Magento can direct the user to our new page. Routes for each module in Magento are defined as either frontend or adminhtml routes. These routes are then aggregated into one file for each type (adminhtml/frontend) by Magento. Since we are working with the frontend, we need to create a etc/frontend/routes.xml
file in our module
etc/frontend/routes.xml
<?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="Training_HelloWorld" />
</route>
</router>
</config>
The route id is an unique identifier for the route. The frontname is what the url is actually going to be for this route. URLs in Magneto are constructed like YOUR\_DOMAIN.com/<front\_name>/<controller\_name>/<action\_name>
. Our goal is to create a <your_domain>.com/helloworld/say/hi route which will display a hello world message we specify in a block.
Next we need to create a Controller for our route. Unlike Magento 1, where one controller file could store multiple actions, in Magento 2 one controller file maps to exactly one action located in the execute() function. Since Magento routes are constructed based on controller name and action name, we will create a folder structure like HelloWorld/Controller/Say/Hi.php (ModuleName/Controller_Name/Action_Name)
HelloWorld/Controller/Say/Hi.php
<?php
namespace Training\HelloWorld\Controller\Say;
class Hi extends \Magento\Framework\App\Action\Action
{
protected $_pageFactory;
public function __construct(
\Magento\Framework\App\Action\Context $context,
\Magento\Framework\View\Result\PageFactory $pageFactory)
{
$this->_pageFactory = $pageFactory;
return parent::__construct($context);
}
public function execute()
{
$page = $this->_pageFactory->create();
return $page;
}
}
We defined our frontname in our routes.xml as helloworld. Our controller name will be Say, and the controller action will be Hi. But we also need to create the template, layout, and block associated with this route. We’ll start with creating the block.
HelloWorld/Block/HelloWorld.php
namespace Training\HelloWorld\Block;
class HelloWorld extends \Magento\Framework\View\Element\Template {
public function getText() {
return 'Hello world!';
}
}
This is a very simple block that has a getText() method which returns our ‘Hello world!’ string. It’s time to put this block to work by using templates and layouts. We will create our layout in HelloWorld/view/frontend/layout/helloworld_say_hi.xml
. As you can see this layout file matches the same <frontname>_<controller_name>_<action_name>
pattern as the route, just with underscores instead of slashes.
HelloWorld/view/frontend/layout/helloworld\_say\_hi.xml
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<referenceContainer name="content">
<block class="Training\HelloWorld\Block\HelloWorld" name="helloworld_say_hi" template="Training_HelloWorld::helloworld.phtml" />
</referenceContainer>
</page>
In this layout file we are specifying what container will hold our blocks, and we are defining the template which will receive the block. Next we will create the template file
HelloWorld/view/frontend/templates/helloworld.phtml
<h1><?php echo $this->getText(); ?></h1>
This is a very simple template, it’s only one line! The $this refers to the block which we referenced in our layout (helloworld_say_hi.xml) file. So this will call the getText method of our HelloWorld block and print that information. Now if you visit <your_domain>.com/helloworld/say/hi you should see our hello world text! Try changing the text returned from the block and see what happens. For further practice, follow this same guide to create your own route with a different block, template, and layout. Try making a helloworld/say/bye route which uses different function on the same block to get a different message.
In this article we covered how to setup your own custom module, install it via the CLI, and creating custom blocks, layouts, and templates. In the next article we will cover installation and upgrade scripts related to this custom module and storing values in the database!