Cómo crear un Frontend Controller en Magento2

Routers y Controllers en Magento2

La forma en la cual se define un controller en Magento 2 presenta una serie de cambios, tanto en la forma en que se define el router como en la creación del controller en si mismo.

Para continuar con los ejemplos, voy a crear un frontend controller para el módulo Barbanet_SampleModule, que hasta ahora no tenía funcionalidad alguna.

El primer paso será definir las ruta. En Magento2 se trabaja por separado el frontend y el adminhtml.

Dicho esto, vamos a crear dentro del directorio etc, una carpeta llamada frontend y allí un archivo llamado routes.xml, que tendrá la siguiente definición.

<?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="samplemodule" frontName="samplemodule">
            <module name="Barbanet_SampleModule" />
        </route>
    </router>
</config>

Si miramos el nodo route, veremos que:

  • ID es la identificación única de la ruta.
  • frontName es la url a la cual responderá en el frontend.
  • el nodo module indica, justamente, qué módulo manejará el request.

Luego, vamos a crear nuestro primer controller. Aquí tenemos dos opciones:

  • Crear un único controller con todo el código propio de un controller básico.
  • Crear un controller pero con una clase abstracta y que luego lo demás controllers que pueda tener el módulo extiendan de dicha clase.

En este ejemplo iré por el segundo enfoque, así que el siguiente paso será crear mi clase abstracta en el archivo Controller/index.php.

namespace Barbanet\SampleModule\Controller;
 
use Magento\Framework\App\RequestInterface;
 
/**
 * SampleModule index controller
 */
abstract class Index extends \Magento\Framework\App\Action\Action
{
 
    /**
     * @var \Magento\Framework\Translate\Inline\StateInterface
     */
    protected $inlineTranslation;
 
    /**
     * @var \Magento\Framework\App\Config\ScopeConfigInterface
     */
    protected $scopeConfig;
 
    /**
     * @param \Magento\Framework\App\Action\Context $context
     * @param \Magento\Framework\Translate\Inline\StateInterface $inlineTranslation
     * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
     */
    public function __construct(
        \Magento\Framework\App\Action\Context $context,
        \Magento\Framework\Translate\Inline\StateInterface $inlineTranslation,
        \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
    ) {
        parent::__construct($context);
        $this->inlineTranslation = $inlineTranslation;
        $this->scopeConfig = $scopeConfig;
    }
 
    /**
     * Dispatch request
     *
     * @param RequestInterface $request
     * @return \Magento\Framework\App\ResponseInterface
     * @throws \Magento\Framework\Exception\NotFoundException
     */
    public function dispatch(RequestInterface $request)
    {
        return parent::dispatch($request);
    }
}

Ahora creamos nuestro controller (el que será el encargado de responder a la petición real). En este caso, será el archivo Controller/Index/Index.php.

namespace Barbanet\SampleModule\Controller\Index;
 
class Index extends \Barbanet\SampleModule\Controller\Index
{
    /**
     * Show Sample Module main page
     *
     * @return void
     */
    public function execute()
    {
        $this->_view->loadLayout();
        $this->_view->renderLayout();
    }
}

Nótese que el código es bastante sencillo, ya que ha heredado de \Barbanet\SampleModule\Controller\Index.

Sólo como prueba (dado que aún falta código) voy a cometer el pecado de agregar un echo al comienzo del método. Limpiamos cache y en el frontend deberíamos ver esto.

Frontend controller sin layout en Magento2

Aquí puede verse que la url coincide con lo definido en routes.xml. Si miramos el código fuente, vamos a ver que efectivamente nuestro controller se ha hecho cargo del request.

Código fuente de nuestro controller sin layout en Magento2

El próximo paso será agregar el layout general a nuestro módulo. Aquí vamos a definir lo básico de la vista de nuestro módulo.

Creamos el directorio view/frontend/layout, y allí el archivo default.xml con este contenido.

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <head>
        <title>Barbanet Sample Module Default title</title>
    </head>
</page>

Una vez generado el archivo (y, por supuesto, luego de borrar cache), si refrescamos la URL veremos que nuestro controller hace uso del layout general.

Controller con layout default en Magento2

A diferencia de Magento 1 en donde usábamos un único archivo de layout y, eventualmente, definíamos un layout default y luego especificábamos el layout para una ruta específica, en Magento 2 tenemos un archivo default que será usado por el módulo y luego podemos crear un nuevo archivo por ruta.

Siguiendo con el ejemplo, voy a crear el archivo samplemodule_index_index dentro de view/frontend/layout.

<?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">
    <head>
        <title>Barbanet Sample Module Controller title</title>
    </head>
</page>

Como puede verse, he redefinido el título de la página. Si nuevamente probamos en el frontend, el cambio ya se ha aplicado.

Layout específico para un controller en Magento2

Al comienzo decía que en la definición del router, frontName sería el request al cual nuestro módulo respondería. Si quisiera usar una url más amigable para el usuario, simplemente debería cambiar ese valor.

Si a nuestro módulo le cambiara el frontName de «samplemodule» a «mi-modulo», veríamos que si refrescamos la pantalla, obtendremos una respuesta 404.

Error 404 en Magento2

Pero si apuntamos a nuestra nueva url/definición, volvemos a ver a nuestro controller respondiendo.

Controller con ruta amigable para el usuario en Magento2

Los ejemplos aquí mostrados se encuentran, como ya dije, en el módulo Barbanet_SampleModule en GitHub, y, específicamente, lo agregado en este post, en el release 2.1.0.