# Secretarias — Modelagem de Dados

> Fonte: schema real do banco `prefeituras_v5` (PostgreSQL 10) e migrations de `v6/database/migrations/`.
> Tipos abaixo copiados de `psql \d <tabela>`.

---

## 1. Visão relacional

```
category_contents (id=7, slug='secretarias')
        │
        │ category_contents_id (FK, NOT NULL)
        ▼
contents ─────────────────────────────────────────────────────────────┐
  id, title, slug, acronym, text, galery, status, clicks,             │
  publication_date, deleted_at,                                      │
  secretaria (varchar)  ← slug da secretaria dona da notícia         │
  orgaos_id (FK contents.id)                                         │
        │                                                            │
        │ secretarias_id (FK contents.id)                            │
        ▼                                                            │
categorias_secretarias (departamento)                                │
  id, name, order, deleted_at                                        │
        │                                                            │
        │ categoria_id (FK categorias_secretarias.id)                │
        ▼                                                            │
paginas_secretarias (página)                                         │
  id, title, text, order, deleted_at                                  │
        │                                                            │
        │ reference_id (text, sem FK!)                               │
        ▼                                                            │
archives ─────────────────────────────────────────────────────────────┘
  id, path, name, title, description, type, extension, highlight,
  type_size_id (FK types_sizes.id), width, height, contents_id

calendar_events.secretarias_id (FK contents.id)  → eventos da secretaria
menu_items.contents_id (FK contents.id) + category_contents_id = 7   → menu do portal
resources.slug IN ('secretarias','secretarias-categorias','secretarias-paginas'), modulo_id = 18
menu_orders.slug = 'secretarias', modulo_id = 18
modulos.id = 18, slug = 'secretarias', status = false
```

## 2. Tabela `contents` (armazenamento das secretarias)

Secretaria = conteúdo da categoria 7. Colunas relevantes (schema completo em
`v6/database/migrations/2018_04_02_180433_create_contents_table.php` + alterações):

| Coluna | Tipo | Null | Default | Uso no módulo |
|---|---|---|---|---|
| `id` | `integer` | não | `nextval('contents_id_seq')` | PK; referenciada por `categorias_secretarias.secretarias_id`, `calendar_events.secretarias_id`, `menu_items.contents_id` |
| `title` | `varchar(191)` | sim | — | Nome da secretaria (ex.: "Secretaria da Saúde") |
| `slug` | `varchar(191)` | sim | — | URL `/a-instituicao/secretarias/{slug}`; gerado por `Str::slug` |
| `acronym` | `varchar(191)` | sim | — | **Sigla** — campo do formulário de secretaria (migration `v6/database/migrations/2025_04_23_112404_add_acronym_to_contents.php`) |
| `text` | `text` | sim | — | Corpo HTML (Summernote) |
| `galery` | `boolean` | sim | — | Ativa a galeria de imagens (`1` = sim) |
| `status` | `boolean` | sim | — | Publicação |
| `clicks` | `integer` | sim | — | Contador++; exposto como `interacoes` na API |
| `publication_date` | `timestamp(0)` | sim | — | Filtro do bloco de notícias |
| `category_contents_id` | `integer` | **não** | — | FK → `category_contents.id` (= 7 para secretarias) |
| `secretaria` | `varchar(191)` | sim | — | **Chave do vínculo notícia↔secretaria**: guarda o `slug` da secretaria (não o id) |
| `orgaos_id` | `integer` | sim | — | Espelho para Órgãos (FK `contents.id`) — ver `../orgaos/` |
| `deleted_at` | `timestamp(0)` | sim | — | Soft delete; a view `secretarias` filtra `deleted_at IS null` |

FK ativas em `contents`: `contents_category_contents_id_foreign`, `contents_orgaos_id_foreign`
(`→ contents.id`), `contents_councilmen_id_foreign`.

## 3. Tabela `categorias_secretarias` (departamentos)

Migration: `v6/database/migrations/2023_03_29_104129_create_categorias_secretarias_table.php`

```php
Schema::create('categorias_secretarias', function (Blueprint $table) {
    $table->increments('id');
    $table->string('name');
    $table->integer('secretarias_id');
    $table->integer('order')->nullable();
    $table->softDeletes();
    $table->timestamps();

    $table->foreign('secretarias_id')
        ->references('id')
        ->on('contents');
});
```

