Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Symfony2 Templating without request

Tags:

symfony

I'm trying to send an email from a ContainerAwareCommand in Symfony2. But I get this exception when the email template is render by:

$body = $this->templating->render($template, $data);

Exception:

 ("You cannot create a service ("templating.helper.assets") of an inactive scope ("request").") 

I found in github that this helper need the request object. Anybody knows how can I to instance the Request object?

like image 310
Martin Borthiry Avatar asked Jan 11 '12 16:01

Martin Borthiry


4 Answers

You need to set the container into the right scope and give it a (fake) request. In most cases this will be enough:

//before you render template add bellow code
$this->getContainer()->enterScope('request');
$this->getContainer()->set('request', new Request(), 'request');

The full story is here. If you want to know the details read this issue on github.

like image 157
BetaRide Avatar answered Nov 02 '22 19:11

BetaRide


The problem arises because you use asset() function in your template.

By default, asset() relies on Request service to generate urls to your assets (it needs to know what is the base path to you web site or what is the domain name if you use absolute asset urls, for example).

But when you run your application from command line there is no Request.

One way to fix this it to explicitely define base urls to your assets in config.yml like this:

framework:
  templating:
    assets_base_urls: { http: ["http://yoursite.com"], ssl: ["http://yoursite.com"] }

It is important to define both http and ssl, because if you omit one of them asset() will still depend on Request service.

The (possible) downside is that all urls to assets will now be absolute.

like image 33
dr.scre Avatar answered Nov 02 '22 18:11

dr.scre


Since you don't have a request, you need to call the templating service directly like this:

$this->container->get('templating')->render($template, $data);
like image 44
Matthew Avatar answered Nov 02 '22 19:11

Matthew


Following BetaRide's answer put me on the right track but that wasn't sufficient. Then it was complaining: "Unable to generate a URL for the named route "" as such route does not exist."

To create a valid request I've modified it to request the root of the project like so:

$request = new Request();
$request->create('/');
$this->container->enterScope('request');
$this->container->set('request', $request, 'request');

You might need to call a different route (secured root?), root worked for me just fine.

Symfony2 Docs

Bonus addition:

I had to do so much templating/routing in cli through Symfony2 commands that I've updated the initializeContainer() method in AppKernel. It creates a route to the root of the site, sets the router context and fakes a user login:

protected function initializeContainer()
{
    parent::initializeContainer();
    if (PHP_SAPI == 'cli') {

        $container = $this->getContainer();

        /**
         * Fake request to home page for cli router.
         * Need to set router base url to request uri because when request object
         * is created it perceives the "/portal" part as path info only, not base
         * url and thus router will not include it in the generated url's.
         */
        $request = Request::create($container->getParameter('domain'));
        $container->enterScope('request');
        $container->set('request', $request, 'request');
        $context = new RequestContext();
        $context->fromRequest($request);
        $container->get('router')->setContext($context);
        $container->get('router')->getContext()->setBaseUrl($request->getRequestUri());

        /**
         * Fake admin user login for cli. Try database read,
         * gracefully print error message if failed and continue.
         * Continue mainly for doctrine:fixture:load when db still empty.
         */
        try {
            $user = $container->get('fos_user.user_manager')->findUserByUsername('admin');
            if ($user !== null) {
                $token = $token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
                $this->getContainer()->get('security.token_storage')->setToken($token);
            }
        } catch (\Exception $e) {
            echo "Fake Admin user login failed.\n";
        }
    }
}

You might not need the last $container->get('router')->getContext()->setBaseUrl($request->getRequestUri()); part, but I had to do it because my site root was at domain.com/siteroot/ and the router was stripping /siteroot/ away for url generation.

like image 37
Vigintas Labakojis Avatar answered Nov 02 '22 19:11

Vigintas Labakojis