makaveli/laravel-query-builder

Query Builder for Laravel

Maintainers

Package info

github.com/Ma1kaveli/laravel-query-builder

pkg:composer/makaveli/laravel-query-builder

Statistics

Installs: 27

Dependents: 2

Suggesters: 0

Stars: 0

Open Issues: 0

1.1.4 2026-03-28 11:17 UTC

This package is auto-updated.

Last update: 2026-03-28 11:18:08 UTC


README

Packagist Version Packagist Downloads License

🌍 Languages

Table of Contents

  1. Introduction
  2. Installation
  3. Configuration
  4. Core Components
  5. Quick Start
  6. Integration with BaseRepository
  7. Recommendations
  8. Useful Links

Introduction

makaveli/laravel-query-builder is a powerful and convenient query builder for Laravel Eloquent. It allows you to extract all filtering, sorting, and pagination logic into separate classes that extend BaseQueryBuilder. This approach keeps controllers and repositories clean while making the filtering code readable, extensible, and safe.

Key goals of the library:

  • Readability – each $this->apply...() method describes one clear condition.
  • Security – parameters are only taken from the passed $params array.
  • Extensibility – adding a new filter is easy by implementing FilterInterface.
  • Reusability – a single filter class can be used in different contexts (admin panel, API, reports).
  • Type Safety – all methods are strictly typed, and built‑in type checks (CheckTypes) prevent errors.

The library integrates closely with makaveli/laravel-core. It requires PHP 8.2+ and Laravel 10+.

Installation

  1. Install the library via Composer:

    composer require makaveli/laravel-query-builder
  2. (Optional) Publish the configuration file if you want to change the default settings:

    php artisan vendor:publish --tag=query-builder-config

    This will create the file config/query-builder.php in your project.

Configuration

The file config/query-builder.php contains the following settings:

return [
    // Date and time formats supported by CheckTypes
    'check-types' => [
        'date-formats' => ['Y-m-d', 'd.m.Y', 'm/d/Y', 'd F Y', 'Y-m-d H:i:s'],
        'time-formats' => ['H:i', 'H:i:s', 'H:i:s.u']
    ],

    // Pagination key mapping for PaginatedCollection
    'pagination_map' => [
        'current_page' => 'current_page',
        'first_page_url' => 'first_page_url',
        'from' => 'from',
        'last_page' => 'last_page',
        'last_page_url' => 'last_page_url',
        'links' => 'links',
        'next_page_url' => 'next_page_url',
        'path' => 'path',
        'per_page' => 'per_page',
        'prev_page_url' => 'prev_page_url',
        'to' => 'to',
        'total' => 'total',
    ],
];

You can change the supported date/time formats and the pagination response structure according to your API requirements.

Core Components

BaseQueryBuilder

An abstract base class from which all concrete filter classes inherit. It holds an instance of $query (Eloquent Builder) and the array $params (request parameters). It contains many protected methods for applying filters (e.g., applyInteger(), applyLike(), applyWhereHasWhere(), applyDateRange(), etc.).

A detailed description of all methods with examples is provided in a separate document: base-query-builder.md.

Filterable Trait

The trait is added to a model and allows calling filters via the static filter() method:

trait Filterable
{
    public static function filter(array $params): mixed
    {
        $query = static::query();
        $filterClass = static::getFilterClass();

        if (class_exists($filterClass)) {
            return new $filterClass($query, $params);
        }

        return $query;
    }

    abstract public static function getFilterClass(): string;
}

The model must implement getFilterClass(), returning the fully qualified class name of the filter class.

CheckTypes

The helper class QueryBuilder\Helpers\CheckTypes contains static methods for checking data types. It is used internally by filters to ensure that a passed value matches the expected type (integer, float, string, date, time, array, etc.). This improves security and prevents query errors.

PaginatedCollection

The class QueryBuilder\Resources\PaginatedCollection extends the standard ResourceCollection and makes it easy to adapt the paginated response structure to frontend requirements. It uses the configuration key pagination_map to rename response fields.

Usage example:

use QueryBuilder\Resources\PaginatedCollection;

return new PaginatedCollection(
    $paginator,
    ArticleResource::collection($paginator, ['author'])
);

The result will contain the keys defined in pagination_map, and the actual data will be inside the data key.

DTOs for Complex Parameters

