# Agenda de Eventos — Modelagem de Dados

> Fonte: schema real do banco `prefeituras_v5` (PostgreSQL 10), migrations do `v6`
> (`v6/database/migrations/`) e models em `v6/app/Http/Model/Prefecture/`.
> Comando de verificação usado (somente leitura):
> `docker exec v6_6f22e1bd-postgres-1 psql -U postgres -d prefeituras_v5 -c "\d calendar_events"`

---

## 1. `calendar_events`

**Migration:** `v6/database/migrations/2022_01_07_101548_create_calendar_events_table.php`
**Model:** `v6/app/Http/Model/Prefecture/CalendarEvent.php` (namespace `App\Prefecture`)
**Colunas reais (banco):**

| Coluna | Tipo | Nullable | Default | Origem |
|---|---|:---:|---|---|
| `id` | `integer` (serial) | não | `calendar_events_id_seq` | migration `increments('id')` |
| `title` | `character varying(191)` | não | — | migration `string('title')` |
| `initial_date` | `timestamp(0) without time zone` | sim | — | migration `dateTime('initial_date')->nullable()` |
| `final_date` | `timestamp(0) without time zone` | sim | — | migration `dateTime('final_date')->nullable()` |
| `local` | `character varying(191)` | sim | — | migration `string('local')->nullable()` |
| `source` | `character varying(191)` | sim | — | migration `string('source')->nullable()` |
| `text` | `text` | não | — | migration `text('text')` |
| `status` | `boolean` | sim | — | migration `boolean('status')->nullable()` |
| `galery` | `boolean` | sim | — | migration `boolean('galery')->nullable()` |
| `slug` | `character varying(191)` | sim | — | migration `string('slug')->nullable()` |
| `category_calendar_events_id` | `integer` | sim | — | migration, FK `nullOnDelete` |
| `created_at` | `timestamp(0)` | sim | — | `$table->timestamps()` |
| `updated_at` | `timestamp(0)` | sim | — | `$table->timestamps()` |
| `deleted_at` | `timestamp(0)` | sim | — | `$table->softDeletes()` |
| `secretarias_id` | `integer` | sim | — | `v6/database/migrations/2023_05_26_103328_add_secretarias_to_calendar_events.php` |
| `orgaos_id` | `integer` | sim | — | `v6/database/migrations/2023_05_26_110237_add_orgaos_to_calendar_events.php` |

**Chaves e índices (banco):**

| Tipo | Nome | Colunas |
|---|---|---|
| PK | `calendar_events_pkey` | `id` |
| FK | `calendar_events_category_calendar_events_id_foreign` | `category_calendar_events_id` -> `category_calendar_events(id)` |
| FK | `calendar_events_secretarias_id_foreign` | `secretarias_id` -> `contents(id)` |
| FK | `calendar_events_orgaos_id_foreign` | `orgaos_id` -> `contents(id)` |

> Não há índice secundário em `slug` nem em `status`; o portal filtra por `status` e
> busca por `slug` em full scan.

**Fillable no model:** `title`, `initial_date`, `final_date`, `local`, `source`, `text`,
`slug`, `status`, `galery`, `category_calendar_events_id`, `archives_id`,
`secretarias_id`, `orgaos_id`.
> `archives_id` está no `$fillable` mas **não existe** na tabela `calendar_events`
> (a coluna real é `archives.calendar_events_id`). Não quebra a gravação porque o
> request não envia esse campo.

**Sem casts declarados:** `initial_date`/`final_date` são `timestamp` no Postgres e
`status`/`galery` são `boolean`; o model não declara `$casts`, logo o acesso devolve
string/booleano cru do driver.

---

## 2. `category_calendar_events`

**Migration:** `v6/database/migrations/2022_01_06_105757_create_category_calendar_events_table.php`
**Model:** `v6/app/Http/Model/Prefecture/CategoryCalendarEvent.php`

| Coluna | Tipo | Nullable | Default |
|---|---|:---:|---|
| `id` | `integer` (serial) | não | `category_calendar_events_id_seq` |
| `name` | `character varying(191)` | não | — |
| `slug` | `character varying(191)` | sim | — |
| `created_at` | `timestamp(0)` | sim | — |
| `updated_at` | `timestamp(0)` | sim | — |
| `deleted_at` | `timestamp(0)` | sim | — |

