# Drive — Migração para Laravel Moderno

## 1. Decisão de modelagem

Criar uma tabela `media` própria, com **coleções** (drive, galeria, destaque) e vínculo com o
conteúdo dono. Isso elimina a ambiguidade atual (um `archives` sem dono) e permite listar o Drive
com escopo.

```php
// database/migrations/xxxx_create_media_table.php
Schema::create('media', function (Blueprint $table) {
    $table->id();
    $table->string('collection', 60)->default('drive'); // drive | galeria | destaque
    $table->string('name');            // nome original do arquivo
    $table->string('file_name');       // nome gerado no disco
    $table->string('mime_type', 120);
    $table->string('extension', 20);
    $table->unsignedBigInteger('size')->default(0);
    $table->string('disk', 40)->default('public');
    $table->string('directory')->nullable();
    $table->string('path');
    $table->string('hash', 64)->nullable();
    $table->unsignedInteger('width')->nullable();
    $table->unsignedInteger('height')->nullable();
    $table->unsignedTinyInteger('sort_order')->default(0);
    $table->string('title')->nullable();
    $table->text('description')->nullable();
    $table->nullableMorphs('model');   // conteúdo, página estática ou evento
    $table->timestamps();
    $table->softDeletes();

    $table->index(['collection', 'sort_order']);
});
```

Alternativa: usar `spatie/laravel-medialibrary`, que já resolve nome de arquivo, conversões
(`_min`, `_med`, `_larg`) e vínculo polimórfico.

### Migração dos dados legados

```php
// exemplo ilustrativo
DB::table('archives')->orderBy('id')->get()->each(function ($row) use (&$files) {
    $collection = 'drive';

    if ($row->contents_id)      $collection = 'galeria';
    elseif ($row->static_pages_id) $collection = 'galeria';
    elseif ($row->calendar_events_id) $collection = 'galeria';
    elseif ($row->highlight === 1)  $collection = 'destaque';

    Media::create([
        'legacy_id' => $row->id,
        'collection' => $collection,
        'name' => basename((string) $row->name),
        'file_name' => $row->name,
        'mime_type' => $row->type,
        'extension' => $row->extension,
        'path' => str_replace('storage/', '', (string) $row->path),
        'hash' => $row->hash,
        'title' => $row->title,
        'description' => $row->description,
        'sort_order' => $row->order ?? 0,
        'model_type' => $row->contents_id ? Content::class : null,
        'model_id' => $row->contents_id,
    ]);
});
```

## 2. Modelos

```php
// app/Models/Media.php  (exemplo ilustrativo)
class Media extends Model
{
    use HasFactory, SoftDeletes;

    protected $fillable = ['collection', 'name', 'file_name', 'mime_type', 'extension', 'size',
        'disk', 'directory', 'path', 'hash', 'width', 'height', 'sort_order', 'title', 'description'];

    protected function casts(): array
    {
        return [
            'size' => 'integer',
            'width' => 'integer',
            'height' => 'integer',
            'sort_order' => 'integer',
        ];
    }

    public function model(): MorphTo
    {
        return $this->morphTo();
    }

    public function url(): string
    {
        return Storage::disk($this->disk)->url($this->path);
    }
}
```

Regras de herança do legado a preservar:

| Legado | Equivalente na v7 |
|---|---|
| `archives.contents_id` | `media.model_type`/`model_id` (polimórfico) |
| `archives.highlight = 1` | coleção `destaque` |
| `archives.type_size_id` | `media.width`/`media.height` |
| `archives.order` | `media.sort_order` |
| `archives.static_pages_id` / `calendar_events_id` | `model_type`/`model_id` |
| `archives.highlight_id` | coleção `destaque` + vínculo com `highlights` |

## 3. Form Requests

```php
// app/Http/Requests/StoreMediaRequest.php  (exemplo ilustrativo)
public function rules(): array
{
    return [
        'files' => ['required', 'array', 'max:20'],
        'files.*' => ['required', 'file', 'mimes:jpg,jpeg,png,gif,webp,pdf,doc,docx,xls,xlsx', 'max:20480'],
        'collection' => ['required', Rule::in(['drive', 'galeria', 'destaque'])],
        'model_type' => ['nullable', 'string'],
        'model_id' => ['nullable', 'integer'],
    ];
}
```

