Древовидное меню (списки смежности) -> пошаговый вывод подразделов

Гриша К.

Новичок
Древовидное меню (списки смежности) -> пошаговый вывод подразделов

Здравствуйте.
Пытюсь реализовать вывод меню следующим образом (с перезагрузкой страницы):

(1) Вывод разделов меню
Пункт 1
Пункт 2
Пункт 3

(2) При нажатии на один из разделова выводим его подразделы
Пункт 1
- Подраздел 1.1
- Подраздел 1.2
- Подраздел 1.3
Пункт 1
Пункт 2
Пункт 3

(3) При нажатии на один из подразделов выводим также и его подразделы
Пункт 1
- Подраздел 1.1
-- Подраздел 1.1.1
-- Подраздел 1.1.2
-- Подраздел 1.1.3
- Подраздел 1.2
- Подраздел 1.3
Пункт 1
Пункт 2
Пункт 3

Дерево храню в БД в виде списков смежности, для вывода использую примеры из статьи PHP Club Cookbook & FAQ: Tree -> http://phpclub.ru/faq/wakka.php?wakka=Tree/DemoPrintFunction&v=113o

У меня не получается при выводи подразделов какого-то раздела, вывести также и все разделы, и т.д.
Получается к примеру только так:
Пункт 1
- Подраздел 1.1
-- Подраздел 1.1.3

Если кто-то уже реализовывал такой вывод, подскажите пожалуйста технологию решения.
 

eps

Новичок
Re: Древовидное меню (списки смежности) -> пошаговый вывод подразделов

Написать функцию, которая при выводе разделов проверяет, есть ли у каждого раздела подраздел, если есть, вызывает себя еще раз. Или я не прав?
 

Гриша К.

Новичок
Вот эта функция - http://phpclub.ru/faq/wakka.php?wakka=Tree/DemoPrintFunction&v=113o как раз такая и есть и она выводит все дерево.

Здесь - http://phpclub.ru/faq/wakka.php?wakka=Tree/AlLoad&v=f1o описано как загружать различные элементы дерева, но вот как сделать такой вывод как я описал.

Вывод каждого раздела, я сделал в виде ссылки id='идентификатору раздела из таблицы БД', и но вот как сделать, чтобы в зависимости от этого ид к примеру был необходимый вывод, я не знаю и пока у меня ничего не получается.
 

Mich

Продвинутый новичёк
Или выбирай из базы не все дерево или добавь в условие что-то типа: {if Sstop and $i.id == $currItemId and ! empty($i.childNodes)}, где $currItemId - id выбранного раздела, $stop - флаг остановки рекурсии.

Красивше вряд ли получится сделать. Точнее красивше будет выбирать только те элементы дерева, которые тебе нужны.
 

que_bunt

Новичок
Гриша К. используй рекурсию.

вроде то что тебе надо:
http://myphp.net.ru/lessons/index.php?18

-~{}~ 16.05.06 18:16:

а именно ф-я get_tree() из первого метода того урока.
 

Franzusow

Новичок
если тебе в таком виде нравится:
http://www.hydrotip.de/admi_neu/struktur.php
то если отредактируешь и оптимируешь етот код
http://www.php4-forum.de/forum_292598_l_sung_f_r_einen_navigationsbaum.htm
получешь желаемый резултат.
Я правда чуть все нервы не потерял, обясняя типу что $php_self не надо глобал обявлять.
так што возми с етого кода толко хорошее.
 

Гриша К.

Новичок
Спасибо всем за ответ.
que_bunt, рекурсия в функции дерева используется. Но за ссылку всеравно спасибо.

Franzusow, данный вывод меню мне не подойдет. За ссылку спасибо.


Mich, я никак не пойму, как мне вывести не все дерево из БД (к примеру Пункты 1-3, Подразделы 1.1-1.3 и Подразделы 1.1.1 - 1.1.3), щас сижу пробую разные варианты, но у меня ни как не получается.

А вот с остановкой рекурсии я вас не понял, т.е. данный плагин имеет возможность остановливать рекурсию, при наличии определенного флага?
Если возможно, то поясните пожалуйста, этот вариант.
 

Гриша К.

Новичок
Franzusow, спасибо за ответ.
Вот струтура таблицы и код извлечения из БД
PHP:
t_catalog_tree(k_item, k_parent) k_item #идентификатор элемента, k_parent #идентификатор родительского элемента
t_catalog(k_item, s_name) s_name #название
В том проекте который я делаю (переделываю) сейчас, я вижу такое 3-х уровневое меню и вывод содержания:

