A Magento 2 controller is the entry point for a URL request on the storefront or admin. In this tutorial, you’ll build a complete storefront route for the module Magencode_MyModule
—including routing, a controller action, a block, a layout handle, and a PHTML template— so you can render dynamic content at a clean URL. This guide is Yoast-friendly: the keyphrase “Magento 2 controller” appears in the intro and in subheadings.
Why a Magento 2 Controller Matters for Frontend Pages
A Magento 2 controller receives the request, orchestrates business logic, and returns a result (usually a page). Pairing the controller with a block, layout XML, and a template lets you keep presentation clean and testable.
Module Structure
Your module should look like this:
Magencode/MyModule/ ├── Block │ └── Page │ └── Index.php ├── Controller │ └── Page │ └── Index.php ├── etc │ ├── frontend │ │ └── routes.xml │ └── module.xml ├── registration.php └── view └── frontend ├── layout │ └── magencode_page_index.xml └── templates └── page └── index.phtml
Each part plays a role: routing maps the URL, the controller action builds a page result, the block provides data to the view, the layout handle wires block ↔ template, and the template renders HTML.
Routing Configuration
Define a unique frontName and route ID for the storefront:
<!-- 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="magencode" frontName="magencode"> <module name="Magencode_MyModule"/> </route> </router> </config>
This exposes the URL pattern: /magencode/page/index
.
Module Declaration and Registration
<!-- 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="Magencode_MyModule"/> </config>
<?php // registration.php /** * Copyright © Magencode 2025 All rights reserved. * See COPYING.txt for license details. */ declare(strict_types=1); use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magencode_MyModule', __DIR__);
Building the Controller Action
Create a controller that returns a page result:
<?php // Controller/Page/Index.php /** * Copyright © Magencode 2025 All rights reserved. * See COPYING.txt for license details. */ declare(strict_types=1); namespace Magencode\MyModule\Controller\Page; use Magento\Framework\App\Action\HttpGetActionInterface; use Magento\Framework\Controller\ResultInterface; use Magento\Framework\View\Result\PageFactory; class Index implements HttpGetActionInterface { /** * @var PageFactory */ protected $resultPageFactory; /** * Constructor * * @param PageFactory $resultPageFactory */ public function __construct(PageFactory $resultPageFactory) { $this->resultPageFactory = $resultPageFactory; } /** * Execute view action * * @return ResultInterface */ public function execute(): ResultInterface { return $this->resultPageFactory->create(); } }
Note: Using
HttpGetActionInterface
clarifies this is a GET endpoint.
Providing Data with a Block (View Model Style)
Use a block to expose data and helper methods to the template:
<?php // Block/Page/Index.php /** * Copyright © Magencode 2025 All rights reserved. * See COPYING.txt for license details. */ declare(strict_types=1); namespace Magencode\MyModule\Block\Page; class Index extends \Magento\Framework\View\Element\Template { /** * Constructor * * @param \Magento\Framework\View\Element\Template\Context $context * @param array $data */ public function __construct( \Magento\Framework\View\Element\Template\Context $context, array $data = [] ) { parent::__construct($context, $data); } }
Wiring View Layers with a Layout Handle
Map the controller route to a layout handle and place the block into a container:
<!-- view/frontend/layout/magencode_page_index.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"> <body> <referenceContainer name="content"> <block name="page.index" class="Magencode\MyModule\Block\Page\Index" template="Magencode_MyModule::page/index.phtml"/> </referenceContainer> </body> </page>
Rendering HTML in the Template
Consume block methods for clean, testable markup:
<!-- view/frontend/templates/page/index.phtml --> Hello page/index.phtml
Enable the Module and Test the Route
From your Magento root:
php bin/magento setup:upgrade php bin/magento module:enable Magencode_MyModule php bin/magento cache:flush
Then visit:https://your-domain.com/magencode/page/index
You should see the content rendered by the block and template controlled by your controller route.
Conclusion: Clean Frontend Pages
By combining routing, a controller action, a block, a layout handle, and a template, you create a maintainable storefront page the Magento way. This Magento 2 controller pattern keeps concerns separated, improves testability, and sets you up for SEO-friendly, scalable content on your store.
Leave a Reply