function K_error_handler($code, $msg, $file, $line) { // echo 'code: '.$code.'message: '.$msg.' file: '.$file.' line: '.$line; K_debug::get()->addError('code: ' . $code . 'message: ' . $msg . ' file: ' . $file . ' line: ' . $line); }
/** Выбирает ветку ноды со всей её инфой ,которая храниться в смежных таблицах * * */ public static function gNodes($nodePid, $limit = false, $onlyChilds = false, $conditions = '', $idIndex = false, $joins = false, $opt = false) { $rtypes = array(); $rtypesTables = array(); $rtypesAll = array(); $query = new K_Db_Query(); $parentNodeData = K_Tree::getNode($nodePid); if ($onlyChilds) { if (is_numeric($nodePid)) { $pid = $nodePid; } else { $node = K_Tree::getNode($nodePid); $pid = $node['tree_id']; } $q = 'SELECT * FROM tree where tree_pid="' . $pid . '" ORDER BY tree_lkey ASC limit 1'; } else { $q = 'SELECT * FROM `tree` WHERE `tree_lkey`>=' . $parentNodeData['tree_lkey'] . ' AND `tree_rkey`<=' . $parentNodeData['tree_rkey'] . ' ' . ($opt['filter'] ? ' AND tree_type="' . $opt['filter'] . '"' : '') . ' ORDER BY `tree_lkey` ASC ' . ($limit ? ' LIMIT ' . $limit : ''); if ($opt['filter']) { echo $q; } } $result = $query->q($q); $usedTypes = array(); for ($i = 0; $i < sizeof($result); $i++) { $data[$i] = $result[$i]->toArray(); if (!in_array($data[$i]['tree_type'], $usedTypes)) { $usedTypes[] = $data[$i]['tree_type']; } } $allResults = array(); $rData = array(); // K_debug::get()->dump($usedTypes); $i = 1; $casCadeIds = array(); // при каскаде убираем тип папка, он только будет мешать if ($opt['cascade'] && ($usedTypes[0] = 'folder')) { array_shift($usedTypes); } $casCadeStart = false; foreach ($usedTypes as $type) { //каскадная выборка(сперва выбираються ноды верхнего порядка по типу, к ним применяються условия, потом по pid выбираються ноды следующиего уровня) $inStr = ''; if ($opt['cascade'] && count($casCadeIds)) { $casCadeIdsIn = implode(',', $casCadeIds); $inStr = ' AND tr.tree_pid IN (' . $casCadeIdsIn . ')'; } if (in_array($type, $usedTypes)) { $rtypes[] = $type; $rtypesTables = '`type_' . $type . '` AS ty '; $rtypesAll = 'ty.*'; $tp = $type; //дополнительные условия для выборки $rtypesWhere = '`tr`.`tree_id`=ty.type_' . $type . '_id' . ($conditions[$type] ? $conditions[$type] : ''); // if ($conditions)echo 'SELECT `tr`.*, '.$rtypesAll.' FROM `tree` AS `tr`, '.$rtypesTables.' WHERE '.$rtypesWhere.' AND `tr`.`tree_lkey`>='.$parentNodeData['tree_lkey'].' AND `tr`.`tree_rkey`<='.$parentNodeData['tree_rkey'].' ORDER BY `tr`.`tree_lkey` ASC '.($limit ? ' LIMIT '.$limit : ''); // array('tour'=>array('resort'=>'to_city','hotel'=>'hotel','country'=>'country')); array('price' => array('pid' => array('max' => 'price'))); $agreg = ''; $group = ''; // группировки по типу, найти минимальное, максимальное, сумму для определённого типа if ($opt['group'][$tp]) { $grp = $opt['group'][$tp]; foreach ($grp as $k => $v) { $kCell = 'type_' . $tp . '_' . $k; $group[] = " group by {$k}"; foreach ($v as $t => $y) { $agreg[] = "{$t}(" . 'ty.type_' . $tp . '_' . "{$y})"; } $agreg = ',' . implode(', ', $agreg); $group = implode(' ', $group); } } //дополнительные джойны, для каждого типа, в дальнейшем зделать их автоматическими $jJoin = ''; $jTables = ''; if ($joins[$tp]) { foreach ($joins[$tp] as $k => $v) { $kTable = 'type_' . $k; $jTablesA[] = "{$kTable}.*"; $jJoin[] = " LEFT JOIN {$kTable} ON {$kTable}.{$kTable}" . "_id=ty.type_{$tp}" . '_' . "{$v} "; } $jJoin = implode(' ', $jJoin); $jTables = ',' . implode(',', $jTablesA); } //основной запрос $qv = 'SELECT `tr`.*, ' . $rtypesAll . ' ' . $jTables . ' ' . $agreg . ' FROM `tree` AS `tr`, ' . $rtypesTables . ' ' . $jJoin . ' WHERE ' . $rtypesWhere . ' AND `tr`.`tree_lkey`>=' . $parentNodeData['tree_lkey'] . ' AND `tr`.`tree_rkey`<=' . $parentNodeData['tree_rkey'] . ' ' . $inStr . ' ' . $group . ' ORDER BY `tr`.`tree_lkey` ASC ' . ($limit ? ' LIMIT ' . $limit : ''); $result = $query->q($qv); if ($opt['test']) { K_debug::get()->addMessage($qv); } $casCadeIds = array(); foreach ($result as $key => $value) { $result[$key] = $value->toArray(); $aResults = array(); foreach ($result[$key] as $typeField => $typeValue) { $aResults[str_replace('type_' . $type . '_', '', $typeField)] = $typeValue; // K_debug::get()->dump($key); } $allResults[$aResults['tree_lkey']] = $aResults; //каскадная выборка(сперва выбираються ноды верхнего порядка по типу, к ним применяються условия, потом по pid выбираються ноды следующиего уровня) // сохраним id нод if ($opt['cascade']) { $casCadeIds[] = $aResults["tree_id"]; } $i++; } if ($opt['cascade'] && !count($casCadeIds)) { break; } //$allResults = array_merge($allResults, $rData); } } //K_debug::get()->dump($rAllResults); //сортируем оп левому ключу: ksort($allResults); $claer = true; if ($opt['group']) { // если есть группировка // нумеруем индексы по id $rrAllResults = array(); foreach ($allResults as $key => $value) { $rrAllResults[$value['tree_id']] = $value; } $allResults = $rrAllResults; // K_debug::get()->dump($allResults); $rAllResults = array(); foreach ($allResults as $key => $value) { //если была группировка по этому типу то в массиве ответа к родительским нодам прикрепляем детей в виде индекса node_childs $tp = $value['tree_type']; if ($opt['group'][$tp]) { $tId = $value['tree_pid']; // K_debug::get()->dump($tId); $allResults[$tId]['node_childs'][] = $value['tree_id']; // K_debug::get()->dump($rAllResults[$tId]['node_childs']); } } $claer = false; } //если есть необходимость переназвать иднексы называем их по одному из полей ноды if ($idIndex) { $allResults; foreach ($allResults as $value) { if ($idIndex) { $tId = $value['tree_id']; if (is_string($idIndex)) { $tId = $value[$idIndex]; } $rAllResults[$tId] = $value; } } $claer = false; } // если всё чисто то просто прономеруем все элементы выборки по порядку if ($claer) { $i = 0; foreach ($allResults as $value) { $rAllResults[$i] = $value; $i++; } } if ($opt['test']) { K_debug::get()->dump($rAllResults); } return $rAllResults; }
/** Выбирает ветку ноды со всей её инфой ,которая храниться в смежных таблицах типов * * */ public static function gNodes($opt, $limit = false, $parentNodeData = false) { // Этап 1 формирование настроек для выполнения // для поддержки старого упрощенного варианта if (!is_array($opt)) { $opt = array("id" => $opt, "limit" => $limit); } $opt = array_merge(array("id" => 0, "limit" => false, "offset" => 0, "types" => false, "condit" => '', 'filter' => false, "idIndex" => false, "joins" => false, "group" => false, "order" => false, "more" => array('cascade' => false, "childs" => false, "test" => false, 'meta', 'count' => false, 'orderby' => "ASC", "aliases" => false)), $opt); $rtypes = array(); $rtypesTables = array(); $rtypesAll = array(); $query = new K_Db_Query(); if (!$parentNodeData) { $parentNodeData = K_Tree::getNode($opt["id"]); } //Этап 2 выборка и формирование списка типов с которыми будем работать if (!$opt['types']) { if ($opt['more']['childs']) { /* if (is_numeric($opt["id"])){ $pid=$opt["id"]; } else{ $node=K_Tree::getNode($opt["id"]); $pid=$node['tree_id']; }*/ $q = 'SELECT DISTINCT tree_type FROM tree where tree_pid="' . $parentNodeData['tree_id'] . '" ORDER BY tree_lkey ASC limit 1'; $result = $query->q($q); } else { $q = 'SELECT DISTINCT tree_type FROM `tree` WHERE `tree_lkey`>=' . $parentNodeData['tree_lkey'] . ' AND `tree_rkey`<=' . $parentNodeData['tree_rkey'] . ' ' . ($opt['filter'] ? ' AND tree_type="' . $opt['filter'] . '"' : '') . ' ORDER BY `tree_lkey` ASC ' . ($opt["limit"] ? ' LIMIT ' . $opt["limit"] : ''); $result = $query->q($q); } $usedTypes = array(); for ($i = 0; $i < sizeof($result); $i++) { $data[$i] = $result[$i]->toArray(); if (!in_array($data[$i]['tree_type'], $usedTypes)) { $usedTypes[] = $data[$i]['tree_type']; } } // K_debug::get()->dump($usedTypes); //этап 3 выборка итемов требуемых типов $allResults = array(); $rData = array(); $i = 1; $topLevel = true; $casCadeStart = false; $casCadeIds = array(); // при каскаде убираем тип папка, он только будет мишать if ($opt['more']['cascade'] && ($usedTypes[0] = 'folder')) { array_shift($usedTypes); } } else { $usedTypes = $opt['types']; } foreach ($usedTypes as $type) { //каскадная выборка(сперва выбираються ноды верхнего порядка по типу, к ним применяються условия, потом по pid выбираються ноды следующиего уровня) $inStr = ''; if ($opt['more']['cascade'] && count($casCadeIds)) { $casCadeIdsIn = implode(',', $casCadeIds); $inStr = ' AND tr.tree_pid IN (' . $casCadeIdsIn . ')'; } if (in_array($type, $usedTypes)) { $rtypes[] = $type; $rtypesTables = '`type_' . $type . '` AS ty '; $rtypesAll = 'ty.*'; $tp = $type; //дополнительные условия для выборки $rtypesWhere = '`tr`.`tree_id`=ty.type_' . $type . '_id' . ($opt['condit'][$type] ? $opt['condit'][$type] : ''); // if ($conditions)echo 'SELECT `tr`.*, '.$rtypesAll.' FROM `tree` AS `tr`, '.$rtypesTables.' WHERE '.$rtypesWhere.' AND `tr`.`tree_lkey`>='.$parentNodeData['tree_lkey'].' AND `tr`.`tree_rkey`<='.$parentNodeData['tree_rkey'].' ORDER BY `tr`.`tree_lkey` ASC '.($limit ? ' LIMIT '.$limit : ''); // array('tour'=>array('resort'=>'to_city','hotel'=>'hotel','country'=>'country')); //array('price'=>array('pid'=>array('max'=>'price'))); $agreg = ''; $group = ''; // группировки по типу, найти минимальное, максимальное, сумму для определённого типа if ($opt['group'][$tp]) { $grp = $opt['group'][$tp]; foreach ($grp as $k => $v) { $kCell = 'type_' . $tp . '_' . $k; $group[] = " group by {$k}"; foreach ($v as $t => $y) { $agreg[] = "{$t}(" . 'ty.type_' . $tp . '_' . "{$y})"; } $agreg = ',' . implode(', ', $agreg); $group = implode(' ', $group); } } //дополнительные джойны, для каждого типа, в дальнейшем cделать их автоматическими $jJoin = ''; $jTables = ''; if ($opt['joins'][$tp]) { foreach ($opt['joins'][$tp] as $k => $v) { $kTable = 'type_' . $k; $jTablesA[] = "{$kTable}.*"; $jJoin[] = " LEFT JOIN {$kTable} ON {$kTable}.{$kTable}" . "_id=ty.type_{$tp}" . '_' . "{$v} "; } $jJoin = implode(' ', $jJoin); $jTables = ',' . implode(',', $jTablesA); } $limitStr = ''; if (!$opt['more']['count']) { $topLevel = false; } // при каскадной выборке лимит работает только для верхнего типа if ($opt['more']['cascade']) { if ($topLevel) { $limitStr = $opt['limit'] ? ' LIMIT ' . $opt['offset'] . ', ' . $opt['limit'] : ''; } } else { // при простой выборке лимит делиться на все типы по чучуть if (count($usedTypes) > 1) { $typeCount = count($usedTypes); $limitForType = floor($opt['limit'] / $typeCount); $offsetForType = floor($opt['offset'] / $typeCount); if ($topLevel) { // разницу добавим к верхнему уровню:К ПРИМЕРУ 10 разобьёться на троих так 4 3 3 $limitForType += $opt['limit'] - $limitForType * $typeCount; $offsetForType += $opt['offset'] - $offsetForType * $typeCount; } $limitStr = $limitForType ? ' LIMIT ' . $offsetForType . ', ' . $limitForType : ''; } else { if ($opt['limit']) { $limitStr = ' LIMIT ' . $opt['offset'] . ', ' . $opt['limit']; } } } // сортировка, если её нет то сортируеться по левому ключу if ($opt['order'][$tp]) { if (is_string($opt['order'][$tp])) { $opt['order'][$tp] = array($opt['order'][$tp]); } $orderArr = array(); if (is_string($opt['more']['orderby'])) { $opt['more']['orderby'] = array($opt['more']['orderby']); } $ior = 0; foreach ($opt['order'][$tp] as $v) { $orderArr[] = 'ty.' . 'type_' . $tp . '_' . $v . ' ' . $opt['more']['orderby'][$ior]; $ior++; } $orderFields = implode(',', $orderArr); $orderBY = ' ORDER BY ' . $orderFields . ' '; } else { $orderBY = ' ORDER BY `tr`.`tree_lkey` ASC '; } //основной запроc, $qv = 'SELECT' . ($limitStr ? ' SQL_CALC_FOUND_ROWS ' : '') . ' `tr`.*, ' . $rtypesAll . ' ' . $jTables . ' ' . $agreg . ' FROM `tree` AS `tr`, ' . $rtypesTables . ' ' . $jJoin . ' WHERE ' . $rtypesWhere . ' AND `tr`.`tree_lkey`>=' . $parentNodeData['tree_lkey'] . ' AND `tr`.`tree_rkey`<=' . $parentNodeData['tree_rkey'] . ' ' . $inStr . ' ' . $group . $orderBY . $limitStr; $result = $query->q($qv); if ($limitStr) { $сountResult = $query->q("SELECT FOUND_ROWS() as cItems;"); $countItems += $сountResult[0]['cItems']; } if ($opt['more']['test']) { K_debug::get()->addMessage($countItems); } if ($opt['more']['test']) { K_debug::get()->addMessage($qv); } $casCadeIds = array(); foreach ($result as $key => $value) { $result[$key] = $value->toArray(); $aResults = array(); foreach ($result[$key] as $typeField => $typeValue) { $aResults[str_replace('type_' . $type . '_', '', $typeField)] = $typeValue; // K_debug::get()->dump($key); } if ($opt['more']['aliases'] == true && $aResults['tree_type'] == 'alias') { $aliases[$aResults['tree_lkey']] = $aResults['elementid']; // var_dump( $aResults); } $allResults[$aResults['tree_lkey']] = $aResults; //каскадная выборка(сперва выбираються ноды верхнего порядка по типу, к ним применяються условия, потом по pid выбираються ноды следующиего уровня) // сохраним id нод if ($opt['more']['cascade']) { $casCadeIds[] = $aResults["tree_id"]; } $i++; } if ($opt['more']['cascade'] && !count($casCadeIds)) { break; } //$allResults = array_merge($allResults, $rData); $topLevel = false; } } //Этап 4 постобработка, получение данных в нужном нам виде ,подключение алиасов /** * @todo группировка итемов по типу и вывод в массив * * **/ //K_debug::get()->dump($rAllResults); //проверка на алиасы, если они есть выбираем все элементы по алиасам и заменяем ими алиасы // var_dump($aliases); $aliasesIdsStr = implode(',', array_unique($aliases)); if (count($aliases) && $aliasesIdsStr) { //Узнаём все типы алиасов $q = 'SELECT DISTINCT tree_type FROM tree where tree_id IN(' . implode(',', $aliases) . ')'; $aliasesTypesResult = $query->q($q); //массив таблиц из которых будем выбирать foreach ($aliasesTypesResult as $v) { $aliasesTypes[] = $v['tree_type']; $aliasesTypesTables[] = 'type_' . $v['tree_type']; $aliasesTypesTablesWhere[] = 'type_' . $v['tree_type'] . '.' . 'type_' . $v['tree_type'] . '_id = tree.tree_id'; } //массив условий для выбора типов $qv = 'SELECT * FROM tree, ' . implode(',', $aliasesTypesTables) . ' WHERE tree.tree_id IN(' . implode(',', $aliases) . ') AND ' . implode(',', $aliasesTypesTablesWhere); $aliasesResult = $query->q($qv); foreach ($aliasesResult as $v) { $aliasesResultKeyId[$v['tree_id']] = K_Cupitems::stripFieldsArr($v, 'type_' . $v['tree_type']); } //var_dump($aliasesResultKeyId); foreach ($allResults as $v) { if ($v['tree_type'] == 'alias') { $elementid = $aliases[$v['tree_lkey']]; unset($allResults[$v['tree_lkey']]['tree_type']); $aliasElementResult = array_merge($aliasesResultKeyId[$elementid], $allResults[$v['tree_lkey']]); $allResults[$v['tree_lkey']] = $aliasElementResult; //$aliasesResultKeyId[$elementid]; // array_merge($aliasesResultKeyId[$elementid],$allResults[$v['tree_lkey']]); // var_dump($aliasesResultKeyId[$elementid]); } } } //сортируем по левому ключу: if (!$opt['order']) { ksort($allResults); } $claer = true; if ($opt['group']) { // если есть группировка // нумеруем индексы по id $rrAllResults = array(); foreach ($allResults as $key => $value) { $rrAllResults[$value['tree_id']] = $value; } $allResults = $rrAllResults; // K_debug::get()->dump($allResults); $rAllResults = array(); foreach ($allResults as $key => $value) { //если была группировка по этому типу то в массиве ответа к родительским нодам прикрепляем детей в виде индекса node_childs $tp = $value['tree_type']; if ($opt['group'][$tp]) { $tId = $value['tree_pid']; // K_debug::get()->dump($tId); $allResults[$tId]['node_childs'][] = $value['tree_id']; // K_debug::get()->dump($rAllResults[$tId]['node_childs']); } } $claer = false; } //если есть необходимость переназвать иднексы называем их по одному из полей ноды if ($opt['idIndex']) { $allResults; foreach ($allResults as $value) { if ($opt['idIndex']) { $tId = $value['tree_id']; if (is_string($idIndex)) { $tId = $value[$opt['idIndex']]; } $rAllResults[$tId] = $value; } } $claer = false; } // если всё чисто то просто пронумеруем все элементы выборки по порядку if ($claer) { $i = 0; foreach ($allResults as $value) { $rAllResults[$i] = $value; $i++; } } if ($opt['more']['test']) { K_debug::get()->dump($rAllResults); } if ($opt['more']['meta']) { return array($rAllResults, $parentNodeData); } if ($opt['more']['count']) { return array($rAllResults, $countItems); } return $rAllResults; }