Je suis toujours en cours de développement d’un SQL query builder, dont la première version a été poussée sur le dépôt Clearbricks, mais j’avoue que son usage n’est pas encore celui que j’espérais idéalement ; il reste encore des lourdeurs d’écriture.
Un petit exemple avec le code pour constituer cette requête SQL (syntaxe PostgreSQL) :
SELECT "a"."fullname", "a"."id", "a"."score" FROM "scores" AS "a" INNER JOIN (SELECT "fullname", MAX("score") FROM "scores" WHERE "fullname" = 'Jane Doe' GROUP BY "fullname") AS "b" ON "a"."fullname" = "b"."fullname" AND "a"."score" = "b"."score" WHERE "a"."score" > 50
Le code correspondant ressemble à ça :
use dbQuery as sql;
use dbQueryExpression as sqlExpr;
use dbQueryConditions as sqlCond;
use dbQueryAlias as sqlAlias;
$join = sql::make($con);
$join
->columns(
'fullname',
sqlExpr::make($join, 'MAX(%s)', 'score')
)
->from('scores')
->where(sqlCond::make($join, 'fullname = ?', 'Jane Doe'))
->groupBy('fullname');
$query = sql::make($con);
$query
->columns(
'a.fullname',
'a.id',
'a.score'
)
->from('scores a')
->innerJoin(
sqlAlias::make($query, $join, 'b'),
sqlCond::make($query, 'a.fullname = b.fullname')
->with('a.score = b.score'))
->where(sqlCond::make($query, 'a.score > ?', 50));
Entre autre chose, je voudrais éviter d’avoir à réinjecter l’objet dbQuery a la construction de chaque élément (voir les méthodes make()), ce qui donnerait ceci idéalement :
use dbQuery as sql;
$join = sql::make($con);
$join
->columns(
'fullname',
$join->expression('MAX(%s)', 'score')
)
->from('scores')
->where($join->condition('fullname = ?', 'Jane Doe'))
->groupBy('fullname');
$query = sql::make($con);
$query
->columns(
'a.fullname',
'a.id',
'a.score'
)
->from('scores a')
->innerJoin(
$query->alias($join, 'b'),
$query->condition('a.fullname = b.fullname')
->with('a.score = b.score'))
->where($query->condition('a.score > ?', 50));
Reste également à permettre l’usage du constructeur de requête, à l’origine prévu pour fonctionner via PDO::execute() avec les données injectées à ce moment, en remettant ces mêmes données dans la requête. C’est là que je mesure tout l’intérêt des tests unitaires !
Un autre avantage de ce constructeur est que tout ce qui est possible d’être « échappé » (nom de table, de champs, données, alias, …) l’est, ce qui permet d’éviter autant que possible l’injection de choses bizarres, et que cet échappement se conforme à la syntaxe du gestionnaire de base de données utilisé ;-)
Quoi qu’il en soit je m’amuse bien depuis trois jours !