As query methods of TableGateway
usually accept implementations of Fragment
and FragmentBuilder
, methods to
simplify creation of such objects were also added to default gateway implementations.
namespace sad_spirit\pg_gateway\gateways;
use sad_spirit\pg_gateway\builders\{
ColumnsBuilder,
ExistsBuilder,
JoinBuilder,
ScalarSubqueryBuilder
};
use sad_spirit\pg_gateway\conditions\{
NotCondition,
ParametrizedCondition,
column\BoolCondition,
column\IsNullCondition
};
use sad_spirit\pg_gateway\fragments\{
ReturningClauseFragment,
SelectListFragment
};
class GenericTableGateway implements TableGateway
{
// creating Conditions
public function any(string $column, array $values) : ParametrizedCondition;
public function column(string $column) : BoolCondition;
public function notColumn(string $column) : NotCondition;
public function isNull(string $column) : IsNullCondition;
public function isNotNull(string $column) : NotCondition;
public function notAll(string $column, array $values) : ParametrizedCondition;
public function operatorCondition(string $column, string $operator, mixed $value) : ParametrizedCondition;
public function equal(string $column, $value) : ParametrizedCondition;
public function sqlCondition(string $sql, array $parameters = []) : ParametrizedCondition;
public function exists(string|QualifiedName|TableGateway|SelectProxy $select) : ExistsBuilder;
// creating fragments that modify the output expressions list
public function outputColumns() : ColumnsBuilder;
public function returningColumns() : ColumnsBuilder;
public function outputSubquery(SelectProxy $select) : ScalarSubqueryBuilder;
public function outputExpression(string|Condition $expression, ?string $alias = null) : SelectListFragment;
public function returningExpression(string|Condition $expression, ?string $alias = null) : ReturningClauseFragment;
// creating a builder for joins
public function join(string|QualifiedName|TableGateway|SelectProxy $joined) : JoinBuilder;
// fragments for SELECT statements
public function orderBy(iterable<OrderByElement|string>|string $orderBy) : OrderByClauseFragment;
public function orderByUnsafe(iterable<OrderByElement|string>|string $orderBy) : OrderByClauseFragment;
public function limit(int $limit) : LimitClauseFragment;
public function offset(int $offset) : OffsetClauseFragment;
}
Base Condition
class implements FragmentBuilder
interface, so objects returned by these methods can be
directly passed to the query methods of TableGateway
:
$gateway->select($gateway->any('field', [1, 2, 3]));
$gateway->delete([
$gateway->equal('foo', 1),
$gateway->isNull('option')
]);
When added in that way, Condition
s will be applied to the WHERE
clause of the query.
The above methods generate the following SQL
any()
- generatesself.column = any(:column::column_type[])
. ReturnedParametrizedCondition
decoratesconditions\column\AnyCondition
here, allowing to pass$values
together with condition rather than separately in$parameters
argument to a query method.column()
- generatesself.column
using a column ofbool
type.notColumn()
- generatesNOT self.column
, returnedNotCondition
decoratesBoolCondition
.isNull()
- generatesself.column IS NULL
.isNotNull()
- generatesself.column IS NOT NULL
, returnedNotCondition
decoratesIsNullCondition
.notAll()
- generatesself.column <> all(:column::column_type[])
, returnedParametrizedCondition
decoratesconditions\column\NotAllCondition
.operatorCondition()
- generatesself.column <OPERATOR> :column::column_type
, returnedParametrizedCondition
decoratesconditions\column\OperatorCondition
.equal()
- generates aself.column = :column::column_type
, returnedParametrizedCondition
decoratesOperatorCondition
.sqlCondition()
- embeds manually written SQL as a condition, returnedParametrizedCondition
decoratesconditions\SqlStringCondition
.exists()
- returns a builder for configuring a[NOT] EXISTS(...)
condition. If the argument is a string or an instance ofQualifiedName
it is treated as a table name, a gateway is located for that table andselect()
ed from. If the argument is already aTableGateway
instance then an unconditionalselect()
is done.
Note that all the methods that accept column values do not embed them into SQL, passing them on instead
via ParametrizedCondition
decorator. This way the generated SQL does not depend on specific parameter values
and may be eventually reused with other values.
While sqlCondition()
accepts an SQL string, this won't of course be inserted verbatim into
the generated SQL, e.g.
$condition = $gateway->sqlCondition(
'case when self.foo @@ :foo::foo_type then self.bar else false end',
['foo' => $fooValue]
)
will have the special self
alias replaced as needed, also named :foo
placeholder will be converted
to positional one and its type info extracted and used to properly convert the given $fooValue
.
outputColumns()
- creates a Builder for configuring a list of columns returned by aSELECT
statement.returningColumns()
- creates a Builder for configuring a list of columns in theRETURNING
clause.outputSubquery()
- creates a Builder for configuring a scalar subquery to be added to the output list of aSELECT
statement.outputExpression()
- adds expression(s) to the list of columns returned by aSELECT
statement.returningExpression()
- adds expression(s) to the list of columns in theRETURNING
clause.
join()
- creates a Builder for configuring a join to the given table. The argument has the same semantics as forexists()
method described above.
orderBy()
/orderByUnsafe()
- these create fragments that set theORDER BY
list of aSELECT
query to the given expressions, the difference being that the former allows only column names and ordinal numbers as expressions while the latter allows everything. The reasoning is that sort options are often coming from user input and due to SQL language structure should be embedded in the query without the means to use some parameter-like constructs. "Unsafe" in the method name is a huge hint not to pass user input.limit()
creates a fragment adding theLIMIT
clause. Note that the given$limit
value will not actually be embedded in SQL but passed as a parameter value.offset()
creates a fragment adding theOFFSET
clause.$offset
parameter is also not embedded in SQL.
PrimaryKeyTableGateway
defines one additional builder method:
namespace sad_spirit\pg_gateway\gateways;
use sad_spirit\pg_gateway\conditions\ParametrizedCondition;
class PrimaryKeyTableGateway extends GenericTableGateway implements PrimaryKeyAccess
{
public function primaryKey(mixed $value) : ParametrizedCondition;
}
The returned ParametrizedCondition
decorates conditions\PrimaryKeyCondition
.
It can be used to combine the condition on table's primary key with some additional fragments, as shorthand
methods of PrimaryKeyAccess
do not accept these:
$select = $gateway->selectByPrimaryKey(1);
vs
$select = $gateway->select([
$gateway->primaryKey(1),
$gateway->outputExpression('foo @@@ bar as foobar')
]);