Вывод дерева при помощи рекурсивного вызова -> как встроить в шаблоны Smarty

Гриша К.

Новичок
Вывод дерева при помощи рекурсивного вызова -> как встроить в шаблоны Smarty

Здравствуйте.
Я решил использовать меню с неограниченным уровнем вложенности для своего проекта в виде списков смежности (интернет-магазин, вообще сейчас требуется только 3 уровня вложенности).
За основу беру скрипты-примеры из http://phpclub.ru/faq/wakka.php?wakka=Tree&v=w5u#Demo.

Для вывода дерева в скрипте http://phpclub.ru/faq/wakka.php?wakka=Tree/DemoPrintFunction&v=113o и http://phpclub.ru/faq/wakka.php?wakka=Tree/DemoPrintTemplate&v=1n8 используется рекурсивный вызов.

Для содержания шаблонов я решил использовать Smarty.

Я незнаю как мне разделить логику и содержимое для вывода дерева используя рекурсивный вызов, если вы сталкивались с такой же проблемый и вы можете свободно предложить ваше решение, то напишите пожалуйста его.
Предположительные варианты решения:

(1) Каким-либо образом сделать вывод дерева без рекурсивного вызова?
(2) Использовать рекурсивный вызов в шаблонах Smarty. Как?
(3) Возможно стоит подключить php-скрипт непосредственно в шаблон Smarty, но смысл разделения логики и содержимого теряется.

PHP:
{php}
echo '<ul>';
  for($i=0;$i<count($a_tree);$i++) {
    echo '<li>';
      echo '$a_tree[$i]['s_name']';
      
        if(!empty($a_tree[$i]['a_tree']))
          tree_template_load($a_tree[$i]['a_tree']);
    echo '</li>';
  }
echo '</ul>'; 
{php}
 

sage

Новичок
Каким-либо образом сделать вывод дерева без рекурсивного вызова?
никак. в списках смежности без рекурсии не обойтись. Может быть стоит перейти на NS (Вложенные множества)?
 

Гриша К.

Новичок
Спасибо большое за ответы.
BEZZ, буду пробовать предложенный способ.

sage, я нашел в описание класса db.tree для работы с деревьями http://phpclub.ru/faq/wakka.php?wakka=Tree/NsLibPhpdbtree&v=1d2w пример вывода дерева без рекурсивного вызова.
NS сложный способ хранения и вывода деревьев. ОЧень неудобно удалять элементы дерева (дочерний элемент) и т.д.
Хотя со способом хранения дерева я окончательно не определился.
Но при использовании списков смежности, я по крайней мере думаю сам смогу сделать крипт для удаления, внесения и изменения элементов дерева (готовых стандартных спосоьов я не видел).
Поэтому поводу я думаю создам еще одну тему.
Пока буду пробовать вывести дерево списков смежности, с использованием шаблонов.

-~{}~ 14.05.06 16:47:

Никак немогу понять как при помощи {foreach} в Smarty сделать рекурсивный вызов или его подобие, так сказать:

Вот на этой странице http://phpclub.ru/faq/wakka.php?wakka=Tree/DemoPrintFunction&v=113o привидена небольшая функция, которая использует рекурсивный вызов (tree_print(&$a_tree))

Начал делать так, но как видите, чтобы добраться до каждого подпункта, я вручную вызываю цикл, а можноли сделать подругому?:
PHP:
<ul>
{foreach from=$a_tree item=i}
	<li>{$i.s_name}
		<ul>
		{foreach from=$i.a_tree item=i_1}
	 		<li>{$i_1.s_name}
				<ul>
				{foreach from=$i_1.a_tree item=i_2}
	 				<li>{$i_2.s_name}</li>
				{/foreach}
				</ul>
			</li>
		{/foreach}
		</ul>
	</li>
{/foreach}
</ul>
-~{}~ 14.05.06 18:15:
 

BEZZ

Новичок
убедительная прозьба читать мануал по смарти, там всё есть!

это конечно юмор:
<ul>
{foreach from=$a_tree item=i}
<li>{$i.s_name}
<ul>
{foreach from=$i.a_tree item=i_1}
<li>{$i_1.s_name}
<ul>
{foreach from=$i_1.a_tree item=i_2}
<li>{$i_2.s_name}</li>
{/foreach}
</ul>
</li>
{/foreach}
</ul>
</li>
{/foreach}
</ul>
а если серьёзно то - http://smarty.php.net/manual/ru/language.function.if.php
 

