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

Гриша К.

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

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

(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

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

Написать функцию, которая при выводе разделов проверяет, есть ли у каждого раздела подраздел, если есть, вызывает себя еще раз. Или я не прав?
 
Вот эта функция - http://phpclub.ru/faq/wakka.php?wakka=Tree/DemoPrintFunction&v=113o как раз такая и есть и она выводит все дерево.

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

Вывод каждого раздела, я сделал в виде ссылки id='идентификатору раздела из таблицы БД', и но вот как сделать, чтобы в зависимости от этого ид к примеру был необходимый вывод, я не знаю и пока у меня ничего не получается.
 
Или выбирай из базы не все дерево или добавь в условие что-то типа: {if Sstop and $i.id == $currItemId and ! empty($i.childNodes)}, где $currItemId - id выбранного раздела, $stop - флаг остановки рекурсии.

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

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

-~{}~ 16.05.06 18:16:

а именно ф-я get_tree() из первого метода того урока.
 
если тебе в таком виде нравится:
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>
 
При выводе дерева можно просто не отображать ненужные элементы....
 
в 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>

Массив из БД выводится полностью.
 
Ты можешь применить рекурсию, но для етого тебе надо найти путь, то есть все узлы которые ведут к твоему 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']))
//вызываем сами себя, чтобы показывать ветку
 
Вот немного переделанный скрипт под меня (лишнее убрано), используемый в 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>
 
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
 
Сверху