koolreport/laravel
Integrates KoolReport with Laravel so you can build reports and dashboards inside Laravel apps using familiar controllers, views, and database connections, with report generation, data processing, and export support.
Start by confirming the installed version and where the package hooks into Eloquent:
composer show <vendor>/<package>
php artisan vendor:publish --provider="<Vendor>\<Package>\<PackageServiceProvider>"
The first thing to inspect is the package's query entry point—typically a Query class, a trait applied to models, or a builder macro. In v2.1.0 the key addition is that the query method now supports Eloquent-style retrieval methods, including lazy(). That means you can move from:
$rows = Model::query()->get();
to:
Model::query()->lazy(500)->each(function ($row) {
// process one model at a time
});
Use this first for any command, job, or export that currently loads thousands of models with get(). Confirm the package's retrieving method delegates to Eloquent correctly by checking that lazy() returns a LazyCollection and that the underlying SQL is executed only once.
Chunked processing with lazy()
Use lazy($chunkSize) as the default retrieval method for large datasets. It keeps memory flat because each chunk is hydrated and released as you iterate:
Model::query()
->where('status', 'pending')
->lazy(1000)
->each(fn ($model) => $model->process());
Eager loading still applies
Chain with() before the retrieval method. The package passes the query through to Eloquent, so relationship loading and constraints behave as usual:
Model::query()
->with(['customer', 'lines'])
->lazy(250)
->each(fn ($order) => $order->sync());
Repository and pipeline integration
If you wrap queries in repositories, expose lazy() consistently rather than leaking get() everywhere:
class OrderRepository
{
public function pending(): LazyCollection
{
return Order::query()
->where('status', 'pending')
->with('customer')
->lazy(500);
}
}
Consumers can then compose with map(), filter(), takeWhile(), and other LazyCollection methods without loading everything into memory.
Jobs and commands
Use lazy() inside queued jobs or Artisan commands where memory limits are tight. Pair it with lazyById() if the package exposes that (or if the underlying Eloquent builder does) to avoid skipping or duplicating rows when records are updated during iteration.
Testing lazy retrieval
Because lazy() returns a LazyCollection, assertions differ from normal collections. Convert when needed or assert on iteration:
$results = Model::query()->lazy(10);
$this->assertInstanceOf(LazyCollection::class, $results);
$this->assertSame(3, $results->count());
lazy() is not a Collection. It returns LazyCollection. Methods like count(), toArray(), and first() work but may consume the sequence. If you need multiple passes, materialize intentionally or re-query.where() clauses after calling lazy() will not affect the running query.with() works, but relations are loaded chunk by chunk. Watch for constraint mismatches if you rely on global scopes that change between chunks.lazy() can hold a database connection open for the duration of iteration. Avoid wrapping large lazy loops in a single transaction unless necessary.lazyById(). Plain lazy() with orderBy can skip rows if the ordering column changes mid-iteration. Prefer lazyById() for stable pagination when available.DB::listen() or the package's query logging to confirm the retrieval method is not accidentally calling get() internally. The v2.1.0 feature should produce chunked queries, not one large query.php artisan config:clear.lazy() support consistent across your own abstractions.LazyCollection behavior that changed across Laravel releases.How can I help you explore Laravel packages today?