# 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" >
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://mapasculturais.gitbook.io/documentacao-para-desenvolvedores/formacao-para-desenvolvedores/controladores.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
