# A Instituição — Arquitetura

> Versão analisada: `v6` (Laravel 5.5.50). Referência de comparação: `v5`.

---

## 1. Visão geral das camadas

O módulo **não tem** classes próprias de domínio. Tudo é resolvido por
mecanismos genéricos do portal (`ContentController`, `ContentRequest`,
`Content`, `CategoryContent`) mais uma **view SQL** própria (`a_instituicao`).
Consequência prática: o comportamento do módulo está espalhado por
`routes/web.php`, `routes/basepro.php`, `routes/breadcrumbs.php`,
`ContentController`, `ContentRequest`, `DatatableController` e seis blades de
layout do portal.

```
┌───────────────────────────── CAMADA HTTP (portal público) ─────────────────────────────┐
│                                                                                        │
│  GET /a-instituicao/{slug}                                                            │
│        │                                                                               │
│        ▼                                                                               │
│  routes/web.php:78-83  (prefixo 'a-instituicao', nome 'a-instituicao.index')          │
│        │                                                                               │
│        ▼                                                                               │
│  Portal\PrefectureController@getIndex  (PrefectureController.php:29-98)                 │
│        │  ├─ CalendarEvent::with('categoryCalendarEvents')  → $eventsList              │
│        │  ├─ Content::with('categoryContents','archives')                                │
│        │  │        ->where('secretaria', $slug1)->paginate(6)  → $news_secretary       │
│        │  ├─ getContentDataWith($slug1) → $contentNew (trait)                          │
│        │  ├─ embedVideo($contentNew->linksUnique->link)   → $linkVideo                 │
│        │  ├─ updateClicks($contentNew->id)  → contents.clicks++                        │
│        │  └─ CategoryContent::find($contentNew->category_contents_id) → $category       │
│        │                                                                               │
│        ├── $category->slug == 'secretarias'                                             │
│        │      → portal.components.secretarias.show                                     │
│        ├── $category->slug == 'orgaos'                                                 │
│        │      → portal.layouts.<modelo>.informacoes-instituicao.a-instituicao.show      │
│        │        (com 'sidenews' => $contentNew->orgaos)                                 │
│        └── default (inclui 'a-instituicao')                                            │
│               → portal.layouts.<modelo>.informacoes-instituicao.a-instituicao.show      │
│                 (com 'news_secretary' => coleção paginada)                             │
└────────────────────────────────────────────────────────────────────────────────────────┘

┌───────────────────────────── CAMADA HTTP (CMS /cms) ──────────────────────────────────┐
│                                                                                        │
│  GET /cms/conteudos/a-instituicao                                                      │
│        ▼                                                                               │
│  Prefecture\ContentController@genericIndex  (ContentController.php:91)                  │
│        └─ returnIndex('A Instituição', 'a-instituicao')                                │
│              ├─ generateLog('A Instituição')                                            │
│              ├─ menuClicks('a-instituicao')  → menu_orders.clicks++                     │
│              └─ view prefecture.contents.a-instituicao.index                          │
│                                                                                        │
│  GET /cms/datatable/a_instituicao                                                      │
│        ▼                                                                               │
│  Prefecture\DatatableController@get_a_instituicao  (DatatableController.php:252-256)    │
│        ├─ DB::table('a_instituicao')->get()        ← materializa a view inteira         │
│        ├─ struct_views($data, 'a-instituicao')      ← injeta edit/delete/active/…       │
│        └─ DataTables::of(...)->make(true)           ← Yajra (client-side paging)         │
│                                                                                        │
│  GET /cms/conteudos/cadastrar/a-instituicao  → genericCreate → genericGenerate         │
│  GET /cms/conteudos/editar/a-instituicao/{id} → genericEdit  → genericGenerate         │
│        └─ returnData('A Instituição', 'a-instituicao', null, [], $id)                  │
│              └─ view prefecture.contents.a-instituicao.{create|edit}                    │
│                                                                                        │
│  POST /cms/conteudo  (Route::resource)   → ContentController@store(ContentRequest)     │
│  PUT  /cms/conteudo/{id}                 → ContentController@update(ContentRequest)    │
│  DELETE /cms/conteudo/{id}               → ContentController@destroy                   │
│  DELETE /cms/conteudo/delete/{id}        → ContentController@genericDestroy             │
└────────────────────────────────────────────────────────────────────────────────────────┘
```