| Coluna | Tipo | Null | Default | Observação |
|---|---|---|---|---|
| `id` | `integer` | não | seq | PK |
| `name` | `varchar(191)` | **não** | — | "Nome do departamento" (rótulo do formulário) |
| `secretarias_id` | `integer` | **não** | — | FK → `contents.id` (a secretaria) |
| `order` | `integer` | sim | `NULL` | Preenchido **apenas** por `MenuSecretaryController@orderCategory` (1..N) |
| `deleted_at` | `timestamp(0)` | sim | — | Soft delete (`SoftDeletes` no model) |
| `created_at` / `updated_at` | `timestamp(0)` | sim | — | Laravel timestamps |

Estado atual: **0 linhas**.

## 4. Tabela `paginas_secretarias` (páginas)

Migration: `v6/database/migrations/2023_03_29_104149_create_paginas_secretarias_table.php`

```php
Schema::create('paginas_secretarias', function (Blueprint $table) {
    $table->increments('id');
    $table->string('title');
    $table->text('text');
    $table->integer('categoria_id');
    $table->integer('order')->nullable();
    $table->softDeletes();
    $table->timestamps();

    $table->foreign('categoria_id')
        ->references('id')
        ->on('categorias_secretarias');
});
```

| Coluna | Tipo | Null | Default | Observação |
|---|---|---|---|---|
| `id` | `integer` | não | seq | PK; é o `reference_id` gravado em `archives` |
| `title` | `varchar(191)` | **não** | — | Título da página |
| `text` | `text` | **não** | — | Corpo HTML |
| `categoria_id` | `integer` | **não** | — | FK → `categorias_secretarias.id` |
| `order` | `integer` | sim | `NULL` | **Nunca usado** — a ordenação é do departamento, não da página |
| `deleted_at` | `timestamp(0)` | sim | — | Soft delete |

Estado atual: **0 linhas**.

## 5. Tabela `archives` (destaques e anexos das páginas)

Colunas usadas por `PagesSecretaryController::insertImage()` / `insertArchive()`:

| Coluna | Tipo | Valor gravado pelo módulo |
|---|---|---|
| `reference_id` | `text` | `id` da página (`paginas_secretarias.id`) — **sem FK declarada** |
| `path` | `varchar(300)` | `content/secretarias_paginas/<id>/destaque/<nome>` ou `.../arquivos/<nome>` |
| `name` / `title` | `varchar(300)` | `FileTrait::getFileName($file)` (`uniqid` + 5 chars + extensão) |
| `description` | `text` | texto informado pelo operador no anexo (ou `null`) |
| `type` | `varchar(191)` | `$file->getMimeType()` |
| `extension` | `varchar(191)` | `$file->getClientOriginalExtension()` |
| `highlight` | `boolean` | `true` = imagem de destaque; `false` = arquivo para download |

Relacionamentos no model `App\Http\Model\Prefecture\PaginasSecretaria`:

```php
public function highlight()
{
    return $this->hasOne(Archive::class, 'reference_id', 'id')->where('highlight', true);
}

public function archives()
{
    return $this->hasMany(Archive::class, 'reference_id', 'id')->where('highlight', false);
}
```

⚠ `reference_id` é `text` — a comparação com `id` (integer) funciona no PostgreSQL por
cast implícito, mas impede a criação de índice padrão. Na reconstrução, migrar para
`bigint` com FK real.

## 6. Views SQL

O legado materializa listagens como `CREATE VIEW`. Este módulo usa três.

### 6.1 `secretarias`

Origem: `v6/database/migrations/2023_03_31_101031_create_view_to_secretarias.php`
(sem `slug`) e `v6/database/migrations/2024_11_29_085508_update_view_secretarias.php`
(com `slug`).

```sql
CREATE OR REPLACE VIEW secretarias
AS SELECT row_number() OVER (ORDER BY c.created_at ASC) 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  = 'secretarias'
ORDER by c.created_at desc;
```

| Coluna da view | Origem |
|---|---|
| `value` | `row_number()` — usado como coluna "ID" exibido na grade (1..N, **não** o id real) |
| `titulo` | `contents.title` |
| `id` | `contents.id` |
| `galeria` | `contents.galery` |
| `slug` | `contents.slug` (adicionado em 2024-11-29; usado pelo botão "Acessar secretaria") |

