Symfony2 Twig Inject additional blocks - php

Is there a way with Symfony2 and Twig to always make files available which just contain blocks.
For example, if I wanted to make a block named 'cookie' always available in any template in my system, without having to always include or extend it in the template I'm using.
Any ideas?
Clarification
Say I have a generic block which can do something, like:
{% block myBlock %}
ABC Examples
{% endblock %}
I have a class which knows it wants to be rendered with this block. My template itself doesn't necessarily know this though.
{{ block(myObj.blockName) }}
I would then like to have it so my controller/services/etc. could register the file which contains that block, without my template actually needing to know about it directly (so I could have multiple files like that, each working with some common interface).
Similar to registering custom Twig functions with a TwigExtension. My template doesn't need to explicitly know it's there, it just has to be available at run-time.
Does that make sense?
To clarify a bit further, I'm essentially looking to do something just like how there are default Twig blocks for rendering Forms in Symfony2. I don't have to include the default form file every time, just when I want to change something.

I went digging around in the Symfony source code to try and find my answer. It looks like there isn't some fancy, neat way to embed it from a configuration file or controller directly, which is a bit disappointing, but not a huge deal.
So, to solve my situation, I'll be using the "use" keyword to include my block file in my base template, so it will then be available to everything else.
{# widget_blocks.html.twig #}
{# Widgets #}
{% block my_widget %}
ABC Cookies
{% endblock %}
{# base.html.twig #}
{% use widget_blocks.html.twig %}
{{ block(my_widget.block) }}
Not exactly what I wanted, but sufficiently close.

{% render 'MyMainBundle:Default:credits' with {'arg1': $myObj } %}
Thats what I say. What's the difference between the line above or
{{ block(myObj.blockName) }}
You can register a custom filter but as far as I know it returns only string value. http://twig.sensiolabs.org/doc/advanced.html#id2

Related

Twig include performance with and without passing variables

I am wondering what is best perfomance-wise, using
{% include "_inc/template" %}
or
{% include "_inc/template" with {'foo': bar %}
assuming foo is the only variable used in _inc/template and it's also available in global context.
Will there be significant difference in performance between two approaches, if the include is placed within for having ~50 loops?
I don't have direct answer to your question, but you can use the profiler in dev toolbar to see the timeline of the calls.
Maybe try and tell us...
Which is the version of symfony you use?
I know that in symfony > 2.7 the timeline Graph exist.
Ps : Maybe try it with this too:
{% include 'template.html' with {'foo': 'bar'} only %}

A NULL check in Twig Template is not working which leads to an error (very strange behavior)

I’m currently facing a very strange problem in a Twig template (Symfony v2.8.9; Twig v1.24.1). The basic outline is: I’m trying to overwrite a block in a child template when a specific condition is met.
Here’s a snippet of my template:
{% extends "#App/search/resultList.html.twig" %}
{% if category.teaser %}
{% block description %}{{category.teaser.doSomething()}}{% endblock %}
{% endif %}
This code leads to the following error:
Impossible to invoke a method ("doSomething") on a null variable in #App/search/categoryResult.html.twig
The teaser property of the category indeed is null, which is okay and what the check is for.
I also tried an explicit {% if category.teaser is not null %} which also didn’t work and resulted in the same error.
Now for the really weird part (or maybe I’m just not seeing it why this wouldn’t be weird):
If I change the code to
{% block description %}
{% if category.teaser %}
{{category.teaser.doSomething()}}
{% endif %}
{% endblock %}
it works as intended (mostly, at least, because the parent block will always be overwritten).
This error seems to happen only in the Symfony dev environment.
It seems:
# Twig Configuration
twig:
debug: true
strict_variables: true
has something to do with it, which explains the dev environment only. If both values are set to false it works correctly.
Has anybody ever encountered a problem like this and solved it? Any help in solving this issue would be much appreciated.
As far as I know, it is not possible to conditionally override a block in twig, see also How can I conditionally override a TWIG layout block?
You have done the best practice by testing whether the variable is null or not.
In many other languages you can’t do something on a null. You can use the dump() function to check your variables.

Inherit dynamic template in Phalcon Volt

I need to load a page, that will be "inserted" in a template - as I read it, Volt's Template Inheritance should do the trick and it does... kinda. Hardcoded values, as shown in the examples, work fine - the following example works:
<!-- Template -->
<div id="site_content">
{% block test %}
{% endblock %}
</div>
and the page, that inherits the template:
{% extends "../../templates/de/index.volt" %}
{% block test %}
{{ content() }} {# this is a registered volt function that outputs the generated content #}
{% endblock %}
However, the same page might need to inherit a different template and that must be decided on runtime, so the name of the template must be generated dynamically. Two options occurred to me:
Set the template name to a variable and use it when extending - the problem here is that I don't see a way to use it afterwards. That guy seems to have had the same problem, but there is neither an answer of how to do it, nor a confirmation that it isn't possible at all.
Register another function to generate the complete string (e.g. {% extends "../../templates/de/index.volt" %}) and then compile it, e.g.
$compiler->addFunction('get_template',
function ($resolvedArgs, $exprArgs) use ($volt) {
return $volt->getCompiler()
->compileString('{% extends "../../templates/de/index.volt" %}');
});
and then use that function in the page, e.g.
{{ get_template() }}
{% block test %}
{{ content() }}
{% endblock %}
However, using that approach does not parse the page content (e.g. the content returned by the registered content() function is not shown). I'm also open to other solutions (using Twig instead of Volt is only a last resort, for performance issues), advices of what I'm doing wrong or pointers of useful articles on the topic. Thanks in advance!
Try using partials as documented in the Phalcon doc: Using Partials

Symfony2 fos login, register and forgot password in one view

I need to apply a purchased template to our dashboard. In this template, the login, register and forgot password forms are all under the same view, and switching between them using simple JQuery.
I have been looking for a nice, not-too-flashy way of combining all three forms into one, but I came up empty.
My standing options (as I see them), and why I don't like any of them:
Take the views from the fos bundle, copy them to /app/Resources/FOSUserBundle/views/, remove the {% extend %} part and {% include %} them in my own login view. Reason for dislike: to me this looks a little like a quick-n-dirty fix - "that part's not working? Let's break it off!" :)
Extend the fos bundle, accept an extra parameter in the LoginAction and RegisterAction, use {% render %} with parameters in my own login view. Reason for dislike: extending a whole bundle and modifying two different controllers just to change the way it renders feels like bad MVC.
XHR load everything. Reason for dislike: this approach makes sense when using inner pages, but for pages that reload anyway it just doesn't make sense.
TL;DR version: I'm looking for a non-hack way of including the login, register and forgot password form in one page.
Any help would be greatly appreciated!
I found a solution with which I am comfortable with for my current project. The advantages and disadvantages of the proposed solution upfront:
Advantages:
few LOC to implement
FOSUserBundle update proof (does not override the view scripts*)
Disadvantages:
performance overhead due to subrequests
only forms can be displayed, form submission (and subsequently error handling upon submission) will always go to the pages provided by FOSUserBundle
still feels like a quick-n-dirty fix, but better than other options
* only needs to override the layout.html.twig file
With that being said, here is what I have done:
Render the form in your template
Use embedded controllers to render the forms you need:
<div>
<h2>Login</h2>
{{ render(controller('FOSUserBundle:Security:login', { embeddedForm: true})) }}
</div>
<div>
<h2>Reset</h2>
{{ render(controller('FOSUserBundle:Resetting:request', { embeddedForm: true})) }}
</div>
Override FOSUserBundle layout
As I use the routes provided by the bundle, I had to override the FOSUserBundle layout template file to extend the standard layout of my application. As the overriden FOSUserBundle layout file extends the main applications layout file the layout would be repeated for each call {{ render ... }}. To prevent that, we need to dynamically disarm the extended layout file. Here is what the overriden layout file looks like:
{# app/Resources/FOSUserBundle/views/layout.html.twig #}
{% if app.request.get('embeddedForm') %}
{% set layout = 'AcmeBundle::layout-content.html.twig' %}
{% else %}
{% set layout = 'AcmeBundle::layout.html.twig' %}
{% endif %}
{% extends layout %}
{% block content %}
{% block fos_user_content %}{% endblock %}
{% endblock %}
Create the AcmeBundle::layout-content.html.twig file
This layout should only render the content block of the FOSUserBundle view scripts and is such short and simple:
{# src/Acme/DemoBundle/Resources/views/layout-content.html.twig #}
{% block content %}{% endblock %}
Now the forms will render nicely with all dependencies (CSRF and so forth). Submitting the form will however take you to the FOSUserBundle actions.
Alternative solution:
This answer describes how to manually implement the forms and link them to the FOSUserBundle controller.

ezpublish/symfony renders 404 in Smarty, everything else in Twig

On my eZ publish 5 site I have all my templates in Twig, in the vendor/ezsystems/demobundle/EzSystems/DemoBundle/Resources/views/ subfolders. They are all being used throughout my whole site, no problems there. With one exception: 404 pages. If I go to mysite/nonexistingurl, it gives me a kernel (20) / Error page, with status 404. The template being used for this is the 20.tpl somewhere in eZ publish/symfony, I don't want that, I want to use my own Twig template for this.
How can I achieve this? I added a vendor/ezsystems/demobundle/EzSystems/DemoBundle/Resources/views/Exception/error.html.twig page, but this one is not being called
first add this configuration parameter
parameters:
ezpublish_legacy.default.module_default_layout: 'YourBundle::pagelayout_legacy.html.twig'
you may add it in the parameters.yml file located in path/to/yourezpublishinstall/ezpublish/config, the parameters.yml is usually imported in the config.yml located in the same folder
this would define the twig template located in path/to/yourbundle/Resources/views/pagelayout_legacy.html.twig as the parent template for legacy stack modules templates
inside the pagelayout_legacy.html.twig template, you may use this code
{% extends 'YourBundle::pagelayout.html.twig' %}
{% block content %}
{# module_result variable is received from the legacy controller. #}
{% if module_result.errorCode is defined %}
<h1>{{ module_result.errorMessage }} ({{ module_result.errorCode }})</h1>
{% else %}
{{ module_result.content|raw }}
{% endif %}
{% endblock %}
note in the code, the template extends the pagelayout.html.twig template, that should here define a block named content, the pagelayout.html.twig may usually be the main base layout for your ez publish 5 website
you may modify the pagelayout_legacy.html.twig template to your needs
reference:
http://share.ez.no/forums/developer/overriding-legacy-error-pages-templates

Categories