## 2. Fluxo detalhado — leitura pública

| # | Etapa | Arquivo:linha | O que acontece |
|---|---|---|---|
| 1 | Rota | `v6/routes/web.php:78-83` | Grupo `prefix('a-instituicao')` com `Route::any('/{slug}')` nomeado `a-instituicao.index` e `Route::any('secretarias/{slug}')` **com o mesmo nome**. |
| 2 | Sem middleware no portal | `v6/routes/web.php` | As rotas do portal público não usam `check.logged`; middleware global é o padrão do `RouteServiceProvider`. |
| 3 | Controller | `v6/app/Http/Controllers/Portal/PrefectureController.php:29` | `getIndex($slug1)`. |
| 4 | Eventos | `v6/app/Http/Controllers/Portal/PrefectureController.php:31-36` | `CalendarEvent::with('categoryCalendarEvents')` filtrado por `status = 1` e `final_date >= hoje`, ordenado por `created_at DESC`. **Sem `limit()` no SQL** — o limite só é aplicado nos ramos `secretarias`/`orgaos`. |
| 5 | Notícias | `v6/app/Http/Controllers/Portal/PrefectureController.php:38` | `Content::with('categoryContents','archives')->where('secretaria', $slug1)->orderBy('id','DESC')->paginate(6)`. Note que o filtro é por `contents.secretaria` = **slug do conteúdo**, não por id de secretaria. |
| 6 | Conteúdo | `v6/app/Http/Traits/GetInformationsPrefectureTrait.php:26-29` | `Content::with('archiveHighlight','archiveGallery','categoryContents:id,name,slug')->where('slug', $param)->first()`. |
| 7 | Vídeo | `v6/app/Http/Controllers/Portal/PrefectureController.php:41` | `embedVideo($contentNew->linksUnique->link)`. |
| 8 | Cliques | `v6/app/Http/Traits/GetInformationsPrefectureTrait.php:104-108` | `updateClicks()` faz `UPDATE contents SET clicks = clicks + 1`. |
| 9 | Categoria | `v6/app/Http/Controllers/Portal/PrefectureController.php:50` | `CategoryContent::find($contentNew->category_contents_id)`. |
| 10 | View | `v6/app/Http/Controllers/Portal/PrefectureController.php:76-79` | `portal.layouts.<modelo>.informacoes-instituicao.a-instituicao.show`. |
| 11 | Layout | `v6/app/Http/Traits/GetInformationsPrefectureTrait.php:92-102` | `getTemplateActived()` lê `configurations.key = 'layout'` e valida `View::exists('portal.layouts.<layout>.home.index')`; se não existir, marca `pagina_em_manutencao = 1` e faz `abort(404, 'view_inexistente')`. |

## 3. Fluxo detalhado — escrita no CMS

| # | Etapa | Arquivo:linha | O que acontece |
|---|---|---|---|
| 1 | Listagem | `v6/routes/basepro.php:236` | `GET cms/conteudos/{slug}` → `genericIndex`. |
| 2 | Permissão | `v6/app/Http/Controllers/Prefecture/ContentController.php:64` | Construtor aplica `$this->middleware('check.route.permission')`. |
| 3 | Check de rota | `v6/app/Http/Middleware/CheckRoutePermission.php:22-38` | Se logado e não `super-admin`, exige `hasAnyAccess([$routeName, $accessSegment])`; senão redireciona para o login. |
| 4 | Menu lateral | `v6/app/Providers/MenuServiceProvider.php:46,183-185` | A entrada "A Instituição" só aparece se `has_access("a-instituicao.index")` **e** `modulos.status` for verdadeiro. |
| 5 | Render | `v6/app/Http/Controllers/Prefecture/ContentController.php:114-118` | `returnIndex()` → `view("prefecture.contents.$slug.index", ['title' => $title])`. |
| 6 | Formulário | `v6/app/Http/Controllers/Prefecture/ContentController.php:161` | `genericGenerate('a-instituicao')` → `returnData('A Instituição', $slug, null, [], $id)`. |
| 7 | Dados do form | `v6/app/Http/Controllers/Prefecture/ContentController.php:275-308` | `returnData()` monta `categoryContent` (por slug), `content`, `gallery` (`archives` com `highlight IS NULL` e `extension <> 'pdf'`), `archives`, `category`, `destaque`. |
| 8 | Validação | `v6/app/Http/Requests/ContentRequest.php:230-237` | `case 'a-instituicao'`. |
| 9 | Gravação | `v6/app/Http/Controllers/Prefecture/ContentController.php:616-700` | `store()` dentro de `DB::transaction`, slug por `Str::slug` com colisão resolvida por `slug-<maxid+1>`, arquivos do Summernote movidos para `storage/summernote-files/content/<slug>/<id>/`. |
| 10 | Exclusão | `v6/app/Http/Controllers/Prefecture/ContentController.php:1320-1367` | `destroy()` remove os `menu_items` que apontam para o conteúdo antes de apagar. |

