How can I determine if key is an object (twig)? - symfony

I want to determine if my key is an object:
{% for key in columns %}
{% if key is object %}
This is an object
{% else %}
This in not an object
{% endif %}
{% endfor %}
But I get the error message:
Unknown "object" test.

You can create your own Twig extension. I see you've tagged your question with Symfony so assuming you use Twig in Symfony, you can follow this tutorial:
https://symfony.com/doc/3.4/templating/twig_extension.html
What you need to do is add new TwigTest based on this example:
https://twig.symfony.com/doc/2.x/advanced.html#tests
You should end up with something like this:
// src/AppBundle/Twig/AppExtension.php
namespace AppBundle\Twig;
use Twig\Extension\AbstractExtension;
use Twig\TwigTest;
class AppExtension extends AbstractExtension
{
public function getTests()
{
return array(
new TwigTest('object', array($this, 'isObject')),
);
}
public function isObject($object)
{
return is_object($object);
}
}
Code above is not tested, but should work fine.

An easy way to check if a variable is an object or a string :
{% if var.id|default('') is not same as ('') %}

Related

How to access an entity within an entity in twig

In my controller, I return the entity so that my twig template can use it like so:
return $this->render('review/index.html.twig',[
"list" => $applications
]);
$applications is a query that returns the object I'm looking for:
$applications = $this->getDoctrine()->getRepository(ApplicationQueue::class)->findBy(
array("assignment_mod_user" => $this->getUser()->getId())
);
And within my twig, I use the dump function to see if it's retrieving what I'm looking for. This is what is returned:
As you can see, there are two entities associated to this entity. In twig when I tired to do this, it failed to retrieve the data within:
{% for application in list %}
{{application.application.[whateverhere]}}
{% endfor %}
How do I access the entities within an entity in twig when the data is already being pushed? The current output returns the error of:
Neither the property "application" nor one of the methods "application()", "getapplication()"/"isapplication()"/"hasapplication()" or "__call()" exist and have public access in class "App\Entity\ApplicationQueue".
You need to add an accessor to get the value of $application. This property is currently not accessible from outside the ApplicationQueue class.
class ApplicationQueue
{
protected $application;
public function getApplication(): CreatorApplication
{
return $this->application;
}
}
You have tu put double curly braces around your variable, like that :
{{ application.application.name }}
Look at the twig doc :
{% for user in users %}
<li>{{ user.username|e }}</li>
{% endfor %}

Override twig variable in included template in Symfony 3

I'm trying to override a variable in the included template.
Can I do this in a Symfony3 & Twig?
My twig template looks like this:
{% set foo = 'bar' %}
{% include 'first.html.twig' %}
{% include 'second.html.twig' %}
// first.html.twig
{{ foo }}
{% set foo = 'second' %}
// second.html.twig
{{ foo }}
I get such a result:
bar bar
But I expect:
bar second
The following Twig code:
{% set a = 42 %}
{{ include("first.twig") }}
Will be compiled to this one:
// line 1
$context["a"] = 42;
// line 2
echo twig_include($this->env, $context, "first.twig");
And twig_include prototype is:
# lib/Twig/Extension/Core.php
function twig_include(Twig_Environment $env, $context, $template, $variables = array(), $withContext = true, $ignoreMissing = false, $sandboxed = false)
So variables are passed by copy, not by reference in included templates. Thus your changes in included templates won't be reflected to including templates.
Moreover, since Twig 2.0, you can't call TwigEnvironment::addGlobal once twig runtime is initialized, so you can't glitch using simple extensions.
All in all, you can understand that if you need to update variables cross templates, it means some template contains business logic and Twig isn't build for that. You need to prepare the whole context in controllers.
Alternatively you may call a PHP class method from TWIG. Example of a page-counter needed when generating a pdf.
Custom class :
class PageCounter
{
private $pageNumber = 0;
public function incrementPageCounter()
{
$this->pageNumber ++;
return $this->pageNumber;
}
}
Controller:
....
$twigVariables = [
...
'pageCounter' => new PageCounter()
];
return $this->render('template.html.twig', $twigVariables);
Twig template (object pageCounter available from any included template)
{{ pageCounter.incrementPageCounter() }} / {{totalPages}}
You just need to do the check and override the variable with another variable :))
{% if name is defined %}
{% set foo = name %}
{% else %}
{% set foo = 'bar' %}
{% endif %}
{% include 'first.html.twig' %}
{% include 'second.html.twig' %}
// first.html.twig
{% set name = 'first' %}
// second.html.twig
{% set name = 'second' %}
Why not override your variable with your include tag/function like :
{% include 'first.html.twig' with {'foo': 'second'} %}
or :
{ include('first.html.twig', {foo: 'second'}) }}

Twig: Cannot override a block that's defined in a macro

I am importing a macro from a layout file, and inside that macro I define a few twig blocks.
When I extend my 'base' layout file in a new 'view' file, I cannot override the content of the blocks that I defined in the macro.
Here is a very simplified, minimal version of my use-case.
Structure:
base.twig
{% import 'macros' as macros %}
<section>
{{ macros.render_sections() }}
{% block working %}
content1-default
{% endblock %}
</section>
macro.twig
{% macro render_sections() %}
<section>
{% block notworking %}
content2-default
{% endblock %}
</section>
{% endmacro %}
view.twig
{% extends "base" %}
{% block working %}
content1-override
{% endblock %}
{% block notworking %}
content2-override
{% endblock %}
Expected behavior:
I expect to see "content1-override content2-override" in my html.
What actually happens:
I see 'content1-override content2-default'
Is there a way to pass the blocks scope to a macro?
I have tried defining the macro inside the base.twig file, as to rule out the import function, but that didn't help.
and obviously everything else works because I can see the block that does get overriden.
You should learn about Twig's internals:
all Twig files are converted to PHP classes
{% extends %} is the equivalent for the litteral PHP extends (used for inheritance).
{% import %} assign a property of your context to an object of type "the twig file you're importing"
blocks are no more than php methods, and the native php inheritance let you overwrite Twig blocks sweetly.
So, taking this into account, your converted twig code will look like this:
class base {
public function display() {
$context['macros'] = new macros();
$context['macros']->render_sections();
echo '<section>';
echo $this->blockWorking();
echo '</section>';
}
public function blockWorking() {
echo "content1-default";
}
}
class macros {
public function render_sections() {
echo '<section>';
echo $this->blockNotWorking();
echo '</section>';
}
public function blockNotWorking() {
echo "content2-defualt";
}
}
class view extends base {
public function blockWorking() {
echo "content1-override";
}
public function blockNotWorking() {
echo "content2-override";
}
}
$view = new view();
$view->display();
You can clearly see here that the blockNotWorking() method of the view class can never overwrite the macro.

Import a file with constants in Twig?

Is it possible to import a file with a bunch of constants in another twig file?
Such as :
{% set VAR1 = constant("...:var1") %}
{% set VAR2 = constant("...:var2") %}
{% set VAR3 = constant("...:var3") %}
...
And then, in another file use those constants?
I have tried include, import, use and embed.
None of these seem to fit the situation.
By design, you can't import variables from another context, as {% include %} without parameter will pass your current context by copy, not by reference.
This is the exact same thing for {% macro %} with the _context variable as parameter, so you cannot create new variables in the context from inside a macro and then {% import %} your macro file anywhere.
But, you can create variables from an extended file, and use them in the child file.
For example:
a.html.twig
{% set test = 'Hello!' %}
b.html.twig
{% extends 'a.html.twig' %}
{{ test }}
Will display "Hello!" when rendering b.html.twig, or any other files that extends a.html.twig.
If, by design, you can't extends a parent template in your application, you can create a Twig extension and implement the getGlobals() method (doc).
Example (for me in Fuz/TestBundle, replace all namespaces to fit your needs):
The Twig extension :
PHP Fuz/TestBundle/Twig/Extension/AppGlobalsExtension.php
<?php
namespace Fuz\TestBundle\Twig\Extension;
class AppGlobalsExtension extends \Twig_Extension
{
public function getGlobals()
{
return array(
'var1' => \Fuz\TestBundle\Component\Service::SOME_CONSTANT_A,
'var2' => \Fuz\TestBundle\Component\Service::SOME_CONSTANT_B,
'var3' => \Fuz\TestBundle\Component\Service::SOME_CONSTANT_C,
// ...
);
}
public function getName()
{
return 'appglobals';
}
}
The configuration :
YML Fuz/TestBundle/Resources/config/services.yml
parameters:
fuz_tools.twig.appglobals_extension.class: Fuz\TestBundle\Twig\Extension\AppGlobalsExtension
services:
fuz_tools.twig.appglobals:
class: '%fuz_tools.twig.appglobals_extension.class%'
tags:
- { name: twig.extension }
You can now use {{ var1 }}, {{ var2 }} ... anywhere in your application views.
This should do the trick for you (with with), just pass variables:
{% include "ACMETestBundle::test.html.twig" with {var1: VAR1, var2: VAR2, ... } %}

How can I change symfony2 form fields default options globally?

Is there a way to change default options for form fields globally in symfony2?
More specifically, I want to change the render of ALL datetime fields to use single_text instead of the default choice widget.
Can it be done? Or do I need to implement a custom type and set the default in there, like for example the birthdate type?
I prefer an option that leads to minimal changes in the codebase.
The post is old, but you can use an alternative method, overriding the DateType symfony class ...
service.yml
services:
form.type.date:
class: "YourApp\YourBundle\Form\DateType"
tags:
- { name: "form.type", alias: "date" }
DateType.php
<?php
namespace YourApp\YourBundle\Form;
use Symfony\Component\Form\Extension\Core\Type\DateType as SymfonyDateType;
use Symfony\Component\OptionsResolver\OptionsResolver;
class DateType extends SymfonyDateType
{
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
parent::configureOptions( $resolver );
$resolver->setDefault( 'widget', 'single_text' );
}
}
You can check if the service is taken by container
$ ./app/console debug:container | grep form.type.date
form.type.date YourApp\YourBundle\Form\DateType
form.type.datetime Symfony\Component\Form\Extension\Core\Type\DateTimeType
You have to define a form theme.
It's very easy and requires only a little bit coding time. First of all, you have to know which block to customize; in that case, you can do something like
{% block my_data_widget %}
{% spaceless %}
{% if type is defined and type == 'date' %}
// do all your customization
{% else %}
// don't know ...
{% endif %}
{% endspaceless %}
{% endblock form_widget_simple %}
Now that you have defined this snippet of code, you can use it into your main template (or whatever you use into your form views) in that way
{% form_theme form 'YourBundle:Form:myDataWidget' %}
Last but not least important, you have to place your form theme into Resources/views folder. In my example, your path will be Resources/views/Form/myDataWidget
Update
Did you tried with
{% set type = type|default('single_text') %}
or something like that?

Resources