Гриша К.

Новичок
BEZZ, спасибо за ответ.
Я документацию по Smarty себе даже распечатал, Главу 7, я сейчас перечитывал 3 раз, информацию усваю нормально.

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

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

BEZZ

Новичок
что мешает всё вот это...
PHP:
echo '<ul>'; 
  for($i=0;$i<count($a_tree);$i++) { 
    echo '<li>'; 
      echo '$a_tree[$i]['s_name']'; 
       
        if(!empty($a_tree[$i]['a_tree'])) 
          tree_template_load($a_tree[$i]['a_tree']); 
    echo '</li>'; 
  } 
echo '</ul>';
...преписать с использованием смарти, все необходимые функции там реализованны
а лучше тут посмотри: http://www.phpinsider.com/php/code/SmartyMenu/
 

Гриша К.

Новичок
BEZZ, спасибо за ответ.

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

В шаблонизаторе вот эту часть кода я на что заменю:
PHP:
        if(!empty($a_tree[$i]['a_tree']))  
          tree_template_load($a_tree[$i]['a_tree']);
Вот так вот я попробовал. Других вариантов никаких ненахожу. Если вы действительно знаете, то подскажите пожалуйста, какуюже функцию применить (в данном случае вместо include), но в Smarty нельзя инклудить самого себя:

PHP:
<ul>
{foreach name=name from=$a_tree item=i}
	<li>
	{$i.s_name}
		{if !empty($i.a_tree)}
			{include file='menu_catalog.tpl' a_tree="$i.a_tree"}
		{/if}
	</li>
{/foreach}
</ul>
Подскажите пожалуйста, возможно ли или нет сделать рекурсивный вызов или подобие в шаблонах Smarty. Я попробовал все, что приходило в голову, но ни один вариант не подошел.
 

WP

^_^
За это время мог бы 10 раз решить проблему. Куча способов. 1. Метод _smarty_include по аналогии сделай свой. 2. register_block(). 3. Мазгами пашевели, дарагой.
 

WP

^_^
ns
Еще можно через eval, можно без рекурсии в шаблоне обойтись. Выбирай любой.
 

Гриша К.

Новичок
Спасибо всем за ответы.
Mich, спасибо за ссылки, но я уже находил их на форуме, и не понял как работать с этим модулем.
WP, спасибо за советы. У меня получилось сделать с использованием функции {eval} в шаблоне Smarty:

tree.php
PHP:
$a_tree = array(); // Массив содержащий дерево
$menu_catalog = file_get_contents('./templates/menu_catalog.tpl');
 
$smarty->assign('a_tree',$a_tree);
$smarty->assign('tree_tpl',$menu_catalog);
tree.tpl
PHP:
<ul>
{foreach name=name from=$a_tree item=i}
	<li>{$i.s_name}
		{if !empty($i.a_tree)}
			{assign var="a_tree" value=$i.a_tree}
			{eval var=$tree_tpl}
		{/if}
	</li>
{/foreach}
</ul>
Скажите пожалуйста, нормален ли и приемлем ли такой вариант?
И если возможно вы увидите, что желательно изменить в данном примере, напишите пожалуйста.
 

Mich

Продвинутый новичёк
Самый правильный и рекомендуемый разработчиками способ - через http://me.ssju.de/compiler.defun/ . Что там непонятного?

Зачем извращаться с eval? Почему не include?
 

Гриша К.

Новичок
Mich, спасибо за ответ.
У меня получилось установить данный плагин и использовать в шаблоне рекурсивный вызов.
Путь для установки плагина для рекурсивного вызова в шаблонах Smarty: /Smarty/libs/plugins/compiler.defun.php

tree.tpl
PHP:
{* $a_tree - массив с деревом, a_tree - элемент массива *}

{defun name="testrecursion" list=$a_tree}
<ul>
{foreach from=$list item=i} 
<li>{$i.s_name} 
   {if !empty($i.a_tree)} 
   {fun name="testrecursion" list=$i.a_tree} 
   {/if}
</li> 
{/foreach} 
</ul>
{/defun}
 

jenia

