EasyAdmin3: Passing additional variables to twig-template (EDIT page) - symfony

Background
I want to display entity-independent information in the EDIT-page. According to the official documentation it can be solved with configureResponseParameters-function.
Problem
So I implemented that function into my CRUD-controller.
on INDEX twig template: I can use/find the variable
on EDIT twig template: I cannot use/find the variable
Any idea why?
How can I achieve to pass data to my EDIT-twig template? Many Thanks!
My Files
PersonCrudController.php
class PersonCrudController extends AbstractCrudController {
public function configureCrud(Crud $crud): Crud {
$crud->setFormThemes([
'mystuff/person_edit_form.html.twig', '#EasyAdmin/crud/form_theme.html.twig'
]);
return $crud;
}
public function configureFields(string $pageName): iterable {
// ...
yield TextField::new('specialDisplay')->setFormTypeOption('mapped', false);
// ...
}
public function configureResponseParameters(KeyValueStore $responseParameters): KeyValueStore
{
$responseParameters->set('testtesttest', '123xyz');
return $responseParameters;
}
// ...
}
mystuff/person_edit_form.html.twig
{% block _Person_specialDisplay_row %}
{{ testtesttest }} {# throws: Variable "testtesttest" does not exist. #}
{{ dump() }} {# cannot find "testtesttest" anywhere in dump #}
{# ... here comes the rest that is working perfectly ... #}
{% endblock %}

Related

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

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 ('') %}

twig inheritance and symfony2 controller variables

Im trying my first project using symfony2 + twig. I created basic twig template with defined blocks. It basically looks like this
{% block content %}
some content...
{% endblock %}
{% block footer %}
{{ footer.content}}
{% endblock %}
I want footer to be same for all pages. Footer is loaded from DB and its set in controller. I wanted inherit from template described above to other pages but I have to always set footer in controller otherwise variable is not defined.
My questions is if exists any 'nice' way how to set footer variable for multiple templates inherited from parent template?
The solution : Embedding Controllers
In some cases, you need to do more than include a simple template.
Suppose you have a sidebar in your layout that contains the three most
recent articles. Retrieving the three articles may include querying
the database or performing other heavy logic that can't be done from
within a template.
The solution is to simply embed the result of an entire controller
from your template. First, create a controller that renders a certain
number of recent articles:
Controller
// src/AppBundle/Controller/ArticleController.php
namespace AppBundle\Controller;
// ...
class ArticleController extends Controller
{
public function recentArticlesAction($max = 3)
{
// make a database call or other logic
// to get the "$max" most recent articles
$articles = ...;
return $this->render(
'article/recent_list.html.twig',
array('articles' => $articles)
);
}
}
View
{# app/Resources/views/article/recent_list.html.twig #}
{% for article in articles %}
<a href="/article/{{ article.slug }}">
{{ article.title }}
</a>
{% endfor %}
Layout
{# app/Resources/views/base.html.twig #}
{# ... #}
<div id="sidebar">
{{ render(controller(
'AppBundle:Article:recentArticles',
{ 'max': 3 }
)) }}
</div>
you can do with one of the following, 'write a custom Twig Extension'
<?php
namespace AppBundle\Extension;
class MyTwigExtension extends \Twig_Extension
{
private $em;
private $conn;
public function __construct(\Doctrine\ORM\EntityManager $em) {
$this->em = $em;
$this->conn = $em->getConnection();
}
public function getFunctions()
{
return array(
'users' => new \Twig_Function_Method($this, 'getUsers'),
);
}
public function getUsers()
{
$sql = "SELECT * FROM users ORDER BY accountname";
return $this->conn->fetchAll($sql);
}
public function getName()
{
return 'smproc4_twig_extension';
}
}
Register an Extension as a Service
services:
my.twig.extension:
class: AppBundle\Extension\MyTwigExtension
tags:
- { name: twig.extension }
arguments:
em: "#doctrine.orm.entity_manager"
Using the custom Extension
Hello {{ name }}!
<ul>
{% for user in users() %}
<li>{{ user.accountname }}</li>
{% endfor %}
</ul>
have a look at the
How to Write a custom Twig Extension
Creating an Extension

Set Twig layout in controller

I have multiple subdomains, each with its own layout. Some controllers are shared across subdomains (e.g login), some not. What I'd like to do is to set layout according to domain, so that I would not need to write in each template:
{% if app.request.domain == 'one' %}
{% set layout = '::layout-one.html.twig' %}
{% elseif app.request.domain == 'two' %}
{% set layout = '::layout-two.html.twig' %}
...
{% endif %}
{% extends layout %}
Is it possible to set default layout in controller (or somewhere)? E.g:
class FooController
{
function fooAction()
{
...
$templating = $this->get('templating');
$templating->setLayout($layout);
return $templating->renderResponse($view, $parameters, $response);
}
}
If you have a separate config file for each of the domains, you can put the layout config in there and have it available in twig as a global variable:
config_one.yml
twig:
globals:
base_layout: '::layout-one.html.twig'
Then in twig you can just do:
{% extends base_layout %}
You can set layout variable in your FooController:
class FooController
{
function fooAction()
{
...
return $this->render($template, array(
'layout' => $layout
));
}
}
And then use it in your template:
{% extends layout %}

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, ... } %}

Resources