Skip to content

Latest commit

 

History

History
199 lines (149 loc) · 7.38 KB

conditions.md

File metadata and controls

199 lines (149 loc) · 7.38 KB

Condition classes

These classes serve as a wrapper for Node instances implementing ScalarExpression interface, that should (presumably) return boolean values when used in SQL.

Condition instances are used by fragments that modify WHERE and HAVING clauses and by the JoinFragment for an actual JOIN condition.

Conditions behave like Specifications from the pattern of the same name and can be combined via AND / OR / NOT operators. They do not implement isSatisfiedBy() method, though, for more or less obvious reasons.

Base Condition class

This abstract class implements KeyEquatable and FragmentBuilder interfaces. The latter allows passing instances of Condition to query methods of TableGateway, appending them to the query's WHERE clause.

It also defines the abstract generateExpressionImpl(): ScalarExpression method that should be implemented in child classes and a generateExpression(): ScalarExpression wrapper around it which clones the result of generateExpressionImpl(). This is done to prevent potential problems when reusing the same expression in multiple queries / multiple parts of the query, as pg_builder's Node classes keep a reference to their parent Node.

Method name starts with "generate" as a hint: it should preferably generate the ScalarExpression on "as needed" basis rather than pre-generate and store that. Real world Conditions will use Parser and parsing may be slow.

Combining Conditions via AND / OR / NOT

The Condition class has static methods for combining the conditions using logical operators:

  • and(self ...$children): AndCondition - creates a Condition that combines several other Conditions using AND operator.
  • or(self ...$children): OrCondition - creates a Condition that combines several other Conditions using OR operator.
  • not(self $child): NotCondition - creates a negated Condition.

For example:

$combined = Condition::and(
    Condition::not($firstCondition),
    Condition::or($secondCondition, $thirdCondition)
);

Classes returned by the above methods can also be instantiated explicitly. Constructor of AndCondition / OrCondition accepts variable number of Condition parameters

  • __construct(Condition ...$children)

and will throw an InvalidArgumentException if none are given.

Constructor of NotCondition naturally accepts only one Condition argument.

Using the above example with explicit classes:

$combined = new AndCondition(
    new NotCondition($firstCondition),
    new OrCondition($secondCondition, $thirdCondition)
);

All these classes implement Parametrized interface and will propagate parameters from their child Conditions.

Passing parameters with Conditions

While it is possible to also implement Parametrized in custom conditions, the suggested approach is to use ParametrizedCondition decorator instead.

Its constructor accepts an instance of Condition and an array with parameter values

  • __construct(Condition $wrapped, array<string, mixed> $parameters)

and will throw an InvalidArgumentException if $wrapped already implements Parametrized.

$condition = new ParametrizedCondition(
    new SqlStringCondition($parser, 'foo = :bar::baz'),
    ['bar' => new Baz()]
);

Other subclasses of Condition

It is rarely needed to manually create these classes as they are all supported by custom builder classes and builder methods of FluentBuilder.

AnyCondition

Generates a self.foo = any(:foo::foo_type[]) condition for the foo table column. This is similar to foo IN (...), but requires only one placeholder.

Constructor accepts a Column instance and an implementation of TypeNameNodeHandler:

$condition = new AnyCondition(
    $gateway->getColumns()->get('foo'),
    $locator->getTypeConverterFactory()
);

BoolCondition

Uses the value of the bool-typed column as a Condition. Constructor accepts an instance of Column, will throw LogicException if it is not of type bool.

$condition = new BoolCondition($gateway->getColumns()->get('flag'));

ExistsCondition

Generates the EXISTS(SELECT ...) condition using the given SelectProxy. The constructor may also accept a join Condition and an explicit alias for a table within EXISTS(...) (will be autogenerated if not given):

  • __construct(SelectProxy $select, ?Condition $joinCondition = null, ?string $explicitAlias = null)
$condition = new ExistsCondition(
    $gateway->select(/* ... some configuration ... */),
    new ForeignKeyCondition($foreignKey),
    'custom'
);

ForeignKeyCondition

Generates a join condition using the given foreign key constraint. Constructor accepts a ForeignKey object and a flag specifying whether we are joining from the side of the child table (one having the constraint defined) or the parent one (referenced by constraint). self and joined aliases will be used according to that flag.

// This will use 'self' alias for referenced table and 'joined' alias for child one
$condition = new ForeignKeyCondition($foreignKey, false);

IsNullCondition

Generates a self.foo IS NULL Condition for the foo table column. Constructor accepts an instance of Column.

$condition = new IsNullCondition($gateway->getColumns()->get('foo'));

NotAllCondition

Generates a self.foo <> all(:foo::foo_type[]) condition for the foo table column. This is similar to foo NOT IN (...), but requires only one placeholder.

Constructor accepts a Column instance and an implementation of TypeNameNodeHandler:

$condition = new NotAllCondition(
    $gateway->getColumns()->get('foo'),
    $locator->getTypeConverterFactory()
);

OperatorCondition

Generates a self.foo OPERATOR :foo::foo_type condition for the foo table column. Constructor accepts an instance of Column, an implementation of TypeNameNodeHandler and operator as string:

$condition = new OperatorCondition(
    $gateway->getColumns()->get('foo'),
    $locator->getTypeConverterFactory(),
    '>='
);

PrimaryKeyCondition

A condition for finding a table row by its primary key. Constructor accepts a PrimaryKey object and an implementation of TypeNameNodeHandler:

$condition = new PrimaryKeyCondition(
    $gateway->getPrimaryKey(),
    $locator->getTypeConverterFactory()
);

This class also has an additional normalizeValue(mixed $value): array<string, mixed> which accepts the value probably passed to one of the methods of PrimaryKeyAccess interface and ensures that it can be used in parameter values for the query. E.g.

$condition->normalizeValue(1);

will return a ['id' => 1] array if table's primary key consists of single id column and

$condition->normalizeValue(['foo_id' => 2]);

will throw an Exception if table's primary key consists of foo_id and bar_id columns.

SqlStringCondition

Condition represented by an SQL string. Constructor accepts a Parser instance and a string.

$condition = new SqlStringCondition(
    $parser,
    "current_date between coalesce(self.valid_from, 'yesterday') and coalesce(self.valid_to, 'tomorrow')"
);

Note that the string will eventually be processed by parseExpression() method and added to query AST, so self aliases in it will be replaced if needed and parameter placeholders will be processed.