Smarty: {include} i widoczność zmiennych

11 lutego 2008

Smarty logoDo projektu nad którym teraz siedzę specjalnie wybrałem system szablonów Smarty bo wiedziałem, że będę miał przeróżne dziwaczne potrzeby a Smarty jest bardzo rozbudowane i nie powinno być problemów ze spełnieniem moich zachcianek. Dlatego bardzo byłem zaskoczony gdy się okazało, że wszystko wywala się na banalnym problemie...

Smarty pozwala w jednym szablonie dołączyć inny szablon. Służy do tego komenda {include}. Działa to tak samo jak include w php. A tak przynajmniej do dzisiaj sądziłem...

Prosty przykład użycia {include}:

Plik index.tpl

SMARTY:
  1. Jakiś tekst, html etc.
  2. {include file='test.tpl'}
  3. reszta dokumentu

W ten prosty sposób w miejscu użycia {include} pojawia się zawartość szablonu test.tpl.

Pójdźmy dalej. Załóżmy, że chcemy przekazać do test.tpl jakąś zmienną. Można to osiągnąć przy pomocy samej komendy {include} (zapraszam do dokumentacji) ale skoro to ma działać tak samo jak w php to powinno zadziałać coś takiego:

Plik index.tpl

SMARTY:
  1. {assign var='zmienna' value='wartość zmiennej'}
  2. {include file='test.tpl'}

Plik test.tpl

SMARTY:
  1. {$zmienna}

Krótkie objaśnienie. W pliku index.tpl inicjujemy zmienną $zmienna o zadanej wartości, następnie dołączamy szablon test.tpl i wyświetlamy w nim tą zmienną. Działa? Działa ;)
A więc już wiemy, że w includowanych szablonach są widoczne zmienne wcześniej zainicjowane.

A teraz odwrotny przykład:

Plik index.tpl

SMARTY:
  1. {include file='test.tpl'}
  2. {$zmienna}

Plik test.tpl

SMARTY:
  1. {assign var='zmienna' value='wartość zmiennej'}

W pliku index.tpl dołączamy szablon test.tpl a w nim inicjujemy zmienną $zmienna. Następnie, już po zaincludowaniu szablonu, chcemy wyświetlić zmienną $zmienna, która przecież przed chwilą została zainicjowana. I tu niestety pojawia się problem... Zmienna z pliku test.tpl NIE została przekazana do pliku index.tpl. Nic nam się nie wyświetli.

Niestety jest to dość istotna różnica między {include} w smarty a include w php. Jest to zresztą wyraźnie zaznaczone w dokumentacji czego nie dostrzegłem wcześniej. Co gorsza, nie znalazłem żadnego rozsądnego rozwiązania tego problemu w Internecie. Ludzie kombinują na wiele sposobów ale wszystkie są mało praktyczne. Ja się jednak nie poddałem tak łatwo, musiało to zadziałać bo inaczej rozwalała mi się cała moja koncepcja :)

Powód takiego a nie innego zachowania smarty znalazłem w skompilowanym szablonie. Wygląda to tak:

PHP:
  1. <?php $_smarty_tpl_vars = $this->_tpl_vars;
  2. $this->_smarty_include(array('smarty_include_tpl_file' => "test.tpl", 'smarty_include_vars' => array()));
  3. $this->_tpl_vars = $_smarty_tpl_vars;
  4. unset($_smarty_tpl_vars);
  5. ?>

Widać jak na dłoni, że twórcy smarty specjalnie tak to skonstruowali aby zmienne stworzone w includowanym pliku nie były widoczne na zewnątrz.

Rozwiązanie jest proste chociaż bardzo mi się nie podoba bo wymaga modyfikacji smarty. Ale chyba nie ma innego wyjścia.

Otwieramy plik Smarty_Compiler.class.php i szukamy takiej linijki:

PHP:
  1. "\$this->_tpl_vars = \$_smarty_tpl_vars;\n" .

W mojej wersji smarty (w tej chwili najnowsza, 2.6.18) jest to linijka numer 1003. Zamieniamy ją na to:

PHP:
  1. "\$this->_tpl_vars = array_merge(\$this->_tpl_vars, \$_smarty_tpl_vars);\n" .

Ot zwykłe połączenie tablic. Oba rozwiązania (to standardowe i to moje) mają oczywiście swoje plusy i minusy. To co mnie tak zirytowało zapewne wielu programistów bardzo cieszy. Wszystko zależy od potrzeb. Moim skromnym zdaniem powinno to być jakoś konfigurowalne, najlepiej z poziomu komendy {include}. Wprowadzenie takiego rozwiązania byłoby banalne a oszczędziłoby nie jednej osobie wielu nerwów. A tak ja teraz będę musiał pamiętać o modyfikacji przy każdej aktualizacji smarty... :/

Dodaj komentarz

6 odpowiedzi dla tego wpisu

  1. macem napisał:

    Ale w ten sposób bedzie to nieczytelne, pózniej będziesz się zastanawiał skąd ta zmienna się wzięła i szukał po ‘zaincludowanych’ plikach.

  2. MariuszT napisał:

    Tak jak napisałem, każdy ma inne potrzeby :)

  3. macem napisał:

    Zgadzam się, że każdy ma własne potrzeby, ale warto pisać czytelny kod, łatwiej go utrzymywać i debugować.

  4. MariuszT napisał:

    Ale takie właśnie rozwiązanie jest dla mnie czytelne :P Tak jest w PHP i sądziłem, że tak będzie w Smarty.

  5. Kamil napisał:

    A może lepiej byłoby wywołać include z PHP i wtedy zdefiniować te zmienne?

    {php}
    include („html_dir/section_other.html”);
    {/php}

    Ma to też swoje wady, ale powyższego problemu by raczej nie było :P

  6. MariuszT napisał:

    Nie po to się używa systemu szablonów żeby korzystać z czystego PHP. W wyżej wymienionym projekcie zależało mi aby z poziomu szablonu nie było dostępu do PHP. Znacznik {php} był zablokowany.

Odpowiedz



Podobne wpisy: