Skip to main content
The fluent API lets you build queries by chaining PHP method calls. Every method returns the query object, so calls can be composed in any order.
use FQL\Query;
use FQL\Enum\Operator;

$results = Query\Provider::fromFileQuery('json(./products.json).data.products')
    ->select('id', 'name', 'brand.code')
    ->where('price', Operator::GREATER_THAN, 100)
    ->orderBy('price')->desc()
    ->limit(20)
    ->execute()
    ->fetchAll();
Every query implements \Stringable. Cast a query to a string to see the equivalent FQL representation — useful for debugging.
echo (string) $query;

Selecting fields

Use select() to specify which fields to include. Dot notation accesses nested fields. Multiple select() calls merge their fields.
// All equivalent
$query->select('id, name, address.city, address.state');
$query->select('id', 'name', 'address.city', 'address.state');
$query->select('id', 'name')->select('address.city', 'address.state');
Use selectAll() to select all fields (equivalent to SELECT *). You can combine it with additional fields:
$query->selectAll()->select('totalPrice');

Aliases

Chain as() immediately after select() to alias the last selected field:
$query->select('id')->as('clientId');
$query->select('brand.code')->as('brandCode');
as() only aliases the last field in the preceding select() call. For example, select('id', 'name')->as('o') creates id, name AS o.

DISTINCT

Remove duplicate rows with distinct():
$query->select('category')->distinct();

EXCLUDE

Use exclude() to remove fields from the output — useful when applying functions and you want only the computed result:
$query->select('id', 'name')
    ->round('totalPrice', 2)->as('finalPrice')
    ->exclude('totalPrice');

// Output: id, name, finalPrice (totalPrice removed)

Specifying the data path

Use from() to point to the data root within the file. Dot notation traverses nested structures:
$query->from('data.products');
$query->from('SHOP.SHOPITEM');
When using Query\Provider::fromFileQuery(), the path embedded in the FileQuery string sets the initial from.

Pagination and limits

// Return at most 20 rows starting from row 40
$query->offset(40)->limit(20);

// Page-based helper — page 2, 20 rows per page
$query->page(2, perPage: 20);

Sorting

Chain orderBy() with asc() or desc(). Multiple orderBy() calls define a multi-column sort:
use FQL\Enum\Sort;

$query->orderBy('price')->desc();
$query->orderBy('price', Sort::ASC)->orderBy('name', Sort::DESC);

Conditions

Filter rows with where(), then chain and(), or(), or xor():
use FQL\Enum\Operator;

$query->where('price', Operator::GREATER_THAN, 100)
    ->and('description', Operator::LIKE, '%wireless%')
    ->or('price', Operator::BETWEEN, [300, 500]);
See Conditions for the full operator reference and condition grouping.

Joins

Join other files or queries using innerJoin(), leftJoin(), rightJoin(), or fullJoin(), followed by on() to define the join condition:
$orders = Query\Provider::fromFileQuery('xml(orders.xml).orders.order');

$query->leftJoin($orders, 'o')
    ->on('id', Operator::EQUAL, 'user_id');
See Joining data sources for full details.

Grouping and aggregations

Group rows with groupBy() and apply aggregate functions:
$query->count('id')->as('total')
    ->sum('price')->as('revenue')
    ->groupBy('category.id')
    ->having('total', Operator::GREATER_THAN, 10);
See Grouping and aggregations for all aggregate functions.

UNION

Combine results from multiple queries:
// Remove duplicates
$results = $query1->union($query2)->execute();

// Keep all rows
$results = $query1->unionAll($query2)->execute();
The number of selected columns must match across all combined queries (queries using SELECT * skip this check).

EXPLAIN

Inspect the query execution plan without running the full query, or run it with timing data:
// Plan only — no data processed
$plan = $query->explain()->execute();

// Execute and collect real row counts and timings
$plan = $query->explainAnalyze()->execute();
See EXPLAIN & benchmarking for the full column reference.

Fluent API vs FQL string

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

$query = Query\Provider::fromFileQuery('json(products.json).data.products')
    ->select('brand.code')->as('brandCode')
    ->groupConcat('id', '/')->as('products')
    ->sum('price')->as('totalPrice')
    ->count('id')->as('productCount')
    ->where('price', Operator::LESS_THAN, 300)
    ->or('price', Operator::GREATER_THAN, 400)
    ->groupBy('brand.code')
    ->orderBy('productCount')->desc();
Casting the fluent query to a string produces the FQL representation, so you can always inspect what SQL-like string a fluent query corresponds to.