Вывод меню на главной странице /index.php
Мотоциклы
Автомобили
Самолеты

При нажатии к примеру на раздел "Автомобли" /catalog.php?catid=2, в содержании страницы выводится список марок автомобилей
Мотоциклы
Автомобили
* Мерседес
* Камаз
Самолеты

При нажатии к примеру на подраздел "Автомобили -> Мерседес" /catalog.php?firmid=1, в содержании страницы выводится список видов автомобилеей выбранной Марки
Мотоциклы
Автомобили
* Мерседес
** Бизнес класс
** Элит класс
* Камаз
Самолеты

При нажатии к примеру на подраздел "Автомобили -> Мерседес -> Бизнес класс" /catalog.php?catfirmid=1, в содержании страницы выводится прайс-лист автомобилей соотвествующей подкатегории
//Меню отображается как на странице /catalog.php?firmid=1

И мне кажется, что так как url для категорий, подкатегорий и подподкатегорий будет меняться, я не смогу это реализовать методом вызова рекурсии, просто придется делать 3 цикла.
Вот, что-то такое, но опять же как вывести меню в том виде в котором я описал в начале, я затрудняюсь сказать:
PHP:
<ul>
{foreach from=$tree item=i} 
<li><a href="?catid={$k_item}">{$i.s_name}</a> 

    {if $catid || $firmid || $catfirmid} 
        <ul>
       {foreach from=$i.tree item=i_1} 
       <li><a href="?firmid={$k_item}">{$i_1.s_name}</a>

       {if $firmid || $catfirmid} 
            <ul>
           {foreach from=$i_1.list item=i_2} 
            <li><a href="?catfirmid={$k_item}">{$i_2.s_name}</a></li>
            {/foreach}
             <ul>
        {/if}

       </li>
       {/foreach}
       <ul>
    {/if}

</li> 
{/foreach} 
<ul>
 

Mozart

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

eps

Новичок
в OSCommerce сделано именно так. (в инфобоксе категорий товаров) Если что не получается, посмотри его скрипт.
 

Гриша К.

Новичок
Спасибо за ответы.
eps, нашел в инетрнете сайты OSCommerce.ru .com, но я так понял, что это платный скрипт интернет-магазина и я повидимому не смогу посмотреть его код?

Пробовал кучу всего, чтобы сделать меню как я описал и ИД передавался только 1, но получилось только так:

PHP:
<ul>
{* Вывод категорий товаров *}
{foreach from=$a_tree item=i}
<li><a href="?cat={$i.k_item}">{$i.s_name}</a>
	{if $i.k_item eq $cat and $i.a_tree}
	<ul style="margin-left: 20pt">
		{* Вывод производителей товаров по категориям *}
		{foreach from=$i.a_tree item=i_1} 
		<li><a href="?cat={$i.k_item}&firm={$i_1.k_item}">{$i_1.s_name}</a>
			{if $i_1.k_item eq $firm and $i_1.a_tree}
			<ul style="margin-left: 20pt">
				{* Вывод подкатегорий по производителям товаров *}
				{foreach from=$i_1.a_tree item=i_2} 
				<li><a href="?cat={$i.k_item}&firm={$i_1.k_item}&catfirm={$i_2.k_item}">{$i_2.s_name}</a></li> 
				{/foreach}
			</ul>
			{/if}
		</li> 
		{/foreach}
	</ul>
	{/if}
</li> 
{/foreach}
</ul>