O legado **não valida nada no servidor** (só o `acceptFiles` do cliente). A v7 deve validar MIME,
tamanho e contagem.

## 4. Policies e autorização

```php
// app/Policies/MediaPolicy.php  (exemplo ilustrativo)
public function delete(User $user, Media $media): bool
{
    return $user->can('drive.delete');
}
```

Permissões a criar (padronizando o que hoje é `midias.*`): `drive.index`, `drive.create`,
`drive.edit`, `drive.delete`, `drive.download`.

## 5. Endpoints REST

| Método | URI | Descrição |
|---|---|---|
| GET | `/api/v1/cms/media?collection=drive` | listagem paginada e filtrada |
| POST | `/api/v1/cms/media` | upload (multipart, vários arquivos) |
| PUT | `/api/v1/cms/media/{id}` | título/descrição |
| DELETE | `/api/v1/cms/media/{id}` | exclusão (remove registro **e** arquivo) |
| PUT | `/api/v1/cms/media/ordem` | reordenação |
| GET | `/api/v1/cms/media/{id}/download` | download autenticado |

A listagem deve ser paginada no servidor (o legado devolve tudo e pagina no cliente), com os
parâmetros `collection`, `model_type`, `model_id` e `q` (busca por nome).

## 6. Views Blade modernas

- Componente `<x-media-grid collection="drive" />` com:
  - upload por drag-and-drop (`<input type="file" multiple>` com Alpine ou Dropzone);
  - miniaturas com `loading="lazy"`;
  - busca com `debounce` no servidor (ou filtro local se o volume for pequeno);
  - ações por item (copiar, baixar, visualizar, excluir).
- Galeria dos demais módulos: `<x-media-grid collection="galeria" :model="$content" />`, o que
  elimina as 10 inclusões manuais do JS legado.
- Imagem de destaque: `<x-media-picker collection="destaque" />` com recorte e predefinições
  (Grande 600px, Médio 350px, Pequeno 200px, Personalizado) herdadas de `types_sizes`.

## 7. Fila, cache e storage

| Item | Decisão |
|---|---|
| Storage | disco `public` (link via `storage:link`), pasta `media/drive` e `media/{model}/{id}` |
| Fila | processar imagens (geração de `_min`/`_med`/`_larg` e extração de width/height) em job `ShouldQueue` |
| Cache | `Cache::remember("media:drive:page:" . $page, 60, fn () => Media::ofCollection('drive')->forPage($page)->get())` para a listagem; invalidar ao criar/excluir |
| Events | `MediaUploaded`, `MediaDeleted` (disparados por observers) |
| Logs | `spatie/laravel-activitylog` para upload e exclusão, com quem e qual arquivo |

## 8. Riscos e decisões de equivalência funcional

| Risco | Decisão |
|---|---|
| A listagem atual mostra arquivos de todos os módulos | na v7, o Drive deve mostrar **apenas** a coleção `drive`; a mudança é intencional e precisa de comunicação ao operador |
| Os 87 arquivos órfãos do banco (de `manual-usuario`) | precisam ser classificados na migração: coleções `drive` ou `galeria` |
| O nome original dos arquivos se perde | gravar `name` (original) e `file_name` (gerado) em colunas separadas |
| A exclusão hoje não apaga o binário | decidir: apagar o arquivo (recomendado) ou manter para auditoria |
| Mudar o upload de `acceptFiles` (imagens) para aceitar documentos | o `acceptFiles` do legado restringe a imagens; a v7 deve permitir os tipos aceitos pela galeria de conteúdos |
| O JS compartilhado por 10 telas | o componente único `<x-media-grid>` elimina o acoplamento |
| Rotas `galeria.*` podem ser consumidas por integrações | manter aliases durante um ciclo de transição |
