# Glossário — Modelagem de Dados

> Nenhuma tabela própria de verbetes. O glossário é um recorte de `contents` pela
> categoria `category_contents.id = 11` (slug `glossario`).

---

## 1. Tabelas envolvidas

### 1.1 `contents` — armazena o verbete

| Coluna | Tipo | Null | Default | Observação |
|---|---|---|---|---|
| `id` | serial | não | — | PK |
| `title` | varchar(191) | não | — | **Termo** do verbete |
| `text` | text | sim | — | **Definição** (HTML) |
| `category_contents_id` | integer | não | — | FK -> `category_contents.id` (11 para glossário) |
| `slug` | varchar(191) | sim | — | Gerado a partir do título (`str_slug`) |
| `status` | boolean | sim | — | Usado pelos demais módulos; não exposto no form do glossário |
| `publication_date` | timestamp | sim | — | `cast` = datetime (`v6/app/Http/Model/Prefecture/Content.php:54-56`) |
| `order` | integer | sim | — | Não exposto no form do glossário |
| `clicks` | integer | sim | — | Contador genérico de conteúdo |
| `galery` | boolean | sim | — | Não exposto no form do glossário |
| `highlights` | boolean | sim | — | Não exposto no form do glossário |
| `linha_fina` | varchar(300) | sim | — | Não exposto no form do glossário |
| `created_at` / `updated_at` | timestamp | sim | — | `deleted_at` marca soft delete |

Verificação do mapeamento no model:

```php
// v6/app/Http/Model/Prefecture/Content.php
protected $table = 'contents';

protected $fillable = [
    'title', 'linha_fina', 'text', 'clicks', 'highlights', 'order', 'galery',
    'category_contents_id', 'secretaria', 'status_create', 'publication_date',
    'author', 'source', 'status', 'slug', 'icon', 'referencia', 'carousel',
    'orgaos_id', 'councilmen_id', 'position_id', 'acronym', 'layout',
];

protected $casts = [
    'publication_date' => 'datetime',
];
```

### 1.2 `category_contents` — categoria do verbete

| Coluna | Tipo | Null | Default | Valor para o glossário |
|---|---|---|---|---|
| `id` | serial | não | — | 11 |
| `name` | varchar(191) | não | — | `Glossário` |
| `slug` | varchar(191) | não | — | `glossario` |
| `status_create` | boolean | sim | — | 1 |
| `status` | boolean | sim | true | — |
| `category_contents_id` | integer | sim | — | NULL (categoria de topo) |

Origem do seed:

```php
// v6/database/seeds/CategoryContentTableSeeder.php:76-80
// ID 11
CategoryContent::updateOrCreate(['slug' => 'glossario'], [
    'name' => 'Glossário',
    'status_create' => 1
]);
```

## 2. Relacionamentos

| Relação | Tipo (Eloquent) | Declaração | Uso |
|---|---|---|---|
| `Content.categoryContents()` | `belongsTo(CategoryContent::class, 'category_contents_id', 'id')` | `v6/app/Http/Model/Prefecture/Content.php:105-108` | Filtro do portal e do CMS |
| `Content.archives()` | `hasMany(Archive::class, 'contents_id', 'id')` | idem, linhas 68-71 | Arquivos anexos (não usados pelo glossário) |
| `Content.linksUnique()` | `hasOne(Link::class, 'contents_id', 'id')` | idem, linhas 116-119 | Link externo (não usado pelo glossário) |
| `CategoryContent.contents()` | `hasMany(Content::class, 'category_contents_id', 'id')` | `v6/app/Http/Model/Prefecture/CategoryContent.php` | Conteúdo da categoria |

Não há tabela pivot, nem FK composta, nem tabela de sinônimos/aliases.

## 3. View SQL `glossario`

Criada por `v6/database/migrations/2023_04_10_151502_create_view_to_glossarios.php`:

```sql
CREATE OR REPLACE VIEW glossario AS
SELECT row_number() OVER (ORDER BY c.created_at DESC) AS value,
    c.title AS titulo,
    c.id as id

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

| Coluna da view | Origem | Uso |
|---|---|---|
| `value` | `row_number()` sobre `created_at DESC` | Número sequencial exibido na coluna ID do DataTable |
| `titulo` | `c.title` | Coluna "Título" do DataTable |
| `id` | `c.id` | Link de edição (`editar/glossario/{id}`) |

Observações:

- A view **não** filtra `status` nem `publication_date`: o CMS pode listar verbetes
  desativados que o portal também exibiria.
- A view é a única fonte de dados do DataTable do CMS (`DB::table("glossario")`).
- O portal **não** usa esta view; consulta `contents` via Eloquent.
- O `down()` da migration faz `DROP VIEW if exists public.glossario`.

## 4. Registro de módulo e permissões

| Tabela | Registro | Onde |
|---|---|---|
| `modulos` | id 24, `name = 'Glossário'`, `slug = 'glossario'`, `status = 1` | `v6/database/migrations/2024_11_01_150206_create_modulos_table.php:167-171` |
| `resources` | `slug = 'glossario'`, `name = 'Glossário'`, `modulo_id = 24` | `v6/database/seeds/ResourcesTableSeeder.php:234-239` |
| `menu_orders` | referência opcional via `modulo_id` | — |

```php
// v6/database/migrations/2024_11_01_150206_create_modulos_table.php
// ID 24
DB::table('modulos')->insert([
    'name' => 'Glossário',
    'slug' => 'glossario',
    'status' => 1,
]);
```

Permissões usadas nas views do CMS: `glossario.create`, `glossario.edit`, `glossario.delete`,
`glossario.restore`, `glossario.inactivate` (injetadas por
`DatatableController@struct_views`, linhas 504-520).

## 5. Migrations que criam/alteram as structures

| Migration | Efeito |
|---|---|
| `v6/database/migrations/2018_04_02_180433_create_contents_table.php` | Cria `contents` |
| `v6/database/migrations/2018_04_01_180916_create_category_contents_table.php` | Cria `category_contents` |
| `v6/database/migrations/2023_04_10_151502_create_view_to_glossarios.php` | Cria a view `glossario` |
| `v6/database/migrations/2024_11_01_150206_create_modulos_table.php` | Cria `modulos` e insere o módulo `glossario` |

## 6. Seeds que populam dados iniciais

| Seed | Efeito |
|---|---|
| `v6/database/seeds/CategoryContentTableSeeder.php:76-80` | Cria a categoria `glossario` (id 11) |
| `v6/database/seeds/ResourcesTableSeeder.php:234-239` | Cria o recurso/permissão `glossario` (modulo 24) |
| `v6/database/seeds/ContentsTableSeeder.php` | Conteúdos de demonstração (inclui itens do glossário) |

## 7. Índices e integridade

- `contents.category_contents_id` é FK para `category_contents.id` (índice do próprio
  `category_contents_id`).
- Não há índice dedicado sobre `contents.title`, apesar de o filtro do portal fazer
  `LIKE 'X%'` — o planejador pode usar varredura sequencial.
- Não há unicidade sobre `title`: verbetes duplicados são permitidos (o portal listaria
  o mesmo termo duas vezes).
