Campos de formulário
mad-file-field
mad-multi-file-field
mad-attachments
mad-attach-btn
Arquivo único
mad-file-field: um arquivo por campo, storage disk ou db.
Clique para enviar ou arraste aqui
.pdf,.doc,.docx · Máx. 10MB
mad-file-field puro — nível demo, sem storage: o arquivo não é gravado em disco/banco nesta página.
resources/views/tutor/examples/forms-uploads/arquivo-unico.blade.php
<mad-file-field name="contrato_demo" label="Contrato (arquivo único)"
accept=".pdf,.doc,.docx" max-size="10MB"
hint="mad-file-field puro — nível demo, sem storage: o arquivo não é gravado em disco/banco nesta página." />
Múltiplos arquivos
mad-multi-file-field: vários arquivos, mode=comma ou mode=table.
Anexos (múltiplos arquivos)
Arraste arquivos aqui ou clique para selecionar
Máx. 5 arquivo(s)
mode=comma (padrão): vários arquivos no mesmo campo. Para 1 linha por arquivo numa tabela filha, use mode=table (model + foreign-key).
resources/views/tutor/examples/forms-uploads/multiplos-arquivos.blade.php
<mad-multi-file-field name="anexos_demo" label="Anexos (múltiplos arquivos)"
accept=".pdf,.png,.jpg" :max-files="5"
hint="mode=comma (padrão): vários arquivos no mesmo campo. Para 1 linha por arquivo numa tabela filha, use mode=table (model + foreign-key)." />
Botão compacto
mad-attach-btn: mesma API de storage do multi-file, visual de paperclip pra toolbar.
Nova mensagem
Botão compacto (ícone paperclip) — mesma API de storage do mad-multi-file-field, pensado para toolbar de chat/reply.
resources/views/tutor/examples/forms-uploads/attach-btn.blade.php
{{-- mad-attach-btn não tem prop hint/label — o rótulo/dica aqui é markup ao redor. --}}
<div class="mad-field">
<label class="mad-label">Anexar na conversa</label>
<div style="display:flex; align-items:center; gap:8px;">
<span>Nova mensagem</span>
<mad-attach-btn name="anexo_chat_demo" accept="image/*,.pdf" multiple :max-files="3" />
</div>
<p class="mad-field-hint">Botão compacto (ícone paperclip) — mesma API de storage do mad-multi-file-field, pensado para toolbar de chat/reply.</p>
</div>
Ação wire
mad-form com submit real — dispara TutorFormsUploads::onUploadDemo, que lê os arquivos crus de $_FILES e mostra nome/tamanho no toast (sem persistir).
resources/views/tutor/examples/forms-uploads/wire-submit.blade.php
<mad-form submit="onUploadDemo">
<mad-form-grid :cols="2">
<mad-file-field name="arquivo_unico_wire" label="Arquivo (único)"
accept=".pdf,.png,.jpg" max-size="10MB" />
<mad-multi-file-field name="anexos_wire" label="Anexos (múltiplos)"
accept=".pdf,.png,.jpg" :max-files="3" />
</mad-form-grid>
<mad-form-actions align="right">
<mad-btn type="submit" variant="primary" icon="upload">Enviar (onUploadDemo)</mad-btn>
</mad-form-actions>
</mad-form>
app/control/Tutor/TutorFormsUploads.php
public function onUploadDemo(): MadResponse
{
$unico = MadUploadIngest::filesFor('arquivo_unico_wire');
$multi = MadUploadIngest::filesFor('anexos_wire');
$partes = [];
if ($unico !== []) {
$f = $unico[0];
$partes[] = "arquivo_unico_wire={$f['name']} (" . $this->formatBytes((int) $f['size']) . ')';
}
if ($multi !== []) {
$total = array_sum(array_column($multi, 'size'));
$partes[] = count($multi) . ' anexo(s) em anexos_wire, ' . $this->formatBytes((int) $total) . ' no total';
}
$resumo = $partes !== [] ? implode(' | ', $partes) : 'nenhum arquivo selecionado';
return (new MadResponse())
->toast("Upload recebido: {$resumo}", 'success', 'onUploadDemo');
}
mad-attachments não dá pra demonstrar ao vivo aqui
mad-attachments exige
Sintaxe de referência (numa tela de detalhe real, com o registro já salvo):
model + foreign-key apontando pra um registro PAI que já
existe no banco — ele resolve sozinho o id do registro a partir da tela atual e lista/envia/apaga anexos
reais daquele registro. O MAD Lab é uma página de exemplos sem estado (sem registro persistido nem
tabela de anexos própria), então demonstrar esse componente ao vivo exigiria sair do escopo de "showcase
de campos" — precisaria de uma tabela filha nova (migration/model), um registro pai fixo e trait extra,
o que não cabe no nível demo desta página (ver relatório do lote).
Sintaxe de referência (numa tela de detalhe real, com o registro já salvo):
<mad-attachments model="TicketAttachments" foreign-key="ticket_id" folder="uploads/tickets" />Código-fonte completo da página
app/control/Tutor/TutorFormsUploads.php
<?php
namespace App\Control\Tutor;
use App\Support\Tutor\TutorPage;
use Mad\Form\MadForm;
use Mad\Http\MadResponse;
use Mad\Service\MadUploadIngest;
/**
* TutorFormsUploads — página "Uploads" do catálogo (grupo forms).
*
* Cobre mad-file-field, mad-multi-file-field e mad-attach-btn com demos
* vivas. mad-attachments fica documentado via callout na view — ele exige
* model+foreign-key apontando pra um registro PAI já persistido (resolve o
* id sozinho a partir da tela), o que não existe numa página de exemplos sem
* estado como esta (ver relatório final do lote).
*
* Nível demo: nenhum arquivo é gravado em disco/banco. onUploadDemo() lê os
* arquivos crus de $_FILES via MadUploadIngest::filesFor() (mesmo helper que
* o framework usa internamente antes de gravar) só para exibir nome/tamanho
* no toast — sem storage, sem form->save() num registro.
*/
class TutorFormsUploads extends TutorPage
{
protected string $catalogKey = 'forms_uploads';
public MadForm $form;
public function mount(array $params = []): void
{
$this->form = new MadForm('tutor_forms_uploads');
}
public function onUploadDemo(): MadResponse
{
$unico = MadUploadIngest::filesFor('arquivo_unico_wire');
$multi = MadUploadIngest::filesFor('anexos_wire');
$partes = [];
if ($unico !== []) {
$f = $unico[0];
$partes[] = "arquivo_unico_wire={$f['name']} (" . $this->formatBytes((int) $f['size']) . ')';
}
if ($multi !== []) {
$total = array_sum(array_column($multi, 'size'));
$partes[] = count($multi) . ' anexo(s) em anexos_wire, ' . $this->formatBytes((int) $total) . ' no total';
}
$resumo = $partes !== [] ? implode(' | ', $partes) : 'nenhum arquivo selecionado';
return (new MadResponse())
->toast("Upload recebido: {$resumo}", 'success', 'onUploadDemo');
}
private function formatBytes(int $bytes): string
{
if ($bytes < 1024) {
return "{$bytes} B";
}
if ($bytes < 1024 * 1024) {
return round($bytes / 1024, 1) . ' KB';
}
return round($bytes / (1024 * 1024), 1) . ' MB';
}
}
resources/views/tutor/forms/uploads.blade.php
<x-tutor-page page="forms_uploads">
<x-tutor-demo demo="tutor.examples.forms-uploads.arquivo-unico"
title="Arquivo único" desc="mad-file-field: um arquivo por campo, storage disk ou db." />
<x-tutor-demo demo="tutor.examples.forms-uploads.multiplos-arquivos"
title="Múltiplos arquivos" desc="mad-multi-file-field: vários arquivos, mode=comma ou mode=table." />
<x-tutor-demo demo="tutor.examples.forms-uploads.attach-btn"
title="Botão compacto" desc="mad-attach-btn: mesma API de storage do multi-file, visual de paperclip pra toolbar." />
<x-tutor-demo demo="tutor.examples.forms-uploads.wire-submit"
title="Ação wire" desc="mad-form com submit real — dispara TutorFormsUploads::onUploadDemo, que lê os arquivos crus de $_FILES e mostra nome/tamanho no toast (sem persistir)."
php="TutorFormsUploads::onUploadDemo" />
<mad-callout type="warning" title="mad-attachments não dá pra demonstrar ao vivo aqui" icon="triangle-alert">
mad-attachments exige <code>model</code> + <code>foreign-key</code> apontando pra um registro PAI que já
existe no banco — ele resolve sozinho o id do registro a partir da tela atual e lista/envia/apaga anexos
reais daquele registro. O MAD Lab é uma página de exemplos sem estado (sem registro persistido nem
tabela de anexos própria), então demonstrar esse componente ao vivo exigiria sair do escopo de "showcase
de campos" — precisaria de uma tabela filha nova (migration/model), um registro pai fixo e trait extra,
o que não cabe no nível demo desta página (ver relatório do lote).
<br><br>
Sintaxe de referência (numa tela de detalhe real, com o registro já salvo):
<br>
<code><mad-attachments model="TicketAttachments" foreign-key="ticket_id" folder="uploads/tickets" /></code>
</mad-callout>
</x-tutor-page>