meraproject/module/core/db/structure/query.php

670 lines
25 KiB
PHP
Raw Normal View History

<?
use \core\db\structure\eQueryType;
use \core\db\structure\eQuerySortType;
use core\db\structure\eColumnType;
class Query {
const TABLES = 1;
const SORT = 2;
const WHERE = 4;
const FROM = 5;
const TYPE = 6;
const COUNT = 7;
const OFFSET = 8;
const CALC = 9;
const VALUES = 10;
const TABLE = 11;
const SELECT = 12;
const ON_DUPLICATE_KEY_UPDATE = 13;
const LEFT_JOINS = 14;
const GROUP_BY = 15;
const COLUMN = 16; // Название столбца, в первую очередь сделано для удаления
const COLUMNS_INCREMENT = 17; // Сделано для инкрементации столбца
const INNER_JOINS = 18;
/** @var \Table|null */
protected $table = null;
protected $type = eQueryType::SELECT;
protected $tables = [];
protected $sort = [];
protected $count = 0;
protected $where = 0;
protected $offset = 0;
protected $result = null;
protected $values = [];
protected $select = [];
protected $on_duplicate_key_update = [];
protected $left_joins = [];
protected $inner_joins = [];
protected $calc = false;
protected $options = [];
protected $group_by = [];
protected $column = [];
//-----------------------------------
// MAGIC FUNCTIONS
//-----------------------------------
function __construct(array $options){
// Устанавливаем тип заропса
$this->type = isset($options[$c = Query::TYPE]) ? $options[$c] : eQueryType::SELECT;
$this->setTables( isset($options[$c = Query::TABLES]) ? $options[$c]: null);
$this->setSort( isset($options[$c = Query::SORT]) ? $options[$c]: null);
$this->setSelect( isset($options[$c = Query::SELECT]) ? $options[$c]: null);
$this->setGroupBy( isset($options[$c = Query::GROUP_BY]) ? $options[$c]: null);
$this->setOnDuplicateKeyUpdate( isset($options[$c = Query::ON_DUPLICATE_KEY_UPDATE]) ? $options[$c]: null);
$this->setLeftJoins( isset($options[$c = Query::LEFT_JOINS]) ? $options[$c]: null);
$this->setInnerJoins( isset($options[$c = Query::INNER_JOINS]) ? $options[$c]: null);
$this->count = isset($options[$c = Query::COUNT]) ? intval($options[$c]) : 0;
$this->offset = isset($options[$c = Query::OFFSET]) ? intval($options[$c]) : 0;
$this->table = isset($options[$c = Query::TABLE]) ? $options[$c] : null;
$this->values = isset($options[$c = Query::VALUES]) ? $options[$c] : [];
$this->calc = isset($options[$c = Query::CALC]) ? boolval($options[$c]) : false;
$this->column = isset($options[$c = Query::COLUMN]) ? (string)trim($options[$c]) : false;
if(isset($options[$c = Query::WHERE])) {
$this->where = $options[Query::WHERE];
if (is_array($this->where)) {
$this->where = new \Where($this->where);
//$o[Query::WHERE] = new \Where($this->where);
}
}
$this->options = $options;
//echo '<pre>'.$this.'</pre>';
//echo "\n".$this."\n";
}
function __toString() {
$q = [];
// Тип запроса
$q[] = eQueryType::toString( $this->type );
switch ($this->type) {
case eQueryType::DROP_COLUMN:
$table = $this->table;
$q[] = DB::quotes($table->getName())." DROP ".DB::quotes($this->column);
break;
case eQueryType::ADD_COLUMN:
$table = $this->table;
$cols = $table->getColumns();
$col = $cols[$this->column];
$q[] = DB::quotes($table->getName())." ADD ".$col->toStringForCreateTable();
break;
case eQueryType::DROP:
$table = $this->table;
$q[] = 'TABLE '.DB::quotes($table->getName());
break;
case eQueryType::UPDATE:
$table = $this->table;
$values = $this->values;
$where = $this->whereToString();
$columns = [];
$cols = $table->getColumns();
$names = $table->columnNames();
foreach ($values as $k=>$v){
if(in_array($k,$names)) {
if(is_array($v)) {
$columns[] = DB::quotes($k) . "=" . $v['value'] . "";
} else {
// проверяем тип колонки и проверяем присланные данные на соответствующий формат данных
$isGood = true;
switch ($cols[$k]->getType()) {
case eColumnType::NONE:
$isGood = false;
break;
case eColumnType::TINYINT:
case eColumnType::INT:
case eColumnType::BIGINT:
case eColumnType::DOUBLE:
$v = ($v
? "'" . DB::mq($v) . "'"
: ($cols[$k]->isNull() ? 'NULL' : "'0'")
);
break;
case eColumnType::DATE:
if ($v) {
$v = "'" . DB::mq($v) . "'";
} else {
$v = $cols[$k]->isNull() ? 'NULL' : "'0000-00-00'";
}
break;
default:
if (!is_array($v)) {
$v = ($v
? "'" . DB::mq($v) . "'"
: ($cols[$k]->isNull() ? 'NULL' : "''")
);
}
}
if ($isGood) {
if (is_array($v)) {
$columns[] = DB::quotes($k) . "=" . $v['value'] . "";
} else {
$columns[] = DB::quotes($k) . "=" . $v;
}
}
}
}
if($k==self::COLUMNS_INCREMENT){
foreach ($v as $inc_column=>$inc_value) {
if(in_array($inc_column,$names)) {
$inc_value = intval($inc_value);
$columns[] = DB::quotes($inc_column)."=".DB::quotes($inc_column).($inc_value>0?'+':'-').abs($inc_value);
} else if(in_array($inc_value,$names)) {
$columns[] = DB::quotes($inc_value)."=".DB::quotes($inc_value).'+1';
}
}
}
}
$q[] = Db::quotes($table->getName());
$q[] = 'SET '.implode(",",$columns);
$q[] = $where;
break;
case eQueryType::INSERT:
$table = $this->table;
$cols = $table->getColumns();
$values = $this->values;
$columns = array();
foreach ($table->getColumns() as $k=>$v){
$value = isset($values[$k]) ? $values[$k] : $v->getDefault();
$isGoodColumn = true;
// проверяем тип колонки и проверяем присланные данные на соответствующий формат данных
switch ($cols[$k]->getType()){
case eColumnType::NONE:
$isGoodColumn = false;
break;
case eColumnType::DATE:
if($value) {
$value = "'" . DB::mq($value) . "'";
} else {
$value = $cols[$k]->isNull() ? "NULL" : "'0000-00-00'";
}
break;
case eColumnType::TINYINT:
case eColumnType::INT:
case eColumnType::BIGINT:
case eColumnType::DOUBLE:
if(!$value) { $v->getDefault(); }
if(!$value) { $value = 0; }
$value = cfloatval($value);
switch ($cols[$k]->getType()){
case eColumnType::INT:
case eColumnType::BIGINT:
$value = intval($value);
break;
}
$value = ( $value
? "'" . DB::mq($value) . "'"
: ($cols[$k]->isNull() ? 'NULL' : '0')
);
break;
default:
$value = ( $value
? "'" . DB::mq($value) . "'"
: ($cols[$k]->isNull() ? 'NULL' : "''")
);
}
if ($isGoodColumn) {
$columns[DB::quotes($v->getName())] = $value;//"'" . DB::mq($value) . "'";
}
}
$q[] = ' INTO '.Db::quotes($table->getName()).' ('.implode(",",array_keys($columns)).') VALUES('.implode(",",array_values($columns)).')';
$q[] = $this->onDuplicateKeyUpdateToSting();
break;
case eQueryType::CREATE:
$table = $this->table;
if($table) {
$columns = array();
$primary = null;
$uniques = [];
/**
* @var $k
* @var \core\db\structure\Column $v
*/
foreach ($table->getColumns() as $k=>$v){
$isGoodColumn = true;
switch ($v->getType()) {
case eColumnType::NONE:
$isGoodColumn = false;
break;
}
if($isGoodColumn) {
$columns[] = $v->toStringForCreateTable();
if ($v->getPrimary()) $primary = $v;
if ($v->getUnique()) $uniques[] = " UNIQUE (" . DB::quotes($v->getName()) . ")";
}
}
if($primary) $columns[] = 'PRIMARY KEY ('.Db::quotes($primary->getName()).')';
if($uniques) $columns[] = implode(",",$uniques);
$q[] = 'TABLE IF NOT EXISTS '.DB::quotes($table->getName()).' ('.implode(",",$columns).')
ENGINE=InnoDB DEFAULT CHARSET='.Config::$MYSQL_CHARSET;
//echo implode(" ",$q).'<br>';
}
break;
default:
$select = $this->selectToString();
// Если укзаны таблицы, указываем их через запятую
$tables = $this->tablesToString();
// Определяем сортировку, если она конечно, есть
$sort = $this->sortToString();
$limit = $this->limitToString();
$where = $this->whereToString();
$leftJoins = $this->leftJoinsToString();
$innerJoins = $this->innerJoinsToString();
$groupBy = $this->groupByToString();
if($this->type===eQueryType::SELECT) $q[]=$select;
$q[] = $tables;
$q[] = $leftJoins;
$q[] = $innerJoins;
$q[] = $where;
$q[] = $groupBy;
$q[] = $sort;
$q[] = $limit;
}
return implode(" ",$q);
}
//-----------------------------------
// МЕТОДЫ
//-----------------------------------
function execute(){
$this->result = DB::execute($this);
return $this->result;
}
function toJSON(){
$res = [];
foreach ($this->options as $k=>$v){
$isSet = true;
switch ($k) {
case self::TABLE: $isSet = false; break;
case self::WHERE: $v = $v->toJSON(); break;
}
if($isSet) $res[$k] = $v;
}
unset($res[self::TABLE]);
return $res;
}
/**
* Функция для заданий новых таблиц в запросе
* @param $tables array | string Таблицы
*/
function setTables($tables) {
$this->tables = [];
// Если это массив, то заносим значения в виде строк
// Иначе заносим как строку
if(is_array($tables)) {
foreach ($tables as $v){ $this->tables[] = (string)$v; }
} else if($tables) $this->tables[] = (string)$tables;
}
function setSelect($select) {
$this->select = [];
// Если это массив, то заносим значения в виде строк
// Иначе заносим как строку
if(is_array($select)) {
foreach ($select as $v){ $this->select[] = (string)$v; }
} else if($select) $this->select[] = (string)$select;
}
function setGroupBy($group_by) {
$this->group_by = [];
// Если это массив, то заносим значения в виде строк
// Иначе заносим как строку
if(is_array($group_by)) {
foreach ($group_by as $v){ $this->group_by[] = (string)$v; }
} else if($group_by) $this->group_by[] = (string)$group_by;
}
function setOnDuplicateKeyUpdate($array) {
$this->on_duplicate_key_update = [];
// Если это массив, то заносим значения в виде строк
// Иначе заносим как строку
if(is_array($array)) {
foreach ($array as $k=>$v){ $this->on_duplicate_key_update[$k] = (string)$v; }
}
}
function setLeftJoins($array) {
$this->left_joins = [];
// Если это массив, то заносим значения в виде строк
// Иначе заносим как строку
if(is_array($array)) {
foreach ($array as $k=>$v){
$this->left_joins[$k] = $v;
/*
//var_dump($v[0]);
if (is_array($v)){
foreach ($v as $k1 => $v1) {
$this->left_joins[$k][] = $v;
}
} else {
$this->left_joins[$k] = $v;
}
/**/
}
} else if($array) $this->left_joins[] = (string)$array;
}
function setInnerJoins($array) {
$this->inner_joins = [];
// Если это массив, то заносим значения в виде строк
// Иначе заносим как строку
if(is_array($array)) {
foreach ($array as $k=>$v){
$this->inner_joins[$k] = $v;
/*
//var_dump($v[0]);
if (is_array($v)){
foreach ($v as $k1 => $v1) {
$this->left_joins[$k][] = $v;
}
} else {
$this->left_joins[$k] = $v;
}
/**/
}
} else if($array) $this->inner_joins[] = (string)$array;
}
/**
* Функция для задания новой сортировки в столбце
* @param $sort array | string
*/
function setSort($sort){
// Определяем сортировку
// Если передан массив, то проходимся по нему. Ключ - это столбец, значение - тип сортировки. По умолчанию ASC
if(is_array($sort)) {
foreach ( $sort as $k=>$v) {
if(is_numeric($k)){
$this->sort[$v] = eQuerySortType::ASC;
} else {
$this->sort[(string)$k] = eQuerySortType::check($v);
}
}
} else if($sort) $this->sort[ (string)$sort ] = eQuerySortType::ASC;
}
/**
* Публичная функция для получения запроса в формате строки
* @return string
*/
function getQueryString():string{return $this->__toString();}
//-----------------------------------
// STATIC FUNCTIONS
//-----------------------------------
static function select(array $o){
$o[ self::TYPE ] = eQueryType::SELECT;
return new Query($o);
}
static function delete(string $table, array $o){
$o[ self::TYPE ] = eQueryType::DELETE;
$o[ self::TABLES ] = $table;
return new Query($o);
}
static function insert(Table $table, $values = array(),array $o = array()){
$o[ self::TYPE ] = eQueryType::INSERT;
$o[ self::TABLE ] = $table;
$o[ self::VALUES ] = $values;
return new Query($o);
}
static function update(\Table $table, array $o){
$o[ self::TYPE ] = eQueryType::UPDATE;
$o[ self::TABLE ] = $table;
return new Query($o);
}
static function createTable(\Table $table){
$o[ self::TYPE ] = eQueryType::CREATE;
$o[ self::TABLE ] = $table;
return new Query($o);
}
static function dropTable(\Table $table){
$o[ self::TYPE ] = eQueryType::DROP;
$o[ self::TABLE ] = $table;
return new Query($o);
}
//-----------------------------------
// PROTECTED FUNCTIONS
//-----------------------------------
protected function whereToString(){
return ($this->where)?(string)$this->where:'';
}
protected function quoteTableColumn($column_data){
if($this->table) {
$info = $this->table->getColumns();
$keys = array_keys($info);
if (in_array($column_data, $keys)) {
return \DB::quotes($column_data);
} else {
return "'".\DB::mq($column_data)."'";
}
} else {
return \DB::quotes($column_data);
}
}
protected function innerJoinsToString(){
$res = '';
if($this->inner_joins) {
foreach ($this->inner_joins as $v){
$res .= 'INNER JOIN ('.$v[0].') AS '.$v[1].' ON '.$v[2].'='.$v[3].' ';
}
}
return $res;
}
protected function leftJoinsToString(){
if($this->left_joins) {
$lefts = [];
foreach ($this->left_joins as $k=>$v){
if(is_array($v)){
$columns = [];
if(is_array($v[1])){
foreach ($v[1] as $ck=>$cv){
if(is_array($cv) and is_numeric($cv[0])){
switch ($cv[0]){
case \Where::BETWEEN:
$columns[] = substr((string)new \Where(\Where::_between($cv[1],$cv[2],$cv[3])),5);
break;
case \Where::AND:
$columns[] = substr((string)new \Where(\Where::_and($cv[1])),5);
break;
}
} else {
if (is_array($v[0])) {
$columns[] = DB::quotes($ck) . '=' . $v[0][1] . '.' . $this->quoteTableColumn($cv);
} else {
$columns[] = DB::quotes($ck) . '=' . $this->quoteTableColumn($cv);
}
}
}
$columns = implode(' AND ',$columns);
} else {
if (is_array($v[0])) {
$columns = DB::quotes($v[1])."=".DB::quotes($v[0][1]).".".db::quotes($v[2]);
} else {
$columns = DB::quotes($v[1])."=".db::quotes($v[2]);
}
}
/*
if (is_array($v[0])){
$lefts[] = "LEFT JOIN ".DB::quotes($v[0][0])." AS ".$v[0][1]." ON ".DB::quotes($v[1])."=".DB::quotes($v[0][1]).".".db::quotes($v[2]);
} else {
$lefts[] = "LEFT JOIN ".DB::quotes($v[0])." ON ".DB::quotes($v[1])."=".db::quotes($v[2]);
}
/**/
if (is_array($v[0])){
$lefts[] = "LEFT JOIN ".DB::quotes($v[0][0])." AS ".$v[0][1]." ON ".$columns;
} else {
$lefts[] = "LEFT JOIN ".DB::quotes($v[0])." ON ".$columns;
}
} else if(is_string($v)){
$lefts[] = $v;
}
}
return implode(" ",$lefts);
} else return '';
}
/**
* Фнукция для генерация LIMIT в троку
* @return string
*/
protected function limitToString() : string{
$res = '';
if($this->count) {
$res = ' LIMIT '.$this->count.( $this->offset ? ' OFFSET '.$this->offset : '');
}
return $res;
}
/**
* Фнукция для генерация списка таблиц в строку
* @return string
*/
protected function tablesToString() : string{
if($this->tables) {
$res = implode(",",$this->tables);
switch ($this->type) {
case eQueryType::SELECT:
case eQueryType::DELETE:
$res = "FROM ".$res;
break;
}
return $res;
} else return '';
}
protected function selectToString() : string{
$res = $this->calc ? ' SQL_CALC_FOUND_ROWS ' : '';
if($this->select) {
$res .= implode(",",$this->select);
return $res;
} else return $res.'*';
}
protected function groupByToString() : string{
$res = '';
if($this->group_by) {
$res .= ' GROUP BY '.implode(",",$this->group_by);
return $res;
} else return '';
}
protected function onDuplicateKeyUpdateToSting(){
if($this->on_duplicate_key_update) {
$res = 'ON DUPLICATE KEY UPDATE ';
$cols = [];
foreach ($this->on_duplicate_key_update as $k=>$v){
$cols[] = $k."='".DB::mq($v)."'";
}
return $res.implode(",",$cols);
} else return '';
}
/**
* Функция генерация сортировки в строку
* @return string
*/
protected function sortToString() : string{
$res = '';
// Определяем сортировку, если она конечно, есть и если она нужна
switch ($this->type) {
case eQueryType::SELECT:
if($this->sort) {
$sort = [];
foreach ($this->sort as $k=>$v) $sort[] = $k.' '.$v;
$res = ' ORDER BY '.implode(",",$sort);
}
break;
default:;
}
return $res;
}
}