Новичок
А сейчас кто-то знает, где взять указанный выше плагин? Раньше он находился по адресу http://me.ssju.de/compiler.defun/ , но сейчас этот адрес не доступен.
 

Гриша К.

Новичок
Файл: /Smarty/libs/plugins/compiler.defun.php
С официальными дополнениями для корректной работы на PHP5 (проверено)

PHP:
<?php

/* create code for a function call */
function smarty_compiler_fun($tag_args, &$compiler) {
    $_attrs = $compiler->_parse_attrs($tag_args);

    if (!isset($_attrs['name'])) $compiler->_syntax_error("fun: missing name parameter");
    $_func_name = $compiler->_dequote($_attrs['name']);
    $_func = 'smarty_fun_'.$_func_name;
    $_params = 'array(';
    $_sep = '';
    unset($_attrs['name']);
    foreach ($_attrs as $_key=>$_value) {
        $_params .= "$_sep'$_key'=>$_value";
        $_sep = ',';
    }
    $_params .= ')';
    return "$_func(\$this, $_params); ";

}
$this->register_compiler_function('fun', 'smarty_compiler_fun');


/* create code for a function declaration */
function smarty_compiler_defun($tag_args, &$compiler) {   
    $attrs = $compiler->_parse_attrs($tag_args);
    $func_key = '"' . md5('php-5') . '[[' . md5(uniqid('sucks')) . '";';
    array_push($compiler->_tag_stack, array('defun', $attrs, $tag_args, $func_key));
    if (!isset($attrs['name'])) $compiler->_syntax_error("defun: missing name parameter");

    $func_name = $compiler->_dequote($attrs['name']);
    $func = 'smarty_fun_'.$func_name;
    return $func_key . "if (!function_exists('$func')) { function $func(&\$this, \$params) { \$_fun_tpl_vars = \$this->_tpl_vars; \$this->assign(\$params); ";

}
    

/* create code for closing a function definition and calling said function */
function smarty_compiler_defun_close($tag_args, &$compiler) {
    list($name, $attrs, $open_tag_args, $func_key) = array_pop($compiler->_tag_stack);
    if ($name!='defun') $compiler->_syntax_error("unexpected {/defun}");
    return " \$this->_tpl_vars = \$_fun_tpl_vars; }} " . $func_key . smarty_compiler_fun($open_tag_args, $compiler);
}
$this->register_compiler_function('/defun', 'smarty_compiler_defun_close');


/* callback to replace all $this with $smarty */
function smarty_replace_fun($match) {
    $tokens = token_get_all('<?php ' . $match[2]);

    /* remove trailing <?php */
    $open_tag = '';
    while ($tokens) {
        $token = array_shift($tokens);
        if (is_array($token)) {
            $open_tag .= $token[1];
        } else {
            $open_tag .= $token;
        }
        if ($open_tag == '<?php ') break;
    }

    /* replace */
    for ($i=0, $count=count($tokens); $i<$count; $i++) {
        if (is_array($tokens[$i])) {
            if ($tokens[$i][0] == T_VARIABLE && $tokens[$i][1] == '$this') {
                $tokens[$i] = '$smarty';
            } else {
                $tokens[$i] = $tokens[$i][1];
            }
        }
    }
    return implode('', $tokens);
}


/* postfilter to squeeze the code to make php5 happy */
function smarty_postfilter_defun($source, &$compiler) {
    $search = '("' . md5('php-5') . '\[\[[0-9a-f]{32}";)';
    if ((double)phpversion()>=5.0) {
        /* filter sourcecode. look for func_keys and replace all $this
           in-between with $smarty */
        while (1) {
            $new_source = preg_replace_callback('/' . $search . '(.*)\\1/Us', 'smarty_replace_fun', $source);
            if (strcmp($new_source, $source)==0) break;
            $source = $new_source;
        }
    } else {
        /* remove func_keys */
        $source = preg_replace('/' . $search . '/', '', $source);
    }
    return $source;
}
$this->register_postfilter('smarty_postfilter_defun');


?>
 

ustas

Элекомист №1
главное что бы мучатся дольше,
одной функцией array_walk_recursive массив можно в простой сохранить, добавив одно поле - глубина. Потом выводить. Без рекурсии и идиотских наворотов, тема уже обсуждалась.
 
Сверху