Docs›Componentes (Admin)›mad-multi-file-field
Componentes (Admin)

mad-multi-file-field

Upload múltiplos (mode comma|table).

Upload de multiplos arquivos com drag-and-drop, galeria de thumbnails e preview. Substitui <input type="file" multiple>. Para upload de um unico arquivo, ver <mad-file-field> em file-field.md.

IMPORTANTE: Sempre usar form->save($record) ao inves de fillRecord + $record->save() manual — o MadForm::save() encapsula tudo: preenche campos, salva os arquivos (disco ou BLOB), persiste ($record->save()), e roda os hooks pos-persistencia que gravam os paths/registros filhos.

Props

Prop Tipo Default Descricao
name string '' Nome do campo (obrigatorio, usa name[] automaticamente)
label string '' Label do campo
width string '' Largura inline do wrapper (100px, 50%)
max-width string '' Largura maxima inline
accept string '*' MIME types ou extensoes: image/*,.pdf
max-files int 0 Maximo de arquivos (0 = ilimitado)
max-size int 0 Tamanho maximo por arquivo em KB (0 = ilimitado)
storage string '' disk (filesystem) ou db (BLOB base64 no banco)
folder string 'uploads' Diretorio destino (storage="disk"). Caminho relativo — absoluto, .. (path traversal) ou null byte sao rejeitados em tela (MadUploadPath::assertValidFolder)
mode string 'comma' comma (CSV de paths, so storage="disk") ou table (registro por arquivo numa tabela filha)
model string '' Classe do model Eloquent da tabela filha (mode=table)
foreign-key string '' Coluna FK na tabela filha (mode=table)
path-column string '' Coluna do caminho (disk) ou do BLOB (db) na tabela filha (mode=table)
name-column string '' Coluna para o nome original do arquivo (mode=table)
file-name string 'prefix' Modo de geracao do nome: prefix, unique, original, record
hint string '' Texto de ajuda
error string '' Mensagem de erro
required bool false Campo obrigatorio
disabled bool false Campo desabilitado
attrs string '' Atributos HTML extras

mode="comma" so e processado no save quando storage="disk" — com storage="db" e mode="comma" o save nao grava nada; use mode="table" para BLOB.

Onde os arquivos ficam — Storage/Flysystem

Com storage="disk", o save() grava pela API de Filesystem do Laravel, no disco de uploads: default mad_uploads (root storage/app/mad), ou o disco apontado por MAD_UPLOAD_DISK (config('mad.uploads.disk')) quando voce quer S3 ou outro adapter. Nao ha fallback para a raiz do projeto — nao existe mais escrita direta em filesystem.

O que vai pro banco continua sendo o caminho relativo (folder + nome do arquivo), nunca o caminho absoluto — trocar de disco nao exige migracao de dados. Para servir o arquivo, use o download assinado do framework em vez de montar URL publica.

folder e validado por MadUploadPath::assertValidFolder: precisa ser relativo; absoluto, .. (path traversal) ou null byte sao rejeitados.

Nomeacao do arquivo (file-name)

Mesma logica do <mad-file-field> (MadForm::_buildFileName()), aplicada a cada arquivo do lote.

Modo Resultado Quando usar
prefix 65a7f8b2c4d3e_contrato.pdf Padrao — evita colisao, preserva nome
unique 65a7f8b2c4d3e.pdf Quando nome original nao importa
original contrato.pdf Quando precisa manter o nome exato
record 42_contrato.pdf Vincula ao registro (fallback uniqid se novo)
<mad-multi-file-field name="anexos" label="Anexos"
    storage="disk" folder="uploads/docs" file-name="original" />

Evento change (callback ao adicionar arquivos)

<mad-multi-file-field name="anexos" label="Anexos"
    accept=".pdf,.csv" mad:change="onAnexosAdded" />
use Mad\Service\MadScratchStorage;

public function onAnexosAdded(array $tempKeys): void
{
    foreach ($tempKeys as $key) {
        $conteudo = MadScratchStorage::disk()->get($key); // ler cada arquivo
    }
    $this->form->set('total_anexos', count($tempKeys));
}

O callback recebe chaves relativas de Storage, nao caminhos absolutos de filesystem: o MadComponentHandler faz o staging do $_FILES no disco de scratch mad_tmp (storage/app/mad-tmp) sob o prefixo mad_uploads/, e passa mad_uploads/<uniqid>_<nome> para a acao. Leia sempre via MadScratchStorage::disk() (ou Storage::disk('mad_tmp')) — nunca com file_get_contents() em caminho montado a mao. Arquivos staged com mais de 1h sao removidos por limpeza probabilistica.

Funciona com auto-bind (void) ou MadResponse — mesma assinatura de mad-file-field e mad-image-field, mas recebendo array $tempKeys (multi) ao inves de string $tempKey (single).

Modo virgula — caminhos separados por virgula

<mad-multi-file-field name="anexos" label="Anexos"
    storage="disk" folder="uploads/docs" mode="comma"
    accept=".pdf,.doc" :max-files="5" />

Resultado no banco: anexos = 'uploads/docs/abc.pdf,uploads/docs/xyz.doc'

use Illuminate\Support\Facades\DB;

// Controller — zero codigo de arquivo
DB::connection('business')->transaction(function () {
    $record = MeuModel::findOrNew($this->registroId);
    $this->form->save($record);
    $this->registroId = (int) $record->id;
});

Na edicao, o componente carrega os arquivos existentes (CSV de paths) e mostra na galeria; o que o usuario remover/mantiver volta como hidden inputs (__mad_existing_files) e o save() reconcilia (apaga do disco os removidos, mantem os demais, acrescenta os novos).

Modo tabela — um registro por arquivo

<mad-multi-file-field name="arquivos" label="Arquivos"
    storage="disk" folder="uploads/negociacao" mode="table"
    model="NegociacaoArquivo" foreign-key="negociacao_id"
    path-column="conteudo_arquivo" name-column="nome_arquivo"
    accept=".pdf,image/*" :max-files="10" />

O save() cria um registro na tabela filha para cada arquivo:

  • $child->negociacao_id = $record->id
  • $child->conteudo_arquivo = 'uploads/negociacao/xxx.pdf'
  • $child->nome_arquivo = 'contrato.pdf' (nome original sanitizado)

Na edicao, o componente carrega os arquivos ja vinculados via o model+foreign-key+path-column declarados, e reconcilia remocoes/mantidos do mesmo jeito que o modo comma.

Modo tabela com BLOB — salva base64 no banco

<mad-multi-file-field name="arquivos" label="Arquivos"
    storage="db" mode="table"
    model="NegociacaoArquivo" foreign-key="negociacao_id"
    path-column="conteudo_arquivo" name-column="nome_arquivo"
    accept=".pdf,image/*" :max-files="10" />

Cada arquivo e salvo como BLOB base64 na coluna conteudo_arquivo da tabela filha. Suporta MySQL (LONGBLOB), PostgreSQL (BYTEA), SQL Server (VARBINARY), Oracle (BLOB).

Multi-file sem storage (manual)

<mad-multi-file-field name="anexos" label="Anexos" />

Sem storage, o dev itera $_FILES['anexos'] manualmente — save() nao faz nada com o campo.

form->save() vs fillRecord+save

Cenario Usar
Formulario padrao (recomendado) $this->form->save($record)
Precisa setar campos entre fill e persistir Setar ANTES do save()
Sem campos de arquivo save() funciona igual (fillRecord() + $record->save())
// RECOMENDADO: save() encapsula tudo
$record->negociacao_id = $this->negociacaoId; // setar antes
$this->form->save($record);

// ALTERNATIVA: controle fino
$this->form->fillRecord($record);
$record->campo_extra = 'valor';
$record->save();

Quando usar cada componente

Preciso... Usar
Upload unico no disco ou BLOB <mad-file-field> (ver file-field.md)
Upload multiplos (CSV de paths) <mad-multi-file-field storage="disk" mode="comma">
Upload multiplos (tabela filha, disco) <mad-multi-file-field storage="disk" mode="table">
Upload multiplos (tabela filha, BLOB) <mad-multi-file-field storage="db" mode="table">
Upload de imagem com crop/camera <mad-image-field> (ver image-field.md)

NUNCA fazer

{{-- ERRADO: input nativo multiplo --}}
<input type="file" name="anexos[]" multiple>

{{-- CERTO --}}
<mad-multi-file-field name="anexos" label="Anexos" storage="disk" folder="uploads/docs" />

{{-- ERRADO: codigo de arquivo manual no controller quando storage ja resolve --}}
foreach ($_FILES['anexos']['tmp_name'] as $i => $tmp) {
    move_uploaded_file($tmp, $dest);
}

{{-- CERTO: save() faz tudo --}}
$this->form->save($record);

{{-- ERRADO: usar mode="comma" esperando funcionar com storage="db" --}}
<mad-multi-file-field name="anexos" storage="db" mode="comma" />
{{-- mode=comma so e processado quando storage="disk" --}}

{{-- CERTO: BLOB exige mode=table --}}
<mad-multi-file-field name="anexos" storage="db" mode="table"
    model="Anexo" foreign-key="registro_id" path-column="conteudo" />

{{-- ERRADO: persistir os arquivos filhos manualmente quando mode=table resolve --}}
$this->form->save($record);
foreach ($_FILES['arquivos']['tmp_name'] as $i => $tmp) {
    Anexo::create(['registro_id' => $record->id, 'conteudo' => $tmp]);
}

{{-- CERTO: declarar mode="table" no Blade, save() cria os registros filhos --}}
<mad-multi-file-field name="arquivos" storage="disk" mode="table"
    model="Anexo" foreign-key="registro_id" path-column="conteudo" />