За последние 24 часа нас посетили 16732 программиста и 1646 роботов. Сейчас ищут 1004 программиста ...

Парсинг дерева сайта

Тема в разделе "PHP для новичков", создана пользователем artuska, 6 авг 2009.

  1. artuska

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

    С нами с:
    6 авг 2007
    Сообщения:
    61
    Симпатии:
    0
    Адрес:
    Riga, Latvia
    Здравствуйте.
    Есть такая структура сайта в базе (классические айдишка–парэнт_айдишка):
    [​IMG]
    • Админ
      • Логин
        • Смена пароля
      • Выход

    Допустим, я захожу сюда — www.site.com/admin/login/recover (здесь, типа, напоминалка пароля, например).
    Так вот, как мне узнать айдишку последнего, третьего сегмента (recover)? Причем нужно пошагово пройтись по всем сегментам УРИ и проверить их наличие в базе. Пошагово по дереву нужно пройти потому, что у меня могут быть, например, такие разделы — www.site.com/user/login/recover — здесь тоже третий сегмент называется «recover» и такое дерево реально есть в базе (так же, как и на первой картинке).

    Только пожалуйста, не надо прдираться к структуре — эти УРИ я привел для наглядности. Так же не предлагать передавать вот этот «recover» как параметр.

    Мне нужен совет именно в том, как наиболее нересурсоемко пройтись от вершины дерева до нужного номера сегмента. Сейчас я делаю это такой вот рекурсией:
    PHP:
    1.  
    2. $path = explode("/", $_SERVER['REQUEST_URI']);
    3.  
    4. /*
    5. Array
    6. (
    7.     [0] =>
    8.     [1] => admin
    9.     [2] => login
    10.     [3] => recover
    11. )
    12. */
    13.  
    14. function get_id($p_id, $i='1'){ // $i равна еденице, потому, что массив $path начинается с первого элемента (нулевой элемент пуст — он нам не нужен)
    15.  global $path;
    16.  $sql = "SELECT id FROM `structure` WHERE p_id=".$p_id." AND url='".$path[$i]."' LIMIT 1";
    17.  $res = mysql_query($sql);
    18.  
    19.  if(mysql_num_rows($res)<1){
    20.   return $p_id;
    21.  }
    22.  
    23.  $row = mysql_fetch_object($res);
    24.  
    25.  return get_lastid($row->id, ++$i); // вот тут рекурсия, айдишка передается как парэнт_айдишка
    26. }
    27.  
    28. Получаются такие запросы:
    29. SELECT id FROM `structure` WHERE p_id='0' AND url='admin' LIMIT 1 // сейчас айдишка = 1
    30. SELECT id FROM `structure` WHERE p_id='1' AND url='login' LIMIT 1 // сейчас айдишка = 2
    31. SELECT id FROM `structure` WHERE p_id='2' AND url='recover' LIMIT 1 // сейчас айдишка = 3
    32. всё, узнали айдишку
    33.  
    Много запросов получается. На данный момент я переделал функцию — один единственный запрос выбирает абсолютно всё дерево сайта и заносит его в массив. Дальше уже этот массив я рекурсивно обхожу, чтобы узнать айдишку (и другую инфу). Всеравно не то. Хоть запрос и один, но теперь не нравится рекурсия.

    Так вот, как пройти от вершины дерева (от «admin») до последнего элемента («recover») (ну, или до заданного, неважно)? Как бы вы это сделали?
    В каком виде сохранить само дерево сайта, саму стркутуру разделов, чтобы по дереву можно было легко перемещаться от верхнего уровня до требумоего (ну, напрмер, массив парэнт_айдишек, в нем массив айдишек, а уже в них вся инфа о сайте).
     
  2. Mr.M.I.T.

    Mr.M.I.T. Старожил

    С нами с:
    28 янв 2008
    Сообщения:
    4.586
    Симпатии:
    1
    Адрес:
    у тебя канфетка?
  3. artuska

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

    С нами с:
    6 авг 2007
    Сообщения:
    61
    Симпатии:
    0
    Адрес:
    Riga, Latvia
    Просмотрел.
    1) Видишь ли, не всегда возможна структура сайта по типу — www.example.com/class/method/id/ — этот шаблон записи УРИ используется много где, в Code Igniter, например. Мне это не подходит.
    2) Дальше. У меня динамические УРИ — сегменты могут менять свои первоначальные названия.
    www.example.com/admin/login/recover/
    может измениться в
    www.example.com/administracija/vhod/novij_parol/
    по желанию пользователя (он сам в ЦМСке это исправит по своему вкусу, например). И тогда класс с названием «admin» в котором есть метод «login» не будет подгружаться.

    Поэтому мне нужно разбить УРИ на сегменты и пройтись по базе в такой последовательности, как расположены эти сегменты.
    www.example.com/products/computers/sony/vaio/ — вот такой УРИ и именно такая структура дерева в базе, с айдишками и парэнт_айдишками. А есть и такой раздел:
    www.example.com/products/mp3players/sony/ — так вот чтобы узнать айдишку у «sony», я должен пройтись по ступенькам, от записи «products» в базе, до записи «sony» (в базе для раздела «sony» у меня уже есть запись и с шаблоном и с классом, который должен выполняться в этом разделе).

    Опять таки повторяю, не придерайтесь вы к УРЛ'у — вопрос не в том, как правильно структуру создать. Вопрос в том, как по динамическому УРИ пройтись в базе как по ступенькам.
     
  4. Mr.M.I.T.

    Mr.M.I.T. Старожил

    С нами с:
    28 янв 2008
    Сообщения:
    4.586
    Симпатии:
    1
    Адрес:
    у тебя канфетка?
    зачем? если функционал будет реализовываться sony
     
  5. Mr.M.I.T.

    Mr.M.I.T. Старожил

    С нами с:
    28 янв 2008
    Сообщения:
    4.586
    Симпатии:
    1
    Адрес:
    у тебя канфетка?
    если так тебе вообще нужно только
    site.ru/sony/
     
  6. artuska

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

    С нами с:
    6 авг 2007
    Сообщения:
    61
    Симпатии:
    0
    Адрес:
    Riga, Latvia
    МИТ, ты читаешь то, что я пишу?

    www.example.com/products/computers/sony/
    www.example.com/products/mp3players/sony/

    У меня есть раздел «Компьютеры» и в нем «Сони»
    А еще есть раздел «МП3 Плееры» и в нем тоже «Сони»

    Поэтому и речи не может быть про то, что просто взять «sony» из базы, так как у меня там 2 раза встречается этот «sony». Поэтому нужно пройтись по ступенькам по базе, исходя из того, какой у нас УРИ.
     
  7. artuska

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

    С нами с:
    6 авг 2007
    Сообщения:
    61
    Симпатии:
    0
    Адрес:
    Riga, Latvia
    Нет, у меня именно структура /products/computers/sony/.

    Можно и по-другому, если хочешь — /products/sony/computers/. И в этом случае мой вопрос не изменяется.
     
  8. neverlose

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

    С нами с:
    27 авг 2008
    Сообщения:
    1.112
    Симпатии:
    20
    PHP:
    1. <?php
    2. //$path = explode( '/', $_SERVER['REQUEST_URI']);
    3.  
    4. /*
    5. Array
    6. (
    7.     [0] =>
    8.     [1] => admin
    9.     [2] => login
    10.     [3] => recover
    11. )
    12. */
    13. $path = array( '', 'admin', 'login', 'recover');
    14. function BuildQuery( $path, $step = 0)
    15. {
    16.     $query = '(SELECT `id` FROM `structure` WHERE `url` = "' .mysql_real_escape_string( $path[$step]) .'" ';
    17.  
    18.     if( ++$step != count( $path))
    19.         $query .= 'AND `p_id` = ' .BuildQuery( $path, $step);
    20.  
    21.     $query .= 'LIMIT 1) ';
    22.  
    23.     return $query;
    24. }
    25. function GetID( $path)
    26. {
    27.     if( !is_array( $path) || count( $path) < 2)
    28.         return false;
    29.  
    30.     array_shift( $path);
    31.     $path = array_reverse( $path);
    32.  
    33.     $query = trim( BuildQuery( $path), '() ');
    34.  
    35.     return mysql_result( mysql_query( $query), 0, 'id');
    36. }
    37.  
    38. echo GetID( $path);
    39. ?>
     
  9. Mr.M.I.T.

    Mr.M.I.T. Старожил

    С нами с:
    28 янв 2008
    Сообщения:
    4.586
    Симпатии:
    1
    Адрес:
    у тебя канфетка?
    тогда рекурсия или читать про другие виды деревьев.
    однако я бы сделал так /products/{Category_ID}/ и не парился с велосипедами
    или накрайняк сделал бы сони не как отдельную категорию а как фильтр при выборке
     
  10. Mr.M.I.T.

    Mr.M.I.T. Старожил

    С нами с:
    28 янв 2008
    Сообщения:
    4.586
    Симпатии:
    1
    Адрес:
    у тебя канфетка?
    neverlose
    подзапросы ещё хуже рекурсии =)
     
  11. artuska

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

    С нами с:
    6 авг 2007
    Сообщения:
    61
    Симпатии:
    0
    Адрес:
    Riga, Latvia
    neverlose
    Спасибо тебе за труд, но ты предлагаешь то, отчего я уже избавился — куча запросов к базе.

    Вроде да, придется использовать ее величество.
    А другие виды деревьев — это какие? Материализованный путь? Нэстэд сэт? Они вопрос не решат, полюбому нужно будет перебирать записи в базе.
     
  12. Mr.M.I.T.

    Mr.M.I.T. Старожил

    С нами с:
    28 янв 2008
    Сообщения:
    4.586
    Симпатии:
    1
    Адрес:
    у тебя канфетка?
    artuska
    он предлагает ОДИН запрос, с кучей подзапросов, что ещё хуже чем много запросов
    передавай лучше ID категории, или прям так надо передовать названия?
     
  13. artuska

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

    С нами с:
    6 авг 2007
    Сообщения:
    61
    Симпатии:
    0
    Адрес:
    Riga, Latvia
    Ну тоже самое.

    Нет, нужен именно такой путь в каталогах, который пользователь без труда запомнит ассоциативно. «Я был в разделе продукты, в подразделе компьютеры, в разделе фирмы Сони» — /products/computers/sony/.
    А вот «Я был в разделе продукты, в разделе номер 65399» пользователь так не размышляет.

    Дальше. Админ ЦМС'ки возьмет, и захочет создать раздел вот по такому пути
    www.example.com/products/computers/sony/for_free и напихает туда компы, котрые нахаляву. Вот мне и нужно узнать айдишку раздела «for_free», по которой я потом в базе выберу именно компьютеры фирмы «Сони», которые «бесплатны».
    А сразу взять слово «for_free» я не могу, ведь может быть и такой УРИ:
    www.example.com/products/computers/hewl ... d/for_free

    Поэтому нужно пройтись по нашей структуре как по лестнице, от «products» до «for_free», чтобы не наткнуться на какой-нибудь другой раздел, где есть «for_free».

    А передавать наши «sony», «hewlett_packard» и «for_free» как параметры, я не могу — не смогу потом сделать менюшку сайта в виде дерева.
    • Продукты
      • Компьютеры
        • Sony
          • Vaio
          • Забесплатно
        • Hewlett Packard
          • Забесплатно
     
  14. neverlose

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

    С нами с:
    27 авг 2008
    Сообщения:
    1.112
    Симпатии:
    20
    Вы иначе не сделаете. Мой алгоритм минимален.
    Можно сделать только другую реализацию, чтобы ускорить алгоритм.
    Да и к тому же, количество подзапросов = количество элементов в $path минус 2.
     
  15. alexeurodnepr

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

    С нами с:
    18 июл 2008
    Сообщения:
    244
    Симпатии:
    0
    artuska

    база у тебя не подходящая, ты должен указать в ней уже все дерево а потом как кто-то писал, выбрать "ВСЕ" дерево и обходить его рекурсивно - вариант зе бест

    по твоему примеру, что б понять как тебе перестроить бд
    Код (Text):
    1.  
    2. Продукты (id=1, pid=0)
    3.       Компьютеры (id=2, pid=1)
    4.             Sony (id=2, pid=2)
    5.                   Vaio (id=3, pid=2)
    6.                         Забесплатно (id=4, pid=3)
    7.             Hewlett Packard (id=4, pid=2)
    8.                         Забесплатно (id=6, pid=4)
    т.е. что б нормально без мозгоё.. обойти дерево ты должен указывать дочерней категории родительскую... а иногда и родительской нужно указать есть ли у него ребенок...

    upd

    потом ты имеешь вид бд примерно:
    Продукты (id=1, pid=0)
    Компьютеры (id=2, pid=1)
    Sony (id=2, pid=2)
    Vaio (id=3, pid=2)
    Забесплатно (id=4, pid=3)
    Hewlett Packard (id=4, pid=2)
    Забесплатно (id=6, pid=4)

    1. извлек все
    2. начинаешь обход рекурсивно (без нее тут практически никак)
    3. выбираешь root потом опускаешься ниже и ниже, что б был вид дерева просто делаешь margin-left: n (в зависимости от дизайна) где n рекурсивно увеличиваешь
     
  16. artuska

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

    С нами с:
    6 авг 2007
    Сообщения:
    61
    Симпатии:
    0
    Адрес:
    Riga, Latvia
    alexeurodnepr
    Да, так и буду делать — перестраивать там ничего не нужно, структура идиентична твоей — айдишка-парэнт_айдишка. Без рекурсии действительно не обойтись.
    Всем спасибо, тема может быть закрыта.