⚠ **Inconsistência verificada:** o `row_number()` é calculado com `ORDER BY c.created_at ASC`
enquanto o `ORDER BY` final da view é `c.created_at desc`. O número exibido na coluna "ID"
da grade do CMS **não acompanha** a ordem visual das linhas.

### 6.2 `secretarias_categorias`

Origem: `v6/database/migrations/2023_03_30_132359_create_view_to_secretarias_categorias.php`

```sql
CREATE OR REPLACE VIEW public.secretarias_categorias
AS SELECT row_number() OVER (ORDER BY cs.created_at) AS value,
    cs.name,
    cs.id,
    c.title as nome_secretaria
FROM categorias_secretarias cs
JOIN contents c ON c.id = cs.secretarias_id
WHERE cs.deleted_at IS NULL
ORDER BY cs.created_at DESC;
```

### 6.3 `secretarias_paginas`

Origem: `v6/database/migrations/2023_03_30_132138_create_view_to_secretarias_paginas.php`

```sql
CREATE OR REPLACE VIEW secretarias_paginas
AS SELECT row_number() OVER (ORDER BY ps.created_at ASC) AS value,
    ps.title as title,
    ps."text" as texto,
    cs."name" as categoria,
    ps.id as id,
    c.title as nome_secretaria
FROM paginas_secretarias ps
    JOIN categorias_secretarias cs ON cs.id = ps.categoria_id
    JOIN contents c ON c.id = cs.secretarias_id
WHERE ps.deleted_at IS NULL
ORDER by ps.created_at desc;
```

⚠ `texto` (o corpo HTML) é trazido inteiro para o PHP em cada carregamento da grade,
mesmo sem uso na tela (a coluna exibida é `title`).

### 6.4 Quem consome as views

| View | Consumidor | Consulta |
|---|---|---|
| `secretarias` | `DatatableController@get_secretarias` | `DB::table('secretarias')->get()` — **sem limite** |
| `secretarias` | `CategorySecretaryController::__construct` | `DB::table('secretarias')->get(['id','titulo as name'])` — só para o `<select>` |
| `secretarias` | `MenuSecretaryController::__construct` | idem (drop-down) |
| `secretarias` | `MenuSecretaryController@index` | `DB::table('secretarias')->where('id',$id)->first()` |
| `secretarias_categorias` | `DatatableController@get_secretaria_categorias` | `DB::table('secretarias_categorias')->get()` |
| `secretarias_paginas` | `DatatableController@get_secretaria_paginas` | `DB::table('secretarias_paginas')->get()` |

## 7. Migrations do módulo (v6)

| Data | Migration | Efeito |
|---|---|---|
| 2022-04-19 | `v6/database/migrations/2022_04_19_084011_add_secretaria_to_contents_table.php` | adiciona `contents.secretaria` (string, nullable) |
| 2023-03-29 | `v6/database/migrations/2023_03_29_104129_create_categorias_secretarias_table.php` | cria `categorias_secretarias` |
| 2023-03-29 | `v6/database/migrations/2023_03_29_104149_create_paginas_secretarias_table.php` | cria `paginas_secretarias` |
| 2023-03-30 | `v6/database/migrations/2023_03_30_132138_create_view_to_secretarias_paginas.php` | cria a view `secretarias_paginas` |
| 2023-03-30 | `v6/database/migrations/2023_03_30_132359_create_view_to_secretarias_categorias.php` | cria a view `secretarias_categorias` |
| 2023-03-31 | `v6/database/migrations/2023_03_31_101031_create_view_to_secretarias.php` | cria a view `secretarias` (sem `slug`) |
| 2023-05-26 | `v6/database/migrations/2023_05_26_103328_add_secretarias_to_calendar_events.php` | `calendar_events.secretarias_id` (FK `contents.id`) |
| 2024-11-01 | `v6/database/migrations/2024_11_01_150206_create_modulos_table.php` | cria `modulos`; insere id 18 `secretarias` com `status = 0` |
| 2024-11-05 | `v6/database/migrations/2024_11_05_104026_add_modulo_id_to_resources_table.php` | `resources.modulo_id` (FK `modulos.id`) |
| 2024-11-29 | `v6/database/migrations/2024_11_29_085508_update_view_secretarias.php` | recria a view `secretarias` **com** `slug` |
| 2025-04-23 | `v6/database/migrations/2025_04_23_112404_add_acronym_to_contents.php` | `contents.acronym` (Sigla) |