PK `category_calendar_events_pkey`; é referenciada por
`calendar_events_category_calendar_events_id_foreign`.

> A migration cria a tabela com `$table->engine = 'InnoDB'` — diretiva MySQL inócua no
> PostgreSQL, mas indica que o schema nasceu de um projeto MySQL.

**Fillable:** `name`, `slug`. O slug é calculado no controller
(`CategoryCalendarEventController::extractSave`), com sufixo `-N` em caso de colisão.

---

## 3. `archives` (tabela compartilhada)

**Migration do vínculo:** `v6/database/migrations/2022_01_07_111645_add_calendar_event_id_to_archives.php`
**Model:** `v6/app/Http/Model/Prefecture/Archive.php`
**Repository:** `v6/app/Repositories/Eloquent/Prefecture/Archive/ArchiveRepository.php`
**Contrato:** `v6/app/Repositories/Contracts/IArchiveRepository.php`

A tabela `archives` é **polimórfica manual**: cada módulo adicionou sua própria coluna FK.
Colunas relevantes para a agenda:

| Coluna | Tipo | Papel na agenda |
|---|---|---|
| `id` | `integer` serial | PK |
| `title` | `varchar(300)` | Título do arquivo (galeria) |
| `name` | `varchar(300)` | Nome do arquivo em disco |
| `path` | `varchar(300)` | Caminho relativo, ex.: `storage/calendar-events/<cat>/<id>/destaque/<arquivo>` |
| `extension` | `varchar(191)` | Extensão |
| `type` | `varchar(191)` | MIME type |
| `highlight` | `boolean` | `1` = imagem de destaque; `null` = item de galeria |
| `calendar_events_id` | `integer` | **FK do módulo**: `archives_calendar_events_id_foreign` -> `calendar_events(id)` |
| `hash`, `status_create`, `description`, `order`, `view_pdf`, `type_size_id`, `width`, `height` | — | Campos de uso genérico (usados por outros módulos) |
| `deleted_at`, `created_at`, `updated_at` | `timestamp(0)` | Soft delete + timestamps |

**Acoplamento:** `calendar_events_id` convive com `contents_id`, `static_pages_id`,
`biddings_id`, `requerimento_id`, `tv_camara_id`, `highlight_id` na mesma tabela. Um
registro de `archives` pertence a **um** módulo; o módulo é deduzido pela coluna FK
preenchida. Consequências para a v7:

- a exclusão (soft) de um evento **não** remove os `archives` vinculados;
- consultas por arquivo precisam sempre filtrar a coluna do módulo, sob pena de
  "vazamento" de arquivos entre módulos;
- a FK não tem `ON DELETE CASCADE`, logo uma remoção física do evento quebraria a
  integridade referencial.

---

## 4. Views SQL

| View | Consulta | Usada por |
|---|---|---|
| `agenda_de_eventos` | `row_number() OVER (ORDER BY ce.created_at DESC) AS value`, `ce.title AS titulo`, `CASE WHEN ce.status IS TRUE THEN 'Publicado' ELSE 'Rascunho' END AS status`, `to_char(ce.initial_date,'DD/MM/YYYY') AS data_inicial`, `to_char(ce.final_date,'DD/MM/YYYY') AS data_final`, `COALESCE(cce.name,'Sem Categoria') AS categoria`, `COALESCE(cce.slug,'') AS categoria_slug`, `ce.id`, `ce.galery AS galeria`, `ce.slug` — com `LEFT JOIN category_calendar_events cce ON cce.id = ce.category_calendar_events_id` e `WHERE ce.deleted_at IS NULL` | `Prefecture\DatatableController@get_agenda_eventos` (`v6/app/Http/Controllers/Prefecture/DatatableController.php:153-157`) |

A view é a **única** fonte de dados da listagem do CMS. Ela já resolve o rótulo de
status ("Publicado"/"Rascunho"), a data formatada e o nome/slug da categoria.