Пример вывода (url:_http://magazin/catalog.php?cat=2&firm=11&catfirm=13)
<a href="?cat=1">Мотоциклы</a>
<a href="?cat=2">Автомобили</a>
<a href="?cat=2&firm=11">** Мерседес</a>
<a href="?cat=2&firm=11&catfirm=12">**** Бизнес класс</a>
<a href="?cat=2&firm=11&catfirm=13">**** Элит класс</a>
<a href="?cat=3&firm=14">** Камаз</a>
<a href="?cat=9">Самолеты</a>
Массив из БД выводится полностью.
 

Franzusow

Новичок
Ты можешь применить рекурсию, но для етого тебе надо найти путь, то есть все узлы которые ведут к твоему id.
Принзип:
ты получаешь при клике на Линк id $_GET[id]
создаешь глобалный array и вгоняешь в него все узлы.
--------------------------------------------
$pfad=array();//global
$i=0;
$id_aktual=$_GET[id];
while(true){
//verhnij uzel
$sql="select a.k_item from t_catalog_tree as a, t_catalog_tree as b where a.k_item =b.k_parent and a.k_item =$id_aktual";
$hand=mysql_query($sql) or die(mysql_error());
if(mysql_num_rows($hand)!=1)breack;//prervat
$erg=mysql_fetch_array($hand);
$pfad[]=$id_aktual=$erg[0];
}

//теперь нам извесны все узлы которые мы должны будем расскрывать
$GLOBALS['knoten']=$pfad;

тебе остается толко переделать твою рекрусивную функзию так чтобы ты при высове самой-себя просто проверял
if(in_array($aktualnoe_id,$GLOBALS['knoten']))
//вызываем сами себя, чтобы показывать ветку
 

eps

Новичок
Вот немного переделанный скрипт под меня (лишнее убрано), используемый в OSC. в таблице categories есть categories_id categories_name и parent_id . (и всё таки этот скрипт бесплатен)

PHP:
  function tep_show_category($counter) {
    global $foo, $categories_string, $id;

    if ($foo[$counter]['parent'] == 0) {
      $cPath_new = 'cPath=' . $counter;
    } else {
      $cPath_new = 'cPath=' . $foo[$counter]['path'];
    }

    $categories_string .="ссылка  на index.php?cPath=$cPath_new"; // или что-то подобное

    for ($a=0; $a<$foo[$counter]['level']; $a++) {
      $categories_string .= "&nbsp;&nbsp;";  // здесь задается отступ подразделов
    }  

// display category name
    $categories_string .= $foo[$counter]['name'];

    $categories_string .= '<br>';

    if ($foo[$counter]['next_id']) {
      show_category($foo[$counter]['next_id']);
    }
  }

$categories_string="";
$categories_query =mysql_query("select categories_id, categories_name, parent_id from categories where parent_id = '0' order by categories_name");
  while ($categories = tep_db_fetch_array($categories_query))  {
    $foo[$categories['categories_id']] = array(
                                        'name' => $categories['categories_name'],
                                        'parent' => $categories['parent_id'],
                                        'level' => 0,
                                        'path' => $categories['categories_id'],
                                        'next_id' => false
                                       );

    if (isset($prev_id)) {
      $foo[$prev_id]['next_id'] = $categories['categories_id'];
    }

    $prev_id = $categories['categories_id'];

    if (!isset($first_element)) {
      $first_element = $categories['categories_id'];
    }
  }
// далее если указана категория, в которую перешли  - $cPath вида {id} или {id1}_{id2} или {id1}_{id2}_{id3} и так далее (можно GET передавать)
  if ($cPath) {             
    $new_path = '';
    $id = split('_', $cPath); 
    reset($id);
    while (list($key, $value) = each($id)) {
      unset($prev_id);
      unset($first_id);
      $categories_query = mysql_query("select categories_id, categories_name, parent_id from categories where parent_id = '" . (int)$value . "'  order by categories_name");

      $category_check = mysql_num_rows($categories_query);
      if ($category_check > 0) {
        $new_path .= $value;
        while ($row = mysql_fetch_array($categories_query)) {
          $foo[$row['categories_id']] = array(
                                              'name' => $row['categories_name'],
                                              'parent' => $row['parent_id'],
                                              'level' => $key+1,
                                              'path' => $new_path . '_' . $row['categories_id'],
                                              'next_id' => false
                                             );

          if (isset($prev_id)) {
            $foo[$prev_id]['next_id'] = $row['categories_id'];
          }

          $prev_id = $row['categories_id'];

          if (!isset($first_id)) {
            $first_id = $row['categories_id'];
          }

          $last_id = $row['categories_id'];
        }
        $foo[$last_id]['next_id'] = $foo[$value]['next_id'];
        $foo[$value]['next_id'] = $first_id;
        $new_path .= '_';
      } else {
        break;
      }
    }
  }
show_category($first_element);  // вызов функции
echo $categories_string; // вывод дерева
 

Гриша К.

Новичок
Спасибо всем за ответы.
Franzusow и eps, спасибо большое за коды, буду разбирать.

Вчера начал искать топики на форуме по кл. слову Oscommerce, вот к примеру из этого сообщения: http://phpclub.ru/talk/showthread.php?postid=610728#post610728 я уже решил, что скрипт платный. Заходил на http://www.oscommerce.ru - вчера он работал со сбоями (изображений не было и все очень долго), там я не нашел раздел download. http://www.oscommerce.com - вот сегодня все таки нашел нужный раздел и скачал скрипт, просто посмотрю что это.

-~{}~ 17.05.06 16:38:

У меня получилось сделать таким образом:
Две основные функции: вывод всего дерева и пути к заданной вершине, а дальше остается вывести меню в шаблоне с соответсвующими условиями. Думаю данный вариант будет работать не плохо, потому что функции для работы с деревьями используются из раздела статьи phpclub'a, ну а шаблон используется три цикла и несколько дополнительных условий. Шаблон использует три цикла еще и потому, что каждый подраздел, будет иметь ссылку на разные страницы.

menu.php
---------------------------
PHP:
<?

// В функцию добавлен вывод эелемента k_parent
function tree_list_tree(&$a_list,$k_item=0)
{
  if(empty($a_list[$k_item])) return array();
  $a_tree=array();
  for($i=0;$i<count($a_list[$k_item]);$i++)
  {
    $a_tree[]=array(
      'k_item' => $a_list[$k_item][$i]['k_item'],
      's_name' => $a_list[$k_item][$i]['s_name'],
      'k_parent' => $a_list[$k_item][$i]['k_parent'],
      'a_tree' => tree_list_tree($a_list,$a_list[$k_item][$i]['k_item'])
      );
  }
  return $a_tree;
}

// Загрузить все дерево
function tree_list_load_all()
{
// http://phpclub.ru/faq/wakka.php?wakka=Tree/AlLoadAll&v=n5d
PHP:
}


// Загрузить путь к заданной вершине
tree_list_load_path($k_item)
{
// http://phpclub.ru/faq/wakka.php?wakka=Tree/AlLoadPath&v=d08
PHP:
}



// Массив всего дерева
// Массив пути к заданной вершине
$a_tree_all = tree_list_load_all();
$a_tree_path = tree_list_load_path(@$_GET['catid']);

// Переменные шаблона
// Дерево
// Ид категории
// Ид родителя для всех непосредственных детей заданной вершины (уровень вложенности 1 и 2, учитывая что родитель 0)
$smarty->assign(array(
	"a_tree" => $a_tree_all,
	"catid" => @$_GET['catid'],
	"k_parent_1" => @$a_tree_path[0]['k_item'],
	"k_parent_2" => @$a_tree_path[0]['a_tree'][0]['k_item'])
	);

?>
menu.tpl
---------------------------
PHP:
<ul>
{* Вывод категорий товаров *}
{foreach from=$a_tree item=i}
<li><a href="?catid={$i.k_item}">{$i.s_name}</a>
	{if $k_parent_1 and $i.a_tree}
	<ul>
		{* Вывод производителей товаров по категориям *}
		{foreach from=$i.a_tree item=i_1}
 			{if $i_1.k_parent eq $k_parent_1}
			<li><a href="?catid={$i_1.k_item}">{$i_1.s_name}</a>
				{if $k_parent_2 and $i_1.a_tree}
				<ul>
					{* Вывод подкатегорий по производителям товаров *}
					{foreach from=$i_1.a_tree item=i_2} 
						{if $i_2.k_parent eq $k_parent_2}
						<li><a href="?catid={$i_2.k_item}">{$i_2.s_name}</a></li> 
						{/if}
					{/foreach}
				</ul>
				{/if}
			</li>
			{/if} 
		{/foreach}
	</ul>
	{/if}
</li> 
{/foreach}
</ul>
 

Valdisss

Guest
Re: Древовидное меню (списки смежности) -> пошаговый вывод подразделов

Автор оригинала: Гриша К.

Если кто-то уже реализовывал такой вывод, подскажите пожалуйста технологию решения.
Рекурсия - вот твоё решение.
 

Гриша К.

Новичок
Valdisss, спасибо за ответ.
Я знаю что такое рекурсия, и сначала я использовал такую функцию для вывода целового дерева, или ветки дерева.
Но как сделать, чтобы меню было такое как я описал в начале, у меня получилось только способом описанным в этом сообщение: http://phpclub.ru/talk/showthread.php?postid=611971#post611971

Вы знаете как сделать такое меню при помощи рекурсии?
Самое сложное это извлеч нужные данные из БД, и правильно обработать их при выводе. А так как меню 3-х уровневое, то не так страшно, что используется 3 цикла.



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

------------------------

-~{}~ 22.05.06 03:56:

Вариант решения: http://phpclub.ru/talk/showthread.php?postid=613227#post613227
 
Сверху