Примечание.. Вы можете пропустить снизу, чтобы прочитать фактический вопрос, прежде чем читать все это.
Я работаю над реализацией ACL для CakePHP. В основном я пытаюсь отделить его от AuthComponeny, поэтому я могу использовать Authsome для своих проектов. У меня есть теория реализации, но я ударил немного камнем преткновения.
Очевидно, я хочу свести число запросов к базе данных до минимума. Поэтому я спрашиваю здесь, что это возможно (я серьезно сомневаюсь, что это так.)
Предполагая структуру таблицы следующим образом:
id - int(10), auto_increment, primary_key, not null
parent_id - int(10), null
model - varchar(255), utf8_bin, null
foreign_key - int(10), null
alias - varchar(255), utf8_bin, null,
lft - int(10), null
rght - int(10), null
И несколько записей для проверки (контроллеры - это корень node, и я могу ошибиться в значениях lft и rght):
1, null, null, null, controllers, 1, 14
2, 1, null, null, one_test_controllers, 2, 7
3, 2, null, null, one_action, 3, 4
4, 2, null, null, two_action, 5, 6
5, 1, null, null, two_test_controllers, 8, 13
6, 5, null, null, one_action, 9, 10
7, 5, null, null two_action, 11, 12
И два тестовых пути:
$test1 = '/controllers/one_test_controller/two_action';
$test2 = '/controllers/two_test_controller/two_action';
Предоставление этих результатов, возвращая массив идентификаторов из наиболее релевантных для наименее релевантных:
// Result 1
array(
0 => 4,
1 => 2,
2 => 1
)
// Result 2
array(
0 => 7,
1 => 5,
2 => 1
)
То, что я сейчас делаю, - explode(), введя путь в и массив (используя $test1 для этого примера), сначала обнаруживает все записи, которые соответствуют псевдониму "two_action"; затем перебирает результаты и находит все записи, соответствующие родительскому идентификатору последнего результата, и имеет псевдоним "one_test_controller". Затем повторите до parent_id = 0.
Это работает, но, очевидно, несколько рекурсивных SQL-запросов не идеальны, есть ли волшебный SQL-запрос, который может мне помочь? Или я прав, полагая, что это лучшее, что он может получить?
а? У вас уже есть структура для извлечения данных путем разбора пути за один проход с деревом смежности.
Однако, не сохраняя полный путь/требуя уникальных имен node, вы не можете искать снизу вверх. Подумайте - в обоих тестовых случаях вы начинаете с "two_action", но ищете 2 разных листа. Если вы сохраняете весь путь в таблице (или можете ссылаться на узлы по id из вашего запроса), то....
SELECT ancestors.*
FROM ahier ancestors,
(SELECT lft, rght
FROM ahier ref
WHERE ref.path='/controllers/one_test_controller/two_action') ilv
WHERE (ancestors.lft >= ilv.left AND ancestors.rght <= ilv.rght)
ORDER BY ancestors.lft ASC;
или используя идентификаторы:
SELECT ancestors.*
FROM ahier ancestors,
(SELECT lft, rght
FROM ahier ref
WHERE ref.id=4) ilv
WHERE (ancestors.lft >= ilv.left AND ancestors.rght <= ilv.rght)
ORDER BY ancestors.lft ASC;
В качестве альтернативы вы можете написать запрос, чтобы возвращать все возможные пути, которые имеют определенный псевдоним node, но это не будет очень эффективным....
SELECT treenum, ancestors.*
FROM ahier ancestors,
(SELECT lft, rght, id as treenum
FROM ahier ref
WHERE ref.alias='two_action') ilv
WHERE (ancestors.lft >= ilv.left AND ancestors.rght <= ilv.rght)
ORDER BY treenum, ancestors.lft ASC;
(и его достаточно легко перестроить lft и rght из parent_ids)