## 4. Classes envolvidas

| Classe | Caminho | Papel no módulo |
|---|---|---|
| `App\Http\Controllers\Portal\PrefectureController` | `v6/app/Http/Controllers/Portal/PrefectureController.php` | Render da página pública; decide a view pela categoria do conteúdo. |
| `App\Http\Controllers\Portal\PortalController` | `v6/app/Http/Controllers/Portal/PortalController.php` | `contentList()` (`portal/{slug1}/{slug2}`) redireciona para `route('a-instituicao.index', $slug2)`. |
| `App\Http\Controllers\Prefecture\ContentController` | `v6/app/Http/Controllers/Prefecture/ContentController.php` | CRUD genérico dirigido por slug; `genericIndex`, `genericGenerate`, `store`, `update`, `destroy`, `genericDestroy`. |
| `App\Http\Controllers\Prefecture\DatatableController` | `v6/app/Http/Controllers/Prefecture/DatatableController.php` | `get_a_instituicao()` monta o JSON da grade; `struct_views()` injeta as permissões por linha. |
| `App\Http\Requests\ContentRequest` | `v6/app/Http/Requests/ContentRequest.php` | Regras de validação por `case $slug`. |
| `App\Http\Model\Prefecture\Content` | `v6/app/Http/Model/Prefecture/Content.php` | Eloquent da tabela `contents`; relações `archives`, `secretarias`, `orgaos`, `categoryContents`, `linksUnique`, `archiveHighlight`, `archiveGallery`. |
| `App\Http\Model\Prefecture\CategoryContent` | `v6/app/Http/Model/Prefecture/CategoryContent.php` | Eloquent de `category_contents`; `scopeBySlug()`. |
| `App\Http\Presenters\MenuItemPresenter` | `v6/app/Http/Presenters/MenuItemPresenter.php` | Calcula a URL dos itens de menu que apontam para conteúdo, **por id de categoria**. |
| `App\Http\Middleware\CheckRoutePermission` | `v6/app/Http/Middleware/CheckRoutePermission.php` | Autorização por rota no CMS. |
| `App\Http\Traits\GetInformationsPrefectureTrait` | `v6/app/Http/Traits/GetInformationsPrefectureTrait.php` | `getContentDataWith()`, `getTemplateActived()`, `updateClicks()`. |
| `App\Http\Traits\AppTrait` | `v6/app/Http/Traits/AppTrait.php` | Trait auxiliar usado pelo controller do portal (`generateLog` etc.). |
| `App\Providers\MenuServiceProvider` | `v6/app/Providers/MenuServiceProvider.php` | Monta o menu lateral do CMS; condiciona a entrada por `has_access()` + `modulos.status`. |
| `App\Providers\ModulosServiceProvider` | `v6/app/Providers/ModulosServiceProvider.php` | Injeta `$modulos` (slug → status) em `portal.layouts.modeloN.layout.sidebar.orgaosNews` e afins, via `Cache::rememberForever('modulos')`. |
| `App\Http\Model\Admin\Modulos` | `v6/app/Http/Model/Admin/Modulos.php` | Eloquent de `modulos`. |
| `App\Http\Model\Prefecture\MenuOrder` | `v6/app/Http/Model/Prefecture/MenuOrder.php` | Contador de cliques do menu (`menuClicks()`). |
| `App\Prefecture\Archive` / `Link` | `v6/app/Http/Model/Prefecture/Archive.php`, `v6/app/Http/Model/Prefecture/Link.php` | Arquivos (destaque/galeria) e link do vídeo. |
| `App\Prefecture\CalendarEvent` | `v6/app/Http/Model/Prefecture/CalendarEvent.php` | Agenda de eventos exibida na lateral. |

