Лучший способ генерировать текстовые хлебные крошки из массива PHP, имеющего 3 столбца (идентификатор, путь, имя)?
Для одного возможного решения, см. Мой второй пост ниже.
Наличие массива PHP, хранящего данные из древовидной структуры, с
- первый столбец, хранящий идентификатор узла,
- второй столбец, хранящий путь родительского узла в виде объединения значений идентификаторов,
- третьи столбцы, хранящие имя узла,
Каков наилучший способ для создания пути текста (хлебные крошки) из пути, сделанного из идентификаторов?
Пример записи:
id | path | name
---------------------
1 | 0 | edible
14 | 1 | fruits
53 | 1.14 | apples
54 | 1.14 | pears
122 | 1.14.53 | red apples
123 | 1.14.53 | green apples
124 | 1.14.54 | yellow pears
Введите идентификатор: 122
Соответствующий входной путь: 1.14.53
Выходная строка: edible > fruits > apples
Идея состоит в том, чтобы достичь чего-то вроде:
foreach($cats as $cat) { // for each category
foreach(explode('.', $cat['path']) as $id) { // explode the path into chunks
/*
1) get the name matching the value of $id
2) append label to breadcrumbs string
*/
}
// 3) output breadcrumbs for the given category
// [4) list leaf nodes under the breadcrumbs for the current category]
}
Примечание: сам массив генерируется этим запросом MySQL/MariaDB:
$req = "SELECT c.id,p.path,c.name FROM `".$database['database']."`.`".$database['prefix']."productcategories` c
LEFT OUTER JOIN `".$database['database']."`.`".$database['prefix']."prodcat_path` p
ON c.id = p.id
WHERE c.isparent AND (c.id=".$id." OR (p.path=".$id." OR p.path LIKE('".$id.".%') OR p.path LIKE('%.".$id.".%') OR p.path LIKE('%.".$id."'))) ORDER BY p.path ASC";
$res = mysql_query($req) or die();
Причина сохранения пути в отдельной таблице с отношением один к одному для записей заключается в том, что триггер используется для вычисления и сохранения пути при вставке новой категории.
2 ответа
Вы также можете создать новую функцию на MYSQL, чтобы получить имена из идентификаторов и использовать, как показано ниже:
SELECT p.id,YOURFUNCTION(p.path),.....
https://dev.mysql.com/doc/refman/5.7/en/adding-functions.html
Это мое домашнее решение, написанное на PHP, которое хорошо работает:
function generateBreadcrumbsForNodeAndDescendants($id) {
define ('FOLDER_PATH',dirname(__FILE__).'/');
include(FOLDER_PATH.'db_ajax_connection.inc.php');
// Select node and all subnodes, excepted leafs
// This query works, but is possibly not optimized.
$req = "SELECT c.id,p.path,c.name FROM `".$database['database']."`.`".$database['prefix']."categories` c
LEFT OUTER JOIN `".$database['database']."`.`".$database['prefix']."paths` p
ON c.id = p.id
WHERE c.isparent AND (c.id=".$id." OR (p.path=".$id." OR p.path LIKE('".$id.".%') OR p.path LIKE('%.".$id.".%'))) ORDER BY p.path ASC";
// We would add the following line to the WHERE clause if we wanted to retrieve leaf nodes too:
// OR p.path LIKE('%.".$id."')
$res = mysql_query($req) or die();
$descendants = array();
while($descendant = mysql_fetch_assoc($res)) {
$descendants[] = $descendant;
}
$path = '';
// Get the path to the current node.
// Because the records from the query are ordered by path, this is the first record.
$path = str_replace('.', ',', $descendants[0]['path']);
// This is because there is no record stored in the path table for the first-level nodes
if ($path=='')
$path = '0';
// Find ancestors of the current node
$req = "SELECT c.id,p.path,c.name FROM `".$database['database']."`.`".$database['prefix']."categories` c
LEFT OUTER JOIN `".$database['database']."`.`".$database['prefix']."paths` p
ON c.id = p.id
WHERE FIND_IN_SET(c.id,'".$path."')";
$res = mysql_query($req) or die('');
$ancestors = array();
while($ancestor = mysql_fetch_assoc($res)) {
$ancestors[] = $ancestor;
}
// Build a list of all ancestors and descendants of the current node, i.e. concatenate arrays
$nodes = array_merge($ancestors,$descendants);
$paths = array();
// Build associative key => value pairs: (id => path) and (id => name)
foreach ($nodes as $node) {
$paths[$node['id']]=$node['path'];
$names[$node['id']]=$node['name'];
}
$html='';
// for each "descendant" node (including custom "root" node), translate numeric path into breadcrumbs
foreach ($descendants as $descendant) {
$html .= '<p>';
$path = $paths[$descendant['id']];
if ($path) { // because no path is stored for the 1st level nodes, we must test that $path != ''
$i = 0;
foreach(explode('.', $path) as $id) {
if ($i)
$html .= ' > ';
$html .= $names[$id]; // These nodes can also be encapsulated in html anchors <a href="">...</a> to become links.
$i++;
}
$html .= ' > '; // optional if we want to add the element to the path of its parent nodes (see below)
}
$html .= $names[$descendant['id']]; // optional if we want to add the element to the path of its parent nodes
// else, we should do some post-processing to remove duplicate paths,
// as several nodes may have the same parent path.
$html .= '</p>';
}
echo $html;
}
generateBreadcrumbsForNodeAndDescendants((int) $_POST['key']);
Примечание: добавив OR p.path LIKE('%.".$id."')
в конце предложения WHERE в первом запросе можно также извлечь конечные узлы, однако в этом случае в строке возникает неопределенная ошибка смещения $path = str_replace('.', ',', $descendants[0]['path']);
для листовых узлов, так как они не имеют потомков. Следовательно, некоторое улучшение кода остается возможным.