MAD Lab
/
Tutor > Campos de formulário > Busca

Busca

Campos de formulário

mad-unique-search-field mad-multi-search-field mad-multi-entry-field mad-seek-field
Busca única

mad-unique-search-field: select buscável (TomSelect) com options fixas.

Select buscável (TomSelect) com as options pré-carregadas — aqui vindas da fixture TutorDemoCliente (business.tutor_clientes, ~40 registros). Pra busca AJAX no servidor com listas grandes, veja mad-dbunique-search-field (grupo Busca (DB)).

resources/views/tutor/examples/forms-busca/unique-search.blade.php
@php
    // mad-unique-search-field não tem prop model/database própria (isso é
    // do mad-dbunique-search-field, grupo Busca (DB)) — as options vêm de um
    // array PHP normal. Aqui usamos a fixture do MAD Lab pra não depender
    // de dados inventados.
    $tutorBuscaClientesUnico = \App\Models\TutorDemo\TutorDemoCliente::query()
        ->orderBy('nome')
        ->get(['id', 'nome', 'cidade', 'uf'])
        ->mapWithKeys(fn ($c) => [(string) $c->id => "{$c->nome} — {$c->cidade}/{$c->uf}"])
        ->all();
@endphp
<mad-unique-search-field name="cliente_unico" label="Cliente"
    :options="$tutorBuscaClientesUnico" placeholder="Buscar cliente..."
    hint="Select buscável (TomSelect) com as options pré-carregadas — aqui vindas da fixture TutorDemoCliente (business.tutor_clientes, ~40 registros). Pra busca AJAX no servidor com listas grandes, veja mad-dbunique-search-field (grupo Busca (DB))." />
Busca múltipla

mad-multi-search-field: escolhe vários itens buscando por texto.

mode=comma (padrão): salva os ids escolhidos separados por vírgula numa coluna do registro pai. mode=table grava numa tabela pivot via form->save().

resources/views/tutor/examples/forms-busca/multi-search.blade.php
@php
    $tutorBuscaClientesMulti = \App\Models\TutorDemo\TutorDemoCliente::query()
        ->orderBy('nome')
        ->get(['id', 'nome', 'cidade', 'uf'])
        ->mapWithKeys(fn ($c) => [(string) $c->id => "{$c->nome} — {$c->cidade}/{$c->uf}"])
        ->all();
@endphp
<mad-multi-search-field name="clientes_multi" label="Clientes (vários)"
    :options="$tutorBuscaClientesMulti" placeholder="Buscar e adicionar clientes..."
    hint="mode=comma (padrão): salva os ids escolhidos separados por vírgula numa coluna do registro pai. mode=table grava numa tabela pivot via form->save()." />
Tags / chips

mad-multi-entry-field: tags livres (digita e Enter) ou lista fixa (com options).

Sem options: o usuário cria qualquer tag digitando e pressionando Enter.

Com options: vira uma lista fechada — só dá pra escolher o que já existe.

resources/views/tutor/examples/forms-busca/tags.blade.php
<mad-form-grid :cols="2">
    <mad-multi-entry-field name="tags_livres" label="Tags livres" placeholder="Digite e Enter..."
        hint="Sem options: o usuário cria qualquer tag digitando e pressionando Enter." />

    <mad-multi-entry-field name="modulos_fixos" label="Módulos"
        :options="['erp' => 'ERP', 'crm' => 'CRM', 'financeiro' => 'Financeiro', 'estoque' => 'Estoque']"
        hint="Com options: vira uma lista fechada — só dá pra escolher o que já existe." />
</mad-form-grid>
Busca em popup (primitivo)

mad-seek-field: só dispara o evento mad:seek — a busca em si é montada pela página (Alpine puro, neste exemplo).

O botão só dispara mad:seek — este exemplo escuta com Alpine puro e abre a lista abaixo (10 primeiros clientes da fixture).

resources/views/tutor/examples/forms-busca/seek.blade.php
@php
    // mad-seek-field é um PRIMITIVO: ele só renderiza um input + botão de
    // lupa que despacha o evento "mad:seek" (window) com {name, onSeek, el}.
    // O framework não embute nenhum modal/lista pronta pra essa variante
    // manual (isso é o mad-seek / mad-dbseek-field — grupo Busca (DB) — que
    // já vêm com grid de busca embutida). Quem abre a busca é a página.
    //
    // Este exemplo escuta "mad:seek" com Alpine puro e mostra uma lista
    // simples logo abaixo do campo, reaproveitando as classes CSS do
    // mad-dropdown (mesmo padrão visual, sem duplicar CSS novo).
    $tutorBuscaSeekOptions = \App\Models\TutorDemo\TutorDemoCliente::query()
        ->orderBy('nome')
        ->limit(10)
        ->get(['id', 'nome', 'cidade', 'uf'])
        ->map(fn ($c) => ['id' => $c->id, 'label' => "{$c->nome} — {$c->cidade}/{$c->uf}"])
        ->values()
        ->all();
    $tutorBuscaSeekJson = json_encode($tutorBuscaSeekOptions, JSON_UNESCAPED_UNICODE);
@endphp
<div class="mad-dropdown" style="position:relative; display:block; max-width:420px;"
    x-data="{ open: false, options: {{ $tutorBuscaSeekJson }} }"
    x-on:mad:seek.window="if ($event.detail.name === 'cliente_seek') open = true"
    x-on:click.outside="open = false">

    <mad-seek-field name="cliente_seek" label="Cliente (busca manual)"
        placeholder="Clique na lupa para buscar..." on-seek="abrirBuscaClientes"
        hint="O botão só dispara mad:seek — este exemplo escuta com Alpine puro e abre a lista abaixo (10 primeiros clientes da fixture)." />

    <div class="mad-dropdown-menu" x-show="open" x-cloak @click="open = false">
        <template x-for="opt in options" :key="opt.id">
            <button type="button" class="mad-dropdown-item"
                x-on:click="document.querySelector(&quot;input[name='cliente_seek']&quot;).value = opt.label">
                <span x-text="opt.label"></span>
            </button>
        </template>
    </div>
