Docs›Componentes (Admin)›mad-dbcombo-field
Componentes (Admin)

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 model conhecido a carga usa Eloquent — e por isso que from="a->b->c" funciona. Com apenas :query cru (sem model), 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 (MadRecordPath casta com (string)).
  • Valor vazio → retorna '' sem tentar transformar.
  • transform vazio → passthrough.
  • Spec desconhecido, callable invalido ou callable que lanca → passthrough + error_log + entrada em warnings. 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:

  1. Botao "Cadastrar novo" — abre um form MAD em drawer/modal via Mad.go()
  2. 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" />