18c551fd7SAndreas Gohr<?php 28c551fd7SAndreas Gohr 38c551fd7SAndreas Gohrnamespace dokuwiki\plugin\struct\meta; 48c551fd7SAndreas Gohr 58c551fd7SAndreas Gohr/** 68c551fd7SAndreas Gohr * Class QueryBuilder 78c551fd7SAndreas Gohr * @package dokuwiki\plugin\struct\meta 88c551fd7SAndreas Gohr */ 98c551fd7SAndreas Gohrclass QueryBuilder { 108c551fd7SAndreas Gohr 118c551fd7SAndreas Gohr /** @var array placeholder -> values */ 128c551fd7SAndreas Gohr protected $values = array(); 138c551fd7SAndreas Gohr /** @var array (alias -> statement */ 148c551fd7SAndreas Gohr protected $select = array(); 158c551fd7SAndreas Gohr /** @var array (alias -> statement) */ 168c551fd7SAndreas Gohr protected $from = array(); 178c551fd7SAndreas Gohr /** @var QueryBuilderWhere */ 188c551fd7SAndreas Gohr protected $where; 198c551fd7SAndreas Gohr 208c551fd7SAndreas Gohr /** 218c551fd7SAndreas Gohr * QueryBuilder constructor. 228c551fd7SAndreas Gohr */ 238c551fd7SAndreas Gohr public function __construct() { 248c551fd7SAndreas Gohr $this->where = new QueryBuilderWhere(); 258c551fd7SAndreas Gohr } 268c551fd7SAndreas Gohr 278c551fd7SAndreas Gohr /** 288c551fd7SAndreas Gohr * Adds a column to select 298c551fd7SAndreas Gohr * 308c551fd7SAndreas Gohr * If the alias already exists, the current statement for that alias will be overwritten. 318c551fd7SAndreas Gohr * 328c551fd7SAndreas Gohr * @param string $tablealias The table to select from 338c551fd7SAndreas Gohr * @param string $column The column to select 348c551fd7SAndreas Gohr * @param string $alias Under whichname to slect the column. blank for column name 358c551fd7SAndreas Gohr */ 368c551fd7SAndreas Gohr public function addSelectColumn($tablealias, $column, $alias = '') { 378c551fd7SAndreas Gohr if($alias === '') $alias = $column; 388c551fd7SAndreas Gohr if(!isset($this->from[$tablealias])) throw new StructException('Table Alias does not exist'); 398c551fd7SAndreas Gohr $this->select[$alias] = "$tablealias.$column AS $alias"; 408c551fd7SAndreas Gohr } 418c551fd7SAndreas Gohr 428c551fd7SAndreas Gohr /** 438c551fd7SAndreas Gohr * Add a new select statement (the column part of it) 448c551fd7SAndreas Gohr * 458c551fd7SAndreas Gohr * Basically the same as @see addSelectColumn but accepts any statement. This is useful to 468c551fd7SAndreas Gohr * select things like fixed strings or more complex function calls, but the correctness will not 478c551fd7SAndreas Gohr * be checked. 488c551fd7SAndreas Gohr * 498c551fd7SAndreas Gohr * If the alias already exists, the current statement for that alias will be overwritten. 508c551fd7SAndreas Gohr * 518c551fd7SAndreas Gohr * @param string $statement 528c551fd7SAndreas Gohr * @param string $alias 538c551fd7SAndreas Gohr */ 548c551fd7SAndreas Gohr public function addSelectStatement($statement, $alias) { 558c551fd7SAndreas Gohr $this->select[$alias] = "$statement AS $alias"; 568c551fd7SAndreas Gohr } 578c551fd7SAndreas Gohr 588c551fd7SAndreas Gohr /** 598c551fd7SAndreas Gohr * Adds the the table to the FROM statement part 608c551fd7SAndreas Gohr * 618c551fd7SAndreas Gohr * @param string $table the table to add 628c551fd7SAndreas Gohr * @param string $alias alias for the table, blank for table name 638c551fd7SAndreas Gohr */ 648c551fd7SAndreas Gohr public function addTable($table, $alias = '') { 658c551fd7SAndreas Gohr if($alias === '') $alias = $table; 668c551fd7SAndreas Gohr if(isset($this->from[$alias])) throw new StructException('Table Alias exists'); 678c551fd7SAndreas Gohr $this->from[$alias] = "$table AS $alias"; 688c551fd7SAndreas Gohr } 698c551fd7SAndreas Gohr 708c551fd7SAndreas Gohr /** 718c551fd7SAndreas Gohr * Adds a LEFT JOIN clause to the FROM statement part, sorted at the correct spot 728c551fd7SAndreas Gohr * 738c551fd7SAndreas Gohr * @param string $leftalias the alias of the left table you're joining on, has to exist already 748c551fd7SAndreas Gohr * @param string $righttable the right table to be joined 758c551fd7SAndreas Gohr * @param string $rightalias an alias for the right table, blank for table name 768c551fd7SAndreas Gohr * @param string $onclause the ON clause condition the join is based on 778c551fd7SAndreas Gohr */ 788c551fd7SAndreas Gohr public function addLeftJoin($leftalias, $righttable, $rightalias, $onclause) { 798c551fd7SAndreas Gohr if($rightalias === '') $rightalias = $righttable; 808c551fd7SAndreas Gohr if(!isset($this->from[$leftalias])) throw new StructException('Table Alias does not exist'); 818c551fd7SAndreas Gohr if(isset($this->from[$rightalias])) throw new StructException('Table Alias already exists'); 828c551fd7SAndreas Gohr 838c551fd7SAndreas Gohr $pos = array_search($leftalias, array_keys($this->from)); 848c551fd7SAndreas Gohr $statement = "LEFT OUTER JOIN $righttable AS $rightalias ON $onclause"; 858c551fd7SAndreas Gohr $this->from = $this->array_insert($this->from, array($rightalias => $statement), $pos+1); 868c551fd7SAndreas Gohr } 878c551fd7SAndreas Gohr 888c551fd7SAndreas Gohr /** 898c551fd7SAndreas Gohr * Returns the current WHERE filters and allows to set new ones 908c551fd7SAndreas Gohr * 918c551fd7SAndreas Gohr * @return QueryBuilderWhere 928c551fd7SAndreas Gohr */ 938c551fd7SAndreas Gohr public function getFilters() { 948c551fd7SAndreas Gohr return $this->where; 958c551fd7SAndreas Gohr } 968c551fd7SAndreas Gohr 978c551fd7SAndreas Gohr /** 988c551fd7SAndreas Gohr * Adds a value to the statement 998c551fd7SAndreas Gohr * 100df30dbf7SAndreas Gohr * This function returns the name of the placeholder you have to use in your statement, so whenever 101df30dbf7SAndreas Gohr * you need to use a user value in a statement, call this first, then add the statement through the 102df30dbf7SAndreas Gohr * other functions using the returned placeholder. 1038c551fd7SAndreas Gohr * 1048c551fd7SAndreas Gohr * @param mixed $value 1058c551fd7SAndreas Gohr * @return string 1068c551fd7SAndreas Gohr */ 1078c551fd7SAndreas Gohr public function addValue($value) { 1088c551fd7SAndreas Gohr static $count = 0; 1098c551fd7SAndreas Gohr $count++; 1108c551fd7SAndreas Gohr 1118c551fd7SAndreas Gohr $placeholder = ":!!val$count!!:"; // sqlite plugin does not support named parameters, yet so we have simulate it 1128c551fd7SAndreas Gohr $this->values[$placeholder] = $value; 1138c551fd7SAndreas Gohr return $placeholder; 1148c551fd7SAndreas Gohr } 1158c551fd7SAndreas Gohr 1168c551fd7SAndreas Gohr /** 1178c551fd7SAndreas Gohr * Creates a new table alias that has not been used before 1188c551fd7SAndreas Gohr * 1198c551fd7SAndreas Gohr * @return string 1208c551fd7SAndreas Gohr */ 1218c551fd7SAndreas Gohr public function generateTableAlias() { 1228c551fd7SAndreas Gohr static $count = 0; 1238c551fd7SAndreas Gohr $count++; 1248c551fd7SAndreas Gohr return "T$count"; 1258c551fd7SAndreas Gohr } 1268c551fd7SAndreas Gohr 1278c551fd7SAndreas Gohr /** 1288c551fd7SAndreas Gohr * Returns the complete SQL statement and the values to apply 1298c551fd7SAndreas Gohr * 1308c551fd7SAndreas Gohr * @return array 1318c551fd7SAndreas Gohr */ 1328c551fd7SAndreas Gohr public function getSQL() { 1338c551fd7SAndreas Gohr $sql = 1348c551fd7SAndreas Gohr 'SELECT ' . join(",\n", $this->select) . "\n" . 1358c551fd7SAndreas Gohr ' FROM ' . join(",\n", $this->from) . "\n" . 1368c551fd7SAndreas Gohr ' WHERE ' . $this->where->toSQL() . "\n"; 1378c551fd7SAndreas Gohr 1388c551fd7SAndreas Gohr return $this->fixPlaceholders($sql); 1398c551fd7SAndreas Gohr } 1408c551fd7SAndreas Gohr 1418c551fd7SAndreas Gohr /** 1428c551fd7SAndreas Gohr * Replaces the named placeholders with ? placeholders 1438c551fd7SAndreas Gohr * 1448c551fd7SAndreas Gohr * Until the sqlite plugin can use named placeholder properly 1458c551fd7SAndreas Gohr * 1468c551fd7SAndreas Gohr * @param string $sql 1478c551fd7SAndreas Gohr * @return array 1488c551fd7SAndreas Gohr */ 1498c551fd7SAndreas Gohr protected function fixPlaceholders($sql) { 1508c551fd7SAndreas Gohr $vals = array(); 1518c551fd7SAndreas Gohr 1528c551fd7SAndreas Gohr while(preg_match('/(:!!val\d+!!:)/', $sql, $m)) { 1538c551fd7SAndreas Gohr $pl = $m[1]; 154*2b5df26eSAndreas Gohr 155*2b5df26eSAndreas Gohr if(!isset($this->values[$pl])) throw new StructException('Placeholder not found'); 156*2b5df26eSAndreas Gohr 157*2b5df26eSAndreas Gohr $sql = preg_replace("/$pl/", '?', $sql, 1); 1588c551fd7SAndreas Gohr $vals[] = $this->values[$pl]; 1598c551fd7SAndreas Gohr } 1608c551fd7SAndreas Gohr 1618c551fd7SAndreas Gohr return array($sql, $vals); 1628c551fd7SAndreas Gohr } 1638c551fd7SAndreas Gohr 1648c551fd7SAndreas Gohr /** 1658c551fd7SAndreas Gohr * Insert an array into another array at a given position in an associative array 1668c551fd7SAndreas Gohr * 1678c551fd7SAndreas Gohr * @param array $array The initial array 1688c551fd7SAndreas Gohr * @param array $pairs The array to insert 1698c551fd7SAndreas Gohr * @param string $key_pos The position at which to insert 1708c551fd7SAndreas Gohr * @link https://gist.github.com/scribu/588429 simplified 1718c551fd7SAndreas Gohr * @return array 1728c551fd7SAndreas Gohr */ 1738c551fd7SAndreas Gohr protected function array_insert($array, $pairs, $key_pos) { 1748c551fd7SAndreas Gohr $result = array_slice($array, 0, $key_pos); 1758c551fd7SAndreas Gohr $result = array_merge($result, $pairs); 1768c551fd7SAndreas Gohr $result = array_merge($result, array_slice($array, $key_pos)); 1778c551fd7SAndreas Gohr return $result; 1788c551fd7SAndreas Gohr } 1798c551fd7SAndreas Gohr} 1808c551fd7SAndreas Gohr 181