filter boolean variable in a twig template - symfony

I have a boolean variable(0, 1) in my database and I want to filter it to a word 0 for 'NO', and 1 for 'Yes'. how can I do that in a twig template
I want something like {{ bool_var | '??' }} where the '??' is the filter

Quick way to achieve that is to use the ternary operator:
{{ bool_var ? 'Yes':'No' }}
http://twig.sensiolabs.org/doc/templates.html#other-operators
You could also create a custom filter that would do this. Read about custom TWIG extensions - http://symfony.com/doc/current/cookbook/templating/twig_extension.html

To build on what #dmnptr said in his last paragraph, in your app bundle, create a /Twig folder and create an AppExtension class inside.
class AppExtension extends \Twig_Extension
{
public function getFilters()
{
return array(
new \Twig_SimpleFilter('boolean', array($this, 'booleanFilter')),
);
}
public function booleanFilter($value)
{
if ($value) {
return "Yes";
} else {
return "No";
}
}
public function getName()
{
return 'app_extension';
}
}
Then, in your bundle's Resources/config/ folder, add the following to your services.yml where class is the class of the new class:
app.twig_extension:
class: [YourAppBundleNamespace]\Twig\AppExtension
public: false
tags:
- { name: twig.extension }
The filter will be available in Twig by simply appending a |boolean to any variable.

Or even better you could make a boolean to string transformer and add it to your form.
It might be 'more' code but the upside is reusability. You wouldn't have to make your templates dirty with logic and you could reuse it to all the forms you want :)
Pros:
Not tied to the form component so you can still use it.
Use it anywhere, more functionality than a twig extension.
No need to mess with twig or symfony configuration.
Can use it in forms themselves.
Documentation:
http://symfony.com/doc/current/cookbook/form/data_transformers.html
Example from:
Symfony2 Forms BooleanToStringTransformer Issue
<?php
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException;
class BooleanToStringTransformer implements DataTransformerInterface
{
private $trueValue;
private $falseValue;
public function __construct($trueValue, $falseValue)
{
$this->trueValue = $trueValue;
$this->falseValue = $falseValue;
}
public function transform($value)
{
if (null === $value) {
return null;
}
if (!is_bool($value)) {
throw new TransformationFailedException('Expected a Boolean.');
}
return true === $value ? $this->trueValue : $this->falseValue;
}
public function reverseTransform($value)
{
if (null === $value) {
return null;
}
if (!is_string($value)) {
throw new TransformationFailedException('Expected a string.');
}
return $this->trueValue === $value;
}
}

Related

Add dynamic filters or pagination in ContextBuilder using API Platform

