Skip to main content
Calling execute() on a query returns a ResultsProvider object. This does not immediately process all data — results are lazy by default and only materialized when you iterate or call a fetch method.
$results = $query->execute();

ResultsProvider modes

execute() automatically selects the most efficient result mode, but you can override it explicitly:
use FQL\Results;

// Force in-memory (all rows loaded at once)
$results = $query->execute(Results\InMemory::class);

// Force streaming (rows processed one at a time)
$results = $query->execute(Results\Stream::class);
Queries with JOIN or ORDER BY always use InMemory mode regardless of the explicit parameter, because those operations require full dataset access.

Fetch methods

fetchAll()

Returns a Generator that yields all result rows. Optionally maps each row to a DTO class:
// Iterate over all rows as arrays
foreach ($results->fetchAll() as $row) {
    echo $row['name'] . '\n';
}

// Or collect into an array
$rows = iterator_to_array($results->fetchAll());

getIterator()

Returns a Traversable — equivalent to fetchAll() without DTO mapping. Useful in foreach directly on the results object:
foreach ($results as $row) {
    echo $row['name'] . '\n';
}

fetch()

Returns the first row only, or null if there are no results:
$product = $results->fetch();
if ($product !== null) {
    echo $product['name'];
}

fetchSingle()

Returns a single field value from the first row. Dot notation is supported for nested fields:
$name = $results->fetchSingle('name');
$city = $results->fetchSingle('address.city');

fetchNth()

Returns every nth row as a Generator. Pass an integer for every nth row, or 'even'/'odd' for even/odd-indexed rows (0-based):
// Every 4th row
foreach ($results->fetchNth(4) as $row) { ... }

// Even-indexed rows (0, 2, 4, ...)
foreach ($results->fetchNth('even') as $row) { ... }

// Odd-indexed rows (1, 3, 5, ...)
foreach ($results->fetchNth('odd') as $row) { ... }

exists()

Returns true if at least one result row exists. More efficient than count() because it stops after finding the first row:
if ($results->exists()) {
    echo 'Records found.';
}

count()

Returns the total number of result rows:
$total = $results->count();

Aggregate methods

These methods compute values over the result set without requiring a GROUP BY in the query:
$total   = $results->sum('total_price');       // float
$average = $results->avg('total_price', 2);    // float, 2 decimal places
$highest = $results->max('total_price');       // float
$lowest  = $results->min('total_price');       // float
MethodSignatureReturns
sum()sum(string $field): floatSum of all values in the field
avg()avg(string $field, int $decimalPlaces = 2): floatAverage value, rounded to given decimal places
max()max(string $field): floatMaximum value in the field
min()min(string $field): floatMinimum value in the field

DTO mapping

All fetch methods that return rows accept an optional class-string $dto parameter. When provided, each row is mapped to an instance of the given class using reflection.

Anonymous class DTO

use FQL\Enum\Operator;
use FQL\Query;

$query = Query\Provider::fromFileQuery('json(products.json).data.products')
    ->select('id, name, price')
    ->select('brand.name')->as('brand')
    ->from('data.products')
    ->where('price', Operator::GREATER_THAN, 200)
    ->orderBy('price')->desc();

$dto = new class {
    public int $id;
    public string $name;
    public int $price;
    public string $brand;
};

$product = $query->execute()->fetch($dto::class);
// $product is an instance of the anonymous class with populated properties

Named class DTO

class ProductDto
{
    public int $id;
    public string $name;
    public int $price;
    public string $brand;
}

foreach ($results->fetchAll(ProductDto::class) as $product) {
    echo $product->name . ': ' . $product->price . '\n';
}

Constructor-based DTO

If the class has a constructor, FiQueLa maps field values to constructor parameters by name:
class OrderDto
{
    public function __construct(
        public readonly int $id,
        public readonly string $status,
        public readonly float $total
    ) {}
}

$order = $results->fetch(OrderDto::class);
DTO mapping uses PHP reflection. Public properties are populated by matching field names. If a constructor is present, parameters are matched by name from the result row.

INTO — exporting results

After executing, you can pipe results directly to an output file using into():
use FQL\Query\Provider;

Provider::fromFile('data.csv')
    ->select('NAME', 'PRICE')
    ->execute()
    ->into('json(exports/products.json).root.items');
See the FQL syntax page for the full INTO format reference.