Create event in symfony2 to render a twig file - php

I'm new to symfony2 and I'm looking for solution to create an event that render a twig file.
Assume we have a base template
<!DOCTYPE html>
<html>
<head>
{% block meta %}{% endblock %}
<title>{% block title %}Welcome!{% endblock %}</title>
{% block stylesheets %}{% endblock %}
</head>
<body data-cachetime="{{ cacheTimeStamp() }}">
{% block header %}{% endblock %}
<div id="content">
<div class="container">
{% block content %}{% endblock %}
</div>
</div>
{% block footer %}{% endblock %}
{% block version %}{% endblock %}
{% block javascripts %}{% endblock %}
{% block trackers %}{% endblock %}
</body>
</html>
As you can see, I've a "version" block. I've created an VersionBundle which read the version from a file. The version twig template
{% block version %}
<div id="version"><p>Version: {{ versionString() }}</p></div>
{% endblock %}
calls an "ViewHelper" (i came from Zend ;-)) which calls the function from VersionBundle. But now the tricky part:
The VerionBundle is only registered for 'dev' and 'test' Environment in the AppKernel.
Thats why i create the 'version' block instead of calling the ViewHelper directly in the base twig file.
But i don't know how to create an event to render the version twig template first so the data will passed to the base twig.

I would maybe change the way you try to do it, using more Symfony features and avoiding defining Zend like "helpers". It's a good rule of thumb to keep Twig as minimal as it can be, just rendering view from data the templates receive.
First, define the version variable in parameters_dev.yml and parameters_test.yml files (in app/config). Example:
parameters:
version: 32
Define it as version: ~ in parameters.yml.
Make this variable available in all Twig templates in the app/config/config.yml file:
twig:
globals:
version: %version%
Then, in your version block definition, do the following:
{% block version %}
{% if version is not null %}
<div id="version"><p>Version: {{ version }}</p></div>
{% endif %}
{% endblock %}
Hope it helps!

Related

Symfony, Twig. An advanced way to connect styles and scripts for a page [duplicate]

I would like to inject inside the styles and scripts blocks in my layout new values, but from the embed block.
Of course it throws the error Calling "parent" outside a block is forbidden..
Is there any workaround ?
layout.html.twig:
<!DOCTYPE html>
<html>
<head>
{% block style %}
<link rel="stylesheet" href="foo.css">
{% endblock %}
</head>
<body>
{% block content "" %}
{% block scripts %}
<script src="foo.js"></script>
{% endblock %}
</body>
</html>
list.html.twig:
{% extends 'layout.html.twig' %}
{% block content %}
{% embed datatable.html.twig %}
{% block tbody %}
<tr>
<td>my awesome table</td>
</tr>
{% endblock %}
{% endembed %}
{% endblock %}
datatable.html.twig:
<table id="myDatatable">
<tbody>
{% block tbody "" %}
</tbody>
</table>
{% block styles %}
{{ parent() }}
<link rel="stylesheet" href="dataTables.css">
{% endblock %}
{% block scripts %}
{{ parent() }}
<script src="dataTables.js"></script>
{% endblock %}
(And I cant/wont use the blocks scripts and styles inside the list.html.twig. They are part of the datatable template, it will not make any sens to define theme in the list.html.twig.).
And sadly I cant use use because this function does not support dynamics properties, only strings.
From the doc :
Because use statements are resolved independently of the context passed to the template, the template reference cannot be an expression.
As said in the comment, includes/embeds can't alter blocks from their includer.
That said there is an extension available that could solve your problem.
This Deferred Twig Extension can be found here
Basically the node postpones the execution of a said block.
This way you can create a variable that holds all of your javascript links and output them. This can be seen in the advanced example found on the github.
credits to Eugene Leonovich for making this extension

render option twice in base.html.twig

I got base.html.twig file inside i wanna render two controller
Is that is possible ?
</head>
<body>
{% block header %}{{ render(controller('App\\Controller\\FrontController::front_header')) }}{% endblock %}
{% block content %}{% endblock %}
{% block footer %}{{ render(controller('App\\Controller\\FrontController::front_footer')) }}{% endblock %}
</body>
{% block javascripts %}{% endblock %}
or what way is the best to handle something like that ?
becouse frontcontroller render anothe index.html.twig which will get some stuff to block content and i wanna have this block content between two another controllers.
why not simply use Twig include for your header and your footer?

Symfony : how to dynamically include assets and defer their loading at the end of the page?