## 8. Seeds e dados de configuração

O módulo **não tem seed próprio** na v6. Os registros são inseridos por:

| Origem | Conteúdo |
|---|---|
| `v5/database/migrations/2023_03_31_083904_add_resource_to_secretarias.php` | Insere em `resources` os slugs `secretarias-categorias` ("Departamentos das Secretarias") e `secretarias-paginas` ("Páginas internas de Secretarias") — **existe na v5 e NÃO na v6** |
| `v6/database/migrations/2024_11_01_150206_create_modulos_table.php` | Insere `modulos` id 18 (`secretarias`, `status = 0`) |
| Operador (CMS) | Secretarias (13), departamentos e páginas cadastrados pela mão |
| Banco atual | `resources` ids 15/16/17 com `modulo_id = 18` (as três entradas existem no banco, apesar de a migration da v5 não estar na v6) |

### 8.1 Dados atuais (consulta ao banco)

`contents` com `category_contents_id = 7` — **13 linhas**:

| id | title | slug |
|---|---|---|
| 11 | Secretaria de Assistência Social | assistencia-social |
| 12 | Secretaria da Administração | administracao |
| 13 | Secretaria do Trabalho, Produção e Renda | trabalho-producao-e-renda |
| 14 | Secretaria da Educação e Cultura | educacao-e-cultura |
| 15 | Secretaria das Finanças | financas |
| 16 | Secretaria da Infra-Estrutura | infra-estrutura |
| 17 | Secretaria de Juventude, Esporte e Recreação | juventude-esporte-recreacao |
| 18 | Secretaria do Planejamento | planejamento |
| 19 | Secretaria da Saúde | saude |
| 20 | Secretaria de Turismo | turismo |
| 21 | Secretaria de Meio Ambiente | meio-ambiente |
| 22 | Secretaria de Habitação Social | habitacao-social |
| 25 | Controladoria Geral | controladoria-geral |

`menu_items` ligados à categoria 7 — **25 linhas**:

| Faixa | Descrição |
|---|---|
| id 15 | Item agrupador "Secretarias" (`menus_id = 1`, `contents_id` NULL, `type = 0`) |
| ids 18–29 | 12 secretarias individuais (`menus_id = 1`, `contents_id` = 11..22, `type = 2`) |
| ids 81–92 | As mesmas 12 secretarias com nome abreviado ("Assistência Social", "Administração", …) em `menus_id = 2` |

⚠ A 13ª secretaria (Controladoria Geral, id 25) **não** possui item de menu.

### 8.2 Relations Eloquent

```php
// App\Http\Model\Prefecture\CategoriasSecretaria
public function contentsCategory()
{
    return $this->hasMany(PaginasSecretaria::class, 'categoria_id', 'id');
}

// App\Prefecture\Content
public function secretarias()      // linha 72
{
    return $this->hasMany(Content::class, 'secretaria', 'slug');
}

// App\Http\Model\Prefecture\PaginasSecretaria
public function categoria()          // linha 26 — BUG: concatena 'teste'
{
    return $this->hasOne(CategoriasSecretaria::class, 'id', 'categoria_id') . 'teste';
}
```

`Content::secretarias()` liga pelo **slug** (`contents.secretaria` = `contents.slug`), não
pelo id — coerente com o valor gravado pelo select do formulário de notícias
(`['title as name', 'slug as valor']`, em `v6/app/Http/Controllers/Prefecture/ContentController.php:193`).

## 9. Índices recommendations para a v7

| Tabela | Índice | Motivo |
|---|---|---|
| `contents` | `(category_contents_id, publication_date DESC)` | listagens por categoria |
| `contents` | `(secretaria)` | vínculo notícia↔secretaria (`Content::secretarias()`) |
| `contents` | unique parcial em `slug` | resolve a colisão manual `-<maxid+1>` |
| `categorias_secretarias` | `(secretarias_id, order)` | menu da secretaria |
| `paginas_secretarias` | `(categoria_id)` | páginas do departamento |
| `archives` | `(reference_id, highlight)` | destaque e anexos da página |
