Tipos de Controllers no Magento 2
Tags: php, magento2, braziliandevs, tutorial
Published: 2021-11-16 12:39:54

## Contextualizando

### O que é um controlador?

Um controlador faz parte da camada de controle do padrão de arquitetura de software MVC (*Model*-*Controller*-*View*).

Um controlador é responsável por intermediar as requisições enviadas pela camada de visualização (*View*) com as respostas fornecidas pela camada de modelo (*Model*), processando os dados que o usuário informou e repassando para outras camadas.

---

## Código

*Controllers* da loja devem seguir a estrutura de pastas `\{Vendor}\{Module}\Controller\{ControllerDirectory}\{ControllerName}` e implementar a interface `\Magento\Framework\App\ActionInterface`, que obriga a classe a implementar o método `execute()`.
> Caso esteja utilizando uma versão anterior a 2.4.0 do magento, a interface `\Magento\Framework\App\ActionInterface` não é estará disponível, então deverá estender a classe `\Magento\Framework\App\Action\Action` e ajustar o construtor com suas heranças.

### Json

As classes *Controllers* (controladores) podem retornar objetos Json com o tipo *mime* na resposta.

```php
<?php

namespace {Vendor}\{Module}\Controller\{ControllerDirectory};

use Magento\Framework\Controller\Result\JsonFactory;
use Magento\Framework\App\ActionInterface;
use Magento\Framework\Controller\Result\Json;

class {ControllerName} implements ActionInterface
{
    public function __construct(
        protected JsonFactory $jsonResultFactory
    ) {
    }

    public function execute(): Json
    {
        $data = [
            '{param}' => '{value}',
            'message' => __('{Message to return}'),
            'error' => false
        ];

        $jsonResult = $this->jsonResultFactory->create();
        $jsonResult->setData($data);

        return $jsonResult;
    }
}
```

### Raw

As classes *Controllers* (controladores) podem ter retorno do tipo *string* ou conteúdos binários.

```php
<?php

namespace {Vendor}\{Module}\Controller\{ControllerDirectory};

use Magento\Framework\Controller\Result\RawFactory;
use Magento\Framework\App\ActionInterface;
use Magento\Framework\Controller\Result\Raw;

class {ControllerName} implements ActionInterface
{
    public function __construct(
        protected RawFactory $rawResultFactory
    ) {
    }

    public function execute(): Raw
    {
        $rawResult = $this->rawResultFactory->create();
        $rawResult->setContents('{Custom message}');

        return $rawResult;
    }
}
```

### Redirect

As classes *Controllers* (controladores) podem ter uma *string* com a rota de redirecionamento para outra págna como retorno.

```php
<?php

namespace {Vendor}\{Module}\Controller\{ControllerDirectory};

use Magento\Framework\Controller\Result\RedirectFactory;
use Magento\Framework\App\ActionInterface;
use Magento\Framework\Controller\Result\Redirect;

class {ControllerName} implements ActionInterface
{
    public function __construct(
        protected RedirectFactory $redirectResultFactory
    ) {
    }

    public function execute(): Redirect
    {
        $redirectResult = $this->redirectResultFactory->create();
        $redirectResult->setPath('{router_name}/{controller_directory}/{className}')
            ->setHttpResponseCode(301);

        return $redirectResult;
    }
}
```

### Forward

As classes *Controllers* (controladores) podem ter retorno com um encaminhamento interno para outro controlador sem que o usuário faça uma segunda solicitação.

```php
<?php

namespace {Vendor}\{Module}\Controller\{ControllerDirectory};

use Magento\Framework\Controller\Result\ForwardFactory;
use Magento\Framework\App\ActionInterface;
use Magento\Framework\Controller\Result\Forward;

class {ControllerName} implements ActionInterface
{
    public function __construct(
        protected ForwardFactory $forwardResultFactory
    ) {
    }

    public function execute(): Forward
    {
        $forwardResult = $this->forwardResultFactory->create();
        $forwardResult->setModule('{module_name}')
            ->setController('{controller_directory}')
            ->forward('{controller_name}')
            ->setParams(['{param}' => '{value}']);

        return $forwardResult;
    }
}
```

A convenção é que no método `setModule()` seja definido o nome do diretório do módulo, o `setController()` deve ser o nome do diretório dentro do diretório *Controller* do módulo e o `forward()` é o nome da classe que irá ser renderizada através do controller forward.
É possível passar parâmetros através de um *array*. Caso o nome do módulo não seja seja fornecido, o Magento irá assumir que será o módulo presente no controller do forward.

### Page

As classes *Controllers* (controladores) podem ter retorno em forma de HTML com um arquivo *layout* vinculado ao controlador que renderizará a página.

```php
<?php

namespace {Vendor}\{Module}\Controller\{ControllerDirectory};

use Magento\Framework\View\Result\PageFactory;
use Magento\Framework\App\ActionInterface;
use Magento\Framework\View\Result\Page;

class {ControllerName} implements ActionInterface
{
    public function __construct(
        protected PageFactory $resultPageFactory
    ) {
    }

    public function execute(): Page
    {
        return $this->resultPageFactory->create();
    }
}
```

### Layout

As classes *Controllers* (controladores) podem ter retorno em forma de HTML e pode ser usado como o Page. Além disso o Layout também podem renderizar o HTML de blocos específicos.

```php
<?php

namespace {Vendor}\{Module}\Controller\{ControllerDirectory};

use Magento\Framework\View\Result\LayoutFactory;
use Magento\Framework\App\ActionInterface;
use Magento\Framework\View\Result\Layout;

class {ControllerName} implements ActionInterface
{
    public function __construct(
        protected LayoutFactory $resultLayoutFactory
    ) {
    }

    public function execute(): Layout
    {
        return $this->resultLayoutFactory->create()
            ->getLayout()
            ->getBlock('{block.name}')
            ->toHtml();
    }
}
```

### Admin

*Controllers* do admin devem seguir a estrutura de pastas `\{Vendor}\{Module}\Controller\Adminhtml\{ControllerDirectory}\{ControllerName}` e estender a classe abstrata `\Magento\Backend\App\AbstractAction`, que obriga a classe a implementar o método `execute()`.

```php
<?php

namespace {Vendor}\{Module}\Controller\Adminhtml\{ControllerDirectory};

use Magento\Framework\View\Result\PageFactory;
use Magento\Backend\App\AbstractAction;
use Magento\Backend\App\Action\Context;
use Magento\Framework\View\Result\Page;

class {ControllerName} extends AbstractAction
{
    public function __construct(
        Context $context,
        protected PageFactory $resultPageFactory
    ) {
        parent::__construct($context);
    }

    public function execute(): Page
    {
        return $this->resultPageFactory->create();
    }
}
```

O exemplo mostra como declarar um *controller* do tipo *Page* na área administrativa no Magento, mas pode ser utilizado qualquer um dos tipos de *controllers*.

---

## Finalizando

> Valores entre chaves (`{test}`) devem ser alterados na implementação do código.

### Habilitando as alterações

Execute o comando PHP para gerar a configuração das injeções de dependência e todas as classes ausentes que precisam ser geradas (proxys, interceptors, etc).

```bash
php bin/magento setup:di:compile
```

### Diretórios e Arquivos

Segue a a lista de diretórios e arquivos que devem ser criados.

```
- app/
  - code/
    - {Vendor}/
        - {Module}/
          - Controller/
            - {ControllerDirectory}/
              - {ControllerName}.php
          - etc/
            - module.xml
          - registration.php
          - composer.json
```