# Redes Sociais — Migração para Laravel Moderno

## 1. Decisão de modelagem

Criar a tabela `social_networks`, separando a rede do conteúdo genérico. O módulo é pequeno e
tipado; mantê-lo em `contents` (com URL em `links`) força joins e regras condicionais no Controller.

```php
// database/migrations/xxxx_create_social_networks_table.php
Schema::create('social_networks', function (Blueprint $table) {
    $table->id();
    $table->string('name', 120);
    $table->string('icon', 191);
    $table->string('url', 2999);
    $table->boolean('is_blank')->default(false);
    $table->boolean('active')->default(true);
    $table->unsignedInteger('sort_order')->default(0);
    $table->unsignedTinyInteger('position')->default(3); // 1 cabeçalho, 2 rodapé, 3 ambos
    $table->timestamps();
    $table->softDeletes();

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

### Migração dos dados legados

```php
// exemplo ilustrativo
$links = DB::table('links')->whereNotNull('contents_id')->get()->keyBy('contents_id');

DB::table('contents')
    ->join('category_contents', 'contents.category_contents_id', '=', 'category_contents.id')
    ->where('category_contents.slug', 'rede-social')
    ->whereNull('contents.deleted_at')
    ->orderBy('contents.id')
    ->get()
    ->each(function ($row) use ($links) {
        SocialNetwork::updateOrCreate(
            ['legacy_id' => $row->id],
            [
                'name' => $row->title,
                'icon' => $row->icon,
                'url' => $links[$row->id]->link ?? '',
                'is_blank' => (bool) ($links[$row->id]->isBlank ?? false),
                'active' => (bool) $row->status,
                'sort_order' => $row->order ?? 0,
                'position' => $row->position_id ?? 3,
            ]
        );
    });
```

## 2. Modelos

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

    protected $fillable = ['name', 'icon', 'url', 'is_blank', 'active', 'sort_order', 'position'];

    protected function casts(): array
    {
        return [
            'is_blank' => 'boolean',
            'active' => 'boolean',
            'sort_order' => 'integer',
            'position' => SocialPosition::class,
        ];
    }
}
```

```php
// app/Enums/SocialPosition.php  (exemplo ilustrativo)
enum SocialPosition: int
{
    case Cabecalho = 1;
    case Rodape = 2;
    case Ambos = 3;
}
```

Enum opcional para o provedor (a lista de brands do Font Awesome 5 usada pelo
`v6/resources/views/components/inputs/icon-picker.blade.php`): `facebook`, `instagram`, `youtube`,
`twitter`, `linkedin`, `tiktok`, `whatsapp`, etc.

## 3. Form Requests

```php
// app/Http/Requests/StoreSocialNetworkRequest.php  (exemplo ilustrativo)
public function rules(): array
{
    return [
        'name' => ['required', 'string', 'max:120'],
        'icon' => ['required', 'string', 'max:191'],
        'url' => ['required', 'url', 'max:2999'],
        'position' => ['required', Rule::enum(SocialPosition::class)],
        'sort_order' => ['required', 'integer', 'min:1'],
        'active' => ['boolean'],
        'is_blank' => ['boolean'],
    ];
}
```

Regras do legado a preservar: `title` obrigatório (máx. 120), `icon` obrigatório, `link` obrigatório
(máx. 2999), `order` obrigatório, `category_contents_id` obrigatório e numérico.

## 4. Policies e autorização

```php
// app/Policies/SocialNetworkPolicy.php  (exemplo ilustrativo)
public function update(User $user, SocialNetwork $network): bool
{
    return $user->can('redes-sociais.edit');
}
```

Ao migrar, **unificar o prefixo das permissões**: o legado usa `redes-sociais.*` no seed e
`rede-social.*` no código. Escolher `redes-sociais.*` e atualizar as views.

## 5. Endpoints REST

### Portal (público, cacheável)

| Método | URI | Descrição |
|---|---|---|
| GET | `/api/v1/redes-sociais` | redes ativas, com filtro `?position=cabecalho|rodape|ambos` |

```php
// app/Http/Resources/SocialNetworkResource.php  (exemplo ilustrativo)
return [
    'id' => $this->id,
    'name' => $this->name,
    'icon' => $this->icon,
    'url' => Str::startsWith($this->url, 'http') ? $this->url : 'http://' . $this->url,
    'is_blank' => $this->is_blank,
    'position' => $this->position->value,
];
```

### CMS (autenticado)

| Método | URI | Descrição |
|---|---|---|
| GET | `/api/v1/cms/redes-sociais` | listagem paginada e reordenável |
| POST | `/api/v1/cms/redes-sociais` | criação |
| PUT | `/api/v1/cms/redes-sociais/{id}` | atualização |
| DELETE | `/api/v1/cms/redes-sociais/{id}` | exclusão |
| PUT | `/api/v1/cms/redes-sociais/reordenar` | recebe a lista de ids na nova ordem |

## 6. Views Blade modernas

- **Componente único** `<x-social-links position="cabecalho" />` e `<x-social-links position="rodape" />`,
  incluído em todos os layouts. Elimina as ~11 cópias da query (armadilha A1).
- O componente recebe a coleção por injeção (`@inject`) ou por `View::composer`; melhor: um
  `View::composer` global que supply a coleção já filtrada por posição.
- Ícones: `<i class="{{ $network->icon }}"></i>` mantidos, mas com validação no servidor contra uma
  lista fechada de classes (evita HTML arbitrário no campo `icon`).
- CMS: tabela com Livewire ou Inertia, botão de status (toggle) e ordenação por drag-and-drop
  enviando a lista de ids para o endpoint de reordenação.
- Pagina de “Redes Sociais” no portal: **opcional**. O legado não tem; se for criada, usar
  `GET /redes-sociais` com breadcrumb próprio.

## 7. Fila, cache e storage

| Item | Decisão |
|---|---|
| Cache | `Cache::remember('social-networks', 300, fn () => SocialNetwork::active()->get())` para a API pública; invalidado por `saved`/`deleted` do model |
| Fila | desnecessária |
| Storage | desnecessária (o ícone é CSS, não imagem) |
| Logs | `spatie/laravel-activitylog` para create/update/delete/toggle |

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

| Risco | Decisão |
|---|---|
| Perder o link por não haver `links` correspondente | o script de migração deve reportar conteúdos sem `link` |
| Ícones fora da lista de brands | manter o valor como está e reportar os desconhecidos |
| Perder a ordem | migrar `contents.order` para `sort_order` |
| Módulo desativado (`modulos.status = false`) | na v7, decidir se a v7 mantém a chave liga/desliga ou se a simples ausência de registros resolve |
| URL relativa (sem domínio) | manter a normalização `http://` para não quebrar links existentes |
| Trocar `position_id` numérico por enum | gravar o valor numérico para manter compatibilidade com dados legados |
