Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Perl: разработка для Web > Многоуровневое меню. Вывод ветки


Автор: Arx 21.4.2006, 15:52
ДАНО:
Многоуровневое меню... в БД след. формат таблицы:

----------------------------------------
|    id    |  parent_id   |    name    |
----------------------------------------


НАДО:
При каждом клике на пункт меню выводить только один следующий уровень, при этом все предыдущие узлы тоже должны выводится, т.е. получается мы постепенно должны открыть всю ветку.

Использую рекурсивную функцию.
2 дня бьюсь!!! Ничего не выходит.
Уверен что решение элементарное!
Подскажите как это сделать или лучше рабочий кусок кода дайте 

Автор: BlackLFL 21.4.2006, 15:55
какая база данных 

Автор: Arx 22.4.2006, 11:52
MySQL 

Автор: Arx 24.4.2006, 12:11
Ну чего народ???
неужели никто не писал??? 

Автор: levcom 2.5.2006, 00:13
Я тоже одно время бился, но остановился на несколько ином решении (возможно оно покажется странным, но на универсальность не претендую)
 Вначале я использовал рекурсивный спуск, и одращался к БД, хотя потом решил что куча запросов к БД это не эффективно и придумал следующее:
1) я использую модуль apache mod_rewrite для "красивых путе" (т.к. сайт на единой системе публикации) и в файле .htaccess прописано примерно слудующее:
...
RewriteRule ^(.*)news/$ $1newsline.pl?_nav_build=0-1
...
причем фаил формируется программно при обновлении в системе навигации обновляется тоже, обратите внимание на  _nav_build=0-1 - это формируется автоматом при обновлении при рекурсивном спуске, когда вся навигация загружается в хэш. При обрашении к странице по этому параметру любое древовидное меню строится быстро и с помощью одного запроса к БД. Замечу, что для выдачи содержания страницы достаточно взять контент из БД опять же одним запросом по id равному последней цифре параметра _nav_build=0-1. 
Для малых сайтов это решение очень удобно, для больших я хочу кэшировать часть таблицы навигации, для формирования навигации "на лету" без пометок в файле конфигурации.  

Автор: GoodBoy 3.5.2006, 09:55
Arx, попробуй по этому шаблону:

Код
our %groups - хэш данных $groups{$i} = {'id'=>$i, 'parent_id'=>parent_id, 'name'=>name};
our $str = 'что нужно от корневого элемента';

&select_option(0);


sub select_option {
    my $pid = shift;
    foreach my $id (sort { $groups{$a}->{'name'} cmp $groups{$b}->{'name'} } keys %groups) {
        my $hash = $groups{$id};
        if ($hash->{'parent_id'} == $pid) {

            $str .= 'что нужно сделать с этим элементом';

            select_option(id);
        }
    }
}
 

Автор: Arx 10.5.2006, 12:53
levcom, есть рабочий скриптец? smile

Мозги закипают уже.

GoodBoy, твой кусок походу выведет все дерево, а мне не надо все. Точнее надо но постепенно

есть структура меню:

О КОМПАНИИ
КУХОННАЯ ЛОГИСТИКА 
      СТЕЛЛАЖНЫЕ СИСТЕМЫ 
           Стеллажи из нержавеющей стали 
           Полки настенные 
           Стеллажи из алюминия 
      ТРАНСПОРТИРОВОЧНОЕ ОБОРУДОВАНИЕ 
           Тележки для транспортировки корзин с посудой 
           Тележки шпильки 
           Тележки для сбора посуды 
      ДИСПЕНСЕРЫ 
           Диспенсеры для тарелок 
           Диспенсеры универсальные 
           Встраиваемые диспенсеры 
      КУХОННЫЕ АКСЕССУАРЫ 
           Банкетные тележки и аксессуары 
      КОНВЕЕРНЫЕ СИСТЕМЫ 
      ТЕЛЕЖКИ С ИНДУКЦИОННЫМ ПОДОГРЕВОМ И ОХЛАЖДЕНИЕМ 
МЕДИЦИНСКОЕ ОБОРУДОВАНИЕ 

НАДО
на главной странице:

О КОМПАНИИ
КУХОННАЯ ЛОГИСТИКА 
МЕДИЦИНСКОЕ ОБОРУДОВАНИЕ

ДАЛЕЕ жму КУХОННАЯ ЛОГИСТИКА

О КОМПАНИИ
КУХОННАЯ ЛОГИСТИКА 
      СТЕЛЛАЖНЫЕ СИСТЕМЫ 
      ТРАНСПОРТИРОВОЧНОЕ ОБОРУДОВАНИЕ 
      ДИСПЕНСЕРЫ 
      КУХОННЫЕ АКСЕССУАРЫ 
      КОНВЕЕРНЫЕ СИСТЕМЫ 
      ТЕЛЕЖКИ С ИНДУКЦИОННЫМ ПОДОГРЕВОМ И ОХЛАЖДЕНИЕМ 
МЕДИЦИНСКОЕ ОБОРУДОВАНИЕ

ДАЛЕЕ жму ТРАНСПОРТИРОВОЧНОЕ ОБОРУДОВАНИЕ 

О КОМПАНИИ
КУХОННАЯ ЛОГИСТИКА 
      СТЕЛЛАЖНЫЕ СИСТЕМЫ 
      ТРАНСПОРТИРОВОЧНОЕ ОБОРУДОВАНИЕ 
           Тележки для транспортировки корзин с посудой 
           Тележки шпильки 
           Тележки для сбора посуды 
      ДИСПЕНСЕРЫ 
      КУХОННЫЕ АКСЕССУАРЫ 
      КОНВЕЕРНЫЕ СИСТЕМЫ 
      ТЕЛЕЖКИ С ИНДУКЦИОННЫМ ПОДОГРЕВОМ И ОХЛАЖДЕНИЕМ 
МЕДИЦИНСКОЕ ОБОРУДОВАНИЕ

При этом при каждом клике страница перезагружается, скрипту передаются какие-то параметры, например ID ссылки на которую кликнули и т.д.

Надеюсь понятно объяснил суть проблемы.
Готов даже приплатить чуваку, который решит мою проблему, написав такой скрипт smile

 

Автор: sharq 10.5.2006, 13:37
Arx, для твоей задачи могу посоветовать другой способ проектирования БД - Nested Sets. Вот http://phoinix.ucoz.ru/publ/5-1-0хорошие статейки на эту тему.

 smile  

Автор: GoodBoy 10.5.2006, 14:21
Arx, у меня этот швблон и взят из скрипта, который строит меню именно так, как ты описываешь!  только для тебя он (скрипт) будет бесполезен, т. к. там используется много сторонних модулей... 

Автор: nitr 10.5.2006, 14:24
Если всё же не хочешь БД, то можно хеши... и ссылки на них... Алгоритм был приведён в cookbook perl в разделе CGI smile 

Автор: levcom 10.5.2006, 15:07
levcom, есть рабочий скриптец? smile
Мозги закипают уже.

Замечу, что скрипт строит древовидное меню _и_ строит к нему ссылки в виде /category1/category2/.. в зависимости от вложенности. Само построение дерева проше.

Всего 5 функций:

Код


участвует при разборе запроса вытягивает непосредственно _nav_build
sub get_navigation_id
{
    my($self,$master)=@_;
    my $nav = $master->{query}->param("_nav_build") || "0";
    my @element = split /-/, $nav;
    return $element[-1];
}


##########################################################

Строит хэш (замечу, что есть несколько дополнительных полей в БД
name - отображаемое значение в меню (например, 'Для дома')
uri - название категории (например 'home')

sub build_menu
{
    my($self,$master)=@_;
    my $nav = $master->{query}->param("_nav_build") || "0";
    @element = split /-/, $nav;
    my $str = join " OR id_parent=", @element;

    my $sth=$master->{db}->get->prepare (qq{
    SELECT 
        id_category,
        id_parent,
        name,
        uri
    FROM navigation
    WHERE (id_parent=$str)
    ORDER BY sort DESC});
    $sth->execute();

    while (my $ref=$sth->fetchrow_arrayref())
    {
        $_ = $ind{$ref->[1]};
        push @{$_}, $ref->[0];
        $msg{$ref->[0]} = [($ref->[2], $ref->[3])];
        $ind{$ref->[1]} = $_;
    }

    $self->tree_menu($master, 0, "");
}

##########################################################
Строит дерево (эту функцию и вставлять в тело программы страницы)
    
sub tree_menu
{
    my($self,$master,$i)=@_;
    my $s;
    my $path=$_[3];

    if($i==0)
    {
        $path='';
    }
    else
    {
        $path=$path."/".$msg{$element[$i]}[1];
    }

    foreach $s (@{$ind{$element[$i]}})
    {
        print "<div><a href=\"$path/$msg{$s}[1]/\">$msg{$s}[0]</a>\n";
        if($s == $element[$i+1])
        {
            $self->tree_menu($master, $i+1, $path);
        }
        print "</div>"
    }
}


##########################################################
Строит хэш для файла .htaccess

sub build_test
{
    my($self,$master)=@_;
    my $sth = $master->{db}->get->prepare (qq{
    SELECT 
        id_category,
        id_parent,
        name,
        uri,
        script
    FROM navigation
    ORDER BY sort DESC});
    $sth->execute();

    while (my $ref = $sth->fetchrow_arrayref())
    {
        $_ = $ind{$ref->[1]};
        push @{$_}, $ref->[0];
        $msg{$ref->[0]} = [($ref->[2], $ref->[3], $ref->[4])];
        $ind{$ref->[1]} = $_;
    }

    open(CONFIG, "> $fileconfig") or die
     "<div>Error: $!\n</div>";
    select CONFIG;

    foreach $_(@config)
    {
        print "$_";
    }
    print "\n### \$levcom_generic\n";

    $self->tree_test($master, 0, "", "0");
    select STDOUT;
}
##########################################################
Вносит изменения в фаил .htaccess

sub tree_test
{
    my($self,$master)=@_;
    my $path = $_[3];
    my $num = $_[4];
    my $s;

    foreach $s (@{$ind{$_[2]}})
    {
        if($path eq "")
        {
            print "RewriteRule ^(.*)$msg{$s}[1]/\$ \$1$msg{$s}[2]?_nav_build=$num-$s\n";
            $self->tree_test($master, $s, $msg{$s}[1], $num."-".$s);
        }
        else
        {
            print "RewriteRule ^(.*)$path/$msg{$s}[1]/\$ \$1$msg{$s}[2]?_nav_build=$num-$s\n";
            $self->tree_test($master, $s, $path."/".$msg{$s}[1], $num."-".$s);
        }
    }
}
##########################################################
Запоминат все что в файле до раздела формируемого этим скиптом (этот раздел последний), 
после чего его исправляет

sub get_config_file
{
    my($self,$master)=@_;
    open(CONFIG, "< $fileconfig") or die
     "<div>Error: $!\n</div>";

    while(<CONFIG>)
    {
        if(/^### \$levcom_generic/)
        {
            return;
        }
        push @config, $_;
    }
    close CONFIG;
}


Это 6 функций из модуля Navigation.pm всего из 7. У меня на сайте работает.

Я пока еще только очень начинающий поэтому заранее буду благодарен любым замечаниям по коду и предложениям.
Сейчас переделываю этот механизм (при тойже структуре данных) с отказом от параметра _nav_build и поддержкой кэширования части меню.

Данный моду дополненый еще несколькими функциями состовляет довольно универсальную и простую систему публикации. (К сожаления, даже не представляю как подобные вещи устроены на других сайтах, поэтому реализация может показаться _глупой_)
 

Автор: nitr 10.5.2006, 15:11
Ну совет по кешу один, желательно кешировать то что либо не изменяется, либо очень редко smile
К примеру ТОВАР - данная таблица может меняться, как по ценнам, так и по количеству, или даже по составу (если БД не "накапливающая").
А вот ГОРОДА естественно не меняются ;) - можно кешировать... 

Автор: Arx 18.5.2006, 10:38

Готов заплатить 1000 рублей за готовый скрипт меню
Подробности в личку или на мыло (в профиле) 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)