PHP - проблема с вложенным mysql_fetch_array() сводит меня с ума

Запрос MySQL:

SELECT title,alias,parent FROM '._prefix.'categories 
WHERE (language = \''._language.'\' || language = \'all\') 
&& status = \'published\'
ORDER BY rank ASC

Результат (из phpMyAdmin):

title    |  alias   |  parent
Home     | home     |    0
Todo     | todo     |    0
Multiuser| todo_mu  |    21
Modulsys | todo_mod |    21

PHP:

while($c = mysql_fetch_array($category_result)) {
    if($c['parent'] == 0) {
        $output .= '<li><a href="'._rewrite_string.$c['alias'].'" title="'.$c['title'].'">'.$c['title'].'</a>';

        $output .= '<ul>';

        mysql_data_seek($category_result,0);

        while($d = mysql_fetch_array($category_result)) {

            $output .= '<li class="space_left"><a href="'._rewrite_string.$c['alias'].'/'.$d['alias'].'" title="'.$d['title'].'">'.$d['title'].'</a>';

            $output .= '</li>';

        }

        $output .= '</ul>';

        $output .= '</li>';
    }
}

Это должно создать такой список категорий

кошка 1

  • подкатегория 1
  • подкатегория 2

кошка 2

кошка 3

но он генерирует что-то вроде этого

кошка 1

  • кошка 1
  • кошка 2
  • подкатегория 1
  • подкатегория 2

использование mysql_fetch_array в другом (вложенном) без использования mysql_data_seek вызывает прерывание после вызова вложенного mysql_fetch_array. он выводит только cat1 и ничего больше.

подскажите решение, спасибо


person Henry    schedule 17.02.2010    source источник
comment
ваша проблема не во вложенном fetch_array. Почему вы перематываете данные с помощью mysql_data_seek? какой запрос вы используете для получения результата category_result?   -  person Ben    schedule 17.02.2010
comment
На этот вопрос невозможно ответить без набора результатов SQL.   -  person Nicole    schedule 17.02.2010
comment
Можете ли вы опубликовать, что на самом деле возвращает SQL-запрос? Я не вижу причин, по которым вы вообще вкладываете свой массив выборки. и mysql_data_seek ничего не делает, насколько я могу судить.   -  person Anthony    schedule 17.02.2010
comment
Я могу ошибаться, но я думаю, что он хочет снова выполнить запрос на основе текущей строки результата запроса. Не в настроении писать спекулятивный ответ, так что кто-то другой может это сделать.   -  person Anthony    schedule 17.02.2010
comment
Подробности SQL: img99.imageshack.us/img99/4776/unbenannt1ki.jpg   -  person Henry    schedule 17.02.2010
comment
Что означает родитель 21? И это действительно помогло бы вашему делу, если бы вы включили детали в вопрос, а не связывали их из комментариев.   -  person Anthony    schedule 17.02.2010
comment
родитель 21 означает, что это дочерний элемент категории с идентификатором 21   -  person Henry    schedule 17.02.2010


Ответы (4)


$i = 0;
while($c = mysql_fetch_array($category_result)) {
    $parent = $c['parent'];
    $next_row = $i + 1;

    $not_last_row = (mysql_data_seek($category_result, $next_row)); //Move ahead to get next parent or returns false if on last row...
    $next_parent = (!$not_last_row) ? $c['parent'] : FALSE;
    mysql_data_seek($category_result, $i); //Move back to current row

    $open_list = ($parent != $prev_parent);
    $close_list = (($parent > $next_parent) || !$not_last_row); //close list if next parent is smaller number or if this is last row.

    $output .= ($open_list) ? "<ul>" : "";

    $output .= '<li>'.$c['title'].'</li>';

    $output .= ($close_list) ? '</ul>' : "";

    $prev_parent = $parent; //Set this so that on next loop, we can see if they match
    $i++;
}

Это немного поспешно, но я думаю, что это то, что вы имеете в виду. По сути, если предыдущий родитель не соответствует текущему родителю, откройте список. Если текущий родитель больше, чем следующий родитель, закройте список.

Вероятно, вам нужно будет что-то добавить, чтобы учесть, что это последняя итерация.

person Anthony    schedule 17.02.2010
comment
Разрешенный размер памяти 67108864 байт исчерпан (попытался выделить 64487341 байт) ... похоже, это вызывает некоторое переполнение памяти - person Henry; 17.02.2010
comment
Вероятно, это было бы не так, если бы вы фактически использовали многомерный массив, а затем вместо этого перебирали его. Насколько велик этот список? Только четыре? - person Anthony; 17.02.2010
comment
да только 4 на данный момент... если я использую вашу обновленную версию, я получаю ошибку смещения с mysql_data_seek... но я думаю, что мы на правильном пути с этим... но один плохой момент в том, что у нас нет родительский псевдоним для создания URL-адреса... $c['alias'].'/'.$d['alias'] ... c-alias - person Henry; 17.02.2010

Вы получаете ожидаемый результат. Если у вас есть результирующий набор a,b,c,d, вы начинаете с a, которая является родительской категорией, поэтому она перематывается к началу набора и снова проходит через a,b,c,d как подкатегории. Теперь вы находитесь в конце набора, поэтому оба цикла завершатся, так как данных больше нет.

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

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

И еще одно: не используйте mysql_fetch_array, вместо этого используйте mysql_fetch_assoc. В противном случае вы получите числовые и ассоциативные ключи и массив строк, который содержит вдвое больше данных, чем должен.

person Duncan    schedule 17.02.2010

Вы можете попробовать быстрое и грязное решение, подобное этому:

  1. подготовьте 2 набора результатов с одним и тем же SQL-запросом, чтобы получить $category_result и $category_result2.

  2. Затем используйте $category_result для внешнего цикла и $category_result2 для внутреннего цикла.

Надеюсь, поможет.

person Box    schedule 17.02.2010

Подумайте, что делает ваш код:

  1. извлекает строку данных в $c и выдает заголовок cat1
  2. Входит во внутренний цикл и многократно извлекает новые данные в $d, выплевывая subcat1, subcat2 и т. д.
  3. Цикл достигает конца набора результатов и выходит в родительский цикл $c.
  4. Больше нет данных для извлечения, поэтому родительский цикл завершается.

Синтаксически, если бы вы удалили все операции с базой данных, вы, по сути, сделали бы это:

 for ($i = 0; $i < 10; $i++) {   // $c = mysql_fetch_array()
    for ($i = 1; $i < 10; $i++) {  // $d = mysql_fetch_array()
         echo $i;
    }
 }

К тому времени, когда внутренний цикл завершится, $i будет равно 10, что убьет внутренний цикл, а затем также убьет внешний цикл.

person Marc B    schedule 17.02.2010