Smarty: Как положить возврат из зарегистрированной PHP-функции в Smarty-переменную?

xintrea

Новичок
Smarty: Как положить возврат из зарегистрированной PHP-функции в Smarty-переменную?

Здравствуйте!

Вопрос по Smarty, только начал разбираться с ним.

Ситуация: есть PHP-функция, которая возвращает ассоциативный массив.
(В реальной функции массив конструируется из базы)

PHP:
function php_get_menu_array()
{
 $menu[1]['name']='Главная';
 $menu[1]['url']='index.php';

 $menu[2]['name']='Новости';
 $menu[2]['url']='news.php';

 $menu[3]['name']='Гостевая';
 $menu[3]['url']='guestbook.php';

 return $menu;
}
Эта функция зарегистрирована в объекте Smarty.

PHP:
$smarty->register_function('smt_get_menu_array','php_get_menu_array');
Вопрос. Как в шаблоне получить возврат из этой функции в Smarty-переменную? То есть, я хочу иметь в Smarty-переменной ассоциативный массив.

Пробую так: {assign var='menu' value={smt_get_menu_array}}

Но вместо массива переменная $menu остается пустой, а в браузер на месте такой конструкции появляется одна скобка "}" и все. Как же положить возвращаемый массив из PHP-функции в Smarty-переменную?


PS: Пожалуйста, не советуйте делать внутри PHP-функции конструкцию
smarty->assign('menu',$menu); Если так сделать, то нужно и в шаблоне
и в PHP-коде постоянно синхронизироваться - какие переменные
заполняются в PHP-коде, и соответственно использовать их в шаблоне.
Такой подход лишает основных преимуществ использования шаблонизатора.
 

Mich

Продвинутый новичёк
Вместо
PHP:
$smarty->register_function('smt_get_menu_array','php_get_menu_array');
PHP:
$smarty->assign('menu', php_get_menu_array());
то нужно и в шаблоне и в PHP-коде постоянно синхронизироваться - какие переменные заполняются в PHP-коде, и соответственно использовать их в шаблоне.
Ага, а также надо смотреть какие функции определены в PHP, под каким именем зарегистрированы в Smarty и использовать их в шаблоне. Тоже проблема.
 

xintrea

Новичок
В общем, я так понял, чтобы положить результат PHP функции в Smarty-переменную, нужно ее вызывать например таким синтаксисом:

{smt_get_menu_array var='menu'} - Smarty-переменной $menu присвоится массив с пунктами меню.

{smt_get_menu_array var='n'} - Smarty-переменной $n присвоится массив с пунктами меню.

и. т. д.


А PHP-функции всегда делать по такому принципу:

PHP:
function php_get_menu_array($params, &$smarty)
{
 // Обязательная команда для парсинга переданных из Smarty переменных
 extract($params); 

 $menu[1]['name']='Главная';
 $menu[1]['url']='index.php';

 $menu[2]['name']='Новости';
 $menu[2]['url']='news.php';

 $menu[3]['name']='Гостевая';
 $menu[3]['url']='guestbook.php';

 // Запись в Smarty-переменную результата
 $smarty->assign($var,$menu);

 return;
}
Вот. Так конечно работает, но что-то не очень красиво все выглядит...

Может быть всеже есть способ вызывать обычные PHP-функции (с любой "конструкцией" и возвратом результата через return) и принять результат в Smarty-переменную?
 

jonjonson

Охренеть
xintrea, вообще-то я дал пример как массив из темплейта грузить. Функция в данном контексте, нечто универсальное, расширяющее функционал, а не выплёвывающее непонятные данные. Как минимум идеологически не красиво.

Информация о меню обычно используется ещё в контроллере, посему Mich предложил обычный вариант передачи массива в темплейт.

Теперь мне не понятно ваше понимание красоты... Данные относящиеся к приложению должны быть в элементах приложения, но ни как не в API. API наплевать какой массив данных вы ему передадите.
 

xintrea

Новичок
вообще-то я дал пример как массив из темплейта грузить
Да, но я спрашивал как массив из PHP-кода грузить а не из темплейта.

посему Mich предложил обычный вариант передачи массива в темплейт
Mich предложил обычный вариант передачи массива в темплейт в жестко заданную переменную, имя которой задается в PHP коде, а не в темплейте. А это как раз жестко связывает PHP-код и темплейт. Нам же нужно чтоб PHP-код был оторван от темплейта и не думал, в какую темплейт-переменную размещать данные.


В принципе я уже придумал как можно вызывать совершенно произвольные PHP-функции из темплейта и ложить результат в заданную темплейтом переменную.

Можно написать примерно такой плагин

