# A Instituição — Modelagem de Dados

> Versão analisada: `v6`. Comparação com `v5` ao final.

---

## 1. Tabela `contents` — o conteúdo institucional

Fonte: `v6/database/migrations/2018_04_02_180433_create_contents_table.php`.
Estado atual verificado no banco `prefeituras_v5`.

| Coluna | Tipo | Null | Default | Papel no módulo |
|---|---|---|---|---|
| `id` | `integer` (serial) | não | `contents_id_seq` | PK. |
| `title` | `varchar(191)` | sim | — | Título exibido na listagem do CMS e no cabeçalho da página pública. |
| `text` | `text` | sim | — | Corpo em HTML (Summernote). Renderizado com `{!! !!}` (HTML não escapado). |
| `clicks` | `integer` | sim | — | Contador incrementado por `updateClicks()` a cada visita. |
| `highlights` | `boolean` | sim | — | Não usado pelo módulo (o destaque é `archives.highlight = 1`). |
| `galery` | `boolean` | sim | — | Quando `1`, a galeria de fotos é exibida. |
| `status` | `boolean` | sim | — | Não filtrado na leitura pública deste módulo. |
| `source`, `author`, `icon` | `varchar(191)` | sim | — | Não usados neste módulo. |
| `slug` | `varchar(191)` | sim | — | **Parte da URL pública** (`/a-instituicao/{slug}`). Gerado por `Str::slug`; colisão → `slug-<maxid+1>`. |
| `publication_date` | `timestamp(0)` | sim | — | Cast `datetime`. **Não é filtrado** na leitura de A Instituição. |
| `order` | `integer` | sim | — | Não usado neste módulo. |
| `category_contents_id` | `integer` | **não** | — | FK → `category_contents.id`. Para o módulo vale `6`. |
| `created_at`, `updated_at` | `timestamp(0)` | sim | — | `created_at` ordena a view SQL. |
| `deleted_at` | `timestamp(0)` | sim | — | Soft delete; a view SQL filtra `deleted_at IS NULL`. |
| `secretaria` | `varchar(191)` | sim | — | **Chave lógica** (slug da secretaria) usada por `Content::secretarias()` e pelo filtro de notícias do controller. |
| `carousel` | `boolean` | sim | — | Não usado. |
| `orgaos_id` | `integer` | sim | — | FK auto-referenciada `contents.id` — liga uma notícia a um órgão. |
| `position_id` | `bigint` | sim | — | FK para `positions`. |
| `acronym` | `varchar(191)` | sim | — | Não usado. |
| `layout` | `integer` | sim | — | Não usado. |
| `councilmen_id` | `integer` | sim | — | FK `councilmen.id`. |
| `linha_fina` | `varchar(300)` | sim | — | Não usado neste módulo. |
| `referencia` | `varchar(50)` | sim | — | Não usado neste módulo. |

**Índices e constraints relevantes**

| Nome | Tipo | Colunas |
|---|---|---|
| `contents_pkey` | PRIMARY KEY | `id` |
| `contents_category_contents_id_foreign` | FK | `category_contents_id` → `category_contents(id)` |
| `contents_orgaos_id_foreign` | FK | `orgaos_id` → `contents(id)` |

**Estado atual (consulta de leitura) — 5 conteúdos na categoria 6**

| id | title | slug | galery | clicks | secretaria | orgaos_id |
|---|---|---|---|---|---|---|
| 5 | Prefeito | prefeito | — | — | NULL | NULL |
| 6 | Vice-prefeito | vice-prefeito | — | — | NULL | NULL |
| 7 | Aspectos Gerais | aspectos-gerais | — | — | NULL | NULL |
| 8 | Localização | localizacao | — | — | NULL | NULL |
| 9 | Sobre | sobre | — | — | NULL | NULL |

Observação: `contents.secretaria` e `contents.orgaos_id` estão **100% NULL** em todo o
banco — os ganchos de vínculo notícia↔secretaria/órgão nunca foram preenchidos.
Isso explica por que o bloco "Notícias de &lt;título&gt;" raramente aparece.

## 2. Tabela `category_contents` — a categoria do módulo

Fonte: `v6/database/migrations/2018_04_01_180916_create_category_contents_table.php`.

| Coluna | Tipo | Null | Papel |
|---|---|---|---|
| `id` | `increments` | não | PK. `6` = A Instituição. |
| `name` | `varchar(191)` | não | "A Instituição". |
| `slug` | `varchar(191)` | não | `a-instituicao` — é a chave usada por código e pela view SQL. |
| `status_create` | `boolean` | sim | — |
| `quantidade_itens` | `integer` | sim | — |
| `order` | `integer` | sim | — |
| `layout` | `integer` | sim | — |
| `created_at`, `updated_at` | `timestamp` | sim | — |
| `deleted_at` | `timestamp` | sim | Soft delete. |
| `category_contents_id` | `integer` | sim | Auto-referência (pai). Para id 6 é **NULL** — é uma categoria raiz. |

**Registros que o módulo referencia**

