Render Symfony 5 service as twig global - php

I'm using a service as twig global variable. In the service constructor I set a default value of the property $title. It works initially.... Twig render the property value using the command {{ service.getTitle() }} in a template file. But after update the service property by the controllers constructor and rendering the view, the value is not updated at screen. The goal is set a twig global variable by the controllers to render in all views. How to do it?
twig.yaml
twig:
globals:
pageMap: "#Base.PageMap"
services.yaml
services:
Base.PageMap:
class : App\Bundle\Base\Services\PageMap
public: true
controller
public function __construct(PageMap $pageMap)
{
$pageMap->setTitle('Registration listing');
}
twig template:
<div class="title">{{ pageMap.getTitle() }}</div>

Twig globals are setup at init time and compiled/cached for the duration of an execution.
If you want to update and be able to call things dynamically, you should create a RuntimeExtension (see documentation here : https://symfony.com/doc/current/templating/twig_extension.html#creating-lazy-loaded-twig-extensions)
Calling it from your template will be a little more expensive (but more correct !)

Related

Customising the layout of Pagerfanta pagination with a custom template

I have Pagerfanta installed and working, however I am having difficulty in customising the layout. I read on Github that I need to pass through my_template, however I am unsure where this should be configured and what specificially this refers to.
Custom template
If you want to use a custom template, add another argument
<div class="pagerfanta">
{{ pagerfanta(my_pager, 'my_template') }}
</div>
Ideally I would like to have my own Twig template that I can modify, however I don’t know if Pagerfanta supports this. Is it all done in PHP?
I don't think it supports Twig templates but for sure you can write your custom Template class to render the pagination however you want.
Let's say in your AppBundle, you will need to create MyCustomTemplate class which should extend Pagerfanta\View\Template\DefaultTemplate:
<?php
namespace Acme\AppBundle\Template;
use Pagerfanta\View\Template\DefaultTemplate;
class MyCustomTemplate extends DefaultTemplate
{
// override whatever you need here ...
}
then register it in your services.yml file together with the view service:
services:
acme_app.template.my_template:
class: Acme\AppBundle\Template\MyCustomTemplate
pagerfanta.view.my_template:
class: Pagerfanta\View\DefaultView
public: false
arguments:
- "#acme_app.template.my_template"
tags: [{ name: pagerfanta.view, alias: my_template }]
then in your Twig templates you will be able to use:
{{ pagerfanta(my_pager, 'my_template') }}
which will result in displaying your custom pagination template.
my_template is the alias of your view. The next section in the Github link you have provided explains more.
It would look something like this
services:
app.view.my_template:
class: App\View\MyView
public: false
tags: [{ name: pagerfanta.view, alias: my_template }]

Symfony service as global twig variable

I have a service called General, this is the configuration :
services:
app_bundle.general:
class: AppBundle\Services\General
I'm trying to set this service as a global variable for my twig templates, so in config.yml I added (like the documentation say) :
parameters:
general_service: "#services.app_bundle.general"
But with this I have this error : You cannot dump a container with parameters that contain references to other services (reference to service "services.app_bundle.general" found in "/general_service").
How can I set my service to a global variable for Twig ? And in twig, use it like : {{general_service.myMethod()}}
Thanks !
As Artamiel suggested, add your service to the twig engine globals:
#app/config/config.yml
twig:
globals:
general_service: "#app_bundle.general"
Please note: your service has the id app_bundle.general and not services.app_bundle.general as indicated in your example.

What's the most convenient way to globally set 2 different form themes for app back-end and front-end

So, theming a form in Symfony2 is easy. You create a custom theme file and you add it to your config.yml file to load it. Done.
However, I have 2 different form themes. One for the front-end of the application and one for the back-end of the application.
I went through the documentation (http://symfony.com/doc/2.3/cookbook/form/form_customization.html) but couldn't find a good and easy way to do this.
When I add the theme to the config.yml file, I have the same theme in both front-end and back-end. I could also include the form within each view like this
{% form_theme form 'form_table_layout.html.twig' %}
However, that means I have to do it within each view.
Is there somehow a way to create a separate config file for front-end and back-end? Can I somehow indicate in the base template file which form theme should be used?
Anything else I could do?
If you use the Symfony2 default directory structure, i.e. you have a single kernel for both the frontend and the backend, you can only (as you mentioned) either set the form theme in each template, or use the same template application-wide by setting it in the config.yml file.
The alternative solution you mentioned, that is creating two base templates, each one setting a "global" form_theme tag would theoretically work. Create a base front-end.html.twig template for all your frontend pages with the following tag:
{% form_theme form 'form-front-end.html.twig' %}
That would work, but you would be forced to have a form variable in each inherited template. You would also not be able to set the theme to multiple forms in the same page.
You could improve the solution by checking if the form variable is defined before styling it:
{% if form is defined %}
{% form_theme form 'form-front-end.html.twig' %}
{% endif %}
or even better, if you want to be able to passing multiple form to the same template, you could do it by using a forms array:
{% if forms is defined %}
{% for form in forms %}
{% form_theme form 'form-front-end.html.twig' %}
{% endfor %}
{% endif %}
The good thing is that this would not throw any exceptions, even if you don't pass the variable at all, but you have to remember to put any forms to be rendered into the forms array.
Obviously, you would do the same thing for the back-end base template.
There may be better solutions, but in the meanwhile I hope this helps!
I also faced this problem some times ago, found this post, and followed the way as Andrea Sprega suggested. Recently I came out of a way which can save some repetitive typings.
Warning: This is somewhat "hackish", is probably not the best way, and is not guarantee to work in next framework release. You'll see why when you read below.
Goal: We want to have different default form templates depending on the application "environment" (the most common examples would be "frontend" and "backend). But fundamentally Symfony isn't aware of such "environment". Although it is possible to split the project into different environments and load different configurations, this is definitely an overkill if what we want is just having different form templates.
I think the best way would be an ability to set/override the default form themes in the base template. So we can have form theme A in frontend base template, and form theme B in backend base template. This sounds the best solution to me because as form theme is a "view" stuff, it makes perfect sense to have it changed in the view. However, the problem seems to be that, when the twig (template) code is executed, the form view is already initialized. So it would be too late to change the default form theme there. (I could be wrong here, because I didn't dig deep enough to have 100% confidence)
So I decided to do it another way. It works like this:
First, I made an assumption that all frontend controllers will extend the same base class (e.g. "BaseFrontendController"). Similarly, all backend controllers will extend the same BaseBackendController class. Here's how we distinguish a frontend and backend environment. This is actually the case in my project.
The default twig templates will be added to these base controller classes. It can be done via a method, or an annotation. In this post I'll use a public method.
Before the controller is executed, overwrite the defined default twig templates.
When the form view is initialized, it will use the default twig template defined in the controller.
Here's how it's done:
Firstly, the default form themes defined in your config.yml will be passed to the constructor of \Symfony\Component\Form\AbstractRendererEngine, and is assigned to the local instance $defaultThemes. This field is protected so that it can be used by its derived classes, but there is no setter to change its value.
So, we need to roll our own Symfony\Bridge\Twig\Form\TwigRendererEngine:
namespace AppBundle\Form\Twig;
use Symfony\Bridge\Twig\Form\TwigRendererEngine as BaseTwigRendererEngine;
class TwigRendererEngine extends BaseTwigRendererEngine
{
/**
* #param array $defaultThemes
*/
public function addDefaultThemes($defaultThemes)
{
$this->defaultThemes = array_merge($this->defaultThemes, $defaultThemes);
}
}
This custom renderer engine is plain simple - it just add a new method to append the default themes to the existing one. And this is why I say it's "hackish" - it will no longer work when the internal is changed.
Secondly, define an interface TwigTemplateProvider which is going to be implemented by the base frontend/backend controller classes:
namespace AppBundle\Form\Twig;
interface TwigTemplateProvider
{
/**
* #return array|string The form template path
*/
public function getDefaultFormTwigTemplates();
}
Thirdly, we need a listener which will run when the controller is executed.
<?php
namespace AppBundle\EventListener;
use AppBundle\Form\Twig\TwigRendererEngine;
use AppBundle\Form\Twig\TwigTemplateProvider;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
class PerControllerFormTemplateListener
{
/**
* #var \Twig_Environment
*/
private $twig;
public function __construct(\Twig_Environment $twig)
{
$this->twig = $twig;
}
public function onKernelController(FilterControllerEvent $event)
{
$controller = $event->getController();
if (!is_array($controller)) {
return;
}
if ($controller[0] instanceof TwigTemplateProvider) {
/** #var \Symfony\Bridge\Twig\Extension\FormExtension $formExtension */
$formExtension = $this->twig->getExtension('form');
$engine = $formExtension->renderer->getEngine();
if ($engine instanceof TwigRendererEngine) {
$templates = (array)$controller[0]->getDefaultFormTwigTemplates();
$engine->addDefaultThemes($templates);
}
}
}
}
The listener will get the form template name (in string or array) supplied by controllers implementing TwigTemplateProvider interface. Then it will add it to the default theme list and pass it to the form renderer engine.
Now, wire them together by adding the followings to your services.yml:
parameters:
twig.form.engine.class: AppBundle\Form\Twig\TwigRendererEngine
services:
app.form.per_controller_template_listener:
class: AppBundle\EventListener\PerControllerFormTemplateListener
arguments: ["#twig"]
tags:
- { name: kernel.event_listener, event: kernel.controller, method: onKernelController }
Here we set the %twig.form.engine.class% parameter to our own implementation, and added our event listener to the stack.
Using it is very simple. For example, in your base frontend controller, implement TwigTemplateProvider and add the following method:
public function getDefaultFormTwigTemplates()
{
return 'frontend/form_layout.html.twig';
}
Then this layout will be added to the form template stack when your front controller is executed.

symfony2: how to access service from template

If I created a service is there a way to access it from twig, without creating a twig.extension?
You can set the service a twig global variable in config.yml, e.g
#app/config/config.yml
twig:
globals:
your_service: "#your_service"
And in your template.html.twig file you can invoke your service this way:
{{ your_service.someMethod(twig_variable) }}
See here.
To get this done in Symfony 5, first you must declare the service in services.yaml, for example:
App\Service\NavigationHelper:
arguments:
foo: bar
Then you can declare the service for its use in Twig. To achieve this, you must add it as a variable in the "globals" section of the Yaml file located in packages/twig.yaml:
globals:
navHelper: '#App\Service\NavigationHelper'
Now you can use your service methods from the templates as Mun Mun Das suggested in his last code snippet.

Symfony2: Session Global variable in PHP template

Symfony doc says:
During each request, Symfony2 will set a global template variable app
in both Twig and PHP template engines by default. The app variable is
a GlobalVariables instance which will give you access to some
application specific variables automatically: app.security - The
security context. app.user - The current user object.
app.request - The request object. app.session - The session
object. app.environment - The current environment (dev, prod,
etc). app.debug - True if in debug mode. False otherwise.
Examples:
In twig: {{ app.request.method }}
In PHP: echo $app->getRequest()->getMethod()
In twig: {{ app.user.username }}
But for the session object:
In twig: {{ app.session.varname }}
In PHP: // I don't know, do you know how to call it?
I've tried: $session = $app->getSession('uid'); but when I try to store it to a database it tells me:
Catchable Fatal Error: Object of class
Symfony\Component\HttpFoundation\Session could not be converted to
string in C:\wamp\www...
There's a lack of resources when it comes to PHP templates, but in my case I can't switch for some reasons.
The question in other words, what is the equivalent in PHP templating of:
{{ app.session.varname }}?
In twig: {{ app.session.varname }}
In PHP: echo $app->getSession()->get('uid');
$session = $this->get('session');
if ($session->has('varname')) {
echo $session->varname
}
have you tried to error_log $app->getSession() to see what it returns?
error_log(var_dump($app->getSession(), true));

Categories