Skip to content

SqlBuilder: Easily Create Secure SQL Statements

Ryan Fischbach edited this page May 28, 2017 · 2 revisions

How can I convert this SQL statement into something using SqlBuilder?

Given the following SQL statement, how would one convert it to use the SqlBuilder class?

"SELECT pack_test_id, text from {$this->tnPackageTests} pt JOIN {$this->tnTests} t on pt.test_id = t.test_id WHERE package_id = '$package_id' && stopped = 0 && acronym in (3, 5, 7, 9, 14, 16, 17)"

//Knowing how to write SQL statements with PHP's PDO classes, especially using
//  parametrized queries, will help you greatly understand the SqlBuilder class.
//  First, we create a new instance and map this model to it so that queries will
//  use the correct database connection to execute the query.
//  Most of these methods can be chained together but will not be done so here so
//  that notes about the statement can be placed above each method.
$theSql = SqlBuilder::withModel($this);

//supply the parameters the SQL will likely be using (can be an object or an array)
$theSql->obtainParamsFrom(array(
		'package_id' => $package_id,
		'acronym' => array(3, 5, 7, 9, 14, 16, 17),
));
//NOTE: deliberately leaving "stopped" out as an example of a missing-but-required parameter.

//Once we have the object instance, start building our query.
$theSql->startWith('SELECT');

//add the field list desired for the statement
$theSql->add('pack_test_id, text');

//do not hardcode the table name, use the table name variable instead. this allows us
//  to easily change the database name used in the database connection as well as
//  let the website admin change the table prefix being used without having to change
//  any code.  Being able to change the database name and/or table prefix is a
//  requirement to be able to install websites into 3rd party hosted data servers
//  which might utilize shared databases.
$theSql->add('FROM')->add($this->tnPackageTests);
$theSql->add('JOIN')->add($this->tnTests)->add('USING (test_id)');

//tell SqlBuilder that we are starting to construct the WHERE clause; this allows us
//  to treat NULL parameters correctly so that instead of the usual "field=:field"
//  string generation which will not work if the value is NULL in the WHERE clause.
//  Since SqlBuilder knows we are connected to a MySQL database, once we call
//  startWhereClause(), the string generated would be "field IS NULL" which will
//  make MySQL happy.  While not strictly _needed_ here, it is best to get into the
//  habit now so that if other quirks with other database types come up, we can easily
//  accomodate them in SqlBuilder and your code will already be able to handle it.
$theSql->startWhereClause();

//use parameterized queries whenever you have a "variable value" and ESPECIALLY if
//  that value is supplied by a webform in order to prevent SQL Injection attacks.
$theSql->mustAddParam('package_id');

//how should the next paramater in the WHERE clause be added? AND? OR? etc.
//  the string will not be appended to the SQL statement unless a mustAddParam(),
//  addParam(), or similar method actually appends a parameter to the SQL string
//  being built. This allows us to make some parameters optional and only get
//  appended to the SQL string correctly if there is data in the parameter.
//  NOTE: spaces are important here and must be supplied as they will not
//        be automatically added to the SQL statement.
$theSql->setParamPrefix(' AND ');

//if a required parameter is missing from the array (or object) passed into
//  obtainParamsFrom(), we can specify a default value.
$theSql->mustAddParam('stopped', 0);

//once a prefix is set, no need to set it again unless you want to change it
//$theSql->setParamPrefix(' AND ');

//a parameter can be an array of values -- SqlBuilder will detect it and convert it
//  to be an IN (...) clause automatically.
$theSql->mustAddParam('acronym');

//once done with the WHERE clause, tell SqlBuilder.
$theSql->endWhereClause();

//now that we're done building the SQL statement, it is now time to execute the query.
//  place the execute method inside a try/catch block.
try {
	$thePDOStatement = $theSql->query();
	//do whatever else you need to here...
}
catch (\PDOException $pdoe)
//catching the PDOException and then re-throwing it via newDbException() will
//  toss the exception expected by other aspects of the framework as well as gets
//  automatically logged into the Strings::errorLog() mechanism.
{ throw $theSql->newDbException(__METHOD__, $pdoe); }

Clone this wiki locally