| id | name | slug | category_contents_id | status |
|---|---|---|---|---|
| 4 | A cidade | a-cidade | NULL | true |
| 6 | A Instituição | a-instituicao | NULL | true |
| 7 | Secretarias | secretarias | NULL | true |
| 12 | Perguntas Frequentes | perguntas-frequentes | NULL | true |
| 32 | Órgãos | orgaos | NULL | true |

A v6 usa os ids 6, 7, 12 e 32 como **constantes implícitas** em
`v6/app/Http/Presenters/MenuItemPresenter.php:30-38` — acoplamento frágil
(documentado em `05-REGRAS-E-PORTABILIDADE.md`).

## 3. View SQL `a_instituicao`

Criada por `v6/database/migrations/2024_10_16_145149_create_view_to_a_instituicao.php`
e redefinida por `v6/database/migrations/2024_11_29_100640_update_view_a_instituicao.php`
(que acrescenta a coluna `slug`).

```sql
-- v6/database/migrations/2024_11_29_100640_update_view_a_instituicao.php
DB::statement("
  CREATE OR REPLACE VIEW a_instituicao AS
    SELECT row_number() OVER (ORDER BY c.created_at DESC) AS value,
    c.title AS titulo,
    c.id as id,
    c.galery AS galeria,
    c.slug AS slug
   FROM contents c
    JOIN category_contents cc ON cc.id = c.category_contents_id
  WHERE c.deleted_at IS NULL AND cc.slug = 'a-instituicao'
  ORDER BY c.created_at DESC;
");
```

| Coluna da view | Origem | Uso |
|---|---|---|
| `value` | `row_number()` | Número exibido na primeira coluna da grade (ordenação). |
| `titulo` | `contents.title` | Coluna "Título" da grade. |
| `id` | `contents.id` | Usado para editar/excluir. |
| `galeria` | `contents.galery` | Decide se o botão de galeria é exibido. |
| `slug` | `contents.slug` | Monta a URL `/a-instituicao/{slug}` do botão "Acessar Instituto". |

Características importantes:

- A view **não** expõe `status`, `publication_date`, `text`, `clicks` nem `secretaria`.
- `row_number()` reordena a cada consulta: não há chave estável — a coluna "value"
  não pode ser usada como identificador.
- O filtro `cc.slug = 'a-instituicao'` amarra a view ao **slug** da categoria: renomear
  o slug sem recriar a view esvazia a grade do CMS.
- A view é usada **apenas** pela grade do CMS. A leitura pública usa Eloquent direto.

Estado atual da view no banco: 5 linhas, na ordem `Prefeito`, `Vice-prefeito`,
`Aspectos Gerais`, `Localização`, `Sobre`.

## 4. Tabelas auxiliares e relações

| Tabela | Colunas usadas | Ligação com o módulo |
|---|---|---|
| `archives` | `id, title, description, path, extension, highlight, contents_id, order, view_pdf, type_size_id, width, height` | `contents_id` FK. `highlight = 1` → imagem de destaque (`Content::archiveHighlight()`); `highlight IS NULL` → galeria (`Content::archiveGallery()`). |
| `links` | `contents_id` (único), `link` | Vídeo do YouTube (`Content::linksUnique()`). |
| `menu_items` | `contents_id`, `category_contents_id`, `menus_id`, `name`, `type` | Itens de menu do portal que apontam para um conteúdo institucional. `menus_id = 1` é o menu do cabeçalho. |
| `menu_orders` | `id = 11`, `slug = 'a-instituicao'`, `clicks`, `modulo_id = 12` | Contador de cliques do menu, incrementado por `menuClicks()`. |
| `resources` | `id = 13`, `slug = 'a-instituicao'`, `name = 'A Instituição'`, `modulo_id = 12` | Entrada correspondente no CMS. |
| `modulos` | `id = 12`, `name = 'A Instituição'`, `slug = 'a-instituicao'`, `status = true` | Habilita (ou oculta) a entrada do menu lateral do CMS. |
| `configurations` | `key = 'layout'`, `value = 'modelo1'` | Define qual layout do portal renderiza a página. |
| `calendar_events` | `secretarias_id`, `orgaos_id`, `initial_date`, `final_date`, `status`, `title`, `slug` | Agenda exibida na lateral. |
| `categories_secretarias` | `secretarias_id` | Menu de abas internas da secretaria. |
| `roles` | `permissions` (JSON) | Papéis; o papel `administrador` tem todas as permissões `a-instituicao.*`. |

## 5. Relacionamentos Eloquent

Definidos em `v6/app/Http/Model/Prefecture/Content.php`:

```php
// v6/app/Http/Model/Prefecture/Content.php:63-86
public function archives()
{
    return $this->hasMany(Archive::class, 'contents_id', 'id');
}

public function secretarias()
{
    return $this->hasMany(Content::class, 'secretaria', 'slug');
}

public function orgaos()
{
    return $this->hasMany(Content::class, 'orgaos_id', 'id');
}
```

