PHP code example of power-vending / laravel-api-query-builder

1. Go to this page and download the library: Download power-vending/laravel-api-query-builder library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

power-vending / laravel-api-query-builder example snippets


// Sem o pacote - você precisa tratar cada caso manualmente
if ($request->has('name')) {
    $query->where('name', 'like', '%' . $request->name . '%');
}
if ($request->has('min_price')) {
    $query->where('price', '>=', $request->min_price);
}
if ($request->has('category')) {
    $query->where('category_id', $request->category);
}

// Com o pacote - uma linha resolve tudo
return Product::query()->requestPaginate();

return [
    // Operadores de busca disponíveis
    'operators' => [
        // Lista de classes de operadores
    ],
    
    // Colunas que nunca podem ser acessadas via query
    'global_forbidden_columns' => [
        'password',
        'remember_token',
    ],
    
    // Outras configurações...
];



namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use PowerVending\LaravelApiQueryBuilder\Traits\ApiQueryBuilder;

class Product extends Model
{
    use ApiQueryBuilder;
}

'global_forbidden_columns' => [
    'password',           // Senhas nunca devem ser retornadas
    'remember_token',     // Tokens de sessão
    'api_token',          // Tokens de API
    'secret_key',         // Chaves secretas
    'credit_card',        // Dados bancários
    'cpf',                // Dados pessoais sensíveis
],



namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use PowerVending\LaravelApiQueryBuilder\Traits\ApiQueryBuilder;

class User extends Model
{
    use ApiQueryBuilder;
    
    /**
     * Colunas que não podem ser acessadas via API query
     */
    protected $forbiddenColumns = [
        'password',
        'remember_token',
        'two_factor_secret',
        'api_token',
    ];
}



use PowerVending\LaravelApiQueryBuilder\Http\Controllers\SchemaController;

return [
    'resource_models' => [
        'products' => \App\Models\Product::class,
        'product-categories' => \App\Models\Category::class,
    ],

    'routes' => [
        'engineering.api.v1.api_query_builder.schema.show' => [
            'method' => 'get',
            'uri' => 'eng-api/v1/api-query-builder/{resource}/schema',
            'action' => [SchemaController::class, 'show'],
            'middlewares' => ['api', 'auth:sanctum', 'acl'],
        ],
    ],
];

'routes' => [
    'api.query_builder.schema.show' => [
        'method' => 'get',
        'uri' => 'api-query-builder/{resource}/schema',
        'action' => [\PowerVending\LaravelApiQueryBuilder\Http\Controllers\SchemaController::class, 'show'],
        'middlewares' => ['api'],
    ],
],

'routes' => [
        'nome.completo.da.rota' => [
                'method' => 'get',
                'uri' => 'api-query-builder/{resource}/schema',
                'action' => [\PowerVending\LaravelApiQueryBuilder\Http\Controllers\SchemaController::class, 'show'],
                'middlewares' => ['api'],
        ],
],

'routes' => [
        'engineering.api.v1.api_query_builder.schema.show' => [
                'method' => 'get',
                'uri' => 'eng-api/v1/api-query-builder/{resource}/schema',
                'action' => [\PowerVending\LaravelApiQueryBuilder\Http\Controllers\SchemaController::class, 'show'],
                'middlewares' => ['api', 'auth:sanctum', 'acl'],
        ],
],

// Exemplo típico — o schema NÃO consegue introspeccionar o modelo de destino
public function requester(): MorphTo
{
    return $this->morphTo();
}

// Relacionamentos auxiliares tipados — o schema resolve cada modelo corretamente
public function requesterUser(): BelongsTo
{
    return $this->belongsTo(User::class, 'requester_id')
        ->where('requester_type', User::class);
}

public function requesterOperator(): BelongsTo
{
    return $this->belongsTo(Operator::class, 'requester_id')
        ->where('requester_type', Operator::class);
}

'operators' => [
    StartsWith::class,            // STARTS_WITH:   (13 chars)
    EndsWith::class,              // ENDS_WITH:      (10 chars)
    Like::class,                  // LIKE:           (5 chars)
    NotEquals::class,             // NE:             (3 chars)
    NotBetween::class,            // NB:             (3 chars)
    LessThanOrEqual::class,       // LE:             (3 chars)
    GreaterThanOrEqual::class,    // GE:             (3 chars)
    Between::class,               // BT:             (3 chars)
    Equals::class,                // EQ:             (3 chars)
    LessThan::class,              // LT:             (3 chars)
    GreaterThan::class,           // GT:             (3 chars)
],



namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use PowerVending\LaravelApiQueryBuilder\Traits\ApiQueryBuilder;

class Product extends Model
{
    use ApiQueryBuilder;
    
    protected $fillable = [
        'name',
        'description',
        'price',
        'category_id',
        'stock',
    ];
}



namespace App\Http\Controllers\Api;

use App\Models\Product;
use Illuminate\Http\Request;

class ProductController extends Controller
{
    public function index(Request $request)
    {
        // Método requestPaginate() - retorna resultados paginados
        return Product::query()->requestPaginate();
    }
    
    public function search(Request $request)
    {
        // Método requestQuery() - retorna o Builder para customizações
        $query = Product::query()->requestQuery();
        
        // Você pode adicionar condições extras ao Builder
        $query->where('company_id', auth()->user()->company_id);
        
        return $query->get();
    }
}

