mad-dbcombo-field
Select com MAD Select + busca + auto-query do banco.
Componente select que carrega options automaticamente do banco de dados via model Eloquent.
Props
| Prop | Tipo | Default | Descricao |
|---|---|---|---|
| name | string | '' | Nome do campo |
| label | string | '' | Label do campo |
| model | string | '' | Classe do modelo (ex: Produto, Pessoa) |
| display | string | 'nome' | Campo a exibir. Suporta template: '{nome} - {documento}' |
| key | string | 'id' | Campo da PK |
| database | string | MAIN_DATABASE | Conexao do banco |
| order-by | string | display | Campo para ordenar |
| query | Builder | null | Eloquent/Query Builder pronto (alternativa a model+filters; incompativel com depends-on) |
| filters | array | [] | Filtros: [['campo', 'op', 'val'], ...] |
| depends-on | string | '' | Nome do campo pai para cascata automatica |
| depends-column | string | depends-on | Coluna do model a filtrar (se diferente do campo pai) |
| selected | string | '' | Valor pre-selecionado |
| placeholder | string | 'Selecione...' | Opcao vazia |
| hint | string | '' | Texto de ajuda |
| error | string | '' | Mensagem de erro |
| required | bool | false | |
| disabled | bool | false | |
| attrs | string | '' | Atributos HTML extras |
| id | string | name | ID do elemento |
| no-results-create-action | string | '' | Classe::metodo — botao no dropdown quando busca retorna 0 resultados, abre form em drawer/modal |
| no-results-create-label | string | 'Cadastrar novo' | Texto do botao Create |
| no-results-create-icon | string | 'plus' | Icone Lucide do botao Create |
| no-results-create-class | string | 'mad-btn mad-btn-primary mad-btn-sm' | Classe CSS do botao Create |
| no-results-quick-register-action | string | '' | Classe::metodoEstatico — input + botao inline que cadastra sem sair da tela |
| no-results-quick-register-label | string | 'Adicionar' | Texto do botao Quick Register |
| no-results-quick-register-icon | string | 'check' | Icone Lucide do botao Quick Register |
| no-results-quick-register-class | string | 'mad-btn mad-btn-success mad-btn-sm' | Classe CSS do botao Quick Register |
| no-results-message | string | '' | Mensagem exibida no topo do bloco quando nao ha resultados |
| auto-fill | array | [] | Mapa de auto-fill do registro selecionado. Normalmente NAO se escreve a mao: use as tags filhas <fill> (ver abaixo) |
Uso basico
<mad-dbcombo-field name="tipo_produto_id" label="Tipo" model="TipoProduto" display="descricao" />
Display composto
<mad-dbcombo-field name="pessoa_id" label="Pessoa" model="Pessoa" display="{nome} - {documento}" />
Ordenacao
<mad-dbcombo-field name="pessoa_id" label="Pessoa" model="Pessoa" display="nome" order-by="nome" />
Filtro direto
<mad-dbcombo-field name="pessoa_id" label="Pessoa" model="Pessoa" display="nome"
:filters="[['ativo', '=', '1']]" />
Filtro com subselect (SQL + placeholders ?)
{{-- Simples --}}
<mad-dbcombo-field name="fornecedor_id" label="Fornecedor" model="Pessoa" display="nome"
:filters="[['id', 'in', 'SELECT pessoa_id FROM pessoa_grupo WHERE grupo_pessoa_id = ?', GrupoPessoa::FORNECEDOR]]" />
{{-- Multiplos binds --}}
<mad-dbcombo-field name="fornecedor_id" label="Fornecedor" model="Pessoa" display="nome"
:filters="[['id', 'in', 'SELECT pessoa_id FROM pessoa_grupo WHERE grupo_pessoa_id = ? AND ativo = ?', [GrupoPessoa::FORNECEDOR, '1']]]" />
{{-- Combinando filtro direto + subselect --}}
<mad-dbcombo-field name="fornecedor_id" label="Fornecedor" model="Pessoa" display="nome"
:filters="[
['ativo', '=', '1'],
['id', 'in', 'SELECT pessoa_id FROM pessoa_grupo WHERE grupo_pessoa_id = ?', GrupoPessoa::FORNECEDOR],
]" />
Depends-on (cascata automatica)
Ao selecionar o campo pai, o campo filho recarrega automaticamente filtrando pelo valor selecionado.
<mad-dbcombo-field name="familia_produto_id" label="Familia"
model="FamiliaProduto" display="nome" />
<mad-dbcombo-field name="tipo_produto_id" label="Tipo"
model="TipoProduto" display="descricao"
depends-on="familia_produto_id" />
Se a coluna do model filho for diferente do nome do campo pai, use depends-column:
<mad-dbcombo-field name="estado_id" label="Estado"
model="Estado" display="nome" />
<mad-dbcombo-field name="cidade_id" label="Cidade"
model="Cidade" display="nome"
depends-on="estado_id" depends-column="estado_id" />
Evento change (chamada ao backend)
<mad-dbcombo-field name="tipo_produto_id" label="Tipo"
model="TipoProduto" display="descricao"
mad:change="onTipoChange" />
No controller PHP:
public function onTipoChange(string $value): MadResponse
{
// $value = ID selecionado
return MadToast::info("Tipo selecionado: {$value}");
}
Auto-fill do registro selecionado (<fill>)
Tags filhas <fill> preenchem outros campos do formulario com dados do
registro escolhido no combo — resolucao 100% server-side.
<mad-dbcombo-field name="cliente_id" label="Cliente" model="Cliente" display="nome">
<fill field="email" from="email" />
<fill field="cidade" from="cidade->nome" />
<fill field="nasc" from="data_nasc" transform="date:d/m/Y" only-empty />
</mad-dbcombo-field>
Tags que aceitam <fill> como filho: <mad-dbcombo-field>,
<mad-dbselect-field> e <mad-dbunique-search-field>.
Atributos do <fill>
| Atributo | Obrig. | Descricao |
|---|---|---|
field |
sim | Campo do FORMULARIO a preencher (destino) |
from |
sim | Origem no registro: coluna, caminho de relacao (a->b->c) ou template {x} |
transform |
nao | DSL server-side ou callable — ver tabela abaixo |
only-empty |
nao | Booleano: so preenche se o campo destino estiver vazio |
<fill> sem field ou sem from e ignorado silenciosamente.
Como funciona
O pre-compilador Mad\Form\MadAutoFillCompiler roda no
MadBlade::compileString(), extrai os <fill> do corpo e injeta
:auto-fill="[...]" no componente pai (por isso a prop auto-fill existe, mas
raramente e escrita a mao).
No render, o componente cifra a config (model, key, database, o
query_sql + bindings do proprio combo e o mapa de fills) com
MadStateCrypt::encrypt() e embute em data-mad-autofill-token. Ao selecionar,
o JS chama /app/services/auto-fill/resolve?static=1 mandando apenas
{ token, value } — nada de config vem do cliente.
O Mad\Service\MadAutoFillService decripta, valida o value contra o conjunto
filtrado do combo (derived table + WHERE key = value, portanto um id forjado
fora do filtro nao retorna nada), recarrega o registro, resolve cada from via
MadRecordPath e aplica o transform via MadFillTransform. Resposta JSON:
{ values, onlyEmpty, warnings }.
Com
modelconhecido a carga usa Eloquent — e por isso quefrom="a->b->c"funciona. Com apenas:querycru (semmodel), a carga usa a propria derived table: so colunas planas; caminhos de relacao resolvem para''.
DSL de transform
Vocabulario fixo (sem eval), encadeavel com | da esquerda pra direita:
| Spec | Efeito |
|---|---|
upper |
MAIUSCULAS |
lower |
minusculas |
title |
Primeira Letra De Cada Palavra |
trim |
Remove espacos das pontas |
date:FMT |
Carbon::parse(...)->format($fmt) — default d/m/Y |
money |
number_format pt-BR com 2 casas (1234.5 → 1.234,50) |
number:N |
number_format pt-BR com N casas |
mask:PADRAO |
Preenche os placeholders #, 9, A do padrao (ex.: mask:###.###-##) |
Classe::metodo |
Callable do dev: metodo(string $valor, ?object $registro): string |
<fill field="doc" from="cpf" transform="mask:###.###.###-##" />
<fill field="nome" from="nome" transform="trim|title" />
<fill field="preco" from="valor" transform="money" />
Callable: a forma curta gerada pelo MadBuilder (FillTransformer::formataCpf,
DocumentTransformer::…, GridTransformer::…, sem namespace) e resolvida para
\App\Transformer\…. O spec viaja cifrado no token, entao o cliente nao injeta
callable — mesmo nivel de confianca do navigate="Classe::metodo".
Regras de borda
- Input e sempre
string(MadRecordPathcasta com(string)). - Valor vazio → retorna
''sem tentar transformar. transformvazio → passthrough.- Spec desconhecido, callable invalido ou callable que lanca → passthrough +
error_log+ entrada emwarnings. Um typo nunca derruba o auto-fill inteiro.
No-results create + quick register
Quando o usuario busca algo que nao existe no combo, voce pode oferecer:
- Botao "Cadastrar novo" — abre um form MAD em drawer/modal via
Mad.go() - Input "Quick register" — cadastro inline sem sair da tela (via service estatico)
Botao Create (abre form em drawer)
<mad-dbcombo-field name="cliente_id" label="Cliente" model="Pessoa" display="nome"
no-results-create-action="ClienteForm::show"
no-results-create-label="Novo cliente"
no-results-message="Cliente nao encontrado" />
No ClienteForm::mount(), ler os parametros implicitos:
public function mount(MadRequest $req): void
{
$this->form = new MadForm('form');
$this->_originFieldName = $req->string('_field_name'); // 'cliente_id' (nome do campo que abriu)
$term = $req->string('_term'); // texto digitado no combo
if ($term) {
$this->form->set('nome', $term); // pre-preenche o nome
}
}
Ao salvar, retornar ops pra atualizar o combo original:
public function onSave(): MadResponse
{
// ... salvar ...
$items = Pessoa::orderBy('nome')->pluck('nome', 'id')->all();
return (new MadResponse())
->reloadCombo($this->_originFieldName, $items, (string)$pessoa->id)
->closeDrawer()
->toast('Cliente cadastrado!', 'success');
}
Input Quick Register (cadastro inline)
Criar um metodo estatico no controller que recebe o termo digitado e retorna
['value' => $id, 'label' => $displayText]:
class ClienteForm extends MadComponent {
public static function quickRegister(array $params): array
{
$nome = trim($params['term'] ?? '');
if ($nome === '') {
throw new Exception('Nome obrigatorio');
}
$p = new Pessoa();
$p->nome = $nome;
$p->save();
return [
'value' => (string)$p->id,
'label' => $p->nome,
'toast' => 'Cliente cadastrado!',
];
}
}
E no Blade:
<mad-dbcombo-field name="cliente_id" label="Cliente" model="Pessoa" display="nome"
no-results-quick-register-action="ClienteForm::quickRegister"
no-results-quick-register-label="Cadastrar"
no-results-quick-register-icon="zap" />
O usuario digita "Maria Silva", clica no botao, e o registro e criado + selecionado no combo sem reload.
Ambos combinados
<mad-dbcombo-field name="cliente_id" label="Cliente" model="Pessoa" display="nome"
no-results-create-action="ClienteForm::show"
no-results-quick-register-action="ClienteForm::quickRegister"
no-results-message="Nao achou? Cadastre agora." />
Quick Register multi-campo (<mad-quick-form> declarativo)
Quando o quick register precisa de mais de um campo (ex: cidade =
nome + estado + IBGE), use a tag filha <mad-quick-form> com os campos como
filhos. O pre-compilador (MadQuickFormCompiler) extrai os filhos e injeta
como :no-results-quick-fields no pai automaticamente.
<mad-dbunique-search-field name="cidade_id" model="Cidade" display="nome" min-length="2">
<mad-quick-form action="CidadeForm::quickRegister" label="Cadastrar"
icon="check" message="Cadastre a cidade:">
<mad-input-field name="nome" label="Nome" required />
<mad-dbcombo-field name="estado_id" label="Estado"
model="Estado" display="nome" order-by="nome" required />
<mad-input-field name="codigo_ibge" label="IBGE" />
</mad-quick-form>
</mad-dbunique-search-field>
Atributos do <mad-quick-form> (mapeados pra props no pai):
| Atributo | Mapeia para |
|---|---|
action |
no-results-quick-register-action |
label |
no-results-quick-register-label |
icon |
no-results-quick-register-icon |
class |
no-results-quick-register-class |
message |
no-results-message |
Tags filhas suportadas:
<mad-input-field type="text|number|email|tel|password|date">— input nativo<mad-date-field>— campo de data<mad-numeric-field>— vira input text<mad-dbcombo-field model="..." display="...">— auto-query do banco<mad-select-field :items="...">— options inline
No PHP, o callable recebe $params['fields'] com array assoc:
public static function quickRegister(array $params): array
{
$fields = $params['fields']; // ['nome' => ..., 'estado_id' => ..., 'codigo_ibge' => ...]
if (empty($fields['nome'])) throw new \Exception('Nome obrigatorio');
if (empty($fields['estado_id'])) throw new \Exception('Estado obrigatorio');
$cidade = new Cidade();
$cidade->nome = trim($fields['nome']);
$cidade->estado_id = (int) $fields['estado_id'];
$cidade->codigo_ibge = trim($fields['codigo_ibge'] ?? '');
$cidade->save();
return ['value' => (string)$cidade->id, 'label' => $cidade->nome];
}
Alternativa (array PHP): quando os campos sao gerados dinamicamente,
use :no-results-quick-fields direto com array PHP (mesma estrutura final):
<mad-dbcombo-field name="cidade_id" ...
:no-results-quick-fields="[
['name' => 'nome', 'label' => 'Nome', 'type' => 'text', 'required' => true],
['name' => 'estado_id', 'label' => 'Estado', 'type' => 'dbcombo',
'model' => 'Estado', 'display' => 'nome', 'required' => true],
]" />
NUNCA usar
{{-- ERRADO: gerar options manualmente quando o dbcombo faz isso automaticamente --}}
<mad-select-field name="tipo_id" label="Tipo">
@foreach($tipos as $id => $nome)
<option value="{{ $id }}">{{ $nome }}</option>
@endforeach
</mad-select-field>
{{-- CERTO: usar dbcombo --}}
<mad-dbcombo-field name="tipo_id" label="Tipo" model="TipoProduto" display="descricao" />