Maximum function nesting when extending twig template for sonata admin - symfony

I am using the following template to extend specific widget
{% use 'SonataAdminBundle:Form:form_admin_fields.html.twig' %}
{% form_theme form _self %}
{% block textarea_widget %}
{% spaceless %}
<div class="textarea_widget">
<textarea {{ block('widget_attributes') }}>{{ value|raw }}
</textarea>
</div>
{% endspaceless %}
{% endblock textarea_widget %}
The main problem is that it's reaching maximum function nesting. There must be some infinite recursion when extending template. When i copied code from extended template everything works. Any idea how to make this working?

This is probably caused by xDebug that you must configure with xdebug.max_nesting_level parameter. Default is 100 but I usually upgrade it to 1000 to be quiet.
On a Debian, it's in /etc/php5/conf.d/xdebug.ini.
Official doc about this parameter:
xdebug.max_nesting_level Type: integer, Default value: 100
Controls the protection mechanism for infinite recursion protection.
The value of this setting is the maximum level of nested functions
that are allowed before the script will be aborted.

to fix this error i needed to create form_admin_fields.html.twig file in app/Resources/SonataAdmin/views/Form with the following content:
{% block textarea_widget %}
{% spaceless %}
<div class="textarea_widget">
<textarea {{ block('widget_attributes') }}>{{ value|raw }} </textarea>
</div>
{% endspaceless %}
{% endblock %}
So there is no necessity to use or extend template just redefine some block.

Related

Twig Runtime Error: Impossible to invoke a method ("test") on a string variable

I have the following twig template (the code is in the same file):
{% macro renderJob(fields) %}
// renders the job UI block, but I've removed it for simplicity
Hello world.
{% endmacro %}
{% block _jobs_widget %}
<div id="jobsContainer">
{% for fields in form.children %}
{% dump fields %}
{{ _self.renderJob(fields) }}
{% endfor %}
</div>
{% endblock %}
For some reason, after upgrading to twig/twig = v2.1.0 I'm receiving the follwing error:
Impossible to invoke a method ("renderJob") on a string variable ("#AppBundle/Jobs/form/job.html.twig").
I have been trying to figure out what's causing this without any luck. This used to work just fine in 1.3.x. The fields variable contains the proper data, but it appears it can't pass it to the renderJob macro or it can't find the macro (which is kind of odd)?
Have you tried the following ?
{% import _self as renderJobMacro %}
{% macro renderJob(fields) %}
// renders the job UI block, but I've removed it for simplicity
Hello world.
{% endmacro %}
{% block _jobs_widget %}
<div id="jobsContainer">
{% for fields in form.children %}
{{ renderJobMacro.renderJob(fields) }}
{% endfor %}
</div>
{% endblock %}
I think _self is depricated from twigg 2.0, May be you need to check without _self.
Check {{ renderJob(fields) }} instead of {{ _self.renderJob(fields) }}

Symfony Twig customize an Individual field for a collection