The package provides several DTO classes for passing complex parameters to filters:

  • AvailableSort and AvailableSorts – for describing allowed sorts on fields of related tables.
  • DeepWhereHasWhereParam and DeepWhereHasWhereParams – for building nested whereHas conditions.

These DTOs are used in the methods sortByRelationField() and applyDeepWhereHasWhere().

Filters

All filters reside in the namespace QueryBuilder\Filters and are organized into categories:

Category # of Filters Example Methods / Features
Datetime ~20 applyToday(), applyLastWeek(), applyDateRange(), applyCurrentHour(), applyTimeStartEnd(), applyLastMonth()
Numeric ~15 applyNumericRange(), applyMultipleOf(), applyEvenNumeric(), applyArrayInteger(), applyNumericNot(), applyNumericNotIn()
String ~10 applyLike(), applyLikeStart(), applyLikeEnd(), applyRegex(), applyFullTextSearch()
Logic ~8 applyNull(), applyTrue(), applyFalse(), applyNotNull(), applyEmpty()
Relation ~10 applyDeepWhereHasWhere(), applyCrossUponCrossWhereHasWhere(), applyWhereHasLikeArray()
Combine ~8 applyWhereHasWhere(), applyWhereHasLike(), applyWhereHasNull()
System ~12 applyLimit(), applySelect(), applyDistinct(), with(), withCount(), inRandomOrder(), applyWhereNot(), applyWhereNotIn()
Geo 2 applyGeoRadius(), applyGeoBoundingBox()
Special 4 applyIpAddress(), applyRating(), applyStock(), applyDomain()
Custom 2 applyArchived(), applyWhereNotMe()

A complete list of all filters with detailed descriptions, parameters, and examples can be found in base-query-builder.md.

Quick Start

1. Create a filter class

<?php

namespace App\Modules\Article\Filters;

use Illuminate\Pagination\LengthAwarePaginator;
use QueryBuilder\BaseQueryBuilder;

class ArticleFilters extends BaseQueryBuilder
{
    public function list(): LengthAwarePaginator
    {
        // Eager loading
        $this->with(['author', 'category']);

        // Count related records
        $this->withCount(['comments', 'likes']);

        // Search across multiple fields
        $this->applyLike(['title', 'content'], 'search');

        // Filter by integer field
        $this->applyInteger('category_id');

        // Filter by boolean field
        $this->applyBoolean('is_published');

        // Show deleted records if requested
        $this->applyWithDeleted();

        // Sorting
        $this->sortBy(['title', 'created_at'], 'created_at');

        // Pagination with support for all-rows
        return $this->applyPaginate(canAllRows: true);
    }
}

2. Add the Filterable trait to the model

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use QueryBuilder\Traits\Filterable;

class Article extends Model
{
    use Filterable;

    public static function getFilterClass(): string
    {
        return \App\Modules\Article\Filters\ArticleFilters::class;
    }
}

3. Use it in a controller or repository

public function index(Request $request): JsonResponse
{
    $filters = Article::filter($request->all());
    $articles = $filters->list(); // Returns a LengthAwarePaginator

    return ApiResponse::success($articles);
}

Integration with BaseRepository

The makaveli/laravel-core package provides a BaseRepository with a getPaginatedList() method that can easily be adapted to work with filters:

use Core\Repositories\BaseRepository;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;

class ArticleRepository extends BaseRepository
{
    public function __construct()
    {
        parent::__construct(Article::class);
    }
}

Now you can pass a DTO containing the request parameters to the repository’s getPaginatedList method, and the repository will automatically apply all filters, sorts, and pagination as defined in your filter class’s list method.

Recommendations

  • Always extend BaseQueryBuilder for each module/entity.
  • Use applyOrWhereGrouped() to combine conditions into OR groups – this improves readability and correctly groups complex expressions.
  • Explicitly list allowed sorting fields in sortBy() for security.
  • When working with soft deletes, call applyWithDeleted() or applyOnlyDeleted() depending on the context.
  • For complex relationship filters, use the ready‑made methods like applyWhereHasWhere(), applyWhereHasLike(), etc. – they already check for the presence of the parameter and apply the filter correctly.
  • Create custom filters if the built‑in ones are insufficient – just implement FilterInterface and register them in your filter class.
  • For APIs, use PaginatedCollection to unify the pagination response format.

Useful Links

Good luck with your projects and happy filtering! 🚀