## 5. Dependências externas

| Pacote | Uso neste módulo |
|---|---|
| `laravel/framework` 5.5.50 | Roteamento, Eloquent, Blade, Form Requests, `Str::slug`. |
| `yajra/laravel-datatables-oracle` | `DataTables::of($collection)->make(true)` na grade do CMS. |
| `cartalyst/sentinel` | `Sentinel::check()`, `Sentinel::inRole('super-admin')`, `hasAnyAccess()` na autorização. |
| `laracasts/presenter` | `ContentPresenter` (trait `PresentableTrait` em `Content`). |
| `intervention/image` | Recorte/redimensionamento das imagens de destaque e galeria. |
| `doctrine/dbal` (implícito) | `DB::statement()` das migrations de view SQL. |
| `adm-lte` / AdminLTE 1.22 + Bootstrap 3 | Layout do CMS. |
| Bootstrap 4 + lightGallery | Layout do portal e galeria de fotos. |
| Summernote | Editor de texto rico do corpo do conteúdo. |

## 6. Onde o módulo se encaixa no portal

| Camada | Componente | Caminho |
|---|---|---|
| Layout do portal | `modelo1..modelo6/informacoes-instituicao/a-instituicao/show.blade.php` | `v6/resources/views/portal/layouts/modelo1/informacoes-instituicao/a-instituicao/show.blade.php` (e o equivalente em modelo2, modelo3, modelo4, modelo5, modelo6) |
| Componente de cada modelo | `components-a-prefeitura/index.blade.php` | `v6/resources/views/portal/layouts/modelo1/informacoes-instituicao/components/components-a-prefeitura/index.blade.php` (e o equivalente em modelo2, modelo3, modelo4, modelo5, modelo6) |
| Galeria | lightGallery | `v6/resources/views/portal/components/galerias/index.blade.php` |
| Notícias | "Últimas notícias" paginada | `v6/resources/views/portal/components/secretarias/noticias.blade.php` |
| Vídeo | iframe responsivo | `v6/resources/views/portal/statics/video.blade.php` |
| Lateral (modelo1) | sidebar completo | `v6/resources/views/portal/layouts/modelo1/layout/sidebar/layout.blade.php` |
| Lateral (notícias do órgão) | "Notícias de &lt;título&gt;" | `v6/resources/views/portal/layouts/modelo1/layout/sidebar/orgaosNews.blade.php` |
| Breadcrumb | registro por slug | `v6/routes/breadcrumbs.php:199-220` |

O nome do diretório de views ainda é `components-a-prefeitura` (herança do v5) embora o
módulo se chame A Instituição — renomear o diretório exigiria tocar nos 6 modelos.

## 7. Módulos que dependem deste (e vice-versa)

| Módulo | Relação |
|---|---|
| `../secretarias/` | A página de secretaria é servida pelo **mesmo** `PrefectureController@getIndex`; o conteúdo institucional é o "pai" lógico das secretarias (menu, breadcrumb e rota `a-instituicao/secretarias/{slug}`). |
| `../orgaos/` | Idem: ramo `orgaos` do mesmo controller, com `sidenews = $contentNew->orgaos`. |
| `../noticias/` | As "Últimas notícias" e o bloco "Notícias de &lt;título&gt;" consomem `contents` da categoria `noticias`; a coluna `contents.secretaria` liga a notícia ao slug da secretaria. |
| `../agenda-de-eventos/` | `$eventsList` (CalendarEvent) aparece na lateral da página institucional. |
| `../menus/` | `MenuItemPresenter` decide a URL final de itens de menu que apontam para conteúdos. |
| `../a-cidade/` | Compartilha o mesmo controller genérico, o mesmo `ContentRequest` e — por bug — o mesmo formulário Blade. |
