За последние 24 часа нас посетили 18602 программиста и 1599 роботов. Сейчас ищут 978 программистов ...

Рекурсивная функция

Тема в разделе "PHP и базы данных", создана пользователем travelbook, 15 апр 2011.

  1. travelbook

    travelbook Активный пользователь

    С нами с:
    8 ноя 2010
    Сообщения:
    7
    Симпатии:
    0
    Не могу создать массив с нужной структурой:
    нужно чтобы создавало простую структуру, не зависимо от глубины функции, например:

    $display[1]['name'] = Категория 1;
    $display[2]['name'] = Категория 1.1;
    $display[3]['name'] = Категория 1.1.1;
    $display[4]['name'] = Категория 2;



    function PriceTree($catid) { // построение дерева прайса

    $sql = "SELECT * FROM ".TABLE_PCATEGORIES." WHERE parent_id = '$catid'"; // вывод категорий прайса
    $result = mysql_query($sql);


    while ($row = mysql_fetch_array($result)) {
    $i++;
    $cid = $row['pcategories_id'];
    $cname = $row['pcategories_name'];

    $display[$i]['name'] = $cname;

    $display[$i]=PriceTree($cid);
    }

    return $display;
    }


    PriceTree(0);
     
  2. centnerik

    centnerik Активный пользователь

    С нами с:
    11 мар 2011
    Сообщения:
    32
    Симпатии:
    0
    Сразу предупреждаю - по голове не бить. Это хоть и первое, что пришло, но все таки в голову )))

    PHP:
    1.  
    2. <?php
    3. function getPriceTree($id_cat = 0)
    4. {
    5.     $sql = sprintf("SELECT * FROM %s WHERE parent_id = %d", $table_name, $id_cat);
    6.     $result = mysql_query($sql);
    7.     while($row = mysql_fetch_assoc($result)){
    8.         $display[]['name'] = $row['name'];
    9.         getPriceTree($row['id']);
    10.     }
    11.     return $display;
    12. }
    13. ?>
    14.  
    И еще... Не знаю, будут ли проблемы с областью видимости $display.
    Если не получится - извини, сам только недавно знаком с PHP
     
  3. titch

    titch Активный пользователь

    С нами с:
    18 дек 2010
    Сообщения:
    847
    Симпатии:
    0
    ой не... жуть какая. во-первых, запрос на каждом уровне, а во-вторых, отсутствие сортировки (которая аукнется, если добавится редактирование этого списка и реальный порядок не будет совпадать с порядком PRIMARY), в-третьих, в функцию в таких деревьях нужно передавать параметры по ссылке. лучше эту реализацию не использовать
     
  4. titch

    titch Активный пользователь

    С нами с:
    18 дек 2010
    Сообщения:
    847
    Симпатии:
    0
    могу дать свою. но она под другую базу со своей спецификой, но допилить можно при желании. что-то очень старое.... помню, что в $tail остаются неиспользованные детали, а в $tree - само дерево. отдрачивалось на скорость выполнения

    PHP:
    1. <?php
    2.     $res = mysql_query("SELECT * FROM `blogs` ORDER BY `id`;");
    3.  
    4.     $tail = array();
    5.     while($data = mysql_fetch_assoc($res))
    6.         array_push($tail,$data);
    7.    
    8.     function f_Tree_Blog_Fast (&$tail)
    9.     {
    10.         $arr_pare = array();
    11.         foreach ($tail as $key=>$item)
    12.             $arr_pare[$key]=$item['mother_blog_id'];
    13.         asort($arr_pare);
    14.         $tree = array();
    15.         foreach($arr_pare as $key=>$item)
    16.             if($item == null)
    17.             {
    18.                 array_push($tree,$tail[$key]);
    19.                 unset($arr_pare[$key]);
    20.                 unset($tail[$key]);
    21.             }
    22.             else
    23.                 break;
    24.         foreach($tree as $key=>$item)
    25.             f_BuildLayer($arr_pare,$tail,$tree[$key]);
    26.         return $tree;
    27.     }
    28.  
    29.     function f_BuildLayer(&$arr_pare,&$tail,&$base)
    30.     {
    31.         $base['next'] = array();
    32.         $i = 0;
    33.         while($pos = array_search($base['id'],$arr_pare))
    34.         {
    35.             array_push($base['next'],$tail[$pos]);
    36.             unset($arr_pare[$pos]);
    37.             if($tail[$pos]['is_group']=='y')
    38.             {
    39.                 f_BuildLayer($arr_pare,$tail,$base['next'][$i]);
    40.                 unset($tail[$pos]);
    41.             }
    42.             $i++;
    43.         }
    44.         if(count($base['next'])==0)
    45.             unset($base['next']);
    46.     }
    47.  
    48.     $tree = f_Tree_Blog_Fast($tail);
    49. ?>
     
  5. centnerik

    centnerik Активный пользователь

    С нами с:
    11 мар 2011
    Сообщения:
    32
    Симпатии:
    0
    по идее должно вывести что-то типа
    Категория1
    Категория1.1
    Категория1.2
    Категория2 и т.д.
    А PRIMARY же задает только уникальный идентификатор и как бы на результат вывода не сильно влияет. Или я не прав
     
  6. titch

    titch Активный пользователь

    С нами с:
    18 дек 2010
    Сообщения:
    847
    Симпатии:
    0
    если я поменяю местами категория 1.1 и 1.2?
    хотя... один фиг. в моей тоже поменяется... для таких целей нужен usort по какому-то параметру типа weight
     
  7. centnerik

    centnerik Активный пользователь

    С нами с:
    11 мар 2011
    Сообщения:
    32
    Симпатии:
    0
    Можно же потом отсортировать готовый массив и как раз получиться то что надо, даже связь с ключами сохранять не надо
     
  8. titch

    titch Активный пользователь

    С нами с:
    18 дек 2010
    Сообщения:
    847
    Симпатии:
    0
    если дерево 200 пунктов, то 200 запросов - это очень медленно в любом случае
     
  9. centnerik

    centnerik Активный пользователь

    С нами с:
    11 мар 2011
    Сообщения:
    32
    Симпатии:
    0
    Согласен. Но вопрос был только создать массив определенной структуры, а дальше уже работать только с этим массивом
     
  10. travelbook

    travelbook Активный пользователь

    С нами с:
    8 ноя 2010
    Сообщения:
    7
    Симпатии:
    0
    Всем спасибо, решил проблему сам...
     
  11. Volt(220)

    Volt(220) Активный пользователь

    С нами с:
    11 июн 2009
    Сообщения:
    1.640
    Симпатии:
    1
  12. Radiocity

    Radiocity Активный пользователь

    С нами с:
    24 апр 2011
    Сообщения:
    5
    Симпатии:
    0
    Доброго времени суток. Сразу предупрежу, что не очень хорошо разбираюсь в PHP,MySQL. Столкнулся с необходимостью изменить редактор каталога, добавив функцию удаления "папки" со всем ее содержимым.
    Данные хранятся в таблице myGroups (отстальное содержимое пока меня не интересует)

    Логика подсказывает, что необходимо написать рекурсивную функцию, а посты выше, что делать в этой функции 400+ запросов - неверный шаг. Вариант, предложенный Volt`ом уж очень непонятен и сложен для понимания =(

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

    Будет ли данный вариант оптимален?
     
  13. titch

    titch Активный пользователь

    С нами с:
    18 дек 2010
    Сообщения:
    847
    Симпатии:
    0
    не супероптимально, но верно. учитывайте, что вы можете упереться в быстродействие (грубый код на 100 узлов выполнялся аж 0,4с!). посмотрите мой код выше, может это вас натолкнёт на какие-то соображения
     
  14. Radiocity

    Radiocity Активный пользователь

    С нами с:
    24 апр 2011
    Сообщения:
    5
    Симпатии:
    0
    Код не понял. Тяжело разбираться, когда комментариев нет =(

    За подсказку с оптимальностью спс. 0.4с переживу ))
     
  15. titch

    titch Активный пользователь

    С нами с:
    18 дек 2010
    Сообщения:
    847
    Симпатии:
    0
    PHP:
    1. <?php
    2.     //запрос в базу. получаем все записи, которые могут быть использованы в дереве блогов
    3.     $res = mysql_query("SELECT * FROM `blogs` ORDER BY `id`;");
    4.  
    5.     //собираем их в массив, для того чтобы потом этот массив скормить построителю дерева
    6.     $tail = array();
    7.     while($data = mysql_fetch_assoc($res))
    8.         array_push($tail,$data);
    9.  
    10.     function f_Tree_Blog_Fast (&$tail) //построитель
    11.     {
    12.         $arr_pare = array(); //создается легкий массив для быстрого индекса записей большого массива
    13.         foreach ($tail as $key=>$item)
    14.             $arr_pare[$key]=$item['mother_blog_id'];
    15.         asort($arr_pare);
    16.  
    17.         //строим первый слой дерева. для этого ищутся блоги, у которых 'mother_blog_id'==null.
    18.         //делается это для скорости на индексном массиве
    19.         $tree = array();
    20.         foreach($arr_pare as $key=>$item)
    21.             if($item == null)
    22.             {
    23.                 array_push($tree,$tail[$key]); //нашли хоть и в индексном массиве, но в дереве подцепляем уже большую запись
    24.                 unset($arr_pare[$key]); //удаляем из индекса
    25.                 unset($tail[$key]); //удаляем из входного массива
    26.             }
    27.             else
    28.                 break; // после asort все null находились вверху. если встретилась нормальная запись, значит первый слой сделан
    29.  
    30.         //каждая запись может стать основанием новой ветки дерева. построим.
    31.         foreach($tree as $key=>$item)
    32.             f_BuildLayer($arr_pare,$tail,$tree[$key]);
    33.         return $tree;
    34.     }
    35.  
    36.     function f_BuildLayer(&$arr_pare,&$tail,&$base) //&индекс-массив, &остатки входного массива, &основа для строительства ветки
    37.     {
    38.         $base['next'] = array(); //ветку будем достраивать сразу в элементе
    39.         $i = 0;
    40.         while($pos = array_search($base['id'],$arr_pare))
    41.         {
    42.             array_push($base['next'],$tail[$pos]);
    43.             unset($arr_pare[$pos]);
    44.  
    45.             //база спроектирована так, что элемент знает: конечный он или не конечный
    46.             //проверку на группу можно запросто убрать. всё должно работать
    47.             if($tail[$pos]['is_group']=='y')
    48.             {
    49.                 f_BuildLayer($arr_pare,$tail,$base['next'][$i]);
    50.                 unset($tail[$pos]);
    51.             }
    52.             $i++;
    53.         }
    54.         //если ветка не выросла, то закрываем её
    55.         if(count($base['next'])==0)
    56.             unset($base['next']);
    57.     }
    58.  
    59.     //скармливаем массив построителю
    60.     $tree = f_Tree_Blog_Fast($tail);
    61. ?>
     
  16. Radiocity

    Radiocity Активный пользователь

    С нами с:
    24 апр 2011
    Сообщения:
    5
    Симпатии:
    0
    Пытался написать функцию удаления "папки" из каталога со всм содржимым, но так ничего не получилось. Подскажите, пожалуйста, где может быть ошибка? Как заставить код функционировать?

    Таблица mysql, содержащая каталоги:
    ---------------------------------
    ID (int) - уникальный идентификатор
    PID (int) - иднтификатор родителя
    data (text) - прочая информация

    Переменные:
    ----------------------------------
    $ecmode - тип операции (в данном случае удаление)
    $_SITE['DB_PREFIX'] - некоторый префикс бд (s_)
    $ecdir - id папки, которую удаляем
    $ecbuffer = array (); - буфер со значениями каталогов
    $ecdbuffer[] = array(); - буфер, удаляемых элементов, который в конечном счете чистится

    PHP:
    1.  
    2. <?php
    3. # DELETE FOLDER:
    4.  if ($ecmode==5)
    5.   {
    6.     $sql = "SELECT id, pid FROM ".$_SITE['DB_PREFIX']."gCatalogGroups ORDER BY id";
    7.     $ecfbuffer_sz =0;
    8.     if ($rowq=mysql_query($sql))
    9.     {
    10.       # создаем буфер вида: $ecbuffer[<id>] = <pid>
    11.             while ($row = mysql_fetch_object($rowq))
    12.             {
    13.                 $ecfbuffer[$row->id] = $row->pid;
    14.                 $ecfbuffer_sz++;
    15.             }
    16.             mysql_free_result($rowq);
    17.             # ищем ключик в буфере        
    18.             if (array_key_exists($ecdir, $ecfbuffer)){
    19.           @oops('dir exists');
    20.           ecdirdrop($ecdir, $ecfbuffer);
    21.           @oops('set parent');
    22.           for ($i=0; $i<count($ecdbuffer); $i++)
    23.           {
    24.              # удаляем "файлы"
    25.             $sql="DELETE FROM ".$_SITE['DB_PREFIX']."gCatalogGoodies WHERE pid='".$ecdbuffer[$i]."'";
    26.              mysql_query($sql);
    27.              # удаляем "папки"
    28.             $sql="DELETE FROM ".$_SITE['DB_PREFIX']."gCatalogGroups WHERE pid='".$ecdbuffer[$i]."'";
    29.              mysql_query($sql);
    30.           }
    31.           $ecdir = $ecfbuffer[$ecdir];
    32.          
    33.             }
    34.             else
    35.             {
    36.           @oops('Del: target folder does not exists.');
    37.           $ecdir = 0;
    38.             }          
    39.     }
    40.     else
    41.     {
    42.       @oops('Del: buffer is empty');
    43.     }
    44.   }
    45. ?>
    46.  
    Функция ecdirdrop():

    PHP:
    1.  
    2. <?php
    3. function ecdirdrop($fid, $fbuf){
    4.   # Пока есть наследник FindFirstChild мы ищем конец
    5.  while(($ecdd=array_search($fid,$fbuf))&&($ecdd!==NULL)){
    6.   if ($ecdd!=NULL)
    7.     ecdirdrop($ecdd,$fbuf);
    8.     oops(array_search($fid,$fbuf));
    9.   }  #while
    10.  # Когда конечный каталог найден и у него нет наследника, мы его чистим от файлов и удаляем
    11.  $ecdbuffer[] = $fid;
    12.   # При этому станавливаем  его ключ в буфере в минус, чтобы не находить
    13.  $fbuf[$fid]=1000000;
    14.   @oops('Удалена папка');
    15. }
    16. ?>
    17.  
    В итоге страница виснет и удаление не происходит.