> A view é versionada por migrations (não mantida manualmente):
> `v6/database/migrations/2023_04_06_143633_create_view_to_agenda_de_eventos.php` (criação),
> `v6/database/migrations/2023_05_25_095627_update_eventos_view_date_hour.php` (formatação
> de data/hora) e `v6/database/migrations/2024_11_29_093605_update_view_agenta_de_eventos.php`
> (versão vigente, que passa a expor também `ce.slug`).

---

## 5. Migrations do módulo

| Data / arquivo | Efeito |
|---|---|
| `v6/database/migrations/2022_01_06_105757_create_category_calendar_events_table.php` | Cria `category_calendar_events` (id, name, slug, timestamps, softDeletes) |
| `v6/database/migrations/2022_01_07_101548_create_calendar_events_table.php` | Cria `calendar_events` + FK `category_calendar_events_id` (`nullOnDelete`) |
| `v6/database/migrations/2022_01_07_111645_add_calendar_event_id_to_archives.php` | Adiciona `archives.calendar_events_id` + FK para `calendar_events` |
| `v6/database/migrations/2023_05_26_103328_add_secretarias_to_calendar_events.php` | Adiciona `calendar_events.secretarias_id` (FK `contents.id`) |
| `v6/database/migrations/2023_05_26_110237_add_orgaos_to_calendar_events.php` | Adiciona `calendar_events.orgaos_id` (FK `contents.id`) |
| `v6/database/migrations/2023_04_06_143633_create_view_to_agenda_de_eventos.php` | Cria a view SQL `agenda_de_eventos` (listagem do CMS) |
| `v6/database/migrations/2023_05_25_095627_update_eventos_view_date_hour.php` | Ajusta a view para formatar data/hora |
| `v6/database/migrations/2024_11_29_093605_update_view_agenta_de_eventos.php` | Versão vigente da view (inclui `ce.slug` no `SELECT`) |

As mesmas migrations existem na `v5` (`v5/database/migrations/2022_01_07_101548_create_calendar_events_table.php`) com conteúdo
equivalente.

---

## 6. Seeds relacionados

| Arquivo | O que faz |
|---|---|
| `v6/database/seeds/ResourcesTableSeeder.php:468` | `resources`: slug `agenda-evento`, nome "Agenda de Eventos", `modulo_id = 39` |
| `v6/database/seeds/MenuOrderSeeder.php:33` | `menu_orders`: slug `agenda-evento`, `clicks = 0`, `modulo_id = 39` |
| `v6/database/seeds/CategoryContentTableSeeder.php:222` | `category_contents`: slug `agenda-eventos`, nome "Agenda de eventos" (id 40) — categoria do **portal**, distinta de `category_calendar_events` |
| `v6/database/seeds/MenuItemsTableSeeder.php:173` | `menu_items`: item "Agenda" (ordem 9, tipo 2), ligado a `category_contents_id` de `agenda-eventos` |
| `v6/database/seeds/RolesTableSeeder.php:28` | Permissões Sentinel do papel "Administrador": `agenda-evento.index/.create/.edit/.delete/.inactivate/.restore/.show` |
| `v6/database/seeds/AjustesMigracao.php:58-68` | Ajusta o recurso legado de slug `agenda` para `agenda-evento` com `modulo_id = 39` |

> Nenhum seed cria eventos ou categorias de agenda: as tabelas `calendar_events` e
> `category_calendar_events` estavam **vazias** no banco `prefeituras_v5` na data da
> análise.

---

## 7. Relacionamentos (resumo)

```
contents (secretarias / orgaos)
        ^                ^
        | FK             | FK
calendar_events -------+
        |  ^ FK (category_calendar_events_id)
        |  |
category_calendar_events
        ^
        | FK
archives (calendar_events_id, highlight = 1 -> destaque; NULL -> galeria)
```

- `calendar_events.category_calendar_events_id` -> `category_calendar_events.id` (n:n
  via FK simples, apenas **uma** categoria por evento).
- `calendar_events.secretarias_id` / `orgaos_id` -> `contents.id` (apenas **um**
  secretaria e **um** órgão por evento).
- `archives.calendar_events_id` -> `calendar_events.id` (N arquivos por evento).
