Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > PHP: Общие вопросы > Оцените скрипт построения списка-дерева


Автор: LittleFuntik 13.6.2009, 17:47
Захотел сам, не смотря ни на какие исходники написать скрипт, формирующий список-дерево. Не пугайтесь кода! Он очень шустрый. Я попытался сделать без классов и глобальных переменных.

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

Код

function tree_ul(&$a,$pId=0,$next=false){
    if($next==false){$_r = $a;}
    foreach($a as $i => $v){
        if( isset($v) && $pId == $v['pId'] ){
            $r .= '<li>';    
            $r .= $v['title'];
            $Id = $v['Id'];
            unset($a[$i]);
            $r .= tree_ul($a,$Id,true);
            $r .= '</li>'; 
        }
    }
    if( $next==false && sizeof($a) > 0 ){
        foreach($a as $v){
            $r .= '<li>';    
            $r .= $v['title'];
            $Id = $v['Id'];
            unset($a[$i]);
            $r .= tree_ul($a,$Id,true);
            $r .= '</li>'; 
        }
    }
    if( trim($r) != '' ){
        $r = '<ul>'.$r.'</ul>';
    }
    if($next==false){$a = $_r;}
    return $r;
}


Эту функцию легко использовать, например этот скрипт возвратит такой результат:

Код

$r[0] = array('Id' => 4,'pId' => 2,'title' => '111');
$r[1] = array('Id' => 5,'pId' => 4,'title' => '222');
$r[2] = array('Id' => 6,'pId' => 100,'title' => '333');
$r[3] = array('Id' => 7,'pId' => 6,'title' => '444');
$r[4] = array('Id' => 1,'pId' => 0,'title' => 'Книги для велосипедиста');
$r[5] = array('Id' => 2,'pId' => 1,'title' => 'Нужные');
$r[6] = array('Id' => 3,'pId' => 1,'title' => 'Свалка книг');
echo tree_ul($r);


Видим такое:
http://img193.imageshack.us/i/111oav.jpg/


Заметьте, что ветки, в которых родитель не нашелся, тоже отображаются, но в конце. И еще, если эти ветки имеют потомков, то они выводятся, как потомки.

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

Код

function tree_ul_new(&$a,&$tpl=null,$pId=0,$next=false,&$lvl=0,&$li_number=0){
    $lvl++;
    
    if( !$tpl['default'] ){$tpl['default']=array('ul'=>'<ul>','/ul'=>'</ul>','li'=>'<li>{li_number}) {title}','/li'=>'</li>');}
    
    if($next==false){$_r = $a;}
    foreach($a as $i => $v){
        if( $pId == $v['pId'] ){
            $Item = $v;
            $childrens = 0;
            foreach($a as $k1 => $v1){if($v1['pId']==$a[$i]['Id']){$childrens++;}}
            if( trim($tpl[$i]) != '' ){ $t = $tpl[$i];}else{$t=$tpl['default'];}
            if( $childrens > 0 ){
                if( isset($t['li_']) ){$t['li']=$t['li_'];}
                if( isset($t['/li_']) ){$t['/li']=$t['li_'];}
            }
            $li_number++;
            $r1 = $t['li'];
            foreach($Item as $k1 => $v1){ $r1 = str_replace('{'.$k1.'}',$v1,$r1); }
            $r1 = str_replace(array('{lvl}','{childrens}','{li_number}'),array($lvl,$childrens,$li_number),$r1);
            
            $r .= $r1;
            unset($a[$i]);
            $r .= tree_ul_new($a,&$tpl,$Item['Id'],true,$lvl,$li_number);
            
            $r1 = $t['/li'];
            foreach($Item as $k1 => $v1){ $r1 = str_replace('{'.$k1.'}',$v1,$r1); }
            $r1 = str_replace(array('{lvl}','{childrens}','{li_number}'),array($lvl,$childrens,$li_number),$r1);
            
            $r .= $r1;
        }
    }
    if( $next==false && sizeof($a) > 0 ){
        foreach($a as $i => $v){
            if( trim($tpl[$i]) != '' ){ $t = $tpl[$i];}else{$t=$tpl['default'];}
            
            $childrens = 0;
            foreach($a as $k1 => $v1){if($v1['pId']==$a[$i]['Id']){$childrens++;}}
            
            $li_number++;
            $r1 = $t['li'];
            foreach($v as $k1 => $v1){ $r1 = str_replace('{'.$k1.'}',$v1,$r1); }
            $r1 = str_replace(array('{lvl}','{childrens}','{li_number}'),array($lvl,$childrens,$li_number),$r1);
            
            $r .= $r1;
            unset($a[$i]);
            $r .= tree_ul_new($a,&$tpl,$v['Id'],true,$lvl,$li_number);
            
            $r1 = $t['/li'];
            foreach($v as $k1 => $v1){ $r1 = str_replace('{'.$k1.'}',$v1,$r1); }
            $r1 = str_replace(array('{lvl}','{childrens}','{li_number}'),array($lvl,$childrens,$li_number),$r1);
            
            $r .= $r1;
        }
    }
    if( trim($r) != '' ){ $r = $tpl['default']['ul'].$r.$tpl['default']['/ul']; }
    if($next==false){$a = $_r;}
    $lvl--;
    return $r;
}


