Зарегистрируйте расширения Twig и каталог кеша

У меня проблемы с добавлением расширений Twig.

У меня есть контроллеры Bundle, расширяющие пользовательский класс BaseController:

class DefaultController extends BaseController

И вот мой класс BaseController (только его часть).

class BaseController extends Controller {

public function setContainer(\Symfony\Component\DependencyInjection\ContainerInterface $container = null)
{
    parent::setContainer($container);
    $this->onContainerSet();
}

public function onContainerSet()
{
  // many other tasks

    $this->get('twig')->addExtension(new \Twig_Extension_StringLoader());

    $this->get('twig.loader')->addPath('../app');

    $function = new \Twig_SimpleFunction('stars', function ($number, $maximum_stars = 5) {
        $this->get('twig')->addGlobal('star_number',sprintf("%.1f",$number));
        $this->get('twig')->addGlobal('star_max',$maximum_stars);

        $full_stars = floor($number);
        $half_stars = ($number - $full_stars) * 2;
        $empty_stars = $maximum_stars - $full_stars - $half_stars;

        $this->get('twig')->addGlobal('full_stars_number',$full_stars);
        $this->get('twig')->addGlobal('half_stars_number',$half_stars);
        $this->get('twig')->addGlobal('empty_stars_number',$empty_stars);


        echo $this->renderView(
            'views/stars.html.twig'
        );;
    });


    $function2 = new \Twig_SimpleFunction('inurl', function ($anchor, $code) {
        echo '<a href="'.$this->internalLinks[$code].'">'.$anchor."</a>";

    });


    $this->get('twig')->addFunction($function);
    $this->get('twig')->addFunction($function2);

   }
}

Эта проблема:

Когда я очищаю каталог кеша, у меня есть первое сообщение:

КРИТИЧЕСКОЕ — необработанное исключение PHP LogicException: «Невозможно зарегистрировать расширение «string_loader», поскольку расширения уже инициализированы». at ...\vendor\twig\twig\lib\Twig\Environment.php строка 660 Контекст: {"Exception":"Object(LogicException)"}

Но когда я перезагружаю страницу (папка кеша уже создана), она работает нормально (без исключений).

Однако, если я прокомментирую строку:

// $this->get('twig')->addExtension(new \Twig_Extension_StringLoader());

и очистить каталог кеша. У меня есть исключение:

КРИТИЧЕСКОЕ - Uncaught PHP Exception LogicException: «Невозможно добавить функцию «звездочки», так как расширения уже инициализированы». at ...\vendor\twig\twig\lib\Twig\Environment.php строка 946 Контекст: {"Exception":"Object(LogicException)"}

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

Вопрос - как его решить самым простым способом?


person Marcin Nabiałek    schedule 30.05.2014    source источник
comment
Почему вы не загружаете расширение через сервис?   -  person Bartek    schedule 30.05.2014
comment
Я мог бы попробовать, но я не знаю, как это сделать. Я пытался с Twig_Extension_StringLoader минуту назад, но это не сработало   -  person Marcin Nabiałek    schedule 30.05.2014
comment
Зачем вам регистрировать расширение ветки в контроллере? Документы: symfony.com/doc/current/cookbook/templating/twig_extension.html< /а>   -  person Igor Pantović    schedule 30.05.2014
comment
Во-первых, я не знаю, как включить Twig_Extension_StringLoader таким образом. И еще одна проблема, я не знаю, как передать дополнительные данные в Estension, так как у меня есть $this-›internalLinks в $function2   -  person Marcin Nabiałek    schedule 30.05.2014


Ответы (2)


Создайте свой курс в YourBundle\Twig

class YourExtension extends \Twig_Extension
{

    /**
     * @var Router
     */
    protected $router;

    function __construct(Router $router)
    {
        $this->router = $router;
    }

    /**
     * @return array
     */
    public function getFilters()
    {
        return [
            new \Twig_SimpleFilter('my_filter', [$this, 'myFilter'], ['is_safe' => ['html']]),
        ];
    }

    /**
     * @return string
     */
    public function myFilter(User $user)
    {
        return 'FILTERED: ' . $user->getName();
    }

    /**
     * @return string
     */
    public function getName()
    {
        return 'my_filter_extension';
    }

}

Затем зарегистрируйте свое расширение как службу: (в этом случае я ввожу router в качестве аргумента)

yourbundle.twig.my_filter_extension:
    class: Acme\YourBundle\Twig\YourExtension
    arguments: [@router]
    tags:
        - { name: twig.extension }

Если вы хотите включить Twig_Extension_StringLoader, добавьте к вашим услугам:

