๐Ÿ”ง Builder

The Builder provides a high-level, entity-oriented way to perform queries on your data models. It is built on top of the lower-level QueryBuilder and integrates deeply with Hector ORM entity management.

๐Ÿ” Accessing the Builder

You can access the builder using the static Entity::query() method, or instantiate it directly:

use Hector\Orm\Query\Builder;

$builder = MyEntity::query();
// or
$builder = new Builder(MyEntity::class);

The builder internally uses hectororm/query: see QueryBuilder documentation. You can also use QueryBuilder independently of the ORM: see hectororm/query on GitHub or Packagist.

๐ŸŽฏ Finding Entities

Find by Primary Key

$entity = MyEntity::find(1);

Returns the entity or null if not found.

Warning: Passing multiple IDs to find() is deprecated. Use findAll() instead for multiple entities.


Find All

Returns a collection of entities matching the given primary key(s):

$collection = MyEntity::findAll(1);
$collection = MyEntity::findAll(1, 2, 3);

Find or Fail

Throws NotFoundException if not found:

use Hector\Orm\Exception\NotFoundException;

try {
    $entity = MyEntity::findOrFail(1);
} catch (NotFoundException $e) {
    // Handle exception
}

Find or New

Returns existing entity or creates a new one with default values:

$entity = MyEntity::findOrNew(1, ['foo' => 'value']);

๐Ÿ“Š Get by Offset (non-PK access)

These methods retrieve entities by their position in the result set (zero-based offset), not by primary key. Useful when you need the Nth result of a query.

Get / Get or Fail / Get or New

// Get first result (offset 0)
$entity = MyEntity::query()->get();

// Get third result (offset 2)
$entity = MyEntity::query()->where('active', true)->get(2);

// Throw NotFoundException if no result at offset
$entity = MyEntity::query()->getOrFail(0);

// Return new entity with default values if no result at offset
$entity = MyEntity::query()->getOrNew(0, ['status' => 'draft']);

๐Ÿ“‹ Retrieving All Entities

$collection = MyEntity::all();

Also available via the builder:

$collection = MyEntity::query()->all();

๐Ÿ”„ Chunking and Yielding

Use chunk() for memory-friendly batch processing:

MyEntity::query()->chunk(100, function (Collection $entities) {
    foreach ($entities as $entity) {
        // Process each entity
    }
});

With lazy mode disabled (eager fetch per chunk):

MyEntity::query()->chunk(100, function (Collection $collection) {
    // Each chunk is fetched eagerly from DB
}, lazy: false);

Use yield() to iterate using a lazy generator. Entities are hydrated one by one as you iterate, minimizing memory usage:

foreach (MyEntity::query()->yield() as $entity) {
    // Each entity is loaded on-demand
}

Tip: yield() returns a LazyCollection. The underlying query is executed once, but entities are hydrated lazily during iteration.


๐Ÿ”ข Counting

$count = MyEntity::query()->count();

This will ignore any limit() that was previously applied.

๐Ÿ“ฆ Limiting and Offsetting Results

MyEntity::query()->limit(10)->offset(5)->all();

๐Ÿ”ข Ordering Results

MyEntity::query()->orderBy('created_at', 'DESC')->all();
MyEntity::query()->orderBy('name')->all(); // ASC by default

๐Ÿงฎ Conditions

You can filter entities using a fluent API similar to the QueryBuilder.

Where / Having

MyEntity::query()->where('column', 'value')->orWhere('column', '<', 10);

Where In / Not In

MyEntity::query()->whereIn('column', ['foo', 'bar']);
MyEntity::query()->whereNotIn('column', ['foo', 'bar']);

Between / Not Between

MyEntity::query()->whereBetween('column', 1, 10);
MyEntity::query()->whereNotBetween('column', 1, 10);

Greater / Less

MyEntity::query()->whereGreaterThan('column', 5);
MyEntity::query()->whereLessThan('column', 100);

Exists / Not Exists

MyEntity::query()->whereExists($subQuery);
MyEntity::query()->whereNotExists($subQuery);

Where Equals (Smart Inference)

MyEntity::query()->whereEquals([
    'status' => 'active',           // WHERE status = 'active'
    'category_id' => [1, 2, 3],     // AND category_id IN (1, 2, 3)
]);

Automatically uses = for scalar values and IN for arrays.


๐Ÿ”— Compatibility with QueryBuilder

All filtering, ordering, joining, and limiting operations are passed to the underlying QueryBuilder, which can be used directly for low-level control.

If you need full SQL flexibility or want to decouple from ORM, refer to the standalone QueryBuilder documentation.

Last updated: Tue, 13 Jan 2026 08:51