А вот пример использования:

Код

$ro[0] = array('Id' => 4,'pId' => 2,'title' => '111');
$ro[1] = array('Id' => 5,'pId' => 4,'title' => '222');
$ro[2] = array('Id' => 6,'pId' => 100,'title' => '333');
$ro[3] = array('Id' => 7,'pId' => 6,'title' => '444');
$ro[4] = array('Id' => 1,'pId' => 0,'title' => 'Книги для велосипедиста');
$ro[5] = array('Id' => 2,'pId' => 1,'title' => 'Нужные');
$ro[6] = array('Id' => 3,'pId' => 1,'title' => 'Свалка книг');

$z['default']=array('ul'=>'<ul>','/ul'=>'</ul>','li'=>'<li>{li_number}) {title} (уровень_списка={lvl}) Id={Id}','/li'=>'</li>');
$z['1']['/li']='<hr /></li>';
$z['2']['li']='<li>{title} (кол-во потомков: {childrens}) Id={Id}';

        echo tree_ul_new($ro,$z);
    }
}


Результат выполнения:
http://img199.imageshack.us/i/111ncb.jpg/

Здесь в шаблонах можно указывать дополнительные переменные:
 - {li_number} текущий номер элемента, начиная считать с первого;
 - {childrens} количество потомков в текущего элемента;
 - {lvl} уровень вложенности текущего элемента списка.

$z['2']['li'] ...
 - 'li' - указываем шаблон для 2-го элемента списка, который не имеет потомков;
 - 'li_' - указываем шаблон для 2-го элемента списка, который имеет потомков. Если шаблон не указать, то используется предыдущий шаблон;

$z['default'] - здесь храним стандартный шаблон. Вот так вот.

Хочу узнать свой уровень программирования по 100 бальной шкале smile