yourbundle.twig.extension.loader:
    class: Twig_Extension_StringLoader
    tags:
        - { name: 'twig.extension' }

Twig_Extension_StringLoader не загружается по умолчанию.

person Bartek    schedule 30.05.2014
comment
Хорошо, но те функции, которые я использую, нужны во всех комплектациях. И я не знаю, как передать им $this-›internalLinks и как прописать \Twig_Extension_StringLoader(), так как это расширение встроено - person Marcin Nabiałek; 30.05.2014
comment
Они будут доступны во всех ваших шаблонах, во всех пакетах ;) - person Bartek; 30.05.2014
comment
Но я до сих пор не знаю, как включить расширение Twig_Extension_StringLoader, чтобы оно заработало. - person Marcin Nabiałek; 30.05.2014
comment
Хорошо спасибо. Это мне очень помогло. Но последнее - как передать внутренние ссылки, которые являются собственностью BaseController, в расширение Twig? Это не константа, а переменная - person Marcin Nabiałek; 30.05.2014
comment
@MarcinNabiałek Определите свой DefaultController как службу, а затем передайте эту службу как argument вашей службы расширения - person Bartek; 30.05.2014
comment
должен ли я сделать это для каждого контроллера, который, наконец, будет использовать это Twig_Extension? Я решаю эту проблему, возможно, не лучшим способом, просто используя addGlobal, чтобы передать этот параметр расширению Twig, как в моем ответе выше. - person Marcin Nabiałek; 30.05.2014
comment
IMO, вы должны объявить свой DefaultController сервисом, но я думаю, что есть лучший вариант: создайте новый сервис InternalLinksService, после чего вы сможете внедрить свой InternalLinksService в другие сервисы или использовать его во всех контроллерах следующим образом: $this->get('your_bundle.internal_links')->yourMethod() - person Bartek; 30.05.2014

Что я в итоге сделал для достижения результата (может быть у кого-то будет похожая проблема в будущем):

В config.yml я добавил:

services:
    yourbundle.twig.extension.loader:
        class: Twig_Extension_StringLoader
        tags:
            - { name: 'twig.extension' }

    yourbundle.twig.stars_extension:
        class: Mnab\Twig\Stars
        tags:
            - { name: 'twig.extension' }

    yourbundle.twig.inurl_extension:
        class: Mnab\Twig\InternalUrl
        tags:
            - { name: 'twig.extension' }

в моем BaseController я оставил только код вопроса:

    $this->get('twig.loader')->addPath('../app');

но также добавил:

    $this->get('twig')->addGlobal('internal_links',$this->internalLinks);

использовать его в расширении Twig

И я создал 2 класса:

<?php
//InternalUrl.php
namespace Mnab\Twig;

use Symfony\Component\DependencyInjection\ContainerInterface;

class InternalUrl extends \Twig_Extension {


    public function getFunctions()
    {
        return array(
            new \Twig_SimpleFunction('inurl', array($this, 'inUrlFunction'), array('needs_environment' => true, 'is_safe' => array('html'))),
        );
    }


    public function inUrlFunction(\Twig_Environment $env, $anchor, $code)
    {
        return '<a href="'.$env->getGlobals()['internal_links'][$code].'">'.$anchor."</a>";
    }


    public function getName()
    {
        return 'inurl_extension';
    }
} 

а также

<?php
// Stars.php

namespace Mnab\Twig;


class Stars extends \Twig_Extension
{

    public function getFunctions()
    {
        return array(
            new \Twig_SimpleFunction('stars', array($this, 'starsFunction'), array('needs_environment' => true, 'is_safe' => array('html'))),
        );
    }

    public function starsFunction(\Twig_Environment $env, $number, $maximum_stars = 5)
    {


        $env->addGlobal('star_number',sprintf("%.1f",$number));
        $env->addGlobal('star_max',$maximum_stars);

        $full_stars = floor($number);
        $half_stars = ($number - $full_stars) * 2;
        $empty_stars = $maximum_stars - $full_stars - $half_stars;

        $env->addGlobal('full_stars_number',$full_stars);
        $env->addGlobal('half_stars_number',$half_stars);
        $env->addGlobal('empty_stars_number',$empty_stars);


        return $env->render(
            'views/stars.html.twig'
        );

    }


    public function getName()
    {
        return 'stars_extension';
    }
} 

Теперь он работает независимо от того, создан кеш или нет. Таким образом, кажется, лучше регистрировать службы, когда вы хотите использовать расширения Twig, чем регистрировать расширения в контроллере.

person Marcin Nabiałek    schedule 30.05.2014