# Avisos — Modelagem de Dados

> O aviso é um `contents` (título + status) + um `links` (destino) + um `archives`
> (imagem de destaque). Não há tabela própria.

---

## 1. `contents` — o aviso

| Coluna | Tipo | Null | Default | Uso no aviso |
|---|---|---|---|---|
| `id` | serial | não | — | PK |
| `title` | varchar(191) | não | — | Título do comunicado |
| `text` | text | sim | — | Não exposto no formulário do aviso |
| `status` | boolean | sim | — | **Ativo (1) / inativo (0)** — define se o aviso aparece |
| `category_contents_id` | integer | não | — | 28 (categoria `anuncios`) |
| `slug` | varchar(191) | sim | — | Gerado do título |
| `order` | integer | sim | — | Não exposto no formulário |
| `publication_date` | timestamp | sim | — | Não exposto no formulário |
| `created_at`/`updated_at` | timestamp | sim | — | `deleted_at` (soft delete) |

O CMS usa `status` como chave de exibição: a coluna `status` da view SQL vira
"Ativado"/"Desativado".

## 2. `category_contents` — a categoria "Anúncios"

| Coluna | Valor |
|---|---|
| `id` | 28 |
| `name` | `Anúncios` |
| `slug` | `anuncios` |
| `status_create` | 1 |
| `status` | true (default) |
| `category_contents_id` | NULL (categoria de topo) |

```php
// v6/database/seeds/CategoryContentTableSeeder.php:161-166
// ID 28
CategoryContent::updateOrCreate(['slug' => 'anuncios'], [
    'name' => 'Anúncios',
    'status_create' => 1
]);
```

> Atenção: a categoria chama-se **"Anúncios"**, mas o módulo do CMS chama-se **"Avisos"**.

## 3. `links` — o destino do aviso

Gravada em `ContentController@store` (linhas 717-728) e `@update` (linhas 1062-1100):

| Coluna | Valor no aviso |
|---|---|
| `contents_id` | FK do `contents` do aviso |
| `link` | URL de destino (sanitizada por `sanitizeLink(normalizeVideoLink($request->get('link')))`) |
| `title` / `name` / `alt` | Cópia do título do aviso |
| `isBlank` | 1 quando o operador marca "Abrir em nova guia" |

Relação: `Content.linksUnique()` -> `hasOne(Link::class, 'contents_id', 'id')`
(`v6/app/Http/Model/Prefecture/Content.php:116-119`).

## 4. `archives` — a imagem do aviso

| Coluna | Valor no aviso |
|---|---|
| `contents_id` | FK do `contents` do aviso |
| `highlight` | 1 (marca a imagem de destaque usada no modal) |
| `path` | `storage/content/anuncios/{id}/destaque/{nome}` |
| `name`, `extension`, `type`, `width`, `height` | Metadados do arquivo |

A view do CMS exibe a imagem atual por esse caminho:

```blade
{{-- v6/resources/views/prefecture/contents/anuncios/form.blade.php:44 --}}
src="{{ url("storage/content/anuncios/{$content->id}/destaque/{$destaque->name}") }}"
```

Relação: `Content.archives()` -> `hasMany(Archive::class, 'contents_id', 'id')`.

## 5. View SQL `anuncios`

`v6/database/migrations/2023_04_10_152724_create_view_to_anuncios.php`:

```sql
CREATE OR REPLACE VIEW anuncios AS
SELECT row_number() OVER (ORDER BY c.created_at DESC) AS value,
    c.title AS titulo,
    CASE
        WHEN c.status IS TRUE THEN 'Ativado'::text
        ELSE 'Desativado'::text
    END AS status_name,
    c.id as id,
    c.status as status

FROM contents c
    JOIN category_contents cc ON cc.id = c.category_contents_id
WHERE c.deleted_at IS NULL AND cc.slug = 'anuncios'
ORDER BY c.created_at DESC;
```

| Coluna | Origem | Uso |
|---|---|---|
| `value` | `row_number()` sobre `created_at DESC` | Coluna ID do DataTable |
| `titulo` | `c.title` | Coluna Título |
| `status_name` | CASE sobre `c.status` | Coluna Status ("Ativado"/"Desativado") |
| `id` | `c.id` | Links de edição e de status |
| `status` | `c.status` | Estado do botão liga/desliga |

Observações:

- A view é consumida **apenas** pelo CMS (`DB::table("anuncios")`); o portal consulta
  `contents` via Eloquent.
- Não há restrição de unicidade: a view pode conter várias linhas com `status = true`
  se o dado for inserido fora do CMS (o portal exibiria apenas a primeira, por `first()`).

## 6. Registro de módulo, permissões e menu

| Tabela | Registro | Onde |
|---|---|---|
| `modulos` | id 26, `name = 'Avisos'`, `slug = 'avisos'`, `status = 1` | `v6/database/migrations/2024_11_01_150206_create_modulos_table.php:178-183` |
| `resources` | `slug = 'anuncios'`, `name = 'Avisos'`, `modulo_id = 26` | `v6/database/seeds/ResourcesTableSeeder.php:252-258` |
| `menu_orders` | `slug = 'anuncios'`, `modulo_id = 26` | `v6/database/seeds/MenuOrderSeeder.php:141-148` |

```php
// v6/database/migrations/2024_11_01_150206_create_modulos_table.php
// ID 26
DB::table('modulos')->insert([
    'name' => 'Avisos',
    'slug' => 'avisos',
    'status' => 1,
]);
```

O menu do CMS é montado em `v6/app/Providers/MenuServiceProvider.php:249-251`:

```php
case 'anuncios':
    $event->menu->add(['text' => 'Avisos', 'url' => '/cms/conteudos/anuncios', 'icon' => 'bookmark']);
    break;
```

## 7. Migrations e seeds

| Migration / Seed | Efeito |
|---|---|
| `v6/database/migrations/2018_04_02_180433_create_contents_table.php` | Cria `contents` |
| `v6/database/migrations/2018_04_02_181150_create_links_table.php` | Cria `links` |
| `v6/database/migrations/2018_04_02_181807_create_archives_table.php` | Cria `archives` |
| `v6/database/migrations/2023_04_10_152724_create_view_to_anuncios.php` | Cria a view `anuncios` |
| `v6/database/migrations/2024_11_01_150206_create_modulos_table.php` | Cria `modulos` (id 26 = "Avisos") |
| `v6/database/seeds/CategoryContentTableSeeder.php:161-166` | Cria a categoria `anuncios` (id 28) |
| `v6/database/seeds/ResourcesTableSeeder.php:252-258` | Cria o recurso `anuncios` (modulo 26) |

Não há seed de conteúdo de aviso: o módulo nasce vazio em todas as instalações.

## 8. Integridade e índices

- `contents.category_contents_id` é FK para `category_contents.id`.
- `links.contents_id` e `archives.contents_id` referenciam `contents.id`.
- Não há índice único que garanta **um único aviso ativo** — a exclusividade é
  garantida só pelo código do CMS.
- Não há colunas de janela (validade/período) do aviso: um aviso ativo fica no ar
  indefinidamente.
