Query Builder e Motor de Busca¶
O Query Builder do MIDDAG é uma ferramenta poderosa para construção de consultas complexas de forma fluente, segura e orientada a objetos.
Ele substitui a necessidade de escrever SQL manual ($DB->get_records_sql) dentro de Services e Controllers, oferecendo uma abstração de alto nível que lida automaticamente com paginação, joins de metadados e hidratação de objetos.
Arquitetura do Componente¶
O sistema de busca é dividido em 4 partes para garantir a Imutabilidade e a separação de responsabilidades:
[Image of Query Builder Architecture Diagram]
- Factory (
factory): O ponto de entrada. Cria instâncias de queries e oferece atalhos sintáticos. - Query Object (
query): Um objeto de valor imutável que armazena o que deve ser buscado (filtros, ordens), mas não sabe como buscar. - Executor (
executor): O "motorista". Pega o objeto Query, traduz para comandos do Repositório e coordena a execução. - Result (
result): Um container iterável que guarda os resultados, contagem total e metadados de paginação.
Construindo Consultas (Fluent Interface)¶
A forma recomendada de iniciar uma busca é injetando o serviço local_middag\core\service\item\item_search_service.
Exemplo Básico¶
<?php
use local_middag\core\middag;
use local_middag\core\service\item\item_search_service;
use local_middag\core\enum\operator;
// 1. Obter o serviço de busca
$search = middag::get(item_search_service::class);
// 2. Construir a query (Imutável!)
$query = $search->newQuery()
->domain(course_item::class) // Define o tipo de retorno
->where('status', operator::EQ, 'published')
->where('visible', operator::EQ, 1)
->orderBy('timecreated DESC')
->paginate(0, 20); // Página 0, 20 itens
// 3. Executar
$result = $search->search($query);
// 4. Usar os dados
echo "Total encontrado: " . $result->total();
foreach ($result as $item) {
echo $item->get_fullname();
}
Imutabilidade: Um Alerta Importante¶
O objeto query é imutável. Cada método chamado retorna uma nova instância modificada.
❌ Forma Errada (Não funciona):
<?php
$query = $search->newQuery();
$query->where('id', 1); // O retorno é ignorado!
$result = $search->search($query); // Executa uma query vazia (todos os itens)
✅ Forma Correta:
<?php
$query = $search->newQuery();
$query = $query->where('id', 1); // Atribui o novo estado
$result = $search->search($query);
Ou encadeado (Fluent):
Filtros Avançados¶
O builder suporta operadores complexos e filtros em tabelas relacionadas (metadados).
Metadados (Metadata)¶
Não é necessário fazer JOINs manuais. O builder abstrai isso:
<?php
$query = $query
->whereMeta('sku', operator::LIKE, '%PROD%')
->whereMeta('price', operator::GTE, 100);
Operadores Disponíveis¶
Use o enum local_middag\core\enum\operator para type-safety:
| Operador | SQL Equivalente |
|---|---|
operator::EQ |
= |
operator::NEQ |
!= |
operator::GT / GTE |
> / >= |
operator::LT / LTE |
< / <= |
operator::LIKE |
LIKE |
operator::IN |
IN (...) |
operator::BETWEEN |
BETWEEN ? AND ? |
Eager Loading (Carregamento Ansioso)¶
Para evitar o problema de performance N+1, você pode instruir o builder a carregar relações e metadados adicionais em lote.
Carregando Metadados¶
Se você precisa exibir o metadado cpf na listagem, peça antecipadamente:
Isso garante que, ao acessar $item->get_meta('cpf'), o valor já esteja na memória, sem disparar nova query.
Carregando Relações¶
Se o seu item possui relações (ex: parent, children, courses), carregue-as junto:
O executor usará o relation_loader para buscar todos os filhos de todos os itens encontrados em uma única consulta adicional.
Objeto Result¶
O retorno local_middag\core\search\result é um container inteligente.
<?php
// Iterável (como um array)
foreach ($result as $item) { ... }
// Contagem da página atual
count($result); // ex: 20
// Contagem total no banco (para paginação)
$result->total(); // ex: 5340
// Acesso direto
$first = $result->first();
$last = $result->last();
// Verificar vazio
if ($result->isEmpty()) { ... }
Modos de Retorno¶
Por padrão, o executor hidrata Objetos de Domínio (item). Se você precisa de performance bruta e apenas dados para leitura (ex: exportação CSV), use o modo stdClass: