Download the PHP package power-vending/laravel-api-query-builder without Composer

On this page you can find all versions of the php package power-vending/laravel-api-query-builder. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package laravel-api-query-builder

Laravel JSON Query Builder

Índice

  1. O que é este pacote
  2. Como funciona
  3. Requisitos do sistema
  4. Instalação
  5. Configuração inicial
  6. Rota de schema
  7. Operadores de busca
  8. Uso básico
  9. Parâmetros disponíveis
  10. Trabalhando com relacionamentos
  11. Exemplos práticos completos
  12. Customizações avançadas
  13. Erros retornados pela API
  14. Solução de problemas
  15. Testes
  16. Créditos e licença

O que é este pacote

Este pacote Laravel permite que você construa consultas (queries) dinâmicas no banco de dados usando parâmetros JSON através de requisições HTTP. Em vez de criar manualmente cada filtro, ordenação e paginação em seus controllers, este pacote processa automaticamente os parâmetros enviados pelo frontend.

Para que serve

Imagine que você tem uma API REST que retorna uma lista de produtos. Sem este pacote, você precisaria criar código para cada tipo de filtro possível:

Com este pacote, o frontend envia um JSON estruturado e tudo é processado automaticamente:

Origem

Este pacote foi originalmente desenvolvido por Neal Arec (nealarec/laravel-api-query-builder) e foi internalizado pela Power Vending para permitir customizações específicas e manutenção independente nos projetos internos da empresa.


Como funciona

O pacote funciona interpretando parâmetros JSON enviados via query string (GET) ou body (POST) de requisições HTTP e transformando-os em queries Eloquent do Laravel.

Fluxo de funcionamento

  1. O frontend faz uma requisição HTTP com parâmetros JSON
  2. O pacote intercepta esses parâmetros
  3. Valida e processa cada parâmetro (filtros, ordenação, paginação, etc)
  4. Constrói a query Eloquent correspondente
  5. Retorna os dados de acordo com as especificações

Exemplo visual


Requisitos do sistema

Para usar este pacote, você precisa ter as seguintes versões instaladas:

PHP

Laravel

O pacote é compatível com múltiplas versões do Laravel:

Doctrine DBAL

O Doctrine DBAL é necessário para algumas operações de análise de schema do banco de dados.

Composer

Necessário para gerenciar as dependências do PHP.


Instalação

Instalação via Composer

Instale o pacote usando o Composer:

O pacote será instalado automaticamente e o Laravel irá registrar o service provider.


Configuração inicial

Passo 1: Publicar arquivos de configuração

Publique o arquivo de configuração do pacote para o seu projeto:

Este comando irá criar o arquivo config/api-query-builder.php no diretório de configurações do seu projeto.

Passo 2: Entender o arquivo de configuração

Abra o arquivo config/api-query-builder.php. Você verá algo parecido com:

Passo 3: Adicionar a trait ao Model

Para que um Model aceite os parâmetros do pacote, adicione a trait ApiQueryBuilder:

A trait disponibiliza os métodos requestQuery() e requestPaginate() no Model. Sem ela, o pacote não tem efeito sobre o Model.

Passo 4: Configurar colunas proibidas (Segurança)

É muito importante configurar quais colunas não devem ser acessíveis via query para proteger dados sensíveis:

Qualquer tentativa de acessar essas colunas será bloqueada automaticamente.

Também é possível definir colunas proibidas diretamente nos Models, o que é útil para proteger campos específicos de cada tabela:

Ordem de precedência das colunas proibidas:

  1. global_forbidden_columns (config) - aplica a todos os models
  2. $forbiddenColumns (model) - específico da model
  3. model_options[Model::class]['forbidden_columns'] (config) - sobrescreve tudo

IMPORTANTE: As três fontes são mescladas (união). Se quiser usar apenas config, não defina $forbiddenColumns na model.

Passo 5: Configurar estrutura de rotas do pacote

O pacote expõe suas rotas por meio da chave routes na configuração.

No projeto consumidor, configure diretamente no arquivo config/api-query-builder.php:

Se você não quiser customizar o path, pode manter a rota padrão:

Estrutura esperada:

Campos da rota:

  1. method: verbo HTTP (get, post, put, patch, delete, options)
  2. uri: caminho completo da rota
  3. action: array com [Controller::class, 'method']
  4. middlewares: lista de middlewares por rota

Exemplo de override no projeto consumidor:

Observação importante:

  1. A rota só é registrada quando a action aponta para controller/método válidos do namespace do pacote
  2. Configurações inválidas de action são ignoradas para evitar exposição indevida

Rota de schema

A rota de schema retorna metadados para construção de filtros no frontend, incluindo:

  1. model e table
  2. searchable_columns (tipo, operadores e nullable)
  3. sortable_columns
  4. relations (árvore aninhada)

Rota padrão:

Parâmetros aceitos:

  1. resource (path): chave definida em api-query-builder.resource_models
  2. relations[] (query, opcional): lista de relações extras a serem mescladas ao retorno padrão

Exemplos de chamada:

Comportamento de relations:

  1. Sem relations: auto-descobre e carrega automaticamente todas as relações públicas do modelo
  2. relations[] informado: adiciona essas relações ao resultado (mescla com as auto-descobertas)
  3. Para cada caminho enviado em relations[], o schema carrega somente o primeiro nível de cada nó do caminho
  4. Relação inexistente: lança erro de validação de relação (tratável pelo Handler da aplicação)

Exemplo:

  1. GET /api-query-builder/terminals/schema retorna automaticamente todas as relações públicas do model Terminal
  2. GET /api-query-builder/terminals/schema?relations[]=company retorna as relações de primeiro nível de Terminal + primeiro nível de company (ex.: company.address, company.users)
  3. GET /api-query-builder/terminals/schema?relations[]=company.users retorna as relações de primeiro nível de Terminal + primeiro nível de company + primeiro nível de company.users (ex.: company.users.profile)

Exemplo resumido de resposta:

Relacionamentos polimórficos

Relacionamentos polimórficos criados com morphTo() não definem um modelo de destino fixo, o que impede o schema de resolver corretamente os campos e relacionamentos do lado polimórfico.

Para que o schema consiga carregar os metadados corretamente, é recomendado (opcional) criar relacionamentos auxiliares tipados com belongsTo, um por cada modelo de destino possível. Cada relacionamento deve filtrar pelo campo *_type correspondente:

Com isso, ao solicitar o schema, você pode referenciar cada variação diretamente:

E o retorno trará os campos de cada modelo destino de forma independente:

Nota: O relacionamento morphTo() original pode ser mantido no model normalmente — os relacionamentos auxiliares são apenas para uso com o schema e não interferem no comportamento padrão do Eloquent.


Operadores de busca

Antes de começar a usar o pacote, é fundamental entender os operadores disponíveis. Os operadores definem como a comparação será feita nos filtros (igual a, maior que, contém, etc).

Sintaxe geral

Para múltiplos valores, use ; (ponto e vírgula) como separador:

Comportamento de EQ e NE em colunas string

Os operadores EQ: e NE: têm comportamento condicional que depende do tipo da coluna resolvido via introspecção de schema e da quantidade de valores:

Situação SQL gerado
Tipo resolvido como string + valor único (sem wildcards) LIKE / NOT LIKE
Qualquer outro caso (múltiplos valores, tipo generic, tipo numérico, relação aninhada) IN / NOT IN

O tipo da coluna é determinado em tempo de execução consultando o schema do banco. Se a coluna não for encontrada no schema do model (por exemplo, em buscas dentro de relações aninhadas ou quando a introspecção falha), o tipo cai para generic e o comportamento passa a ser sempre IN.

Exemplo com tipo resolvido como texto (string, varchar, text, etc.):

Exemplo com tipo numérico ou genérico:

Comportamento do EQ: com um único valor de texto: Agora detecta corretamente colunas do tipo varchar, text, char, etc. (além de string) e usa LIKE em vez de IN. Para busca parcial (contém), use o micro-operador % nos valores ou prefira os operadores LIKE:, STARTS_WITH: ou ENDS_WITH:.

PostgreSQL: Usa ILIKE em vez de LIKE nas situações em que LIKE é gerado.

Lista completa de operadores

EQ - Equals (IN / LIKE condicional)

Comportamento:

Dica: Para buscas textuais com wildcards automáticos, use LIKE: (adiciona % em ambos os lados), STARTS_WITH: ou ENDS_WITH:.

NE - Not Equals (NOT IN / NOT LIKE condicional)

Inverso do EQ:. Segue as mesmas regras de detecção de tipo.

Comportamento:

GT - Greater Than (Maior que)

Aceita exatamente um valor.

GE - Greater than or Equal (Maior ou igual a)

Aceita exatamente um valor.

LT - Less Than (Menor que)

Aceita exatamente um valor.

LE - Less than or Equal (Menor ou igual a)

Aceita exatamente um valor.

BT - Between (Entre dois valores)

Aceita exatamente dois valores separados por ;.

NB - Not Between (Não está entre)

Inverso do BT:. Aceita exatamente dois valores separados por ;.

LIKE - Like (Contém)

Busca o valor em qualquer posição do texto. O % é adicionado automaticamente em ambos os lados.

Nota: Diferente do EQ:, o operador LIKE: sempre usa LIKE, independente da quantidade de valores ou tipo da coluna.

STARTS_WITH - Starts With (Começa com)

ENDS_WITH - Ends With (Termina com)

Micro-operadores

Micro-operadores são prefixos aplicados diretamente em cada valor (após o operador principal) para modificar o comportamento de busca individualmente. Funcionam com EQ: e NE:.

! — Negação de valor individual

Nega somente aquele valor dentro de uma lista. Combina IN e NOT IN na mesma query.

Diferença entre NE: e !:

  • NE:val1;val2 — nega toda a lista: NOT IN ('val1', 'val2')
  • EQ:val1;!val2 — val1 vai para IN, val2 vai para NOT IN

% — Wildcard (LIKE parcial)

Adiciona busca LIKE quando colocado no início, no fim, ou em ambas as extremidades do valor.

Funciona com negação também:

null e !null — Verificação de nulo

Operadores lógicos por coluna (&& e ||)

Permitem combinar múltiplas condições de operadores para uma mesma coluna usando AND ou OR. A sintaxe é colocar o operador lógico entre dois pares OPERADOR:valor.

Atenção: A precedência segue a lógica booleana padrão: AND tem prioridade sobre OR. Portanto x&&y||z&&q equivale a (x AND y) OR (z AND q).

Operadores lógicos como chave (&& e || top-level)

Além de usar && e || dentro do valor de uma coluna, você também pode usá-los como chaves dentro do objeto search para agrupar condições de colunas diferentes com AND ou OR.

&& como chave — AND entre grupos de colunas

|| como chave — OR entre grupos de colunas

Combinando || e && com condições normais

Exemplo avançado: agrupamento aninhado

Diferença entre os dois usos:

  • Dentro do valor ("name": "EQ:A||EQ:B") — aplica OR/AND sobre a mesma coluna
  • Como chave ("||": [...]) — aplica OR/AND sobre grupos de colunas distintas

IMPORTANTE - Ordem dos operadores na configuração

A ordem dos operadores no arquivo config/api-query-builder.php é crítica. Operadores com mais caracteres devem vir antes dos com menos caracteres, pois o parser busca o primeiro que encontrar na string.

Não altere essa ordem a menos que saiba exatamente o que está fazendo.


Uso básico

Passo 1: Adicionar o Trait ao Model

Para usar o pacote em um Model, adicione o trait ApiQueryBuilder:

O trait ApiQueryBuilder adiciona os métodos requestQuery() e requestPaginate() ao seu model, que processam automaticamente os parâmetros JSON das requisições.

Passo 2: Usar no Controller

No seu controller, use o método requestPaginate() para processar automaticamente os parâmetros da requisição:

Diferença entre os métodos:

Pronto! Agora seu endpoint já aceita todos os parâmetros JSON do pacote.

Passo 3: Fazer uma requisição do frontend

Do frontend, você pode fazer requisições como:


Parâmetros disponíveis

Agora que você já conhece os operadores de busca, vamos explorar todos os parâmetros que você pode usar nas requisições JSON para construir queries dinâmicas.

1. search (Filtros)

Permite filtrar os dados usando os operadores que você acabou de aprender na seção anterior.

Sintaxe:

Exemplo:

Query string:

2. relations (Carregar relacionamentos)

Carrega relacionamentos Eloquent junto com a entidade principal (eager loading).

Sintaxe básica (relacionamentos simples)

Exemplo:

Query string:

Isso irá executar algo como:

Sintaxe avançada (relacionamentos com configurações)

Você pode passar configurações específicas para cada relacionamento, permitindo filtrar, ordenar e selecionar campos dentro do relacionamento:

O que acontece:

Possibilidades dentro das configurações de relacionamento:

Exemplo completo: Produtos com reviews filtradas

Resultado:

Casos de uso práticos

1. Carregar últimos 5 pedidos de cada cliente:

2. Carregar comentários ativos, mais recentes primeiro:

3. Misturar relacionamentos simples e configurados:

3. order_by (Ordenação)

Define a ordem dos resultados.

Sintaxe:

Exemplo:

Query string:

Valores aceitos:

4. limit e offset (Paginação manual)

Controla quantos registros retornar e a partir de qual posição.

Sintaxe:

Explicação:

Exemplo:

5. page e _per_page (Paginação automática)

Paginação estilo Laravel Paginator.

Sintaxe:

Explicação:

Exemplo:

Resposta:

6. returns (Selecionar colunas específicas)

Retorna apenas as colunas especificadas (SELECT específico).

Sintaxe:

Exemplo:

Query string:

Isso gera:

Em vez de:

Benefícios:

7. excepts (Excluir colunas específicas)

Retorna todas as colunas EXCETO as especificadas.

Sintaxe:

Exemplo:

Query string:

Nota: Não use returns e excepts ao mesmo tempo. Escolha um ou outro.

8. count (Adicionar contagem)

Adiciona count(*) as count ao SELECT. Útil para queries de agregação.

Sintaxe:

Exemplo:

9. group_by (Agrupamento)

Agrupa os resultados por uma ou mais colunas.

Sintaxe:

Exemplo:

Query string:

Isso gera:

10. soft_deleted (Incluir registros deletados)

Inclui registros com soft delete na query. Por padrão, o Eloquent exclui registros com deleted_at preenchido. Este parâmetro remove esse escopo.

Sintaxe:

Requer que o Model use a trait SoftDeletes do Laravel:

Exemplo:

11. doesnt_have_relations (Excluir registros com relacionamento)

Filtra apenas registros que não possuem os relacionamentos especificados (equivale ao doesntHave() do Eloquent).

Sintaxe:

Exemplo:

Query string:

Isso gera:

Útil para encontrar registros "órfãos":

→ Retorna produtos que não estão associados a nenhuma categoria.


Trabalhando com relacionamentos

O pacote suporta busca, ordenação e carregamento através de relacionamentos Eloquent usando notação de ponto (dot notation).

Tipos de relacionamentos suportados

Busca em campos de relacionamentos

Você pode buscar por colunas de tabelas relacionadas usando a notação de ponto no parâmetro search.

Exemplo: Buscar produtos por nome da categoria

Model:

JSON:

SQL gerado:

Busca em múltiplos níveis de relacionamento

Você pode buscar em relacionamentos aninhados:

Ordenação por relacionamento

Você pode ordenar os resultados pela coluna de uma tabela relacionada usando notação de ponto.

Exemplo de Model com relacionamento

Ordenando por coluna do relacionamento

JSON:

O que acontece internamente:

  1. O pacote detecta que brand.name é um relacionamento
  2. Verifica se o método brand() existe no model
  3. Faz um LEFT JOIN com a tabela brands
  4. Ordena pelo campo name da tabela relacionada

SQL gerado:

Qualificação automática de colunas

Quando você usa ordenação por relacionamento, pode ocorrer ambiguidade se ambas as tabelas tiverem colunas com o mesmo nome (ex: id, name, created_at).

O pacote resolve isso automaticamente qualificando as colunas do returns com o nome da tabela.

Exemplo:

SQL gerado:

Sem a qualificação automática, o SQL seria:

Exemplo completo com relacionamento

JSON completo:

Explicação:

Convenção de nomes

O nome do relacionamento no JSON deve corresponder ao nome do método no Model:

Combinando busca e ordenação em relacionamentos

Você pode combinar busca, ordenação e eager loading em relacionamentos para queries complexas.

Exemplo completo: Produtos com busca e ordenação por categoria

Models:

JSON:

SQL gerado:

Configurações avançadas de relacionamentos

Além de buscar e ordenar usando JOINs (notação de ponto), você pode configurar como os relacionamentos são carregados passando objetos no parâmetro relations.

Sintaxe: Relacionamentos com configurações inline

O que isso faz:

Exemplo 1: Últimos 5 pedidos de cada cliente

Model:

JSON:

Resultado:

Exemplo 2: Produtos com reviews aprovadas e bem avaliadas

Model:

JSON:

Resultado:

Exemplo 3: Misturando configurações simples e avançadas

Resultado:

Diferença: JOIN vs Eager Loading Configurado

JOIN (notação de ponto em search ou order_by):

→ Usa LEFT JOIN, afeta a query principal, filtra produtos

Eager Loading Configurado (objeto em relations):

{
    "relations": [
        {
            "reviews": {
                "search": {"rating": "GE:4"}
            }
        }
    ]
}

→ Usa subquery separada, não afeta produtos, apenas filtra reviews carregadas

Casos de uso práticos

1. Last 10 activities de cada usuário:

{
    "relations": [
        {
            "activities": {
                "order_by": {"created_at": "desc"},
                "limit": 10
            }
        }
    ]
}

2. Apenas notificações não lidas:

{
    "relations": [
        {
            "notifications": {
                "search": {"read_at": "EQ:null"}
            }
        }
    ]
}

3. Top 3 produtos de cada categoria:

{
    "relations": [
        {
            "products": {
                "order_by": {"sales_count": "desc"},
                "limit": 3
            }
        }
    ]
}

Limitações e boas práticas

✅ Suportado:

⚠️ Atenção:

💡 Dica de performance:


Exemplos práticos completos

Exemplo 1: Lista de produtos com filtros básicos

Cenário: Listar produtos ativos, com preço entre 100 e 1000, ordenados por preço crescente.

JSON:

{
    "search": {
        "status": "EQ:active",
        "price": "BT:100;1000"
    },
    "order_by": {
        "price": "asc"
    },
    "_per_page": 20,
    "page": 1
}

Query string:

GET /api/products?search={"status":"EQ:active","price":"BT:100;1000"}&order_by={"price":"asc"}&_per_page=20&page=1

Controller:

Exemplo 2: Busca de usuários por nome e email

Cenário: Buscar usuários cujo nome ou email contenha "silva".

JSON:

{
    "search": {
        "name": "LIKE:silva"
    },
    "returns": ["id", "name", "email", "created_at"],
    "_per_page": 50
}

Nota: Para buscar em múltiplos campos com OR, você precisará customizar a query.

Exemplo 3: Relatório de vendas por período

Cenário: Vendas realizadas entre duas datas, com informações completas do cliente.

JSON:

{
    "search": {
        "created_at": "BT:2024-01-01;2024-12-31",
        "status": "EQ:completed"
    },
    "relations": ["customer", "items", "items.product"],
    "order_by": {
        "created_at": "desc"
    },
    "returns": [
        "id",
        "customer_id",
        "total",
        "status",
        "created_at"
    ]
}

Exemplo 4: Listagem com relacionamento ordenado

Cenário: Produtos ordenados pelo nome da marca.

JSON:

{
    "search": {
        "status": "EQ:active"
    },
    "relations": ["brand"],
    "order_by": {
        "brand.name": "asc",
        "id": "asc"
    },
    "_per_page": 25
}

Exemplo 5: Clientes com últimos pedidos filtrados e ordenados

Cenário: Listar clientes ativos com seus 5 últimos pedidos completados.

Models:

JSON:

{
    "search": {
        "status": "EQ:active"
    },
    "relations": [
        "address",
        {
            "orders": {
                "search": {
                    "status": "EQ:completed"
                },
                "order_by": {
                    "completed_at": "desc"
                },
                "returns": ["id", "total", "status", "completed_at"],
                "limit": 5
            }
        }
    ],
    "order_by": {
        "created_at": "desc"
    },
    "_per_page": 20
}

Resultado:

Controller:

Exemplo 6: Blog posts com comments aprovados e tags ordenadas

Cenário: Posts publicados com comentários aprovados e tags ordenadas alfabeticamente.

JSON:

{
    "search": {
        "status": "EQ:published",
        "published_at": "LE:2026-04-14"
    },
    "relations": [
        "author",
        "category",
        {
            "comments": {
                "search": {
                    "status": "EQ:approved",
                    "parent_id": "EQ:null"
                },
                "order_by": {
                    "created_at": "desc"
                }
            }
        },
        {
            "tags": {
                "order_by": {
                    "name": "asc"
                }
            }
        }
    ],
    "order_by": {
        "published_at": "desc"
    },
    "_per_page": 15
}

Resultado:

Exemplo 7: Exportação com todos os campos exceto sensíveis

Cenário: Exportar todos os dados de usuários exceto campos sensíveis.

JSON:

{
    "excepts": ["password", "remember_token", "api_token", "two_factor_secret"],
    "limit": 10000
}

Exemplo 8: Dashboard com múltiplos filtros

Cenário: Dashboard de produtos com múltiplos filtros aplicados.

JSON:

{
    "search": {
        "category_id": "EQ:5",
        "stock": "GT:0",
        "price": "LT:5000",
        "name": "LIKE:Notebook",
        "is_featured": "EQ:1"
    },
    "relations": ["category", "manufacturer", "reviews"],
    "order_by": {
        "featured_order": "asc",
        "price": "asc"
    },
    "returns": [
        "id",
        "name",
        "price",
        "stock",
        "category_id",
        "manufacturer_id"
    ],
    "_per_page": 12,
    "page": 1
}

Customizações avançadas

Criar um operador customizado

Se os operadores padrão não atendem sua necessidade, você pode criar operadores personalizados.

Passo 1: Criar a classe do operador

Crie um arquivo em app/SearchCallbacks/CustomOperator.php:

Passo 2: Registrar o operador na configuração

Edite config/api-query-builder.php:

Passo 3: Usar o operador

{
    "search": {
        "name": "CUSTOM:SuaEmpresa"
    }
}

Exemplos de operadores customizados úteis

Operador IN (valores múltiplos)

Uso:

{"category_id": "IN:1,5,8,12"}

Operador NULL

Uso:

{"deleted_at": "NULL:true"}   // Registros com deleted_at NULL
{"deleted_at": "NULL:false"}  // Registros com deleted_at NOT NULL

Modificar query antes de executar

Você pode adicionar condições adicionais à query antes dela ser executada:

Validar parâmetros antes de processar


Erros retornados pela API

Quando uma requisição contém parâmetros inválidos, o pacote lança exceções que podem ser mapeadas para respostas HTTP 400. Esta seção descreve cada mensagem de erro, sua causa e como corrigi-la.


Relation '<RELATION>' does not exist on model '<MODEL_CLASS>'.

Exceção: InvalidRelationException

