Symfony2: setFlash with basic html in the message - symfony

I'm trying to set a flash message for my template that includes very basic anchor to another page in the app.
Using the standard method apparently won't do the job:
$this->get('session')
->setFlash('message', 'Some link');
I found some suggestions in Symfony's 2.0 forums, but they don't work either:
$this->get('session')
->setFlash('message', sprtintf('Some %s', 'link'));
$this->get('session')
->setFlash('message', sprtintf('Some %s', link_to('routeHere', 'link')));
Edit
In my template I render all flashes in the most common way:
{% for label, flash in app.session.getFlashes() %}
<div class="message {{ label }}">
{{ flash }}
</div>
{% endfor %}
What am I doing wrong here?

The content of {{ flash }} is escaped by Twig automatically. You need to use the raw filter like
{{ flash|raw }}

Related

Symfony : easy way to format FORMS with CSS?

We're building a web site with Symfony 2. We generate a unique URL and send it by email to user who forgot their password, so they can reset their password.
We're building a simple form to reset a password. We have two labels ('Enter your new password' and 'Enter your new password again') with a textbox beside each.
We wanted the textbox to align with each other.
Lazy solution was to figure out two strings of the same length (!)
But I would have wanted to format them with CSS or put them in a table ...
Is that possible at all with Symfony's form ? I read documentation about customizing templates, but when we tried the solution proposed by Symfony's docs the widgets (textboxes) were not rendering ...
Here are some pics of the issue :
Crooked textboxes
Lazy solution
Here is the code of the twig where I think(!) the formatting should be done
{% block blockPrincipal_mp %}
<h1>{{ titre }}</h1>
{{ form_start(form) }}
<div class="containerForm">
<div class="error">
{{ form_errors(form) }}
</div>
{{ form_rest(form) }}
{{ form_end(form) }}
{% for flashMessage in app.session.flashbag.get('success') %}
<div class="confirm"><p> {{ flashMessage }}</p></div>
{% endfor %}
</div>
{% endblock %}
You can render all the different elements of the forms individually as opposed to just rendering it all at one time with form_rest(form) as you have in your example. form_rest() is going to render whatever hasn't been rendered yet. And up to this point, All that's been rendered are the errors.
I don't know what your form property's names are but here's an example:
{{ form_start(form) }}
<div class="form_errors">{{ form_errors(form) }}</div>
{# output all pieces of the username element individually #}
<div class="form_label">{{ form_label(form.username) }}</div>
<div class="form_input">{{ form_widget(form.username) }}</div>
<div class="form_errors">{{ form_errors(form.username) }}</div>
{# output all pieces of the password element individually #}
<div class="form_label">{{ form_label(form.password) }}</div>
<div class="form_input">{{ form_widget(form.password) }}</div>
<div class="form_errors">{{ form_errors(form.password) }}</div>
{{ form_rest(form) }}
{{ form_end(form) }}
This way you can control what HTML wrappers surround each piece of your form elements.
Note that you can also output the username and password fields by doing...
{{ form_row(form.username) }} {{ form_row(form.password) }}
...and it will still output the label, widget and errors but will use the default layout for those form types that is defined in your twig templates. So you have more control of rendering the parts if you do them individually.
This is great for custom forms and custom templates, however you can also override the default form element's layout if you want more control over how individual form elements are rendered throughout your site, by extending the form fields template.
https://symfony.com/doc/current/form/form_customization.html
The RepeatedType field can be dispayed separately:
{{ form_row(form.password.first) }}
{{ form_row(form.password.second) }}
or more controlled:
{{ form_label(form.password.first) }}
{{ form_widget(form.password.first) }}
{{ form_label(form.password.second) }}
{{ form_widget(form.password.second) }}

How to assign class to invalid Symfony 2 form field, rendered with Twig?

Lets assume that few fields values of my form, build on Symfony 2 and rendered with Twig are not valid and I received validation errors. I want not only to see this errors, but also assign special class to each invalid field.
For example:
<input type="text" class="error">
How can I do that? As I understand, there is need to redeclare my form template. Is there any working example how to assign attributes in case of concrete field validation failure.
All I found now, is that I need to set this class in form template:
{% set attr = attr|merge({'class': attr.class|default('') ~ (errors|length > 0 ? ' error' : '') }) %}
But what I don't understand is how to specify exact field? Any help appreciated.
This works for me:
<div class="input{{ form_errors(form.expiry) == '' ? '' : 'error' }}">
{{ form_widget(form.expiry) }}
</label>
You could also do
{{ form_widget(form.expiry, {'attr': {'class': form_errors(form.expiry) == '' ? '' : 'error'}}) }}
If you use
{{ form(form) }}
for showing your form, I am quite sure you can not accomplish what you want, or at least I am not aware of the possiblity.
If you use something like this
{{ form_row(form.task) }}
{{ form_row(form.dueDate) }}
I am still quite sure you can not get what you want.
My solution for what you need would be to make something like this:
{{ form_start(form) }}
{{ form_errors(form) }}
<div>
{{ form_label(form.task) }}
{{ form_errors(form.task) }}
{{ form_widget(form.task) }}
</div>
<div>
{{ form_label(form.dueDate) }}
{{ form_errors(form.dueDate) }}
{{ form_widget(form.dueDate) }}
</div>
<input type="submit" />
{{ form_end(form) }}
and to simply get information about validation errors from form object and then to replace {{ form_widget(form.task) }} with something like this
{{ form_widget(form.task, {'attr': {'class': 'error'}}) }}
in case that field task failed the validation.
Even more slow and time consuming solution would be to make small twig files that each and every one would actualy represent "your" design for view of each form field and then to call those "little twigs" with an argument which would again come from form object which contains those data about bad validation.
You can read more about form rendering where you actualy make your own form field designs here
http://symfony.com/doc/current/cookbook/form/form_customization.html

How are labels on ArrayCollections in forms rendered?

I have a Customer object, which has many Emails.
I'm building a form for my customer, and I've added his emails as a collection. In my template, I render the emails portion like this:
<h4>Emails</h4>
{% for email in form.emails %}
<li>
{{ form_row(email.addr) }}
{{ form_row(email.isPrimary) }}
</li>
{% endfor %}
...
{{ form_rest(form) }}
This works fine, except if the customer has no emails. Then, form_rest() renders the label 'emails' at the bottom of my template.
Why does this only get rendered when form.emails is empty? How can I customize it? (Note I've already customized my label rendering for other form elements, and I don't want it to be the same for these 'collection labels'.)
I usually solved this problem this way:
{% for email in form.emails %}
{# ... #}
{% else %}
{{ form_widget(form.emails) }}
{% endfor %}
Unless someone suggests a better way of doing this.

Flash Message disappears on redirect in Symfony 2.1

I'm migrating from Symfony 2.0 to Symfony 2.1.
I have the following simple code on my controller:
public function createEntidadeAction() {
$this->get('session')->getFlashBag()->set('error', 'message');
return $this->redirect($this->generateUrl('EntidadeBundle_index'));
}
If I generate an error (for example by passing a bad route), I check on the profiler that the flash message is there.
However if i let the redirect to succeed, the flash message disappears and nothing is displayed. I have the folloing on my corresponding Twig template:
{% for flashMessage in app.session.flashbag.get('error') %}
<div class="flash-notice">
{{ flashMessage }}
</div>
{% endfor %}
I can not figure this out. What am I missing? Flash messages should last after the first redirect, no?
First, try using the add method instead of set on the flash bag. Second, try this template which works for me:
{% for type, flashMessages in app.session.flashbag.all() %}
{% for flashMessage in flashMessages %}
<div class="alert alert-{{ type }}">
{{ flashMessage|trans }}
</div>
{% endfor %}
{% endfor %}
I figured it out.
Flash messages were not appearing due to session issues.
Symfony 2.1 now uses session.storage.native for storage_id and handler_id by default.
Please check how this session issue was solved here.

Extending twig for generate html code

I have to generate something like star rating and I have to generate some html for styling ect.
<div class="star on"><i>*</i></div>
<div class="star on"><i>*</i></div>
<div class="star on"><i>*</i></div>
<div class="star"><i></i></div>
<div class="star"><i></i></div>
I want to render using a twig function passing active stars parameters.
{{ stars(4) }}
Is correct use twig functions for generate html code?
Or maybe should I use {% include ... %}
No need in overengineering for such simple task.
If you generate your array in Controller, then it could look like this:
$stars = array(
true,
true,
true,
false,
false,
);
Then you could render your HTML in Twig:
{% for star in stars %}
<div class="star{{ star ? ' on' }}"<i>{{ star ? '*' }}</i></div>
{% endfor %}
In case if you would like to operate with Twig only, I recommend you to use macro:
{% macro stars(stars, total) %}
{% for item in 1..total %}
{{ item }}<br>
{% if item <= stars %}
<div class="star on"><i>*</i></div>
{% else %}
<div class="star"><i></i></div>
{% endif %}
{% endfor %}
{% endmacro %}
If you've defined your macro in the same template, you should call it via _self, if in another file - just like a function, but not forget to import your file into needed twig. See chapter about macros (linked above).
Following call will produce HTML structure that you described in your question:
{{ _self.stars(3,5) }}
See the Extending Twig section of its docs. According to the table in the first section on that page, using functions for content generation is natural. I create a lot of Twig functions and I suggest you create one to solve your problem.
BTW, your function can render a separate template with HTML code — do not generate the HTML code right in your Twig function's PHP code. To render a separate template from your Twig function, inject the service_container service into it, get the templating service and call the render() method on it:
return $this->container->get('templating')->render($pathToYourCustomTemplate);
Usually, it's best to inject the needed services individually, but if you inject the templating service instead of service_container, you'll get a cyclic dependencies problem. That's why injecting the whole container into Twig extensions is a reasonable exception.

Resources