| Relação | Tipo | Chaves | Observação |
|---|---|---|---|
| `archives()` | hasMany | `contents_id` → `id` | Todas as mídias do conteúdo. |
| `secretarias()` | hasMany | `contents.secretaria` (FK) ↔ `contents.slug` (localKey) | **Chave local invertida**: liga a notícia ao *slug* da secretaria. Se o slug da secretaria mudar, o vínculo se rompe silenciosamente. |
| `orgaos()` | hasMany | `contents.orgaos_id` → `contents.id` | Auto-referenciada: liga notícia → órgão. |
| `categoryContents()` | belongsTo | `category_contents_id` → `id` | Categoria do conteúdo. |
| `linksUnique()` | hasOne | `contents_id` → `id` | Vídeo. |
| `archiveHighlight()` | hasOne + `byHighLight(1)` | `contents_id` | Imagem de destaque. |
| `archiveGallery()` | hasMany + `byHighLight(null)` | `contents_id` | Galeria, ordenada por `created_at DESC`. |

Casts: `publication_date` → `datetime`. `$dates = ['publication_date']`.
`$presenter = ContentPresenter::class` (trait `PresentableTrait`).

## 6. Migrations que afetam o módulo

| Migration | Efeito |
|---|---|
| `v6/database/migrations/2018_04_01_180916_create_category_contents_table.php` | Cria `category_contents` (id 6 = raiz do módulo). |
| `v6/database/migrations/2018_04_02_180433_create_contents_table.php` | Cria `contents` com `clicks`, `galery`, `slug`, `category_contents_id` + FK. |
| `v6/database/migrations/2022_04_19_084011_add_secretaria_to_contents_table.php` | Adiciona `contents.secretaria` (string) — chave lógica do vínculo com secretarias. |
| `v6/database/migrations/2023_05_19_114307_add_orgao_to_content.php` | Adiciona `contents.orgaos_id` (int, FK `contents.id`). |
| `v6/database/migrations/2024_10_16_145149_create_view_to_a_instituicao.php` | Cria a view `a_instituicao` **sem** a coluna `slug`. |
| `v6/database/migrations/2024_11_29_100640_update_view_a_instituicao.php` | Recria a view **com** `slug` (necessário para o botão "Acessar Instituto"). |
| `v6/database/migrations/2024_11_01_150206_create_modulos_table.php` | Cria `modulos` (id 12 = A Instituição) — base do controle de exibição do menu do CMS. |

Observação: **não existe** migration na v6 que renomeie a view `a_prefeitura` para
`a_instituicao`; a v6 apenas cria a view nova. Bancos migrados a partir da v5
mantêm a view `a_prefeitura` órfã.

## 7. Seeds que definem o módulo

| Seed | O que faz |
|---|---|
| `v6/database/seeds/CategoryContentTableSeeder.php:41-53` | `CategoryContent` id 6: se existir `a-prefeitura` (raiz) renomeia para `a-instituicao` / "A Instituição"; senão cria. |
| `v6/database/seeds/AjustesMigracao.php:25-39` | Renomeia o slug `o-instituto` → `a-instituicao` em `menu_orders` e `category_contents`; move o conteúdo `localização` para a categoria "A Instituição" e repara o `menu_item` "Localização". |
| `v6/database/seeds/MenuOrderSeeder.php:76-87` | `menu_orders` slug `a-prefeitura` → `a-instituicao` (`modulo_id = 12`); senão cria. |
| `v6/database/seeds/ResourcesTableSeeder.php:89-105` | `resources` slug `a-prefeitura` → `a-instituicao`, `name = 'A Instituição'`, `modulo_id = 12`; senão cria. |

O slug da categoria é, portanto, resultado de **seed**, não de migration — a
integridade entre `category_contents.slug`, `contents.category_contents_id` e a
view `a_instituicao` depende de o seed ser executado.

## 8. Comparação v5 → v6 (detalhada em `08-GAPS-E-DIVIDAS.md`)

| Objeto | v5 | v6 |
|---|---|---|
| Categoria | `category_contents.id = 6`, `slug = 'a-prefeitura'`, `name = 'A prefeitura'` | `id = 6`, `slug = 'a-instituicao'`, `name = 'A Instituição'` |
| View SQL | `a_prefeitura` (`value, titulo, id, galeria`) | `a_instituicao` (`value, titulo, id, galeria, slug`) |
| Migration da view | `v5/database/migrations/2023_04_10_145149_create_view_to_a_prefeitura.php` | `v6/database/migrations/2024_10_16_145149_create_view_to_a_instituicao.php` + `2024_11_29_100640_update_view_a_instituicao.php` |
| Coluna `slug` na view | adicionada por `v5/database/migrations/2026_03_09_000000_update_views_to_include_slug.php` | já presente na migration de criação |
| Rota nomeada | `a-prefeitura.index` | `a-instituicao.index` |
| Diretório de views | `informacoes-prefeitura/a-prefeitura/` | `informacoes-instituicao/a-instituicao/` |
| Componente de view | `components-a-prefeitura/` | `components-a-prefeitura/` (nome **mantido**) |
