Skip to main content
FiQueLa supports rich filtering through WHERE (pre-aggregation) and HAVING (post-aggregation) conditions. Both use the same operator set and can be combined with logical operators and groups.

WHERE vs HAVING

ClauseWhen it runsUse for
WHEREBefore grouping and aggregationFiltering raw rows
HAVINGAfter grouping and aggregationFiltering aggregated results
use FQL\Enum\Operator;

$query->count('id')->as('total')
    ->sum('price')->as('revenue')
    ->groupBy('category')
    ->where('price', Operator::GREATER_THAN, 0)      // filters raw rows first
    ->having('total', Operator::GREATER_THAN, 5);   // filters after grouping

Comparison operators

Import the Operator enum before using operators in the fluent API:
use FQL\Enum\Operator;
Enum constantOperatorDescription
EQUAL=Loose equality
EQUAL_STRICT==Strict equality (type-safe)
NOT_EQUAL!=Loose inequality
NOT_EQUAL_STRICT!==Strict inequality (type-safe)
GREATER_THAN>Greater than
GREATER_THAN_OR_EQUAL>=Greater than or equal
LESS_THAN<Less than
LESS_THAN_OR_EQUAL<=Less than or equal
ININField value is in the given array
NOT_INNOT INField value is not in the given array
LIKELIKEPattern match — _ matches one character, % matches any sequence
NOT_LIKENOT LIKEInverse of LIKE
REGEXPREGEXPMatch against a regular expression pattern
NOT_REGEXPNOT REGEXPInverse of REGEXP
ISISType check — see IS operator section below
NOT_ISIS NOTInverse type check
BETWEENBETWEENField value is between two values (inclusive)
NOT_BETWEENNOT BETWEENInverse of BETWEEN

Logical operators

Chain multiple conditions using logical operators:
$query->where('price', Operator::GREATER_THAN, 100)
    ->and('stock', Operator::GREATER_THAN, 0)
    ->or('featured', Operator::EQUAL, true)
    ->xor('clearance', Operator::EQUAL, true);
In FQL strings:
WHERE price > 100
  AND stock > 0
  OR featured = true

Basic examples

use FQL\Enum\Operator;

// Comparison
$query->where('price', Operator::GREATER_THAN, 100);

// IN list
$query->where('status', Operator::IN, ['active', 'pending']);

// BETWEEN
$query->where('price', Operator::BETWEEN, [300, 500]);

// LIKE — wildcard matching
$query->where('description', Operator::LIKE, '%wireless%');

// REGEXP
$query->where('name', Operator::REGEXP, '^Product [A-B]$');

LIKE pattern matching

The LIKE operator follows MySQL conventions:
WildcardMatches
%Any sequence of characters (including empty)
_Exactly one character
// Ends with 'Pro'
$query->where('name', Operator::LIKE, '%Pro');

// Starts with 'Widget'
$query->where('name', Operator::LIKE, 'Widget%');

// Contains 'cable'
$query->where('name', Operator::LIKE, '%cable%');

// Exactly 5 characters
$query->where('code', Operator::LIKE, '_____');
Matching is case-insensitive.

REGEXP pattern matching

The REGEXP operator accepts a regular expression pattern. If the pattern includes delimiters (e.g. /pattern/i), they are used as-is. Otherwise the pattern is wrapped in / delimiters automatically.
$query->where('sku', Operator::REGEXP, '^[A-Z]{2}-\\d{4}$');
$query->where('name', Operator::REGEXP, '^Product [A-C]');
WHERE sku REGEXP "^[A-Z]{2}-\d{4}$"

IS operator — type checking

The IS and IS NOT operators check the PHP type of a field value. Pass a Type enum constant as the right-hand operand:
use FQL\Enum\Operator;
use FQL\Enum\Type;

$query->where('price', Operator::IS, Type::NUMBER);
$query->where('tags', Operator::IS, Type::ARRAY);
$query->where('deletedAt', Operator::IS, Type::NULL);
$query->where('active', Operator::NOT_IS, Type::FALSE);
Type constantPHP type check
Type::BOOLEANis_bool()
Type::TRUE=== true
Type::FALSE=== false
Type::NUMBERis_numeric()
Type::INTEGERis_integer()
Type::FLOATis_float()
Type::STRINGis_string()
Type::NULLis_null()
Type::ARRAYis_array()
Type::OBJECTis_object()
In FQL strings, use the type name directly:
WHERE price IS NUMBER
WHERE tags IS ARRAY
WHERE deletedAt IS NULL
WHERE active IS NOT FALSE

Condition grouping

Group conditions with whereGroup(), andGroup(), orGroup(), havingGroup(), and endGroup() to build complex logic. Use havingGroup() specifically to start a grouped condition within the HAVING context:
$query->where('price', Operator::GREATER_THAN, 100)
    ->andGroup()
        ->where('description', Operator::LIKE, '%very usefully')
        ->or('price', Operator::LESS_THAN_OR_EQUAL, 300)
        ->orGroup()
            ->where('price', Operator::EQUAL, 200)
            ->and('description', Operator::LIKE, '%very usefully')
        ->endGroup()
    ->endGroup()
    ->orGroup()
        ->where('price', Operator::LESS_THAN_OR_EQUAL, 300)
    ->endGroup();
This produces the SQL-equivalent:
price > 100
AND (
    description LIKE '%very usefully'
    OR price <= 300
    OR (
        price = 200
        AND description LIKE '%very usefully'
    )
)
OR (
    price <= 300
)
FQL strings also support parentheses for condition grouping:
WHERE price > 100
  AND (description LIKE "%very usefully" OR price <= 300)