Causa: Foi informado um relacionamento que não existe no model (ou em algum nível de relacionamento aninhado).

Situações em que pode ocorrer:

  1. No parâmetro relations.
  2. No parâmetro search quando a chave representa relacionamento (ex.: sub-search em objeto ou notação com ponto como relation.column).
  3. No parâmetro doesnt_have_relations.

Exemplos que causam o erro:

{
    "relations": ["unknown_relation"]
}
{
    "search": {
        "unknown_relation": {
            "search": {
                "id": "EQ:1"
            }
        }
    }
}
{
    "search": {
        "unknown_relation.description": "EQ:abc"
    }
}
{
    "doesnt_have_relations": ["unknown_relation"]
}

Solução:

  1. Verifique o nome da relação no model Eloquent e use o nome correto.
  2. Em relações aninhadas, valide cada segmento do caminho (ex.: orders.items.product).
  3. Se necessário, padronize o nome enviado pelo frontend para o método real da relação no backend.

Os erros abaixo são produzidos pela classe InvalidOperatorUsageException e a mensagem é segura para ser exibida diretamente ao consumidor da API.


The '<OP>' operator is not supported for text-type fields. Only comparable field types (numeric, date, etc.) are allowed.

Operadores afetados: GT, GE, LT, LE

Causa: O operador de comparação foi usado em uma coluna cujo tipo resolvido pelo pacote é textual (string, varchar, char, text, longtext, etc.). Esses operadores exigem tipos comparáveis (numérico, data, etc.).

Exemplo que causa o erro:

{"name": "GE:Notebook"}

Soluções possíveis:

  1. Use EQ: ou LIKE: para colunas de texto.
  2. Se a coluna armazena datas ou números como string (ex: VARCHAR contendo "2024-01-15"), aplique um cast personalizado no model para que o pacote não trate o campo como texto puro:

Isso faz com que o tipo resolvido pelo pacote deixe de ser string e os operadores GT/GE/LT/LE passem a funcionar.


The '<OP>' operator expects exactly one value, but multiple were provided.

Operadores afetados: GT, GE, LT, LE

Causa: Foi passado mais de um valor separado por ; para um operador que aceita apenas um valor.

Exemplo que causa o erro:

{"price": "GE:100;200"}

Solução: Passe apenas um valor:

{"price": "GE:100"}

Se o objetivo é um intervalo, use BT: (between):

{"price": "BT:100;200"}

No value was provided for the '<OP>' operator.

Operadores afetados: GT, GE, LT, LE

Causa: O operador foi enviado sem nenhum valor após os dois pontos.

Exemplo que causa o erro:

{"price": "GE:"}

Solução: Sempre forneça um valor após o operador:

{"price": "GE:100"}

The '<OP>' operator expects exactly 2 values, but N were provided.

Operadores afetados: BT, NB

Causa: O operador BT: (between) ou NB: (not between) exige exatamente dois valores separados por ;, mas a quantidade recebida foi diferente.

Exemplos que causam o erro:

{"price": "BT:100"}          // apenas 1 valor
{"price": "BT:100;200;300"}  // 3 valores

Solução: Forneça exatamente dois valores:

{"price": "BT:100;500"}

Solução de problemas

Erro: "Column not found"

Sintoma: Erro SQL dizendo que uma coluna não existe.

Causas possíveis:

  1. Nome da coluna digitado errado no JSON
  2. Coluna realmente não existe na tabela
  3. Tentando acessar coluna de relacionamento sem JOIN

Solução:

Verifique se a coluna existe:

php artisan tinker
>>> Schema::hasColumn('products', 'nome_da_coluna')

Se for coluna de relacionamento, use notação de ponto:

{"order_by": {"category.name": "asc"}}

Erro: "Ambiguous column"

Sintoma: Column 'id' in SELECT is ambiguous

Causa: Você está usando order_by com relacionamento mas não especificou returns, ou especificou colunas sem qualificação.

Solução:

Sempre especifique returns quando usar ordenação por relacionamento:

{
    "returns": ["id", "name"],
    "order_by": {"category.name": "asc"}
}

O pacote irá qualificar automaticamente as colunas.

Erro: "Forbidden column"

Sintoma: Tentativa de acessar uma coluna proibida.

Causa: A coluna está na lista de colunas proibidas (via model ou config).

Solução:

  1. Verifique a propriedade $forbiddenColumns na model
  2. Verifique config/api-query-builder.phpglobal_forbidden_columns
  3. Verifique config/api-query-builder.phpmodel_options[YourModel::class]['forbidden_columns']
  4. Remova a coluna da lista ou use outra coluna para buscar

Isso é uma proteção de segurança. Se você realmente precisa acessar essa coluna:

  1. Remova da lista de colunas proibidas (não recomendado para campos sensíveis)
  2. Crie um endpoint separado específico para esse caso

Operador não funciona

Sintoma: Operador sendo interpretado como valor literal.

Causas possíveis:

  1. Operador não está registrado na configuração
  2. Ordem dos operadores está incorreta
  3. Sintaxe incorreta

Solução:

Verifique a ordem no config/api-query-builder.php:

Limpe o cache de configuração:

php artisan config:clear
php artisan config:cache

Performance lenta

Sintoma: Queries demorando muito tempo.

Causas possíveis:

  1. Falta de índices no banco de dados
  2. Muitos relacionamentos sendo carregados
  3. Paginação com número muito alto de registros

Soluções:

Adicione índices nas colunas mais filtradas:

Use limit ou _per_page para limitar resultados:

{"_per_page": 25}

Evite carregar relacionamentos desnecessários:

{
    "relations": ["category"],  // apenas o necessário
    // evite: "relations": ["category", "reviews", "images", "manufacturer"]
}

JSON inválido na query string

Sintoma: Erro de parsing JSON.

Causa: JSON mal formatado na URL.

Solução:

Sempre encode o JSON na URL:

// JavaScript
const params = {
    search: JSON.stringify({
        name: "LIKE:Produto",
        status: "EQ:active"
    })
};

const url = '/api/products?' + new URLSearchParams(params);

Ou use ferramentas que fazem isso automaticamente (Axios, etc).

Relacionamento não encontrado

Sintoma: Erro dizendo que o relacionamento não existe.

Causa: Nome do relacionamento incorreto ou método não existe no model.

Solução:

Verifique se o método existe no model:

Use o nome exato do método no JSON:

{
    "order_by": {"category.name": "asc"}  // 'category' = nome do método
}

Testes

O pacote inclui testes automatizados usando PHPUnit.

Executar todos os testes

composer test

Ou com PHPUnit diretamente:

vendor/bin/phpunit

Executar testes específicos

vendor/bin/phpunit --filter NomeDoTeste

Estrutura de testes

Os testes estão localizados em packages/api-query-builder/tests/:

tests/
├── Feature/              # Testes de integração
│   ├── SearchTest.php
│   ├── OrderByTest.php
│   └── RelationTest.php
├── Unit/                 # Testes unitários
│   ├── OperatorTest.php
│   └── ConfigTest.php
└── TestCase.php         # Classe base de testes

Criar novos testes

Para adicionar novos testes, crie uma classe que estenda TestCase:


<?php

namespace Tests\Feature;

use Tests\TestCase;
use App\Models\Product;

class CustomFeatureTest extends TestCase
{
    public function test_custom_operator_works()
    {
        // Arrange
        Product::factory()->create(['name' => 'Test Product']);

        // Act
        $result = Product::jsonSearch(['name' => 'CUSTOM:Test']);

        // Assert
        $this->assertCount(1, $result);
    }
}

All versions of laravel-api-query-builder with dependencies

PHP Build Version
Package Version
Requires php Version ^8.0
doctrine/dbal Version ^3.0|^4.0
illuminate/support Version ^8.0|^9.0|^10.0|^11.0|^12.0
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package power-vending/laravel-api-query-builder contains the following files

Loading the files please wait ...