working on a Symfony PHP CMS-based project, I'm having assets loading questions.
We created several types of widgets available in content management. That allows contributors to add one or several widget on their pages. Each type of widget has its own styles and scripts.
In actual state, we load every widgets assets at the end of the page even if the page does not contain each type of widget.
In order to optimize page weight and loading, we'd like to have a mecanism that add a call to one widget assets (during the widget rendering) and defer their loading at the end of the page.
Do you know a tool that could do the job ?
Not possible to show the whole code, but in short, here is what I'd like to do :
<body>
<article>
<widget_one>// I need #widget_one assets</widget_one>
<widget_two>// I need #widget_two assets</widget_one>
.....
.....
</article>
<assets>// now I load all needed assets</assets>
</body>
I came up with you can use Using Named Assets:
# app/config/config.yml
assetic:
assets:
widget_one:
inputs:
- '#AppBundle/Resources/public/js/widget_one_first_js.js'
- '#AppBundle/Resources/public/js/widget_one_second_js.js'
widget_two:
inputs:
- '#AppBundle/Resources/public/js/widget_two_first_js.js'
- '#AppBundle/Resources/public/js/widget_two_second_js.js'
After that in your template you use it -> if you have widget_one
{% javascripts
'#widget_one'
<script src="{{ asset_url }}"></script>
{% endjavascripts %}
if you have widget_one
{% javascripts
'#widget_two'
<script src="{{ asset_url }}"></script>
{% endjavascripts %}
You should use template inheritance: For example base.html.twig
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>{% block title %}Welcome!{% endblock %}</title>
{% block stylesheets %}{% endblock %}
</head>
<body>
{% block body %}{% endblock %}
{% block javascripts %}{% endblock %}
</body>
</html>
After that:
{% extends '::base.html.twig' %}
{% block javascripts %}
{% for widget in widgets %}
INCLUDE ASSETS
{% endfor %}
{% endblock %}
{% block body %}
{% for widget in widgets %}
SHOW WIDGET!
{% endfor %}
{% endblock %}

Difference between Include, Extends, Use, Macro, Embed in Twig

What is the difference between use and include in Twig?
Documentation:
include
The include statement includes a template and returns the rendered content of that template into the current one:
{% include 'header.html' %}
Body here...
{% include 'footer.html' %}
use
The use statement tells Twig to import the blocks defined in blocks.html into the current template (it's like macros, but for blocks):
blocks.html
{% block sidebar %}{% endblock %}
main.html
{% extends "base.html" %}
{% use "blocks.html" %}
{% block title %}{% endblock %}
{% block content %}{% endblock %}
Possible answer:
I think this should explain the difference:
include is to get all the code from an external file and import it
into your actual file at the right location of the call.
use is completely different as it parses the linked file to find a
particular section of code and then overwrites the blocks with the
same name, in your current file, with the one found in this external
file.
include is like "go find this file and render it with my page here".
use is "parse this other file to find block definitions to use instead
of my owns defined here".
If use command finds nothing matching the task, nothing is displayed
at all from this file.
Question
is the explanation correct? are there any other explanations to this difference?
After months, I am posting an answer for any further reference to this question. I also added some description for extends & import & macro & embed for more clearance:
There are various types of inheritance and code reuse in Twig:
Include
Main Goal: Code Reuse
Use Case: Using header.html.twig & footer.html.twig inside base.html.twig.
header.html.twig
<nav>
<div>Homepage</div>
<div>About</div>
</nav>
footer.html.twig
<footer>
<div>Copyright</div>
</footer>
base.html.twig
{% include 'header.html.twig' %}
<main>{% block main %}{% endblock %}</main>
{% include 'footer.html.twig' %}
Extends
Main Goal: Vertical Reuse
Use Case: Extending base.html.twig inside homepage.html.twig & about.html.twig.
base.html.twig
{% include 'header.html.twig' %}
<main>{% block main %}{% endblock %}</main>
{% include 'footer.html.twig' %}
homepage.html.twig
{% extends 'base.html.twig' %}
{% block main %}
<p>Homepage</p>
{% endblock %}
about.html.twig
{% extends 'base.html.twig' %}
{% block main %}
<p>About page</p>
{% endblock %}
Use
Main Goal: Horizontal Reuse
Use Case: sidebar.html.twig in single.product.html.twig & single.service.html.twig.
sidebar.html.twig
{% block sidebar %}<aside>This is sidebar</aside>{% endblock %}
single.product.html.twig
{% extends 'product.layout.html.twig' %}
{% use 'sidebar.html.twig' %}
{% block main %}<main>Product page</main>{% endblock %}
single.service.html.twig
{% extends 'service.layout.html.twig' %}
{% use 'sidebar.html.twig' %}
{% block main %}<main>Service page</main>{% endblock %}
Notes:
It's like macros, but for blocks.
The use tag only imports a template if it does not extend another template, if it does not define macros, and if the body is empty.
Macro
Main Goal: Reusable Markup with Variables
Use Case: A function which gets some variables and outputs some markup.
form.html.twig
{% macro input(name, value, type) %}
<input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" }}" />
{% endmacro %}
profile.service.html.twig
{% import "form.html.twig" as form %}
<form action="/login" method="post">
<div>{{ form.input('username') }}</div>
<div>{{ form.input('password') }}</div>
<div>{{ form.input('submit', 'Submit', 'submit') }}</div>
</form>
Embed
Main Goal: Block Overriding
Use Case: Embedding pagination.html.twig in product.table.html.twig & service.table.html.twig.
pagination.html.twig
<div id="pagination">
<div>{% block first %}{% endblock %}</div>
{% for i in (min + 1)..(max - 1) %}
<div>{{ i }}</div>
{% endfor %}
<div>{% block last %}{% endblock %}</div>
</div>
product.table.html.twig
{% set min, max = 1, products.itemPerPage %}
{% embed 'pagination.html.twig' %}
{% block first %}First Product Page{% endblock %}
{% block last %}Last Product Page{% endblock %}
{% endembed %}
service.table.html.twig
{% set min, max = 1, services.itemPerPage %}
{% embed 'pagination.html.twig' %}
{% block first %}First Service Page{% endblock %}
{% block last %}Last Service Page{% endblock %}
{% endembed %}
Please note that embedded file (pagination.html.twig) has access to the current context (min, max variables).
Also you may pass extra variables to the embedded file:
pagination.html.twig
<p>{{ count }} items</p>
<div>
<div>{% block first %}{% endblock %}</div>
{% for i in (min + 1)..(max - 1) %}
<div>{{ i }}</div>
{% endfor %}
<div>{% block last %}{% endblock %}</div>
</div>
product.table.html.twig
{% set min, max = 1, products|length %}
{% embed 'pagination.html.twig' with {'count': products|length } %}
{% block first %}First Product Page{% endblock %}
{% block last %}Last Product Page{% endblock %}
{% endembed %}
Note:
It has functionality of both Use & Include together.
Twig performance is spectacular, so chances are you will never care, but be aware that there is a significant difference between using embeds, includes, or macros.
I set up a little test project to demonstrate it: https://github.com/janklan/twig-benchmark
The readme has all the information you need if you're interested to know more, but the general result is, that for the same task executed in a loop of 100k iterations, the times between individual methods on my computer were as follows (lower is better)
embed: 4253ms
include: 3428ms
macro: 2695ms
By the way, the project I shared via Github up generates separate cache directories for each approach (embed/include/macro). If you want to see how different methods in Twig map to the compiled PHP, I recommend you check it out. If you wish to see the cache without having to run the project, check out a branch called with-cache

Twig included template extending parent's block once

Is there a way to do this? I have a template which outputs one blog article.
Now, on index page I show 10 articles by including that template in for loop and on show page I only show one.
index:
{% block stylesheets %}
{# some stylesheets here #}
{% endblock %}
{% for article in articles %}
{% include VendorBundle:article.html.twig with { 'article': article } %}
{% endfor %}
show:
{% block stylesheets %}
{# some stylesheets here #}
{% endblock %}
{% include VendorBundle:article.html.twig with { 'article': article } %}
Now is there a way to make article.html.twig add something to {% block stylesheets %} of templates that included it automatically? If it is possible, how do I prevent it from adding that 10 times when using for loop?
I'm trying to make my "fragment" templates (templates used for inclusion) define stylesheets that they use and make them "inject" those into page.
Did you try to use use?
Unfortunately I'm not completely sure if I got the question right but {% use %}wasn't mentioned here.
As I understand the question you've got your article.html.twig and include it in e.g. index.html.twig. Now you want to add something from article.html.twig into index.html.twig? Namely to the {% stylesheets %} block.
If I got how to use {% use %} you could maybe try it like this.
article.html.twig
{% block stylesheets %}
<link rel="stylesheet" href="{{ asset('bundles/mybundle/css/article.css') }}" type="text/css" />
{% endblock %}
{% block article %}
{# whatever you do here #}
{% endblock %}
index.html.twig
{% use "VendorBundle:article.html.twig" with stylesheets as article_styles %}
{% block stylesheets %}
{{ block('article_styles') }}
{# other styles here #}
{% endblock %}
{% for article in articles %}
{% include VendorBundle:article.html.twig with { 'article': article } %}
{% endfor %}
I have not the chance to test it but the docu states a few very interesting things and it seems like this could be the way to do it.
Horizontal reuse is an advanced Twig feature that is hardly ever needed in regular templates. It is mainly used by projects that need to make template blocks reusable without using inheritance.
I am rather new to stackoverflow. So please if my answer is completely useless could you just post a comment before down voting and I delete it?
However if it does help and there are just some errors in my example, also inform me and I'll fix it.
You can use a new block (not tested):
{# index.html.twig #}
{% block stylesheets -%}
{% block article_styles '' %}
{%- endblock %}
{% for ... -%}
{% include VendorBundle:template.html.twig with {'article': article} %}
{%- endfor %}
{# template.html.twig #}
{% block article_styles -%}
{{ parent() }}
<link rel=stylesheet href=...>
{%- endblock %}
{# ... #}
Edit: Added {{ parent() }}, this will print every content the block already has.

Categories