In the documentation there is a way in Symfony to customize a Individual field, based on the name/id of the widget.
{% form_theme form _self %}
{% block _product_name_widget %}
<div class="text_widget">
{{ block('field_widget') }}
</div>
{% endblock %}
{{ form_widget(form.name) }}
Here, the _product_name_widget fragment defines the template to use for the field whose id is product_name (and name is product[name]).
This works for a normal widget, but not if a widget is inside a collection. Because of the extra columns. Like this:
name="productbundle_product_type[foobar][1][value]" id="productbundle_product_type_foobar_1_value"
What is the way to make the Twig customization work inside the collection?
I thought something like this, but that doesn't work:
{% for db in edit_form.list %}
{% block _productbundle_product_type_foobar_{{ db.name }}_widget %}
<div class="text_widget">
{{ block('field_widget') }}
</div>
{% endblock %}
{% endfor %}
Even the following doesn't work:
{% _productbundle_product_type_foobar_1_value_widget %}
What is the way to make it work?
I was working on a project a couple of evenings ago and encountered precisely this problem - the solution I found is a pair of blocks that look like this (stripped of project-specific code):
{# Collection markup #}
{% block my_collection_widget %}
{# Customise collection row prototype for allow_add #}
{% if prototype is defined %}
{% set data_prototype = block('my_collection_item_widget') %}
<div id="my_prototype" data-prototype="{{ data_prototype }}" style="display: none"></div>
{% endif %}
{% for prototype in form %}
{{ block('my_collection_item_widget') }}
{% endfor %}
{% endblock my_collection_widget %}
{# Collection row markup #}
{% block my_collection_item_widget %}
{# Collection contains simple, single type #}
{{ form_errors(prototype) }}
{{ form_label(prototype) }}
{{ form_widget(prototype) }}
{# OR #}
{# Collection contains a compound type, render child types independantly #}
{{ form_errors(prototype.inner_widget) }}
{{ form_label(prototype.inner_widget) }}
{{ form_widget(prototype.inner_widget) }}
{% endblock my_collection_item_widget %}
I know this is old question, but maybe people still happening upon this. This is explained fragment naming for collections.
You use _entry_ in these cases in place of the collection element number. Use the instructions in the link for fragment naming, but this might vary. Sometimes 'type' is part of the fragment's name, sometimes first letter is upper case, sometimes lower case, etc. I would use a browser developer tool to find the actual name to make sure. You might also be able to customize the names used by adding the getBlockPrefix function to the form class.
Therefore, in your case above, the customized block might look something like:
{% block _ProductBundle_product_entry_widget %}
<div> {{ form_row(form.field)}} </div>
{% endblock %}
Where 'field' would be the name of a field in the element of your collection.
A few things here:
1. Documentation Error
The docs seem to be a bit off with CamelCase entities. As for 5.3 it should be: _taskManager_taskLists_entry_widget (instead of _task_manager_task_lists_entry_widget)
You can confirm the naming by dumping the form or form field: {{ dump(form) }} or {{ dump(form.yourField) }} in your template and then look for unique_block_prefix within the vars section.
2. Do NOT use the block overrides, macros are much more easy
It makes things absolutely more complicated than necessary. Simply define a macro:
{% import _self as formMacros %}
{% macro formatCollection(form) %}
<div class="form-group row">
<div>
{{ form_widget(form.field1) }}
{{ form_widget(form.field2) }}
{{ form_widget(form.field3) }}
</div>
</div>
{% endmacro %}
then simply run it on each collection:
{% for item in form.collectionItems %}
{{ formMacros.formatCollection(item) }}
{% endfor %}
3. Prototype
Get the prototype before rendering the collection field. You can store it in a variable.
{% set prototype = form.collection.vars.prototype %}
Then simply render it whenever you like using our macro:
<div data-js="collection" data-prototype="{{ formMacros.formatCollection(prototype)|e('html_attr') }}">

Control the way form validation errors are displayed in Symfony2

How do I globally change the way form validation errors are displayed in Symfony2? For example, if I want to wrap each one of my error messages in a <span class="error"> or something like that, how would that be done?
Note: I'm aware of this question/answer, but I'm not sure that it's the same question as mine, and I don't understand how to apply the selected answer.
Take a look at the docs: custom global theme, customizing error output
{# SomeBundle:Layout:fields.html.twig #}
{% block field_errors %}
{% spaceless %}
{% if errors|length > 0 %}
<span class="error">
{% for error in errors %}
{{ error.messageTemplate|trans(error.messageParameters, 'validators') }}<br />
{% endfor %}
</span>
{% endif %}
{% endspaceless %}
{% endblock field_errors %}
{# In your form template #}
{% form_theme form 'SomeBundle:Layout:fields.html.twig' %}

Symfony 2 custom themes folder

I'm designing a multi-tenant application with Symfony2, There will be common templates and each tenant could have custom templates. I would like create a theme folder like this(like wordpress with css,img,etc...) :
Themes/commons/base.twig.html
Themes/commons/css/styles.css
Themes/commons/js/script.js
Themes/tenantID/base.twig.html
Themes/tenantID/css/styles.css
Themes/tenantID/js/script.js
Perhaps I'm taking a wrong way...?
Any suggestion ?
Thanks.
There is nothing wrong with your design. I may name "commons" as "default" but thats up to you. Approach with tenantid looks good to me. Whats your question?
https://github.com/fabpot/Twig/issues/17 - no dynamic inheritance
LiipThemeBundle might be a solution: http://symfony2bundles.org/liip/LiipThemeBundle
You can achieve all this by following symfony standard . because if you follow structure you will make full stack full site in frame work and you can also learn how to use frame work.
like in
bundle folder:
userbulndle/css
userbulndle/js
adminbundle/css etc
and use theme forming
{% block gender_widget %}
{% spaceless %}
{% if expanded %}
{% for child in form %}
<div class="radio_ele">
{{form_widget(child) }}
{{form_label(child) }}
</div>
{% endfor %}
{% else %}
{{ block('choice_widget') }}
{% endif %}
{% endspaceless %}
{% endblock %}
{# ----------------------------------------------------------- #}
{% block field_errors %}
{% spaceless %}
{% if errors|length > 0 %}
<div class="error_list">
{% for error in errors %}
{{ error.messageTemplate|trans(error.messageParameters, 'validators') }}
{% endfor %}
</div>
{% endif %}
{% endspaceless %}
{% endblock field_errors %

How to customize a CollectionType() item?

This ยง of the Symfony2 documentation shows an awesome technique for customizing an individual field. I'm using it a lot but there is one particular Type for which I'm having a hard time doing the customization : the CollectionType() Customizing the collection itself is quite easy, you can do something like this:
{% block _mynamespace_mybundle_mytype_mycollectionfield_row %}
{% if prototype is defined %}
{% set attr = attr|merge({'data-prototype': form_widget(prototype) }) %}
{% endif %}
{% spaceless %}
<ul {{ block('widget_container_attributes') }}>
{% spaceless %}
{{ form_errors(form) }}
{% for child in form %}
<li>
{{form_widget(child)}}
</li>
{% endfor %}
{% endspaceless %}
{{ form_rest(form) }}
</ul>
{% endspaceless %}
{% endblock %}
But how can you customize each element of the collection ? And how can you customize the data-prototype using Twig (the data-prototype is a special attribute used to add new items with js)?
I tried doing something like this (for the data-prototype):
{% block _mynamespace_mybundle_mytype_mycollectionfield_$$name$$_row %}
customization ok!
{% endblock %}
But I get errors, because I don't know how to escape the $
Regarding the items, I tried many things:
{% block _mynamespace_mybundle_mytype_mycollectionfield_item_subfield_row %}
customization ok!
{% endblock %}
{% block _mynamespace_mybundle_mytype_mycollectionfield_element_subfield_row %}
customization ok!
{% endblock %}
None of them work.
I asked the question in symfony's bug tracker, and it seems there is no solution, but a good workaround is creating a custom type for each subfield you want to customize. See this issue

Resources