2013-11-20 2 views
2

Итак, вот моя структура таблицы:PHP MySQL меню Сортировка

+--------------------------+ +----------------+ +-------------------------------+ 
|   pages   | |  menus  | |   menu_pages   | 
+--------------------------+ +----+-----------+ +-------------------------------+ 
| id | title | slug | | id | name  | | menu_id | page_id | parent_id | 
+-------+---------+--------+ +----+-----------+ +---------+---------+-----------+ 
| 1 | Home | index | | 1 | default | | 1 | 1 |  0  | 
+-------+---------+--------+ +----+-----------+ +---------+---------+-----------+ 
| 2 | About | about | | 2 | footer | | 1 | 2 |  0  | 
+-------+---------+--------+ +----+-----------+ +---------+---------+-----------+ 
| 3 | Test 1 | test-1 |     | 1 | 3 |  2  | 
+-------+---------+--------+     +---------+---------+-----------+ 
| 4 | Test 2 | test-2 |     | 1 | 4 |  2  | 
+-------+---------+--------+     +---------+---------+-----------+ 
| 5 | Test 3 | test-3 |     | 1 | 5 |  4  | 
+-------+---------+--------+     +---------+---------+-----------+ 

Так в основном, у нас есть страницы, меню, а также menu_pages связывающие таблицы, которая указует меню, страницу, и родитель каждого меню пункт.

Вот мой запрос:

$query = "SELECT pages.id, pages.title, pages.slug, menu_pages.parent_id 
      FROM menus, pages, menu_pages WHERE menus.name = '$menu' 
      AND menus.id = menu_pages.menu_id 
      AND pages.id = menu_pages.page_id"; 

$results = $db->Query($query); 

Вот вопрос: Как я могу получить элементы меню правильно вложены под их родителями в массиве? Я уже пробовал уже немало вещей, но никто из них не работал только на двух уровнях, поэтому я не буду суетиться с этим вопросом. Очевидно, мне нужна какая-то рекурсия в PHP или изменить мой запрос, возможно, таким образом, что я могу заставить SQL вернуть их правильно?

Это должно выглядеть примерно так на выходе:

[0] => array(
    'id'  => 1, 
    'title'  => 'Home', 
    'slug'  => '/', 
    'parent_id' => '0' 
) 
[1] => array(
    'id'  => 2, 
    'title'  => 'About', 
    'slug'  => 'about', 
    'parent_id' => 0, 
    'sub_menu' => array(
    [0] => array( 
     'id'  => 3, 
     'title'  => 'Test 1', 
     'slug'  => 'test-1', 
     'parent_id' => 2 
    ) 
    [1] => array(
     'id'  => 4, 
     'title'  => 'Test 2', 
     'slug'  => 'test-2', 
     'parent_id' => '2', 
     'sub_menu' => array(
     [0]   => array(
      'id'  => 5, 
      'title'  => 'Test 3', 
      'slug'  => 'test-3', 
      'parent_id' => 4 
     ) 
    ) 
    ) 
) 
) 

Спасибо за помощь!

ответ

1

Это не так просто, как это звучит впервые - если вы хотите понять, как это сделать с SQL, вы ищете рекурсивную инструкцию sql.

К сожалению, mysql не поддерживает это напрямую, и вам нужно написать кусок кода, чтобы заставить его работать. Здесь приведен пример того, как это сделать. Не просто.

В случае, если вы заинтересованы в том, чтобы выбрать это врозь, вы бы реализовать его в Oracle, как это:

SELECT p.id, p.title, p.slug, mp.parent_id, level 
FROM menu_pages mp 
JOIN pages  p ON (p.id = mp.page_id) 
JOIN menus  m ON (m.id = mp.menu_id) 
CONNECT BY PRIOR mp.page_id = mp.parent_id 
START WITH (m.name = 'default' AND mp.parent_id = 0) 

Вы в основном говорят:

  • СТАРТ С запросом на верхний уровень меню
  • CONNECT, что обратно в наборе результатов путем присоединения к родителю ребенка

Вы в конечном итоге с результирующего набора, как это:

id title slug parent level 
------------------------------------ 
1 Home index 0 1 
2 About about 0 1 
3 Test 1 test-1 2 2 
4 Test 2 test-2 2 2 
5 Test 3 test-3 4 3 