</div>
Ação wire

mad-form com submit real — dispara TutorFormsBusca::onSubmitDemo.

resources/views/tutor/examples/forms-busca/wire-submit.blade.php
@php
    $tutorBuscaWireClientes = \App\Models\TutorDemo\TutorDemoCliente::query()
        ->orderBy('nome')
        ->get(['id', 'nome'])
        ->mapWithKeys(fn ($c) => [(string) $c->id => $c->nome])
        ->all();
@endphp
<mad-form submit="onSubmitDemo">
    <mad-form-grid :cols="2">
        <mad-unique-search-field name="cliente_wire" label="Cliente"
            :options="$tutorBuscaWireClientes" required />
        <mad-multi-entry-field name="tags_wire" label="Tags" placeholder="Digite e Enter..." />
    </mad-form-grid>
    <mad-form-actions align="right">
        <mad-btn type="submit" variant="primary" icon="send">Enviar (onSubmitDemo)</mad-btn>
    </mad-form-actions>
</mad-form>
app/control/Tutor/TutorFormsBusca.php
/**
 * Submit do exemplo "Ação wire" — lê $this->form->getData() e devolve um
 * toast com o payload recebido (arrays de multi-seleção viram "a+b").
 */
public function onSubmitDemo(): MadResponse
{
    $data = $this->form->getData();

    $preenchidos = [];
    foreach ((array) $data as $campo => $valor) {
        if (is_array($valor)) {
            $valor = implode('+', $valor);
        }
        $valor = trim((string) $valor);
        if ($valor !== '') {
            $preenchidos[] = "{$campo}={$valor}";
        }
    }

    $resumo = $preenchidos !== [] ? implode(' | ', $preenchidos) : 'nenhum campo preenchido';

    return (new MadResponse())
        ->toast("Form recebido: {$resumo}", 'success', 'onSubmitDemo');
}
Código-fonte completo da página
app/control/Tutor/TutorFormsBusca.php
<?php

namespace App\Control\Tutor;

use App\Support\Tutor\TutorDemoSeed;
use App\Support\Tutor\TutorPage;
use Mad\Form\MadForm;
use Mad\Http\MadResponse;

/**
 * TutorFormsBusca — página "Busca" do catálogo (grupo forms).
 *
 * Cobre as variantes MANUAIS (não ligadas a banco) de busca: mad-unique-
 * search-field, mad-multi-search-field, mad-multi-entry-field e
 * mad-seek-field — as variantes "banco" (mad-dbunique-search-field etc.)
 * vivem no grupo "db_busca", fora deste lote.
 *
 * Como essas tags recebem `options` como array puro (não têm prop model/
 * database própria pra auto-carregar do banco), os exemplos consultam a
 * fixture TutorDemoCliente direto no partial (mount() aqui só garante que
 * ela existe via TutorDemoSeed::ensure() — os controllers do Tutor não
 * expõem estado pros partials de exemplo: cada `<x-tutor-demo>` inclui a
 * view isolada, sem o `$that` do controller da página).
 */
class TutorFormsBusca extends TutorPage
{
    protected string $catalogKey = 'forms_busca';

    public MadForm $form;

    public function mount(array $params = []): void
    {
        $this->form = new MadForm('tutor_forms_busca');

        TutorDemoSeed::ensure();
    }

    /**
     * Submit do exemplo "Ação wire" — lê $this->form->getData() e devolve um
     * toast com o payload recebido (arrays de multi-seleção viram "a+b").
     */
    public function onSubmitDemo(): MadResponse
    {
        $data = $this->form->getData();

        $preenchidos = [];
        foreach ((array) $data as $campo => $valor) {
            if (is_array($valor)) {
                $valor = implode('+', $valor);
            }
            $valor = trim((string) $valor);
            if ($valor !== '') {
                $preenchidos[] = "{$campo}={$valor}";
            }
        }

        $resumo = $preenchidos !== [] ? implode(' | ', $preenchidos) : 'nenhum campo preenchido';

        return (new MadResponse())
            ->toast("Form recebido: {$resumo}", 'success', 'onSubmitDemo');
    }
}
resources/views/tutor/forms/busca.blade.php
<x-tutor-page page="forms_busca">
    <x-tutor-demo demo="tutor.examples.forms-busca.unique-search"
        title="Busca única" desc="mad-unique-search-field: select buscável (TomSelect) com options fixas." />

    <x-tutor-demo demo="tutor.examples.forms-busca.multi-search"
        title="Busca múltipla" desc="mad-multi-search-field: escolhe vários itens buscando por texto." />

    <x-tutor-demo demo="tutor.examples.forms-busca.tags"
        title="Tags / chips" desc="mad-multi-entry-field: tags livres (digita e Enter) ou lista fixa (com options)." />

    <x-tutor-demo demo="tutor.examples.forms-busca.seek"
        title="Busca em popup (primitivo)" desc="mad-seek-field: só dispara o evento mad:seek — a busca em si é montada pela página (Alpine puro, neste exemplo)." />

    <x-tutor-demo demo="tutor.examples.forms-busca.wire-submit"
        title="Ação wire" desc="mad-form com submit real — dispara TutorFormsBusca::onSubmitDemo."
        php="TutorFormsBusca::onSubmitDemo" />
</x-tutor-page>