Ответ 1
Этот вопрос часто возникает не только для Closure Table, но и для других методов хранения иерархических данных. Это нелегко в любом из проектов.
Решение, которое я придумал для Closure Table, включает в себя одно дополнительное соединение. Каждый node в дереве соединяется с цепочкой своих предков, например, с запросом типа "хлебные крошки". Затем используйте GROUP_CONCAT(), чтобы свернуть панировочные сухари в строку с разделителями-запятыми, сортируя номера идентификаторов по глубине в дереве. Теперь у вас есть строка, по которой вы можете сортировать.
SELECT c2.*, cc2.ancestor AS `_parent`,
GROUP_CONCAT(breadcrumb.ancestor ORDER BY breadcrumb.depth DESC) AS breadcrumbs
FROM category AS c1
JOIN category_closure AS cc1 ON (cc1.ancestor = c1.id)
JOIN category AS c2 ON (cc1.descendant = c2.id)
LEFT OUTER JOIN category_closure AS cc2 ON (cc2.descendant = c2.id AND cc2.depth = 1)
JOIN category_closure AS breadcrumb ON (cc1.descendant = breadcrumb.descendant)
WHERE c1.id = 1/*__ROOT__*/ AND c1.active = 1
GROUP BY cc1.descendant
ORDER BY breadcrumbs;
+----+------------+--------+---------+-------------+
| id | name | active | _parent | breadcrumbs |
+----+------------+--------+---------+-------------+
| 1 | Cat 1 | 1 | NULL | 1 |
| 3 | Cat 1.1 | 1 | 1 | 1,3 |
| 4 | Cat 1.1.1 | 1 | 3 | 1,3,4 |
| 7 | Cat 1.1.2 | 1 | 3 | 1,3,7 |
| 6 | Cat 1.2 | 1 | 1 | 1,6 |
+----+------------+--------+---------+-------------+
Предостережения:
- Значения id должны иметь единую длину, потому что сортировка "1,3" и "1,6" и "1,327" может не дать заказ, который вы намереваетесь. Но сортировка "001,003" и "001,006" и "001,327" будет. Таким образом, вам нужно либо запустить свои значения id на 1000000+, либо использовать
ZEROFILL
для предка и потомка в таблице category_closure. - В этом решении порядок отображения зависит от числового порядка идентификаторов категории. Этот числовой порядок значений id может не представлять порядок, который вы хотите отобразить в дереве. Или вы можете захотеть изменить порядок отображения, независимо от значений числового идентификатора. Или вы можете захотеть, чтобы одни и те же данные категории отображались в более чем одном дереве, каждый с различным порядком отображения.
Если вам нужна свобода, вам нужно хранить значения порядка сортировки отдельно от идентификатора, и решение становится еще более сложным. Но в большинстве проектов приемлемо использовать короткую вырезку, предоставляя двойной идентификатор категории как порядок отображения дерева.
Ваш комментарий:
Да, вы можете сохранить "порядок сортировки брака" в качестве другого столбца таблицы закрытия, а затем использовать это значение вместо ancestor
для создания строки палитры. Но если вы это сделаете, вы получите много избыточности данных. То есть, данный предок хранится в нескольких строках, по одному для каждого пути, спускающегося с него. Таким образом, вы должны сохранить одно и то же значение для порядка сортировки для каждого из этих строк, что создает риск аномалии.
Альтернативой было бы создать другую таблицу с одной строкой на отдельного предка в дереве и присоединиться к этой таблице, чтобы получить порядок брака.
CREATE TABLE category_closure_order (
ancestor INT PRIMARY KEY,
sibling_order SMALLINT UNSIGNED NOT NULL DEFAULT 1
);
SELECT c2.*, cc2.ancestor AS `_parent`,
GROUP_CONCAT(o.sibling_order ORDER BY breadcrumb.depth DESC) AS breadcrumbs
FROM category AS c1
JOIN category_closure AS cc1 ON (cc1.ancestor = c1.id)
JOIN category AS c2 ON (cc1.descendant = c2.id)
LEFT OUTER JOIN category_closure AS cc2 ON (cc2.descendant = c2.id AND cc2.depth = 1)
JOIN category_closure AS breadcrumb ON (cc1.descendant = breadcrumb.descendant)
JOIN category_closure_order AS o ON breadcrumb.ancestor = o.ancestor
WHERE c1.id = 1/*__ROOT__*/ AND c1.active = 1
GROUP BY cc1.descendant
ORDER BY breadcrumbs;
+----+------------+--------+---------+-------------+
| id | name | active | _parent | breadcrumbs |
+----+------------+--------+---------+-------------+
| 1 | Cat 1 | 1 | NULL | 1 |
| 3 | Cat 1.1 | 1 | 1 | 1,1 |
| 4 | Cat 1.1.1 | 1 | 3 | 1,1,1 |
| 7 | Cat 1.1.2 | 1 | 3 | 1,1,2 |
| 6 | Cat 1.2 | 1 | 1 | 1,2 |
+----+------------+--------+---------+-------------+