> 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/plugins.md).

# Plugins

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

## A Base do Plugin e como Habilitá-lo na Aplicação

Um plugin do Mapas Culturais é uma classe PHP que estende a classe abstrata [MapasCulturais\Plugin](https://github.com/mapasculturais/mapasculturais/blob/master/src/protected/application/lib/MapasCulturais/Plugin.php) e precisa implementar ao menos dois métodos: `_init` e `register`.&#x20;

O método `_init` é onde devem ser utilizados os hooks. O método `register` é onde deve ser registrado os metadados, controllers, etc.&#x20;

A classe Plugin deve residir dentro de uma pasta com o mesmo nome de seu namespace e, para que possa ser executado pela aplicação, deve estar na pasta [protected/application/plugins](https://github.com/mapasculturais/mapasculturais/tree/master/src/protected/application/plugins).

Abaixo, o exemplo mínimo de um plugin:

```php
// arquivo protected/application/plugins/MeuPlugin/Plugin.php
<?php
namespace MeuPlugin;

class Plugin extends \MapasCulturais\Plugin {
   function _init () {}

   function register () {}
}
```

Para habilitar o plugin na aplicação é necessário colocá-lo no array de plugins habilitados na configuração da aplicação:

```php
    'plugins' => [
        'EvaluationMethodTechnical' => ['namespace' => 'EvaluationMethodTechnical']],
        'EvaluationMethodSimple' => ['namespace' => 'EvaluationMethodSimple'],
        'EvaluationMethodDocumentary' => ['namespace' => 'EvaluationMethodDocumentary'],

        'MeuPlugin' => ['namespace' => 'MeuPlugin']
    ]
```

### Estrutura de Arquivos e Pastas

Os plugins podem conter as pastas `layouts`, `view`, `templates`, `pages` e `assets`, utilizadas quando se deseja sobrescrever algum arquivo do tema ou na implementação de uma nova funcionalidade que contenha alguma visão, parte de template ou asset,  etc.&#x20;

Além dessas cinco pastas, o tema pode conter as pastas `Cotrollers`, `Entities` e `Repositories` para abrigar seus controladores, entidades e repositórios, respectivamente. Ainda, o plugin pode conter um arquivo `db-updates.php` onde serão escritas as migrações do banco de dados, como criação de tabelas, colunas ou visões.

### Configurações de um Plugin

Os plugins podem precisar de configurações, as quais devem ser definidas no `__construct` da classe do plugin, como no exemplo abaixo, onde são definidas as configurações `min_items` e `max_items` com os valores padrão `3` e `10`:

```php
class Plugin extends \MapasCulturais\Plugin {

   function __construct($config = []) {
      $config += [
         'min_items' => 3,
         'max_items' => 10,
      ];

      parent::__construct($config);
   }
```

Para modificar o valor de uma configuração, deve-se informar na habilitação do plugin, da seguinte maneira:

```php
    'plugins' => [
         .
         .
         .
         'MeuPlugin' => [
            'namespace' => 'MeuPlugin',
            'config' => ['min_items' => 1, 'max_items' => 5]
         ]
    ]
```

## Utilizando Hooks

Como foi falado na [Introdução](/documentacao-para-desenvolvedores/formacao-para-desenvolvedores/introducao.md), através dos hooks, podemos modificar o comportamento padrão da aplicação. É uma das ferramentas mais utilizadas no desenvolvimento de plugins.&#x20;

Utilizando-se dos hooks, podemos manipular as permissões de um usuário sobre uma entidade, substituir um arquivo de template, criar um getter nas entidades, manipular o retorno do magic getter (em algumas entidades), criar uma rota, executar ação antes ou depois de eventos das entidades (save, insert, update e delete), e dentre muitas outras coisas.

Alguns exemplos:

```php
class Plugin extends \MapasCulturais\Plugin {
    function _init() {
        $app = App::i();

        // criando um getter: $agent->items
        $app->hook('entity(Agent).get(items)', function (&$value) {
            $value = is_array($value) ? $value : [1, 2, 3, 4];
        });

        // modificando o retorno de $agent->name
        $app->hook('entity(Agent).get(name)', function (&$value) {
            $value = strtoupper($value);
        });

        // criando uma rota GET /agent/meu-plugin
        $app->hook('GET(agent.meu-plugin)', function () use($app) {
            $this->requireAuthentication();
            $this->json([
                'agent' => $app->user->profile->simplify('id,name'),
                'items' => $app->user->profile->items
             ]);
        });


        $config = $this->config;

        // cria permissão que retorna true se o valor de 
        $app->hook('can(Agent.addItem)', function ($user, &$can) use($config) {
            $can = count($this->items) < $config['max_items'];
        });

        // cria permissão que retorna true se o valor de 
        $app->hook('can(Agent.deleteItem)', function ($user, &$can) use($config) {
            $can = count($this->items) > $config['min_items'];
        });
    }
```

## Registrando Metadados

A classe do plugin herda os seguintes métodos para auxiliar no registro de metadados e devem ser utilizados dentro do método `register`:

* `registerAgentMetadata`;
* `registerEventMetadata`;
* `registerOpportunityMetadata`;
* `registerProjectMetadata`;
* `registerRegistrationMetadata`;
* `registerSealMetadata`;
* `registerSpaceMetadata`;
* `registerUserMetadata`.

Exemplo de registro de metadado:

```php
class Plugin extends \MapasCulturais\Plugin {
    function register() {
        $max = $this->config['max_items'];
        $min = $this->config['min_items'];

        $this->registerAgentMetadata('metaDeItems', [
            'label' => 'meta de itens',
            'type' => 'number',
            'validations' => [
                "v::positive()" => "a meta de itens deve ser um número positivo"
            ]
        ]);

        $this->registerAgentMetadata('items', [
            'label' => 'items',
            'type' => 'array',
            // private => true,
            'serialize' => function ($value) {
                return json_encode($value);
            },
            'unserialize' => function ($value) {
                return json_decode($value);
            },
            'validations' => [
               // "required" => "items é requerido",
                "v::arrayVal()->length({$min}, {$max})" => "items deve ser um array com no mínimo {$min} e no máximo {$max} itens"
            ]
        ]);
    }
```

### Adicionando um Campo em uma Entidade

Para adicionar o campo de um metadado registrado na página de cadastro de uma entidade, primeiro crie um template part para o campo:

```php
// arquivo layouts/parts/meu-plugin/meta-de-items.php
<label> Meta de itens: 
   <span class="js-editable" 
      data-edit="metaDeItems"
      data-type="number"
      data-original-title="Meta de Itens" 
      data-emptytext="Informe a meta de itens">
      <?=$entity->metaDeItems?>
   </span>
</label>
```

Em seguida, inclua via hook a parte na página da entidade:

```php
class Plugin extends \MapasCulturais\Plugin {
    function _init() {
        $app = App::i();
        .
        .
        .
        $app->hook('template(agent.<<edit|single>>.tab-about):begin', function () {
            $entity = $this->controller->requestedEntity;
            $this->part('meu-plugin/meta-de-items.php', ['entity' => $entity]);
        });
```

## Criando um Controlador

Para criar um controlador, primeiro crie a classe na pasta `Controllers` do plugin:

```php
// arquivo Controllers/MeuPlugin.php
namespace MeuPlugin\Controllers;

class MeuPlugin extends \MapasCulturais\Controller {
   function GET_index () {
      $this->json('GET /');
   }
}
```

E, em seguida, registre o controlador na aplicação:

```php
class Plugin extends \MapasCulturais\Plugin {
   function register() {
      $app = App::i();
      $app->registerController('meu-plugin', Controllers\MeuPlugin::class);
   }
```

## Criando uma Entidade

Para criar uma entidade, deve-se criar a classe na pasta `Entities` do plugin, definindo todos os atributos e mapeamentos:

```php
// arquivo Entities\Item.php
namespace MeuPlugin\Entities;

use Doctrine\ORM\Mapping as ORM;

/**
 * Item 
 * 
 * @ORM\Table(name="item")
 * @ORM\Entity
 * @ORM\entity(repositoryClass="MapasCulturais\Repository")
 */
class Item extends \MapasCulturais\Entity {
   /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="SEQUENCE")
     * @ORM\SequenceGenerator(sequenceName="item_id_seq", allocationSize=1, initialValue=1)
     */
    protected $id;

    /**
     * @var string|object
     *
     * @ORM\Column(name="title", type="string", nullable=false)
     */
    protected $title;
}
```

### Criando uma Tabela

Para criar a tabela para a entidade definida acima, entre no container na pasta `/var/www/html/protected/tools` e execute o comando `./doctrine orm:schema-tool:update --dump-sql | grep {table}` para gerar o SQL de atualização do banco, onde *`{table}`* é o nome da tabela definida na classe:

```bash
# na pasta /var/www/html/protected/tools
./doctrine orm:schema-tool:update --dump-sql | grep item

     CREATE SEQUENCE item_id_seq INCREMENT BY 1 MINVALUE 1 START 1;
     CREATE TABLE item (id INT NOT NULL, title VARCHAR(255) NOT NULL, PRIMARY KEY(id));
```

Após obter o SQL, deve-se criar um arquivo `db-updates.php`:

```php
// arquivo db-updates.php
use function MapasCulturais\__exec;

return [
    'create table item' => function() {
        __exec("CREATE SEQUENCE item_id_seq INCREMENT BY 1 MINVALUE 1 START 1;");
        __exec("CREATE TABLE item (id INT NOT NULL, title VARCHAR(255) NOT NULL, PRIMARY KEY(id));");
    }
];
```

Execute o script `db-update.sh` ou reinicie o container:

```bash
# na pasta /var/www/scripts
./db-update.sh

Applying db update "UPDATING ENUM TYPES":
-------------------------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------------------------
.
.
.

Applying db update "create table item":
-------------------------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------------------------
```

## Criando EntityController para a Entidade Criada

O EntityController deve ter o mesmo nome que a entidade criada, então, crie o arquivo `Item.php` na pasta `Controllers` do plugin:

```php
// arquivo Controllers/Item.php
namespace MeuPlugin\Controllers;
use MapasCulturais\Traits\ControllerAPI;

class Item extends \MapasCulturais\Controllers\EntityController {
   use ControllerAPI;
}
```

Registre o controlador na aplicação:

```php
class Plugin extends \MapasCulturais\Plugin {
   function register() {
      $app = App::i();
      $app->registerController('meu-plugin', Controllers\MeuPlugin::class);
      $app->registerController('item', Controllers\Item::class);
   }
```
