Merge branch 'Issue_#59' into 'master'

Added Joins to the QueryBuilder

Fixes Issue #59

See merge request !26
This commit is contained in:
Abel Hoogeveen 2015-05-14 13:32:55 +02:00
commit bf67daf181
2 changed files with 253 additions and 34 deletions

View File

@ -59,46 +59,133 @@ class Query extends Module {
/**
* From
*
* @param $table null|string The table, if it is null, the table set with setTable() will be used
* @param ...$table null|string The table, if it is null, the table set with setTable() will be used
* @return $this
* @throws Exception
*/
public function from($table = '')
public function from()
{
$tables = func_get_args();
if($table === ''){
$this->query .= ' FROM ';
if($this->table == '')
if(count($tables) == 0)
$tables[] = "`$this->table`";
else foreach($tables as $i => $t) {
if ($t == '' && $this->table == '')
throw new Exception("No table given");
$table = $this->table;
$tables[$i] = strpos($t, ' ') === false ? "`$t`" : '`'.implode('` ', explode(' ', $t));
}
$this->query .= ' FROM `' . $table . '`';
$this->query .= implode(', ', $tables);
return $this;
}
/**
* Where
* (Inner) join
*
* @param $field
* @param string $table The table to join
* @param string $type The type of join to perform (JOIN, LEFT JOIN, RIGHT JOIN, FULL JOIN)
* @return $this
* @throws Exception
*/
public function join($table, $type = ''){
if($table === '') {
if ($this->table == '')
throw new Exception("No table given");
$table = $this->table;
}
if($type != '')
$this->query .= ' '.$type;
$this->query .= ' JOIN ' . (strpos($table, ' ') === false ? "`$table`" : '`'.implode('` ', explode(' ', $table)));
return $this;
}
/**
* Left join
*
* @param string $table The table to join
* @return $this
* @throws Exception
*/
public function left_join($table){
return $this->join($table, 'LEFT');
}
/**
* Right join
*
* @param string $table The table to join
* @return $this
* @throws Exception
*/
public function right_join($table){
return $this->join($table, 'RIGHT');
}
/**
* Full join
*
* @param string $table The table to join
* @return $this
* @throws Exception
*/
public function full_join($table){
return $this->join($table, 'FULL');
}
/**
* On
*
* @param string $field Field name, or raw SQL
* @param $arg2 string, The value of the field, or an operator in which case the value is pushed to $arg3
* @param null|string $arg3 The value of the field when $arg2 is used for an operator
* @return $this
*/
public function where($field, $arg2, $arg3 = null){
public function on($field, $arg2 = null, $arg3 = null){
//The value is the second paramter, unless the second parameter is an operator, in which case it's the third
return $this->where($field, $arg2, $arg3, 'ON');
}
/**
* Where
*
* @param string $field Field name, or raw SQL
* @param string $arg2, The value of the field, or an operator in which case the value is pushed to $arg3
* @param null|string $arg3 The value of the field when $arg2 is used for an operator
* @param string $type Whether this is an WHERE or ON operation
* @return $this
*/
public function where($field, $arg2 = null, $arg3 = null, $type = 'WHERE'){
if($arg2 === null)
return $this->sql(' '.$type.' '.$field);
//The value is the second parameter, unless the second parameter is an operator, in which case it's the third
$value = ($arg3 == null ? $arg2 : $arg3);
//If the third parameter is not given, the default operator should be used
$operator = strtoupper($arg3 == null ? "=" : $arg2);
$field = $this->formatField($field);
switch($operator){
case 'IN':
$this->query .= ' WHERE `' . $field .'` IN (';
$this->query .= ' '.$type.' '.$field.' IN (';
foreach($value as $k => $v){
@ -109,12 +196,11 @@ class Query extends Module {
//Remove the trailing comma and close it
$this->query = rtrim($this->query, ",") . ')';
break;
case 'BETWEEN':
$this->query .= ' WHERE `' . $field .'` BETWEEN ? AND ?';
$this->query .= ' '.$type.' '.$field.' BETWEEN ? AND ?';
$this->binds[] = $value[0];
$this->binds[] = $value[1];
@ -122,7 +208,7 @@ class Query extends Module {
default:
$this->query .= ' WHERE `' . $field .'` ' . $operator . ' ?';
$this->query .= ' '.$type.' '.$field.' ' . $operator . ' ?';
$this->binds[] = $value;
break;
@ -133,7 +219,7 @@ class Query extends Module {
/**
* Where open. Is the start of WHERE(....). To end this call ->close()
*
* @param $field
* @param string $field Field name, or raw SQL
* @param $arg2 string, The value of the field, or an operator in which case the value is pushed to $arg3
* @param null|string $arg3 The value of the field when $arg2 is used for an operator
* @return $this
@ -142,6 +228,7 @@ class Query extends Module {
$old = $this->query;
$this->where($field, $arg2, $arg3);
//Replace the WHERE with WHERE (
$this->query = $old . ' WHERE (' . substr($this->query, strlen($old) + 7);
@ -151,23 +238,28 @@ class Query extends Module {
/**
* Or, this function should be called after ->where() or ->having(). Please use ->or as an alias instead of ->_or()
*
* @param $field
* @param string $field Field name, or raw SQL
* @param $arg2 string, The value of the field, or an operator in which case the value is pushed to $arg3
* @param null|string $arg3 The value of the field when $arg2 is used for an operator
* @return $this
*/
public function _or($field, $arg2, $arg3 = null){
private function _or($field, $arg2 = null, $arg3 = null){
if($arg2 === null)
return $this->sql(' OR '.$field);
//The value is the second paramter, unless the second parameter is an operator, in which case it's the third
$value = ($arg3 == null ? $arg2 : $arg3);
//If the third parameter is not given, the default operator should be used
$operator = strtoupper($arg3 == null ? "=" : $arg2);
$field = $this->formatField($field);
switch($operator){
case 'IN':
$this->query .= ' OR `' . $field .'` IN (';
$this->query .= ' OR ' . $field .' IN (';
foreach($value as $k => $v){
@ -183,7 +275,7 @@ class Query extends Module {
case 'BETWEEN':
$this->query .= ' OR `' . $field .'` BETWEEN ? AND ?';
$this->query .= ' OR ' . $field .' BETWEEN ? AND ?';
$this->binds[] = $value[0];
$this->binds[] = $value[1];
@ -191,7 +283,7 @@ class Query extends Module {
default:
$this->query .= ' OR `' . $field .'` ' . $operator . ' ?';
$this->query .= ' OR ' . $field .' ' . $operator . ' ?';
$this->binds[] = $value;
break;
@ -202,7 +294,7 @@ class Query extends Module {
/**
* Or open. Is the start of OR(....). To end this call ->close()
*
* @param $field
* @param string $field Field name, or raw SQL
* @param $arg2 string, The value of the field, or an operator in which case the value is pushed to $arg3
* @param null|string $arg3 The value of the field when $arg2 is used for an operator
* @return $this
@ -220,23 +312,28 @@ class Query extends Module {
/**
* And, this function should be called after ->where() or ->having(). Please use ->and as an alias instead of ->_and()
*
* @param $field
* @param string $field Field name, or raw SQL
* @param $arg2 string, The value of the field, or an operator in which case the value is pushed to $arg3
* @param null|string $arg3 The value of the field when $arg2 is used for an operator
* @return $this
*/
public function _and($field, $arg2, $arg3 = null){
private function _and($field, $arg2 = null, $arg3 = null){
if($arg2 === null)
return $this->sql(' AND '.$field);
//The value is the second paramter, unless the second parameter is an operator, in which case it's the third
$value = ($arg3 == null ? $arg2 : $arg3);
//If the third parameter is not given, the default operator should be used
$operator = strtoupper($arg3 == null ? "=" : $arg2);
$field = $this->formatField($field);
switch($operator){
case 'IN':
$this->query .= ' AND `' . $field .'` IN (';
$this->query .= ' AND ' . $field .' IN (';
foreach($value as $k => $v){
@ -252,7 +349,7 @@ class Query extends Module {
case 'BETWEEN':
$this->query .= ' AND `' . $field .'` BETWEEN ? AND ?';
$this->query .= ' AND ' . $field .' BETWEEN ? AND ?';
$this->binds[] = $value[0];
$this->binds[] = $value[1];
@ -260,7 +357,7 @@ class Query extends Module {
default:
$this->query .= ' AND `' . $field .'` ' . $operator . ' ?';
$this->query .= ' AND ' . $field .' ' . $operator . ' ?';
$this->binds[] = $value;
break;
@ -321,7 +418,9 @@ class Query extends Module {
}
$this->query .= ' `' . $field . '` ' . $mode;
$field = $this->formatField($field);
$this->query .= ' ' . $field . ' ' . $mode;
}
return $this;
@ -356,11 +455,13 @@ class Query extends Module {
//If the third parameter is not given, the default operator should be used
$operator = strtoupper($arg3 == null ? "=" : $arg2);
$field = $this->formatField($field);
switch($operator){
case 'IN':
$this->query .= ' HAVING `' . $field .'` IN (';
$this->query .= ' HAVING ' . $field .' IN (';
foreach($value as $k => $v){
@ -376,7 +477,7 @@ class Query extends Module {
case 'BETWEEN':
$this->query .= ' HAVING `' . $field .'` BETWEEN ? AND ?';
$this->query .= ' HAVING ' . $field .' BETWEEN ? AND ?';
$this->binds[] = $value[0];
$this->binds[] = $value[1];
@ -384,7 +485,7 @@ class Query extends Module {
default:
$this->query .= ' HAVING `' . $field .'` ' . $operator . ' ?';
$this->query .= ' HAVING ' . $field .' ' . $operator . ' ?';
$this->binds[] = $value;
break;
@ -437,17 +538,19 @@ class Query extends Module {
/**
* Set
*
* @param $array array Key value, $field => $value
* @param $data array|string Key value, $field => $value or raw SQL
* @return $this
*/
public function set($array)
public function set($data)
{
if(is_string($data))
return $this->sql(' SET '.$data);
$this->query .= ' SET';
$first = true;
foreach($array as $field => $value){
foreach($data as $field => $value){
if(!$first)
$this->query .= ',';
@ -507,6 +610,33 @@ class Query extends Module {
return $this;
}
/**
* Add raw SQL to the query string
*
* @param string $sql The SQL to add
* @return string
*/
public function sql($sql){
$this->query .= $sql;
return $this;
}
/**
* Formats the given field
*
* @param string $field The field to format
* @return string The formatted field
*/
private function formatField($field){
if(strpos($field, '.') === false)
$field = "`$field`";
return $field;
}
/**
* Returns the query
*

View File

@ -30,10 +30,28 @@ class QueryTests extends CoreTestAbstract {
public function testSelectSimpleDefaultTable(){
$this->query->setTable("table")->select()->from('table');
$this->query->setTable("table")->select()->from();
$this->assertEquals('SELECT * FROM `table`', $this->query->getQuery());
}
public function testSelectSimpleComboTable()
{
$this->query->select()->from('table', 'table2');
$this->assertEquals('SELECT * FROM `table`, `table2`', $this->query->getQuery());
}
public function testSelectSimpleAlias()
{
$this->query->select()->from('table t');
$this->assertEquals('SELECT * FROM `table` t', $this->query->getQuery());
}
public function testSelectSimpleComboAlias()
{
$this->query->select()->from('table t', 'table2 t2');
$this->assertEquals('SELECT * FROM `table` t, `table2` t2', $this->query->getQuery());
}
public function testSelectSimpleOneField(){
$this->query->select('field1')->from('table');
@ -332,4 +350,75 @@ class QueryTests extends CoreTestAbstract {
$this->assertEquals(array('value1', 'value2'), $this->query->getBinds());
}
/*
* Joins
*/
public function testJoin(){
$this->query->select()->from('table')->join('other')->on("field", "value")->where("field", "value2");
$this->assertEquals('SELECT * FROM `table` JOIN `other` ON `field` = ? WHERE `field` = ?', $this->query->getQuery());
$this->assertEquals(array('value', 'value2'), $this->query->getBinds());
}
public function testJoinLeft(){
$this->query->select()->from('table')->left_join('other')->on("field", "value")->where("field", "value2");
$this->assertEquals('SELECT * FROM `table` LEFT JOIN `other` ON `field` = ? WHERE `field` = ?', $this->query->getQuery());
$this->assertEquals(array('value', 'value2'), $this->query->getBinds());
}
public function testJoinRight(){
$this->query->select()->from('table')->right_join('other')->on("field", "value")->where("field", "value2");
$this->assertEquals('SELECT * FROM `table` RIGHT JOIN `other` ON `field` = ? WHERE `field` = ?', $this->query->getQuery());
$this->assertEquals(array('value', 'value2'), $this->query->getBinds());
}
public function testJoinFull(){
$this->query->select()->from('table')->full_join('other')->on("field", "value")->where("field", "value2");
$this->assertEquals('SELECT * FROM `table` FULL JOIN `other` ON `field` = ? WHERE `field` = ?', $this->query->getQuery());
$this->assertEquals(array('value', 'value2'), $this->query->getBinds());
}
public function testJoinAdvanced(){
$this->query->select()->from('table')
->left_join('other_a')->on("field", "value")
->right_join('other_b')->on("field", "value2")
->full_join('other_c')->on("field", "value3")
->where("field", "value4");
$this->assertEquals('SELECT * FROM `table` LEFT JOIN `other_a` ON `field` = ? RIGHT JOIN `other_b` ON `field` = ? FULL JOIN `other_c` ON `field` = ? WHERE `field` = ?', $this->query->getQuery());
$this->assertEquals(array('value', 'value2', 'value3', 'value4'), $this->query->getBinds());
}
/**
* Inline joins
*/
public function testJoinInline(){
$this->query->select()->from('table t', 'other o')->where("o.field", "value");
$this->assertEquals('SELECT * FROM `table` t, `other` o WHERE o.field = ?', $this->query->getQuery());
$this->assertEquals(array('value'), $this->query->getBinds());
}
public function testJoinInlineAdvanced(){
$this->query->select()->from('table t', 'other o')->where("o.field = t.field");
$this->assertEquals('SELECT * FROM `table` t, `other` o WHERE o.field = t.field', $this->query->getQuery());
$this->assertEmpty($this->query->getBinds());
}
public function testJoinAllInOne(){
$this->query->select()
->from('table t', 'other o')
->left_join('third th')->on('th.field = o.field')
->where("o.field = t.field")
->and('t.thing', '>', 25);
$this->assertEquals('SELECT * FROM `table` t, `other` o LEFT JOIN `third` th ON th.field = o.field WHERE o.field = t.field AND t.thing > ?', $this->query->getQuery());
$this->assertEquals(array(25), $this->query->getBinds());
}
}