> For the complete documentation index, see [llms.txt](https://mapasculturais.gitbook.io/documentacao-para-desenvolvedores/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://mapasculturais.gitbook.io/documentacao-para-desenvolvedores/formacao-para-desenvolvedores/controladores.md).

# Controladores

{% embed url="<https://www.youtube.com/watch?v=1w4GleTB6_c>" %}

Os controladores são instâncias de classes que estendem a classe abstrata [MapasCulturais\Controler](https://github.com/mapasculturais/mapasculturais/blob/master/src/protected/application/lib/MapasCulturais/Controller.php) e precisam ser registrados na aplicação.

```php
class Teste extends \MapasCulturais\Controller {
   function GET_index() {
      echo 'teste';
   }
}
```

## Registrando um Controlador

Para registrar um controlador, deve-se utilizar o método `App::registerController`, dando um id e a classe do controlador. Este registro deve ser feito no método `register` dos temas, plugins ou módulos.

Abaixo, o exemplo de registro do controller de classe `Teste` com o id `teste`:

```php
   function register() {
      $app = App::i();

      $app->registerController('teste', Controllers\Teste::class);
   }
```

Registrado o controller, é possível acessar a rota padrão (GET\_index) pelo id registrado: `http://localhost/teste/`.

## Criando uma Rota (GET, POST, PUT, PATCH, DELETE, ALL)

Para criar uma rota basta criar um método utilizando o tipo de requisição (`GET_`, `POST_`, `PUT_`, `PATCH_` e `DELETE_`), como prefixo. Para aceitar qualquer tipo de requisição, deve-se utilizar o prefixo `ALL_`.

```php
class Teste extends \MapasCulturais\Controller {
   // POST http://localhost/teste/
   function POST_index() { }

   // GET http://localhost/teste/meuTeste/
   function GET_meuTeste() { }

   // PUT http://localhost/teste/meuTeste/
   function PUT_meuTest() { }

   // GET|POST|PUT|PATCH|DELETE http://localhost/teste/outroTeste/
   function ALL_outroTeste() { }
```

## Retornando JSON, Erro ou Renderizando Visão

### Retornando JSON

A classe abstrata [MapasCulturais\Controler](https://github.com/mapasculturais/mapasculturais/blob/master/src/protected/application/lib/MapasCulturais/Controller.php) contém o seguinte método utilizado para retornar JSONs, este pode ser utilizado em qualquer dos controllers:

```php
    public function json($data, $status = 200){
        $app = App::i();
        $app->contentType('application/json');
        $app->halt($status, json_encode($data));
    }
```

Então, para retornar um JSON basta invocar este método passando como primeiro parâmetro os dados a serem codificados como JSON. E como segundo parâmetro opcional, o código http de retorno da requisição.

```php
   /* GET http://localhost/teste/objeto/ 
    * retorna status 200
    * {
         "id": 13,
         "name": "Nome"
      }
    */
    function GET_objeto() {
      $objeto = (object) [
         'id' => 13,
         'name' => 'Nome'
      ];
      $this->json($objeto);
    }
```

### Retornando Erro

* **404**: Para retornar um erro 404, deve-se utilizar o método `App::pass`.

```php
   function GET_objetoInexistente() {
      $app = App::i();
      $app->pass();
   }
```

* **JSON de erro**: Para retornar um erro em formato JSON, utiliza-se o método `errorJson` do controller, informando os dados a serem codificados e o código http de retorno da requisição, como no exemplo a seguir:

```php
   /* GET http://localhost/teste/erroDeValidacao/ 
    * retorna status 400
    * {
        "error": true,
        "data": {
          "name": ["o campo "nome" é obrigatório"],
          "shortDescription":["o campo "descrição curta" é obrigatório"]
        }
      }
    */
   function GET_erroDeValidacao () {
      $errors = [
         'name' => ['o campo "nome" é obrigatório'],
         'shortDescription' => ['o campo "descrição curta" é obrigatório']
      ];

      $this->errorJson($errors, 400);
   }
```

* **Erro customizado**: Para retornar um erro customizado, utiliza-se o método `halt` da aplicação:

```php
   function GET_permissaoNegada() {
      $app = App::i();
      $app->halt(403, "Permissão Negada!");
   }
```

### Renderizando uma Visão

As visões são arquivos de template que ficam dentro da pasta `views/{$controller_slug}` do tema, módulo ou plugin, onde $controller\_slug, por padrão, é o id do controller.

Para renderizar uma visão, utiliza-se o método `render` do controlador, que deve receber como primeiro parâmetro obrigatório o nome do arquivo de template e, como segundo parâmetro opcional, um array com as variáveis a serem enviadas à visão, como no exemplo a seguir:

```php
function GET_lista () {
   $items = [
      'Primeiro item da lista',
      'Segundo item da lista',
      'Terceiro item da lista'
   ];
   $this->render('lista',['items' = $items]);
}
```

O arquivo de visão `views/lista.php`:

```php
<?php foreach($items as $item): ?>
   <?= $item ?>
<?php endforeach; ?>
```

## EntityController

### Endpoints

O [EntityController](https://github.com/mapasculturais/mapasculturais/blob/master/src/protected/application/lib/MapasCulturais/EntityController.php) é uma classe abstrata que implementa uma série de métodos e endpoints para manipular as entidades:

* `POST_index`: Endpoint para criação de entidades;
* `PUT_single`: Endpoint para *substituição* da entidade (na prática acontece um update na entidade);
* `PATCH_single`: Endpoint para atualização da entidade;
* `DELETE_single`: Deleta a entidade;
* `POST_validateProperties`: Valida os campos enviados, sem considerar os campos não enviados;
* `POST_validateEntity`: Valida a entidade completa, considerando inclusive os campos não enviados;
* `GET_propertiesMetadata`: Retorna JSON com os metadados (descrição) dos campos da entidade;
* `GET_single`: Renderiza a *single* (página de visualização) da entidade;
* `GET_edit`: Renderiza a página de edição das entidades.

### Requested Entity

Se houver um parâmetro id na URL após o nome da rota, como nos exemplos abaixo, é possível pedir a entidade requisitada para o controlador:

* <http://localhost/agent/dumpEntity/33>;
* <http://localhost/agent/dumpEntity/id:33>.

```php
class Agent extends EntityController {
   function GET_dumpEntity() {
      $entity = $this->requestedEntity; // ou $this->getRequestedEntity()

      if (!$entity) { // se não encontrou a entidade, 404
         App::i()->pass();
      }

      echo $entity->id; // imprime o id 33
   }
}
```

## Requerendo Autenticação e Checagem de Permissão

A grande maioria das rotas da aplicação requerem que o usuário esteja autenticado e em muitas há uma checagem de permissão do usuário. As exceções costumam ser as páginas onde há somente a leitura de informações públicas.

### Requerendo Autenticação

Para requerer a autenticação basta chamar o método `requireAuthentication` do controlador, normalmente, na primeira linha do método, como no exemplo a seguir:

```php
   function GET_dumpEntity() {
      $this->requireAuthentication();
   }
```

Este método fará um redirect do usuário não autenticado para a página de login, retornando após a autenticação para a rota original. Caso o usuário já esteja autenticado o método não fará ação alguma.

### Checando a Permissão em uma Entidade

É comum que mesmo o usuário estando autenticado, ele não tenha permissão para realizar uma operação em uma entidade. Por exemplo, um usuário não pode deletar um agente de outro usuário.&#x20;

Como o sistema de permissões é implementado no nível do modelo, basta chamar o método `checkPermission` da entidade, informando a permissão a ser verificada.

Abaixo, a implementação completa do endpoint `dumpEntity` com requerimento de autenticação e com checagem de permissão:

```php
class Agent extends EntityController {
   function GET_dumpEntity() {
      $this->requireAutentication();

      $entity = $this->requestedEntity; // ou $this->getRequestedEntity()

      if (!$entity) { // se não encontrou a entidade, 404
         App::i()->pass();
      }

      $entity->checkPermission('viewPrivateData');

      echo $entity->id; // imprime o id 33
   }
}
```

Neste exemplo, se o usuário não tiver permissão para ver os dados privados da entidade (método `canUserViewPrivateData` definido no trait EntityMetadata), será retornado um erro 403 para o usuário.

## Traits de controladores

* [ControllerAPI](https://github.com/mapasculturais/mapasculturais/blob/master/src/protected/application/lib/MapasCulturais/Traits/ControllerAPI.php): Implementa os endpoints da API de busca do MapasCulturais (endpoints `/api/{entity}/find`, `/api/{entity}/findOne` e `/api/{entity}/describe`);
* [ControllerAPINested](https://github.com/mapasculturais/mapasculturais/blob/master/src/protected/application/lib/MapasCulturais/Traits/ControllerAPINested.php): Implementa o endpoint `/api/{entity}/getChildrenIds`. Utilizado pelos controladores de entidades que utilizam o trait `EntityNested`;
* [ControllerAgentRelation](https://github.com/mapasculturais/mapasculturais/blob/master/src/protected/application/lib/MapasCulturais/Traits/ControllerAgentRelation.php): Implementa os endpoints para manipulação dos agentes relacionados. Utilizado pelos controladores de entidades que utilizam o trait `EntityAgentRelation`;
* [ControllerArchive](https://github.com/mapasculturais/mapasculturais/blob/master/src/protected/application/lib/MapasCulturais/Traits/ControllerArchive.php): Implementa os endpoints para arquivar e desarquivar entidades. Utilizado pelos controladores de entidades que utilizam o trait `EntityArchive`;
* [ControllerChangeOwner](https://github.com/mapasculturais/mapasculturais/blob/master/src/protected/application/lib/MapasCulturais/Traits/ControllerChangeOwner.php): Implementa os endpoints para mudança de propriedade (ownerAgent) das entidades. Utilizado pelos controladores de entidades que utilizam o trait `EntityOwnerAgent`;
* [ControllerDraft](https://github.com/mapasculturais/mapasculturais/blob/master/src/protected/application/lib/MapasCulturais/Traits/ControllerDraft.php): Implementa os endpoints para a funcionalidade entidades em rascunho. Utilizado pelos controladores de entidades que utilizam o trait `EntityDraft`;
* [ControllerMetaLists](https://github.com/mapasculturais/mapasculturais/blob/master/src/protected/application/lib/MapasCulturais/Traits/ControllerMetaLists.php): Implementa os endpoints para manipulação dos MetaLists. Utilizado pelos controladores de entidades que utilizam o trait `EntityMetalist`;
* [ControllerOpportunities](https://github.com/mapasculturais/mapasculturais/blob/master/src/protected/application/lib/MapasCulturais/Traits/ControllerOpportunities.php): Implementa o endpoint para criação de oportunidade. Utilizado pelos controladores de entidades que utilizam o trait `EntityOpportunities`;
* [ControllerSealRelation](https://github.com/mapasculturais/mapasculturais/blob/master/src/protected/application/lib/MapasCulturais/Traits/ControllerSealRelation.php): Implementa os endpoints para manipulação dos selos relacionados às entidades. Utilizado pelos controladores de entidades que utilizam o trait `EntitySeals`;
* [ControllerSoftDelete](https://github.com/mapasculturais/mapasculturais/blob/master/src/protected/application/lib/MapasCulturais/Traits/ControllerSoftDelete.php): Implementa os endpoints para a funcionalidade de lixeira das entidades. Utilizado pelos controladores de entidades que utilizam o trait `EntitySoftDelete`;
* [ControllerTypes](https://github.com/mapasculturais/mapasculturais/blob/master/src/protected/application/lib/MapasCulturais/Traits/ControllerTypes.php): Implementa os endpoints `/api/{entity}/getTypes` e `/api/{entity}/getTypeGroups`. Utilizado pelos controladores de entidades que utilizam o trait `EntityTypes`;
* [ControllerUploads](https://github.com/mapasculturais/mapasculturais/blob/master/src/protected/application/lib/MapasCulturais/Traits/ControllerUploads.php): Implementa o upload de arquivos. Utilizado pelos controladores de entidades que utilizam o trait `EntityFiles`.

## ControllerUploads

Para utilizar o trait `ControllerUploads` é necessário registrar os grupos de arquivos utilizando o método `App::registerFileGroup`.&#x20;

O Mapas Culturais registra, por padrão, quatro grupos para as entidades principais: `downloads`, `header`, `avatar` e `gallery`.

```php
$this->registerFileGroup('agent', new Definitions\FileGroup(
      'foto-do-documento', // nome do grupo
      ['^image/(jpeg|png)$'], // validação do mime type
      'O arquivo enviado não é uma imagem válida.', // mensagem de erro quando a validação não passa
      true // se é um grupo de um único arquivo
   )
);
```

Registrado o grupo, pode-se fazer upload do arquivo utilizando um campo file com o nome registrado:

```markup
<input type="file" name="foto-do-documento" >
```