Все это на самом деле дает вам в дополнение к тому, что вы уже имеют:

  • «Уровень» каждой точки в меню.
  • Если в вашей структуре несколько раз появилось подменю, оно будет повторяться правильно.
  • Разделы меню, которые не подключены должным образом, не возвращаются.

Таким образом, для небольших, простых и согласованных меню это, вероятно, слишком много убивает.

Плюс, даже в этом случае вам нужно будет обработать это на PHP, чтобы получить структуру, которую вы ищете.

Итак, используя это как вдохновение, вы можете увидеть, как можно реализовать его в mysql, просто выполнив почтовую обработку.

Вы начинаете свой первоначальный запрос:

SELECT pages.id 
    , pages.title 
    , pages.slug 
    , menu_pages.parent_id 
FROM menus 
    , pages 
    , menu_pages 
WHERE menus.name = 'default' 
AND menus.id = menu_pages.menu_id 
AND pages.id = menu_pages.page_id 

Вы можете затем цикл над этим результатом и построить структуру массива самостоятельно вручную.

Во избежание проблемы рекурсии мы вместо этого воспользуемся тем фактом, что мы можем иметь две переменные, указывающие на одну и ту же структуру данных - мы будем использовать ссылки, чтобы изменить значение переменная в одной ссылке изменит значение переменной в другой.

I.E. Трудность, которую вы получаете, - найти правильную точку в иерархии, чтобы добавить каждого ребенка. С рекомендациями, которые вам не нужно.

  • Создать пустой массив меню
  • Loop над результатами вашего SQL заявление
  • Создать копию каждого пункта меню и поместить его в просто индексируются магазин (по идентификатору пункта)
  • Если у вас есть элемент корневого меню, добавьте его в свой массив меню (в качестве ссылки)
  • Если у вас нет элемента корневого меню, найдите родителя в своем простом хранилище и добавьте к нему свой новый элемент.

В конце вы должны иметь хорошую вложенную структуру, которую ищете.

Как это:

<?php 

// As if it came back from mysql... 
// Assumed that it's ordered so that every possible parent appears before all its childern 

$aResults = array(array('id' => 1, 'title' => 'Home', 'slug' => 'index', 'parent_id' => 0) 
       , array('id' => 2, 'title' => 'About', 'slug' => 'about', 'parent_id' => 0) 
       , array('id' => 3, 'title' => 'Test 1', 'slug' => 'test-1', 'parent_id' => 2) 
       , array('id' => 4, 'title' => 'Test 2', 'slug' => 'test-2', 'parent_id' => 2) 
       , array('id' => 5, 'title' => 'Test 3', 'slug' => 'test-3', 'parent_id' => 4)); 

// the menu you're creating 
$aMenu   = array(); 

// the simple store of the menu items you're going to use to find the parents 
$aBaseMenuIndex = array(); 

foreach($aResults as $aMenuItem) { 

    $aMenuItem['sub_menu'] = array(); 

    // add your menu item to the simple store 
    $aBaseMenuIndex[ $aMenuItem['id'] ] = $aMenuItem; 

    if ($aMenuItem['parent_id'] == 0) { 

     // if it's a base menu item, add it to the menu 
     $aMenu[] =& $aBaseMenuIndex[ $aMenuItem['id'] ]; 

    } else { 

     // if it's not a base item, add it to the sub menu, using the simply indexed store to find it 
     // adding it here will also add it to $aMenu, as $aMenu contains a reference to this 
     $aBaseMenuIndex[ $aMenuItem['parent_id'] ]['sub_menu'][] =& $aBaseMenuIndex[ $aMenuItem['id'] ]; 
    } 
} 

var_dump($aMenu); 
+0

Благодаря Роб! С нетерпением ждем вашего решения - эта проблема заставила меня смутить какое-то время! –

+0

Обновлено, чтобы включить окончательное решение - я думаю, вы можете пинать себя! –

+0

О, боже мой, Роб, я думаю, что это сработает! Лучше, чем рекурсия, так как я предполагаю, что это быстрее. Я собираюсь реализовать это прямо сейчас, и если вы его получите, я соглашусь! :) –

Смежные вопросы