PHP:
function run_php_func($params, &$smarty)
{
 // Обязательная команда для парсинга переданных из Smarty переменных 
 extract($params);

 // Составление строки параметров
 $i=0;
 foreach($params as $key => $value)   
  if(strcmp(substr($key,0,3),'par')==0) {   
   $paramsarray[$i]='$'.$key;
   $i++;
 }
 if(isset($paramsarray))$paramsline=implode(",",$paramsarray);
 else $paramsline='';

 // Вызов PHP функции
 $cmd='$result='.$func.'('.$paramsline.');';
 eval($cmd);

 $smarty->assign($var,$result);
 return;
}
А вызывать PHP-функцию через этот плагин можно так
Код:
{run_php_func var='v' func='anyfunc' par1=$i par2=100 par3='hello'}
где

var - переменная куда будет ложиться результат
func - имя PHP функции
par1...parN - параметры

Но что-то мне подсказывает что я изобретаю велосипед (причем весьма тормозной и уродливый ибо присутствует нехороший eval), а в Smarty есть более простой способ сделать то же самое. Вопрос - есть ли такой способ.
 

jonjonson

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

И правда, нахрена городить кучу кода, что бы инициализировать обычный массив? Темплейт служит для отображения данных, а не генерации их. Если же данные связаны только с шаблонами и больше нигде не используются, то засуньте их в конфиг смарти (впрочем там тоже массивы кажется не любят).

Я дал вам ссылку по инициализации массивов в смарти. Но по мне это всё выкобенивания и пустая трата времени и сил ради абстракции, которая может легко быть заменена договорённостью.
 

Mich

Продвинутый новичёк
xintrea, не хотите слушать - это ваши проблемы ;) Но все-таки советую почитать http://forum.dklab.ru/php/book/NewShablonizatorWithLanguageOfPatternsSmarty.html

От eval поможет избавиться call_user_func_array() .
 

xintrea

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

И правда, нахрена городить кучу кода, что бы инициализировать обычный массив? Темплейт служит для отображения данных, а не генерации их. Если же данные связаны только с шаблонами и больше нигде не используются, то засуньте их в конфиг смарти (впрочем там тоже массивы кажется не любят).

Я дал вам ссылку по инициализации массивов в смарти. Но по мне это всё выкобенивания и пустая трата времени и сил ради абстракции, которая может легко быть заменена договорённостью.
Почитайте пожалусто эту наблу http://dklab.ru/chicken/nablas/16.html.
У меня массив меню берется из базы. Для этого в используемой мной CMS есть пункты "Добавить пункт меню", "Редактировать пункт меню" и. т. д. Посему мне нужно инициализация массивов в PHP для смарти, а не инициализация массива в самом темплейте.

Mich не хотите слушать - это ваши проблемы Но все-таки советую почитать http://forum.dklab.ru/php/book/NewShablonizatorWithLanguageOfPatternsSmarty.html

От eval поможет избавиться call_user_func_array().
Именно это я и делаю. Использую концепцию активных шаблонов, в которой шаблон первичен, а PHP-функции используются только как генераторы данных.

За функцию спасиба, это то что нужно! Думаю новая версия run_php_func() будет работать без особых тормозов.

PHP:
function run_php_func($params, &$smarty)
{
 // Обязательная команда для парсинга переданных из Smarty переменных 
 extract($params);

 // Составление массива параметров
 foreach($params as $key => $value)   
  if(strcmp(substr($key,0,3),'par')==0)
   $paramsarray[$key]=$value;

 // Вызов PHP функции
 $result=call_user_func_array($func,$paramsarray);

 $smarty->assign($var,$result);
 return;
}
Странно что в Smarty для таких вещей нет готового функционала.
 

hermit_refined

Отшельник
xintrea
если гонитесь за эффективностью - http://smarty.php.net/manual/ru/plugins.compiler.functions.php
Странно что в Smarty для таких вещей нет готового функционала.
встроенный assign есть у зарегистрированных объектов.
правда, они могут доставлять другие неудобства.

(но зарегистрированные/присвоенные объекты - в любом случае более правильное решение, чем функции.)
 

jonjonson

Охренеть
Почитайте пожалусто эту наблу http://dklab.ru/chicken/nablas/16.html.
1) Fatal: /chicken/nablas/16.html.: could not find handlers chain for extension ""
2) Fatal: /chicken/nablas/16.html.: could not find handlers chain for extension ""
BEGIN failed--compilation aborted at /home/dklab/domains/www/_Kernel/Scriptor.pl line 26.

У меня массив меню берется из базы. Для этого в используемой мной CMS есть пункты "Добавить пункт меню", "Редактировать пункт меню" и. т. д. Посему мне нужно инициализация массивов в PHP для смарти, а не инициализация массива в самом темплейте.
Тогда тем боле функция ваша полный бред. assign из контроллера самый лучший вариант.
З.Ы. Вы ещё для каждой таблицы вывода данных по функции в смарти отпишите :)
А в первом примере по функции смарти вы вообще лжёте, ибо массив у вас там определяется ручками, да ещё и с ворнингом, ибо $menu не определено.
 

xintrea

Новичок
Да, да я лгу направо и налево быгыгы. В первом примере просто статически забивается массив, потому что это пример. Пример, понимаете? Вы же продвинутый новичок, должны понимать. Вы же продвинутый, ага? Там еще есть приписка что в реальной функции массив конструируется из базы. Вроде по-русски написано. Продвинутые парни, хоть и новички, по-русски вроде умеют четать. А может вы никакой не продвинутый, а просто новичок? Тогда вы лжете всей общественности PHP-клуба! Стыдитесь!
 

jonjonson

Охренеть
в реальной функции массив конструируется из базы.
А вот тут стыдиться мне нечего.
0. Продвинутый новичок - это звание присваиваемое самим форумом автоматически после некоторого количества постов, так что ваше обвинение, мягко скажем замечание не о чём. =))
1. Шаблоны потому и шаблоны, что в них не должно быть никаких обращений к базе данных. То что вы пытаетесь сделать - бред душевнобольного, не понимающего смысл темплейта. Ружьё в руках дикаря - это просто палка. :)
2. Да я повёлся на ваш код, так как код на этом форуме требуется давать хоть минимальный, но реальный. Вы выдали фейк и ввели меня в заблуждение.

После этого общения любая помощь относительно вас бесполезна, так как у вас проблемы личного характера :)
 

hermit_refined

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

jonjonson

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

hermit_refined

Отшельник
активность шаблонов должна строиться на контролах, которые подписываются на события в контроллере и обновляются из него, до отображения темплейта
вы тут ссылаетесь на классическую MVC, которая в web неприменима.
если все данные формируются до обработки шаблона - это не активный шаблон.
или поясните на примере, что вы имеете в виду.
Пихать же вызовы БД в отображение теплейта не нужно и бредово
у него нет никаких обращений к бд в шаблоне.
у него есть вызов функции. что происходит внутри этой функции - шаблона не касается.
 

jonjonson

Охренеть
вы тут ссылаетесь на классическую MVC, которая в web неприменима.
если все данные формируются до обработки шаблона - это не активный шаблон.
или поясните на примере, что вы имеете в виду.
Нет активного шаблона - есть активный вид (view). View - это больше чем шаблон.

Активный View проходит через несколько событий:
- инициализация
- регистрация контролов
- обработка событий, вызванных контролами
- рендеринг (отображение)
- подчистка

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

При "обработке событий, вызванных контролами" вызываются методы презентера связанные с событиями, а в них методы модели. Данные возвращаемые моделью передаются контролам.
И только после обработки всех событий осуществляется рендеринг.

Реализация в ASP.NET набросана тут http://rsdn.ru/article/patterns/ModelViewPresenter.xml
Ссылку я давал, но никто ей не воспользовался.

В смарти же функции выполняются по мере рендеринга и отдачи страницы. Никакого активного шаблона в данном случае нет. Функция смарти это часть шаблона и не применима вне его. Она не связана с другими событиями.
 

hermit_refined

Отшельник
jonjonson, я умные слова тоже знаю ;-) можно поспорить насчет терминологии, но это неважно (в случае web дословная реализация этих паттернов невозможна, и различие между ними становится несущественным).

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

P.S. остается, однако, непонятным - почему в приведенном вами коде шаблон подключает контроллер.
 

jonjonson

Охренеть
hermit_refined, ну вот, умные слова вас раздражают... ;-)
Кстати я с актип и песив view накасячил... :-\
Реч нужно было вести о Passive Model и Active Model...
http://www.phpwact.org/pattern/model_view_controller#model
Именно в Active Model компоненты View должны цепляться к Model.

Следовательно в начале фронт контроллер, выбирающий view.
Далее view цепляет обслуживающий его контроллер или контроллер цепляет view. Не суть важно. Они взаимосвязаны. :)

По последнему вопросу. Там на самом деле скорее схематический вариант для автора темы (возможно и спорный).
И что что вызывает там не самое главное.
По текущему обсуждению могу предложить простецкий такой код (фронт контролеер опущен):
PHP:
<?php
class View {
    var $message = "";
    var $action  = "new_action";

    function display() {
        ?><head><title>Test</title></head><body><?
        $this->displayMessage();
        $this->displayForm();
        ?></body><?
    }

    function setMessage($message) {
        $this->message = $message;
    }

    function displayMessage() {
        if (!empty($this->message)) {
            ?><div><?=$this->message?></div><?
        }
    }

    function displayForm() {
        ?><form action="" method="post"><?
        ?><input type="hidden" name="action" value="<?=$this->action?>" /><?
        ?><input type="submit" value="Go" /><?
        ?></form><?
    }

    function isAction()
    {
        if (isset($_POST["action"]) 
            && $this->action == $_POST["action"]) {
            return true;
        }
        return false;
    }
}

class Controller {
    var $view;

    function Controller(&$view) {
        $this->_view =& $view;
    }

    function onAction() {
        $this->view->setMessage("Action!");
    }

    function process() {
        if ($this->view->isAction()) {
            $this->onAction();
        }
    }
}

$view =& new View();
$controller =& new Controller($view);
$controller->process();
$view->display();
?>
 
Сверху