# Banners — Arquitetura

## 1. Visão geral

O módulo não tem model, controller nem tabela próprios. Ele é um **caso particular do
CRUD genérico de conteúdos**, dirigido pelo `slug` da categoria (`banners` /
`super-banners`). O que existe de específico são:

1. **Views SQL** (`banners`, `super_banners`) que projetam os conteúdos para o grid do CMS.
2. **Um método de controller** (`updateSuperBanner`) para a edição rápida dos super banners.
3. **View composers** (`BannerServiceProvider`) que injetam os banners no portal.
4. **Views de portal** que os exibem em pontos fixos de cada modelo de layout.

```
┌──────────────────────────── CMS (/cms) ────────────────────────────┐
│ /cms/conteudos/banners        → ContentController@genericIndex     │
│ /cms/conteudos/cadastrar/{slug} → ContentController@genericCreate  │
│ /cms/conteudos/editar/{slug}/{id} → ContentController@genericEdit  │
│ /cms/datatable/banners        → DatatableController@get_banners    │
│ /cms/datatable/super_banners  → DatatableController@get_super_banners│
│ /cms/ajust-banners  (POST)    → ContentController@ajustOrder        │
│ /cms/update/super-banner(POST)→ ContentController@updateSuperBanner │
│                     │                                              │
│                     ▼                                              │
│        Content (tabela contents)  ← CategoryContent (category_contents)│
│             │            │                                         │
│             │            └── links (Link)  → URL do banner          │
│             └── archives (Archive, highlight=1) → imagem            │
└─────────────────────────────────────────────────────────────────────┘
                     │
                     ▼
┌──────────────────────────── Portal (público) ─────────────────────┐
│ BannerServiceProvider (v6/app/Providers/BannerServiceProvider.php)  │
│   $banners      ← categoria 'banners'        (status = true)       │
│   $superBanners ← categoria 'super-banners'  (status = true)       │
│      │                                                                 │
│      ▼                                                                 │
│ default/side-banners.blade.php                                       │
│ modelo1/layout/sidebar/banners.blade.php                             │
│ modelo1/home/elementos.blade.php   modelo2/home/elementos.blade.php │
│ modelo2/layout/nav.blade.php                                         │
│ modelo5/home/super-banners/super-banners{,-1,-2,-carousel}.blade.php│
└─────────────────────────────────────────────────────────────────────┘
```

## 2. Classes envolvidas

| Classe | Caminho | Papel |
|---|---|---|
| `ContentController` | `v6/app/Http/Controllers/Prefecture/ContentController.php` | CRUD genérico; `genericIndex`, `genericCreate`, `genericEdit`, `store`, `update`, `destroy`, `ajustOrder`, `updateSuperBanner` |
| `ContentRequest` | `v6/app/Http/Requests/ContentRequest.php` | Regras por `slug` (`banners`, `super-banners`) |
| `DatatableController` | `v6/app/Http/Controllers/Prefecture/DatatableController.php` | `get_banners()`, `get_super_banners()` — leem as views SQL |
| `Content` | `v6/app/Http/Model/Prefecture/Content.php` | Eloquent de `contents`; relações `archives()`, `linksUnique()`, `archiveHighlight()`, `categoryContents()` |
| `CategoryContent` | `v6/app/Http/Model/Prefecture/CategoryContent.php` | Eloquent de `category_contents`; scope `bySlugBanner()` |
| `Archive` | `v6/app/Http/Model/Prefecture/Archive.php` | Imagem do banner (`highlight = 1`) |
| `Link` | `v6/app/Http/Model/Prefecture/Link.php` | URL do banner (`link`, `isBlank`, `alt`) |
| `BannerServiceProvider` | `v6/app/Providers/BannerServiceProvider.php` | View composers do portal |
| `MenuServiceProvider` | `v6/app/Providers/MenuServiceProvider.php` | Menu "Banners" com submenu "Banners Laterais"/"Super Banners" |
| `AppTrait` | `v6/app/Http/Traits/AppTrait.php` | `reorder()` — persistência da ordem arrastada |
| `functions.php` | `v6/app/Helpers/functions.php` | `objBanner()`, `fileBannerExist()`, `fileSuperBannerExist()`, `getLargFile()` |

## 3. Fluxo: listagem no CMS

```
GET /cms/conteudos/banners
  → ContentController@genericIndex('banners')
      → returnIndex('Banners', 'banners')            (linha 103)
      → view('prefecture.contents.banners.index')
          → JS assets/js/custom/contents/banners/datatable.js
              → GET /cms/datatable/banners
                  → DatatableController@get_banners()
                      → DB::table('banners')->get()          (view SQL)
                      → struct_views($banners, 'banners')    (permissões)
```

Para super banners, o mesmo caminho com `slug = 'super-banners'`:

```php
// v6/app/Http/Controllers/Prefecture/ContentController.php:103-104
if ($slug === 'banners') return $this->returnIndex('Banners', $slug);
if ($slug === 'super-banners') return $this->returnIndex('Super Banners', $slug, 'banners');
```

