PHPackages                             jonasraoni/query-builder-extensions - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. jonasraoni/query-builder-extensions

ActiveLibrary

jonasraoni/query-builder-extensions
===================================

Extensions for the Laravel Illuminate\\Database\\Query\\Builder.

1.0.0(2y ago)015MITPHPPHP &gt;=8.2.0

Since Jun 28Pushed 2y ago1 watchersCompare

[ Source](https://github.com/jonasraoni/query-builder-extensions)[ Packagist](https://packagist.org/packages/jonasraoni/query-builder-extensions)[ Docs](http://github.com/jonasraoni/query-builder-extensions)[ RSS](/packages/jonasraoni-query-builder-extensions/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (1)DependenciesVersions (2)Used By (0)

Extensions for the Laravel `Illuminate\Database\Query\Builder`.
===============================================================

[](#extensions-for-the-laravel-illuminatedatabasequerybuilder)

Currently only provides extensions to make it easier to work with paged result sets. Both methods don't buffer the result set, so once a page is consumed, a new query will be issued to retrieve the next page.

The package also has a safer method to count records (`getCount()`), which is supposed to work faster (by dropping `ORDER BY` clause) and be more reliable (works with `GROUP BY` clauses) than the Laravel's `count()` method.

Safely count records with the getCount()
----------------------------------------

[](#safely-count-records-with-the-getcount)

```
getCount(): int
```

If you've got a `SELECT field FROM test GROUP BY field`, the Laravel's `count()` will convert it to `SELECT COUNT(0) FROM test GROUP BY field`, which might retrieve N records. Laravel will retrieve only the value for the first record, which will break user expectations.

This `getCount()` method will instead generate a `SELECT COUNT(0) FROM (SELECT 0 FROM test GROUP BY field)` which will retrieve the proper record count.

Lazy Paginator
--------------

[](#lazy-paginator)

```
paginateLazily(Builder $query, int $rows): \Generator
```

Retrieves a generator that will run through all the records of every page (broken by `$rows`).

The method will not touch your query, it's just a helper, so you must add the sorting by yourself.

Dynamic Paginator
-----------------

[](#dynamic-paginator)

```
bufferedIterator(Builder $query, array $sortMap, int $rows): \Generator
```

It does the same as the previous method... But:

- It's probably much more performatic, as it doesn't use the `LIMIT` clause, which gets slower as you advances through the pages.
- Ensure that past records will not be revisited and also that records will not be lost/skipped, due to updates happening against previous pages (removed/inserted/updated records that provokes a shift effect).

So I think it's great to be used when processing a large result set, as you can consume the data on demand without having much issues due to updates.

### Details

[](#details)

To avoid skipping/reprocessing past records, the code keeps track of the last processed record in the page.

#### `array $sortMap`

[](#array-sortmap)

This argument is used to tell the paginator which fields should be used to sort, and also to help it retrieving the key values for the last record (so it knows what to skip in the next page...).

- The "key" represents the value which will be used in the `ORDER BY` clause (so it can be a valid `ORDER BY` field, such as `table.field DESC`)
- The "value" must be mapped to a field name, available in the `SELECT` clause, which holds the same value used by the `ORDER BY` expression. A callable is also supported (receives an object, the last record of the page, and must return the expected data).

```
use JonasRaoni\QueryBuilder\Extensions;

Extensions::extend();
$records = $connection->table('posts')
    ->select('id', 'date', 'title')
    // Will produce an "ORDER BY id DESC, IF(date IS NULL, 0, 1)"
    ->bufferedIterator(
        [
            // Maps the given sort expression to the "id" field (must be available in the "SELECT")
            'id DESC' => 'id',
            // Maps the given sort expression using a callable
            'IF(date IS NULL, 0, 1)' => function ($record) {
                return $record->date ? 1 : 0;
            }
        ]
    );

foreach ($records as $record) {
    echo $record->id;
}
```

### Requirements

[](#requirements)

- The query must be sorted **only** by the given fields/keys (the method adds the sorting by itself, so it's not needed to do it manually).
- **No duplicated rows are allowed!** It might cause an unexpected behavior. The values from the `$sortMap` must be able to perfectly identify a row (so **watch out** for case-insensitive comparisons, such as `'a' > 'A'`).

PS: In case it's not clear, here's an example of the issue you might face when using a standard paging method:

- Suppose you have a paged result set based on the query: `SELECT * FROM posts WHERE active = 1 ORDER BY id`
- If you're, let's say, in the page 10 and someone updates all the records that you visited in previous pages (e.g. `UPDATE posts SET active = 0 WHERE id < :lastVisitedId`), then when you advance to the page 11, you'll have a little surprise! Some records will be skipped... And the inverse also might happen, after someone doing an operation that adds records to the previous pages (e.g. `UPDATE posts SET active = 1 WHERE active = 0 AND id < :lastVisitedId`).

General Usage
-------------

[](#general-usage)

Install the package:

```
composer require jonasraoni/query-builder-extensions

```

The package just has one class (`JonasRaoni\QueryBuilder\Extensions`) which can be used in two ways:

### A. Macro extensions

[](#a-macro-extensions)

Call the `extend` method, to extend all Builder instances:

```
use JonasRaoni\QueryBuilder\Extensions;

Extensions::extend();
$records = $connection->table('test')
    ->select('field')
    ->orderBy('field')
    ->paginateLazily(100);

foreach ($records as $record) {
    echo $record->id;
}

echo $connection->table('test')->getCount();
```

### B. Calling the methods directly

[](#b-calling-the-methods-directly)

```
use JonasRaoni\QueryBuilder\Extensions;

$queryBuilder = $connection
    ->table('test')
    ->select('field')
    ->orderBy('field');
$records = Extensions::paginateLazily($queryBuilder, 100);

foreach ($records as $record) {
    echo $record->id;
}
```

###  Health Score

25

—

LowBetter than 37% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity6

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity56

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

1055d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/32c66dd29e4133c60890fd5dede747fd8975f0036c5e1f56f99eb7ebb25ee5df?d=identicon)[jonasraoni](/maintainers/jonasraoni)

---

Top Contributors

[![jonasraoni](https://avatars.githubusercontent.com/u/361921?v=4)](https://github.com/jonasraoni "jonasraoni (3 commits)")

---

Tags

illuminate-databaselaravellaravel-extensionlaravel-frameworklaravel-packagephplaravelquerybuilderextension

### Embed Badge

![Health badge](/badges/jonasraoni-query-builder-extensions/health.svg)

```
[![Health](https://phpackages.com/badges/jonasraoni-query-builder-extensions/health.svg)](https://phpackages.com/packages/jonasraoni-query-builder-extensions)
```

###  Alternatives

[anourvalar/eloquent-serialize

Laravel Query Builder (Eloquent) serialization

11320.2M21](/packages/anourvalar-eloquent-serialize)[umbrellio/laravel-pg-extensions

Extensions for Postgres Laravel

102426.5k1](/packages/umbrellio-laravel-pg-extensions)[illuminatech/data-provider

Allows easy build for DB queries from API requests

4413.3k](/packages/illuminatech-data-provider)[brokerexchange/elasticbuilder

Query Builder for Elasticsearch.

111.1k](/packages/brokerexchange-elasticbuilder)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