Product::with(['category', 'manufacturer', 'reviews'])->get();

use Illuminate\Database\Eloquent\SoftDeletes;

class Product extends Model
{
    use SoftDeletes;
    use ApiQueryBuilder;
}

$query->doesntHave('orders')->doesntHave('reviews');

class Product extends Model
{
    use ApiQueryBuilder;
    
    public function category()
    {
        return $this->belongsTo(Category::class);
    }
}



namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use PowerVending\LaravelApiQueryBuilder\Traits\ApiQueryBuilder;

class Product extends Model
{
    use ApiQueryBuilder;
    
    public function brand()
    {
        return $this->belongsTo(Brand::class);
    }
}

// No Model
public function brand()  // método em camelCase
{
    return $this->belongsTo(Brand::class);
}

class Product extends Model
{
    use ApiQueryBuilder;
    
    public function category()
    {
        return $this->belongsTo(Category::class);
    }
    
    public function brand()
    {
        return $this->belongsTo(Brand::class);
    }
}

class Customer extends Model
{
    use ApiQueryBuilder;
    
    public function orders()
    {
        return $this->hasMany(Order::class);
    }
}

class Product extends Model
{
    use ApiQueryBuilder;
    
    public function reviews()
    {
        return $this->hasMany(Review::class);
    }
}

public function index()
{
    return Product::query()->requestPaginate();
}

class Customer extends Model
{
    use ApiQueryBuilder;
    
    public function orders()
    {
        return $this->hasMany(Order::class);
    }
    
    public function address()
    {
        return $this->belongsTo(Address::class);
    }
}

public function index()
{
    return Customer::requestPaginate();
}



namespace App\SearchCallbacks;

use PowerVending\LaravelApiQueryBuilder\SearchCallbacks\AbstractCallback;

class CustomOperator extends AbstractCallback
{
    // Define o prefixo do operador
    public const OPERATOR = 'CUSTOM:';

    /**
     * Executa a lógica do operador
     *
     * @param \Illuminate\Database\Eloquent\Builder $builder
     * @param string $column
     * @param string $value
     * @return void
     */
    public function __invoke($builder, string $column, string $value)
    {
        // Sua lógica customizada aqui
        // Exemplo: busca case-sensitive
        $builder->whereRaw("BINARY {$column} = ?", [$value]);
    }
}

use App\SearchCallbacks\CustomOperator;

return [
    'operators' => [
        CustomOperator::class,  // Adicione no topo (lembre da ordem!)
        // ... outros operadores
    ],
];

class InOperator extends AbstractCallback
{
    public const OPERATOR = 'IN:';

    public function __invoke($builder, string $column, string $value)
    {
        // Espera valores separados por vírgula: "IN:1,2,3,4"
        $values = explode(',', $value);
        $builder->whereIn($column, $values);
    }
}

class IsNullOperator extends AbstractCallback
{
    public const OPERATOR = 'NULL:';

    public function __invoke($builder, string $column, string $value)
    {
        if ($value === 'true' || $value === '1') {
            $builder->whereNull($column);
        } else {
            $builder->whereNotNull($column);
        }
    }
}

public function index(Request $request)
{
    // Adiciona condições adicionais
    $query = Product::query()
        ->where('company_id', auth()->user()->company_id)
        ->where('is_deleted', false);
    
    // Processa os parâmetros JSON na query existente
    return $query->requestPaginate();
}

public function index(Request $request)
{
    // Valida os parâmetros
    $request->validate([
        'search' => 'sometimes|json',
        'order_by' => 'sometimes|json',
        '_per_page' => 'sometimes|integer|min:1|max:100',
    ]);
    
    return Product::requestPaginate();
}

// App/Models/Product.php
protected $casts = [
    'expires_at' => \App\Casts\Iso8601DateTimeString::class,
];

'operators' => [
    // MAIORES PRIMEIRO
    \PowerVending\LaravelApiQueryBuilder\SearchCallbacks\StartsWith::class,
    \PowerVending\LaravelApiQueryBuilder\SearchCallbacks\EndsWith::class,
    // ... depois os menores
],

// Migration
Schema::table('products', function (Blueprint $table) {
    $table->index('status');
    $table->index('category_id');
    $table->index('price');
    $table->index(['status', 'category_id']); // índice composto
});

class Product extends Model
{
    // O método deve existir e ser público
    public function category()
    {
        return $this->belongsTo(Category::class);
    }
}
bash
php artisan vendor:publish --tag=api-query-builder-config
http
GET /api-query-builder/tickets/schema?relations[]=requester_user&relations[]=requester_operator
json
{"price": "BT:100;500"}                       -> WHERE price BETWEEN 100 AND 500
{"stock": "BT:10;100"}                        -> WHERE stock BETWEEN 10 AND 100
{"created_at": "BT:2024-01-01;2024-12-31"}   -> WHERE created_at BETWEEN '2024-01-01' AND '2024-12-31'
json
{"discount": "EQ:null"}     -> WHERE discount IS NULL
{"discount": "EQ:!null"}    -> WHERE discount IS NOT NULL

?limit=10&offset=0   -> Registros 1-10 (primeira página)
?limit=10&offset=10  -> Registros 11-20 (segunda página)
?limit=10&offset=20  -> Registros 21-30 (terceira página)

?page=1&_per_page=20  -> Primeira página, 20 itens
?page=2&_per_page=20  -> Segunda página, 20 itens