O terceiro argumento (`$alt_click = 'banners'`) faz o botão "Cadastrar Banner" apontar
para a categoria `banners` também na tela de super banners.

## 4. Fluxo: cadastro/edição

```
GET  /cms/conteudos/cadastrar/banners → genericCreate → genericGenerate('banners')
  → returnData('Banners', 'banners', 'banners', [], null)
      → $categoryContent = CategoryContent::where('slug','banners')->first()
      → $banners = getContentByCategory('banners')     (para calcular a próxima posição)
      → view('prefecture.contents.banners.create')

POST /cms/conteudo  → ContentController@store(ContentRequest)
  → ContentRequest: case 'banners' → title, category_contents_id, link, order
                                       (+ archive_highlight obrigatório no store)
  → grava Content + Archive(highlight=1) + Link
```

## 5. Fluxo: reordenação por drag & drop

```
jQuery UI sortable (v6/resources/views/prefecture/contents/banners/index.blade.php)
  → POST /cms/ajust-banners   (route 'ajust.banners')
      → ContentController@ajustOrder(Request $request)
          → $this->reorder((new Content()), $request->sortable_data);
          → response()->json(true)
```

`reorder()` (`v6/app/Http/Traits/AppTrait.php:741`) percorre os ids recebidos e grava a
nova sequência na coluna `order`.

## 6. Fluxo: portal

### 6.1 Carga dos dados

```php
// v6/app/Providers/BannerServiceProvider.php:23-31
$configBanners = Content::with('archiveHighlight:path,description,contents_id',
    'linksUnique:id,title,name,isBlank,link,contents_id')
    ->whereHas('categoryContents', function ($query) {
        $query->bySlugBanner();
    })->byStatus()->orderBy('order')->get(['id', 'title', 'slug', 'order']);

view()->composer(['portal.layouts.modelo1.layout.sidebar.layout', 'portal.layouts.modelo2.layout.sidebar.layout',
    'portal.layouts.modelo3.layout.sidebar.layout', 'portal.layouts.modelo4.layout.*', 'portal.layouts.modelo5.home.*'],
    function($view) use ($configBanners) {
        $view->with('banners', $configBanners);
    });
```

O scope `bySlugBanner()` (`v6/app/Http/Model/Prefecture/CategoryContent.php:176`)
implementa `whereIn('slug', ['banners'])` — ou seja, **só** a categoria `banners`.

O provider também carrega os super banners:

```php
// v6/app/Providers/BannerServiceProvider.php:34-43
$configSuperBanners = Content::with('archiveHighlight:path,description,contents_id',
    'linksUnique:id,title,name,isBlank,link,contents_id,alt')
    ->whereHas('categoryContents', function ($query) {
        $query->where('slug', 'super-banners');
    })->byStatus()->orderBy('order')->get(['id', 'title', 'slug', 'order']);

view()->composer(['portal.layouts.modelo1.home.*', 'portal.layouts.modelo2.layout.*', 'portal.layouts.modelo2.home.elementos',
    'portal.layouts.modelo3.home.*', 'portal.layouts.modelo4.home.*', 'portal.layouts.modelo5.home.*', 'portal.layouts.modelo6.home.*'],
    function($view) use ($configSuperBanners) {
        $view->with('superBanners', $configSuperBanners);
    });
```

### 6.2 Posicionamento dos super banners

Cada modelo escolhe o que exibir:

| Modelo | Onde | Critério |
|---|---|---|
| modelo1 | `modelo1/home/elementos.blade.php` | `order == 1` no topo (duas faixas), `order == 2` abaixo das notícias |
| modelo2 | `modelo2/home/elementos.blade.php` | `order == 2` abaixo das notícias; `order == 1` na lateral do logo (`modelo2/layout/nav.blade.php`) |
| modelo5 | `modelo5/home/super-banners/*` | Blocos do modelo personalizável, iterados por `layouts` |

## 7. Dependências

| Recurso | Uso |
|---|---|
| `laravel/framework` 5.5 | Eloquent, FormRequest, Storage |
| `yajra/laravel-datatables-oracle` | Grid do CMS |
| `intervention/image` | Compressão/thumbs via `v6/app/Http/Traits/FileTrait.php` |
| `cartalyst/sentinel` | Autorização (`has_access`) e log de atividade |
| jQuery UI `sortable` | Reordenação por drag & drop |
| Bootstrap 4 / 5 | Estrutura do portal |

## 8. Onde o módulo não tem camada

- Sem model dedicado: usa `Content` + `CategoryContent`.
- Sem `Policy`: a autorização é `has_access('banners.*')` / `has_access('super-banners.*')`.
- Sem API JSON pública para banners.
- Sem testes automatizados específicos.
- Sem `Service`/`Repository` próprio (aproveita o `IContentRepository` injetado no
  constructor do `ContentController` e o trait
  `v6/app/Http/Traits/GetInformationsPrefectureTrait.php`, que traz
  `getContentByCategory($param)`).