...
Подправил, была проблема с $childrens в неопределенных(нижних) элементах(смотрите 2-ю картинку "потомков: 2).

Автор: bars80080 14.6.2009, 09:35
ну да, а в профи как раз такие вопросы обсуждают и дают очки, как на шоу славы

работает? не тормозит? делает то что тебе нужно?
ну и славненько

Автор: ksnk 14.6.2009, 11:31
Хочется добавить, что в пятницу (и в эту,по инерции), народ еще более-менее активно посещает форум, а субботу-воскресенье - традиционно отдыхает.

Ну а по сути, шаблон - это, как правило, отдельная вещь (отдельный файл), которая(ый) в общем случае и пишется даже отдельным человеком. Мне сложно представить как бы я смог объяснить верстальщику, что при верстке именно вот этого участка нужно лезть куда-то в код и писать какие-то странные строчки на php.

Можно посмотреть примерный синтаксис какого-нибудь шаблонизатора (Cмарти, к примеру и для контраста http://codeigniter.com/user_guide/libraries/parser.html) и написать шаблон по принятым у верстальщиков правилам. Потом попробовать, как массив влезает в этот шаблон...

Это я , собственно, про то, что вторая часть просто лишняя...

Автор: MoLeX 15.6.2009, 06:04

M
MoLeX



Модератор: Автор какой смысл было размещать эту тему в разделе РНР: Профи?




Добавлено через 11 секунд
перенесено

Автор: s0lman 11.8.2009, 14:41
В общем, мне тут нечто подобное понадобилось, отчего наткнулся в поиске на это творение.
Вот только никок не пойму - как у него выполняется рекурсивный подъем? (вторую ф-ю не смотрел)
И не могу предположить, как у него получился вывод "Свалка книг" - это код корявый, или у меня руки кривые? ;)

Автор: s0lman 12.8.2009, 00:04
В общем, дома код заработал. Тут же подключился к рабочей машине, коды перекинул - как и раньше: выполняется до момента, когда нужно подниматься на уровень выше, т.е. на http://img193.imageshack.us/i/111oav.jpg/ примере последняя выводимая запись будет 222.
У кого есть какие соображения по этому поводу?

Автор: SneG0K 12.8.2009, 00:13
LittleFuntik, а как по мне, так круче будет брать код из XML
Код

function generateMenu($xmlFile) {
    if (!$xml = simplexml_load_file($xmlFile)) 
        generate_error("Ошибка при инициализации");
        
    $menu = array();    
    foreach ($xml->cat as $cats) {
        $tmp = array();
        $tmp['title'] = (string) $cats->attributes()->title;
        $tmp['link'] = (string) $cats->attributes()->link;
        $tmp['id'] = (string) $cats->attributes()->id;
        
        $tmp['body'] = array();    
        foreach ($cats->sub as $body) {
            $sub_tmp = array();
            $sub_tmp['id'] = (string) $body->attributes()->id;
            $sub_tmp['link'] = (string) $body->attributes()->link;
            $sub_tmp['title'] = (string) $body->attributes()->title;            
            $tmp['body'][] = $sub_tmp;
        }
        $menu[] = $tmp;
    }
        return $menu;
}


Код

<?xml version="1.0" encoding="utf-8"?>
<menu>
    <cat id="0" link="#" title="О компании">
        <sub id="0" link="#" title="Ссылка 1" />
        <sub id="1" link="#" title="132" />
    </cat>
    <cat id="1" link="#" title="Еще что-то">
        <sub id="0" link="#" title="12423" />
        <sub id="1" link="#" title="123142" />
    </cat>
</menu>


А круче этого только яйца.

Добавлено через 1 минуту и 51 секунду
Цитата(LittleFuntik @  13.6.2009,  16:47 Найти цитируемый пост)
Хочу узнать свой уровень программирования по 100 бальной шкале

Садись! два балла ^_^

Автор: Ипатьев 12.8.2009, 09:30
Не очень понял, зачем автор повторяет много раз про шустрость алгоритма.  Вроде бы, разбор небольшого массива никогда не был узким местом, как его не делай.
А других достоинств я в коде не вижу. 

Лично я бы сделал в два этапа. Сначала получил дерево, а потом развернул его в плоский массив.
чтобы в массиве было что-то вроде
open
item
item
open
item
close
close
item
close

И уже этот массив передавать шаблонизатору.

Добавлено @ 09:35
Учитывая, что вся относительная "сложность" алгоритма состоит в необходимости закрывающего тега, то имеет смысл подумать о другом варианте визуализации. Не списками, а, скажем, отступами. Тогда массив получится гораздо проще, и в нем нужно будет указывать только уровень вложенности элемента.

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