I created a global Action :
public function export(
Admin $user,
StructureRepository $structureRepository,
ExportCsvEntity $exportCsvEntity,
string $entityClass
): HttpFoundationResponse {
$zipcodes = $structureRepository->getZipcodesByStructureFromAdmin($user->getId());
$exportCsvEntity->export(
$zipcodes,
$entityClass
);
return $exportCsvEntity->download($entityClass);
}
public function exportButton(): Action
{
return Action::new('export', 'admin.crud.user.field.activities_tracking.button.export')
->linkToCrudAction('export')
->displayAsLink()
->setCssClass('btn btn-primary')
->createAsGlobalAction()
;
}
Then in my Crud Controller I call it :
if ($this->getUser() instanceof Admin) {
$export = $this->exportAction->exportButton();
$actions->add(Crud::PAGE_INDEX, $export);
}
In the doc its written => Global actions are displayed above the listed entries.
But in my case the button is underneath the table
Have a look here
My template is extending '#!EasyAdmin/crud/index.html.twig'then I override the global_actions block :
{% block global_actions %}
{{ parent() }}
{% endblock global_actions %}
Now my button is above the table but also underneath :
Have a look here
What do I do wrong ?
You are correct when trying to do this by overriding the index template.
An easy way to do this considering how the template is organized is to modify the global_actions block by filtering actions you do not want to show above the list. For example by using a css class to not show a global action above the list.
{% block global_actions %}
<div class="global-actions">
{% for action in global_actions|filter(a => 'under-list' not in a.cssClass) %}
{{ include(action.templatePath, { action: action }, with_context = false) }}
{% endfor %}
</div>
{% endblock global_actions %}
And in your crud controller:
Action::new('customAction', $label, $icon)
->addCssClass('under-list')
->createAsGlobalAction()
->linkToCrudAction('customAction');
And you need to add your new list of actions under your list by overriding the main block.
{% block main %}
{{ parent() }}
{% block under_list_global_actions %}
<div class="under-list-global-actions">
{% for action in global_actions|filter(a => 'under-list' in a.cssClass) %}
{{ include(action.templatePath, { action: action }, with_context = false) }}
{% endfor %}
</div>
{% endblock under_list_global_actions %}
{% endblock main %}
And you should get your custom global action (with the css class under-list) under your list.
In my model (Task) I have a function:
public function isTaskOverdue()
{
if ("now"|date('Y-m-d') > task.deadline|date('Y-m-d')){
return false;
} else{
return true;
}
}
In twig (edit) I want to display form:
{% extends 'base.html.twig' %}
{% block title %}app:Resources:Task:edit{% endblock %}
{% block body %}
{{ form(form) }}
{% endblock %}
I want to display form, if this function return true.
How can I call this function in twig?
Pass the task entity to twig and call method from object task :
{% if task.isTaskOverdue %}
{{ form(form) }}
{% endif %}
I think it should be your controller that receives the function result and display the form or not depending on it.
Also you can write your function like so :
public function isTaskOverdue()
{
return ("now"|date('Y-m-d') > task.deadline|date('Y-m-d'));
}
Pass the task entity to twig and do :
{% extends 'base.html.twig' %}
{% block title %}app:Resources:Task:edit{% endblock %}
{% block body %}
{% if "now"|date("Ymd") <= task.deadline|date("Ymd") %}
{{ form(form) }}
{% endif %}
{% endblock %}
But, caution :
If you just not display the form, there is a security issue, because if an attacker submit the form from an self rebuilded HTML page, your controller will receive the form data and apply it.
So I would do the check in the controller, and only create and pass the form to the twig template if the condition is true.
Then, in twig you can use :
{% if form is defined %}
{{ form(form) }}
{% endif %}
In my app I'm trying to display form help block using tips from official Symfony2 cookbook. Here is my code:
{% extends 'form_div_layout.html.twig' %}
{% block form_widget_simple %}
{{ block('base_form_widget_simple') }}
{{ dump(help) }}
{% if help is defined %}
<span class="help">{{ help }}</span>
{% endif %}
{% endblock %}
And using this theme:
{{ form_row(form.pageTitle, {'help': 'some help'}) }}
With this I'm getting error Variable "help" does not exist. Any ideas what have I missed?
P.S. I use Symfony 2.7.1.
Create a Form type extension extending the form type:
namespace Acme\AppBundle\Form\Extension;
use ...
class FieldTypeHelpExtension extends AbstractTypeExtension
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->setAttribute('help', $options['help']);
}
public function buildView(FormView $view, FormInterface $form, array $options)
{
$view->vars['help'] = $options['help'];
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'help' => null
]);
}
public function getExtendedType()
{
return 'form';
}
}
Define the extension as a service:
acme_app.form.extension.field_type_help:
class: Acme\AppBundle\Form\Extension\FieldTypeHelpExtension
tags:
- { name: form.type_extension, alias: form }
Create a template extending form div layout:
{% extends 'form_div_layout.html.twig' %}
{% block field_help %}
{% if help is defined and help %}
<p class="help-block">{{ help|trans }}</p>
{% endif %}
{% endblock field_help %}
{% block integer_widget %}
{{ parent() }}
{{ block('field_help') }}
{% endblock integer_widget %}
{% block form_widget %}
{{ parent() }}
{{ block('field_help') }}
{% endblock form_widget %}
Configure this template as form theme on config.yml
twig:
form_themes:
- 'Form/fields.html.twig'
Now you can use it when render the a field on a template:
{{ form_widget(form.name, { 'help': 'this is help' }) }}
Or in your Form type:
$form->add('name', 'text', ['help' => 'this is help']);
I´ve the following code to create a custom form type that transforms an entity to id in order to create an autocomplete field.
class EntityIdType extends AbstractType
{
/**
* #var EntityManager
*/
private $em;
/**
* #param EntityManager $em
*/
public function __construct(EntityManager $em)
{
$this->em = $em;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$transformer = new EntityIdTransformer($this->em,$options['entity_class']);
$builder->addModelTransformer($transformer);
}
}
I have also created a custom widget to this form type and I want to get the string that represents that entity on it
{% block entity_id_widget %}
{% spaceless %}
<input type="hidden" {{ block('widget_attributes') }} {% if value is not empty %}value="{{ value }}" {% endif %}/>
<input autocomplete="off" value="{{ **GET_VALUE_HERE** }}">
{% endspaceless %}
{% endblock %}
UPDATE
The EntityIdTransformer's transform method just returns the entity's id and reverse transform returns the entity associated to the id passed as parameter
The goal of all this is to create an autocomplete input for an entity with many rows and persist the changes when submiting the form. If any other approach could be better for this example I will apreciate it.
It seems that an entity_identifier field type has been added as a proposal in a PR but doesnt seem to be available in near future yet
Finally I was able to get the string returning an array from the DataTransformer with an id and a string describing the entity
public function transform($entity)
{
if (null === $entity) {
return "";
}
return array("id" => $entity->getId(), "name" => $entity->__toString());
}
While rendering my custom widget now I can access to those values like this
{% block entity_id_widget %}
{% spaceless %}
<input type="hidden" {{ block('widget_attributes') }} {% if value is not empty %}value="{{ value.id }}" {% endif %}/>
<input class="blocked" autocomplete="off" {% if value is not empty %}value="{{ value.name }}" {% endif %}>
{% endspaceless %}
{% endblock %}
I want to validate my form using server side validation only. However, if the browser supports HTML5 it validates using the HTML5 attributes added to the form by symfony2 so I need to prevent HTML5 validation.
Just add novalidate to your <form> tag:
<form novalidate>
If you are rendering the form in TWIG, you can use following.
{{ form(form, {'attr': {'novalidate': 'novalidate'}}) }}
I know its old question but with SF2.6 in FormType, you can do:
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'attr'=>array('novalidate'=>'novalidate')
));
}
While googling for a solution to this I found one, that seems the most elegant if you want to disable html5 validation in your whole app, so I thought i'd share it here. Credits go to the author of this blog article.
The idea is to create an extension for the "form" form type like this:
<?php
// src/AppBundle/Form/Extension/NoValidateExtension.php
namespace AppBundle\Form\Extension;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
class NoValidateExtension extends AbstractTypeExtension
{
public function buildView(FormView $view, FormInterface $form, array $options)
{
$view->vars['attr'] = array_merge($view->vars['attr'], [
'novalidate' => 'novalidate',
]);
}
public function getExtendedType()
{
return 'form';
}
}
?>
Then you just register it in your services.yml like this:
app.no_validation_form_extension:
class: AppBundle\Form\Extension\NoValidateExtension
tags:
- {name: form.type_extension, alias: form}
and you're done. All your forms automatically have a novalidate attribute now.
Symfony 3.3
As of Symfony 3.3 the configuration is slightly different, but still possible.
Slight update to the getExtendedType method to return the FormType class.
// src/AppBundle/Form/Extension/NoValidateExtension.php
namespace AppBundle\Form\Extension;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\Extension\Core\Type\FormType;
class NoValidateExtension extends AbstractTypeExtension
{
public function buildView(FormView $view, FormInterface $form, array $options)
{
$view->vars['attr'] = array_merge($view->vars['attr'], [
'novalidate' => 'novalidate',
]);
}
public function getExtendedType()
{
return FormType::class;
}
}
Plus some a minor addition of the extended_type tag, which is now required in your service declaration:
app.no_validation_form_extension:
class: AppBundle\Form\Extension\NoValidateExtension
tags:
- {name: form.type_extension, alias: form, extended_type: Symfony\Component\Form\Extension\Core\Type\FormType}
Alternatively if for some reason you don't want to do it in twig as in the answer above...
{{ form(form, {'attr': {'novalidate': 'novalidate'}}) }}
or you create your from manually with createFormBuilder then you could simply use createFormBuilder as a second parameter to define form attribute:
//someAction
$form = $this->createFormBuilder(null, ['attr'=>['novalidate'=>'novalidate']])
->add(...)
->add(...)
->add(...)
->getFrom();
return $this->render("-----:----:----.html.twig", [
'form'=>$form->createView()
]);
If you are using Symfony 3 (or 2) and want to turn off validation for a specific field only you can do this.
$form = $this->createFormBuilder($task)
->add('task', TextType::class, array('required' => false))
->add('dueDate', DateType::class)
->add('save', SubmitType::class, array('label' => 'Create Task'))
->add('saveAndAdd', SubmitType::class, array('label' => 'Save and Add'))
->getForm();
In this sample form notice the array('required' => false), you can add this to any element that you want to disable validation for without disabling validation for the others. Very useful if you want to temporarily disable only one element instead of the entire form.
Note this ONLY disables the HTML5 validation! This does not disable server side validation.
Reference: http://symfony.com/doc/current/book/forms.html#field-type-options
If you actually need to remove the validation attributes (if you are using a validation library want to keep all of your validation constraints in one place for example), you can overwrite the widget_attributes block in twig.
If you are already using custom form templates in app/Resources/views/form.html.twig for example (and have enabled it in your config.yml) you can just add a block for
{% block widget_attributes %}
{% spaceless %}
id="{{ id }}" name="{{ full_name }}"{% if read_only %} readonly="readonly"{% endif %}{% if disabled %} disabled="disabled"{% endif %}
{% for attrname, attrvalue in attr %}{% if attrname in ['placeholder', 'title'] %}{{ attrname }}="{{ attrvalue|trans({}, translation_domain) }}" {% else %}{{ attrname }}="{{ attrvalue }}" {% endif %}{% endfor %}
{% endspaceless %}
{% endblock widget_attributes %}
All I have done here is remove the attributes related to validation:
{% if required %} required="required"{% endif %}{% if max_length %} maxlength="{{ max_length }}"{% endif %}{% if pattern %} pattern="{{ pattern }}"{% endif %}
To disable Regex validation for specific field using formType class:
->add('foo',null,array=>('attr'=>('pattern'=>'/[^~,]/'))
Use form theming:
First create form theme template, e.g app/Resources/views/form/fields.html.twig:
{% extends 'form_div_layout.html.twig' %}{# or some other base layout #}
{% block form_start %}
{% if attr.novalidate is not defined %}
{% set attr = attr|merge({'novalidate':'novalidate'}) %}
{% endif %}
{{ parent() }}
{% endblock %}
Then use that form theme in your template:
{% form_theme form with 'form/fields.html.twig' %}
{{ form_start(form) }} <-- now renders with novalidate attribute
...
{{ form_end(form) }}
Or, apply theme globally (app/config/config.yml):
twig:
form_themes:
- ':form/fields.html.twig'