I am trying to force filters or pagination dynamically using a ContextBuilder.
For example, I want to force pagination for the group public:read:
namespace App\Serializer;
use ApiPlatform\Core\Serializer\SerializerContextBuilderInterface;
use Symfony\Component\HttpFoundation\Request;
final class FooContextBuilder implements SerializerContextBuilderInterface
{
private $decorated;
public function __construct(SerializerContextBuilderInterface $decorated)
{
$this->decorated = $decorated;
}
public function createFromRequest(Request $request, bool $normalization, ?array $extractedAttributes = null): array
{
$context = $this->decorated->createFromRequest($request, $normalization, $extractedAttributes);
if (($resourceClass === Foo::class
$context['operation_type'] ?? null) === 'collection' &&
true === $normalization
) {
if ((isset($context['groups']) &&
in_array('public:read', $context['groups'])
) {
$context['filters']['pagination'] = true;
}
}
return $context;
}
}
services.yml:
services:
...
App\Serializer\RouteContextBuilder:
decorates: 'api_platform.serializer.context_builder'
arguments: [ '#App\Serializer\RouteContextBuilder.inner' ]
autoconfigure: false
Unfortunately, it seems that $context['filters'] is built as a later stage as it is not available in the ContextBuilder yet. $context['filters'] is available later e.g. in a DataProvider.
I tried to change the decoration priority in services.yml without success:
services:
App\Serializer\RouteContextBuilder:
...
decoration_priority: -1
How can I add dynamic filters or pagination through the context? Is there another interface that can be decorated which is called a later stage of the normalization process and before the filters are applied?
The serialization process is executed after data retrieval this can't work. Use a data Provider.

How return an array from a symfony2 controller action?

in my symfony2 project i need call the same action in many controllers and this action should return a very simple php array that then will be passed to a twig template by these controllers. How can i do it?
A pratical example can explain my situation better.
1.shared controller
// Acme/DemoBundle/Controller/MetasController
class MetasController extends Controller {
public function metasAction() {
$myArray= array();
return $myAarray;
}
}
page render controller
// Acme/DemoBundle/Controller/PageController
class PageController extends Controller {
protected $property = "test";
public function indexAction() {
$metas= $this->forward('AcmeDemoBundle:Metas:metas');
return $this->render('AcmeDemoBundle:Page:index.html.twig', array('property'=>property, 'metas'=>$metas));
}
}
when i do this i get an error: the controller must be a response array given.
You should create a service
// Acme/DemoBundle/Controller/MetasController
class MetasController {
public function metasAction() {
$myArray= array();
return $myAarray;
}
}
declare as service in Acme\DemoBundle\Resources\config\services.yml
services:
demo.metas:
class: "Acme\DemoBundle\Controller\MetasController"
Then you can use it in any other controller
// Acme/DemoBundle/Controller/PageController
class PageController extends Controller {
protected $property = "test";
public function indexAction() {
$metas= $this->get('demo.metas')->metas();
return $this->render('AcmeDemoBundle:Page:index.html.twig', array('property'=>property, 'metas'=>$metas));
}
}
In your action controller :
<?php
...
$arrayExample = array();
return $this->render('ExampleBundle:ExampleFolder:exampleTemplate', array('myArray' => $arrayExample));
And in your twig template now you have access to your array using myArray
Example :
{% for data in myArray %}
...
{% endfor %}
Try this :
use Symfony\Component\HttpFoundation\Response;
public function indexAction()
{
...
$content = $this->renderView(
'AcmeDemoBundle:Page:index.html.twig',
array('property'=> $property,
'metas' => $metas
));
return new Response($content);
}
Yes, you can register your controller as a service as it said above but I would recommend to isolate this logic in a different place. It might be a service but not controller.
As I understand you need the same array in several places. So, it might be some class registered as service or some simple class with static method providing this array. In this case your code will be much cleaner.
If you need this array only in view you can define custom twig method which will return array you need. If this array might be different time to time (if it might depend on some data) you can pass entity manager to the service providing this array or to the twig extension.
(The best use of controllers is to be just a proxy between view and data layer. It's not a good idea to use it for such purposes as you described (in my opinion of course).)

Create a simple twig filter

I get this error message when i create since i have created a simple twig filter. The message is not clear at all.
An exception has been thrown during the compilation of a template
("Warning: Illegal offset type in app/cache/dev/classes.php line
3342")
My filter:
class simpleFilter extends Twig_Extension
{
public function getFilters()
{
return array('simpleFilter' => new Twig_SimpleFilter($this, 'simpleFilter'));
}
public function simpleFilter($value)
{
return 'test'.$value;
}
public function getName()
{
return 'some_extension';
}
}
My config
my.twig.extension.simpleFilter:
class: Bundle\Twig\Filter\SimpleFilter
tags:
- { name: twig.extension }
Am i missing something?
Try to change your getFilters to look like this:
public function getFilters()
{
return array(
new Twig_SimpleFilter('simpleFilter', array($this, 'simpleFilter'))
);
}
I had to use Twig_Filter_Method
public function getFilters()
{
return array(
new Twig_Filter_Method('simpleFilter', array($this, 'simpleFilter'))
);
}
even if i do not know the difference beetween them.

Exception when a twig extension service is used

I am trying to add functions to the twig gramework using twig extension.
this is the extension (I dont know if it works because I've had no opportunity to use it because of the problem I have :
class CnamtsStyleExtension extends \Twig_Extension {
protected $loader;
public function __construct(FilesystemLoader $loader)
{
$this->loader = $loader;
}
public function getFunctions()
{
return array(
'modal' => new \Twig_SimpleFunction($this, 'getModal', array('is_safe' => array('html')))
);
}
public function getModal($parameters=null) {
$template = htmlspecialchars($this->loader->getSource('component/modal.html.twig'));
return $this->getTemplateCode($template, $parameters===null ? null : json_decode($parameters));
}
protected function getTemplateCode($template, $parameters) {
$html_template = '';
if($parameters !== null) {
foreach ($parameters as $key => $value) {
$html_template = str_replace('{{' .$key. '}}', $value, $template);
}
}
return $html_template;
}
public function getName() {
return 'cnamts_style_extension';
}
}
Here is my service :
services:
cnamts.twig.cnamts_style_extension:
class: Cnamts\StyleGuideBundle\Twig\CnamtsStyleExtension
tags:
- { name: twig.extension }
arguments: ["#twig.loader"]
and the twig :
{% extends "::base.html.twig" %}
{% block body %}
Hello world
{% endblock %}
as you can see, my twig does not use any function of my extension. it is just a simple 'hello world'.
So I clear the cache (even manually to be sure), and I send the route....
I have two exceptions :
Exception number 1 in my twig:
ContextErrorException: Warning: Illegal offset type in my_project\vendor\twig\twig\lib\Twig\Environment.php line 1167
Exception number 2: even the web tool bar cannot display and there is a 500 error from the server
Illegal offset type "#WebProfiler/Collector/config.html.twig
but coming originally from the same exception in Environment.php
I am sure it is linked to the extension because when I deactivate the service I have added, there are no errors
Thank you for you help;
PS : I could debug and see that the loader is not null or whatever (it seems good)... my class is the problem because I tried to load the same service giving another class extension and I dont have the problem.
Try to use this
public function getFunctions(){
return array(
'getmodal' => new \Twig_Function_Method($this, 'getModal');
);
}

Creating my first twig extension to provide global variables to base templates

I need to populate a variable with some HTML code and make it available to my base.html.twig file.
To achive this I have made a twig extension. This is my first time using twig extentions so im not sure if this is the correct way of doing things.
Here is what I have so far:
Extension code:
class GlobalFooterExtension extends \Twig_Extension
{
public function getFilters()
{
return array(
new \Twig_Filter_Function('GlobalFooter', array($this, 'GlobalFooter')),
);
}
public function GlobalFooter()
{
$GlobalFooter = file_get_contents('http://mysite.co.uk/footer/footer.html.twig');
return $GlobalFooter;
}
public function getName()
{
return 'GlobalFooter_extention';
}
}
config.yml:
services:
imagine.twig.GlobalFooterExtension:
class: Imagine\GdmBundle\Twig\GlobalFooterExtension
tags:
- { name: twig.extension }
base.html.twig:
{{GlobalFooter}}
This give the following error:
Twig_Error_Runtime: Variable "GlobalFooter" does not exist in "ImagineGdmBundle:Default:product.html.twig" at line 2
Im sure im missing something really obvious. How do I make $GlobalFooter from my GlobalFooterExtension class available to my base.hmtl.twig file?
You want to set a global variable, not a function.
Just use getGlobals and return your variable:
class GlobalFooterExtension extends \Twig_Extension
{
public function getGlobals()
{
return array(
"GlobalFooter" => file_get_contents('http://mysite.co.uk/footer/footer.html.twig'),
);
}
public function getName()
{
return 'GlobalFooter_extention';
}
}
Or, if you want to lazy load the value of the variable, create a function and change your template to:
{{ GlobalFooter() }}
Besides this, if the footer file is on the same site, it's better to use the {% include '...' %} tag.
rename function getFilters to getFunctions

Resources