Displaying returned array as radio buttons - symfony

I'm trying to display a set of radio buttons (male or female) on my index page. Within my Form I build the form like this:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('gender', ChoiceType::class, array(
'choices' => array(
'Male' => 1,
'Female' => 2,
)));
}
Then within the Controller:
public function indexAction(Request $request)
{
$participantEntry = new Participants();
$form = $this->createForm(ParticipantsType::class, $participantEntry,[
'action' => $request->getUri()
]);
$form->handleRequest($request);
return $this->render('SurveyBundle:Page:index.html.twig',
['form' => $form->createView()]);
}
However, I'm not sure how to actually display as RadioButtons within my Twig file, this is what I've tried:
{% for g in form.gender %}
{{ g.value }}
{% endfor %}
BTW: The $gender within my Entity is an int
Which doesn't work, does anyone know what I am doing wrong?

Change the add into builder as follow
->add('gender', ChoiceType::class, array(
'choices' => array(
'Male' => 1,
'Female' => 2,
),
'expanded' => true,
'multiple' => false
);
The combination of 'expanded' => true and 'multiple' => false will generate radio buttons, then in twig, just use
form_widget(form.gender)

Related

How can I change error message in Symfony

I create a form and then when I click on submit button, show this error message:
Please select an item in the list.
How can I change this message and style it ( with CSS )?
Entity:
...
/**
* #ORM\Column(type="string", length=255)
* #Assert\NotBlank(message="Hi user, Please select an item")
*/
private $name;
...
Controller:
...
public function index(Request $request)
{
$form = $this->createForm(MagListType::class);
$form->handleRequest($request);
return $this->render('index.html.twig', [
'form' => $form->createView()
]);
}
...
Form:
...
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', EntityType::class,[
'class' => InsCompareList::class,
'label' => false,
'query_builder' => function(EntityRepository $rp){
return $rp->createQueryBuilder('u')
->orderBy('u.id', 'ASC');
},
'choice_label' => 'name',
'choice_value' => 'id',
'required' => true,
])
->add('submit', SubmitType::class, [
'label' => 'OK'
])
;
...
}
In order to use custom messages, you have to put 'novalidate' on your HTML form. For example:
With Twig:
{{ form_start(form, {attr: {novalidate: 'novalidate'}}) }}
{{ form_widget(form) }}
{{ form_end(form) }}
Or in your controller:
$form = $this->createForm(MagListType::class, $task, array( //where task is your entity instance
'attr' => array(
'novalidate' => 'novalidate'
)
));
This Stackoverflow answer has more info on how to use novalidate with Symfony. You can also check the docs for more info.
As for the styling, you can use JavaScript to trigger classes, which you can then style in your CSS, like in this example taken from Happier HTML5 Form Validation . You can also take a look at the documentation on MDN and play with the :valid and :invalid selectors.
const inputs = document.querySelectorAll("input, select, textarea");
inputs.forEach(input => {
input.addEventListener(
"invalid",
event => {
input.classList.add("error");
},
false
);
});
EDIT:
You are probably not seeing your custom message because it comes from server side, the message that you're currently seeing when submitting your form is client-side validation. So you can either place novalidate on your field, or you can override the validation messages on the client side by using setCustomValidity(), as described in this SO post which also contains many useful links. An example using your Form Builder:
$builder
->add('name', EntityType::class,[
'class' => InsCompareList::class,
'label' => false,
[
'attr' => [
'oninvalid' => "setCustomValidity('Hi user, Please select an item')"
]
],
'query_builder' => function(EntityRepository $rp){
return $rp->createQueryBuilder('u')
->orderBy('u.id', 'ASC');
},
'choice_label' => 'name',
'choice_value' => 'id',
'required' => true,
])
->add('submit', SubmitType::class, [
'label' => 'OK'
]);

Symfony Dynamic Modification of Form without Entity

I am following the Symfony (v2.7) Cookbook recipe for dynamic form modification. What I am aiming for is displaying certain fields based on a user's radio button selection. For example, if a user wishes to filter a search based on records from the last fiscal year, he selects the "Fiscal Year" radio button from the criteriaFilter choice field type (example below), and the appropriate fields are generated. If he changes his mind and selects "Semester" instead, the fiscal year fields are replaced with the semester fields, and so on.
Example code:
$builder
->add('librarian', 'entity', array(
'class' => 'AppBundle:Staff',
'query_builder' => function(EntityRepository $er){
$qb = $er->createQueryBuilder('st');
$qb
->where('st.employmentStatus = :employmentStatus')
->setParameter('employmentStatus', 'faclib')
->orderBy('st.lastName', 'DESC')
->getQuery();
return $qb;
},
'placeholder' => 'All Librarians',
'required' => false
))
->add('program', 'entity', array(
'class' => 'AppBundle:LiaisonSubject',
'query_builder'=>function(EntityRepository $er){
$qb = $er->createQueryBuilder('ls');
$qb
->orderBy('ls.root, ls.lvl, ls.name', 'ASC')
->getQuery();
return $qb;
},
'property' => 'indentedTitle',
'placeholder' => 'All Programs',
'required' => false,
'label' => 'Program'
))
->add('criteriaFilter', 'choice', array(
'expanded' => true,
'multiple' => false,
'choices' => array(
'academic' => 'Academic Year',
'fiscal' => 'Fiscal Year',
'semester' => 'Semester',
'custom' => 'Custom Range'
),
))
;
This seems pretty straighforward based on the cookbook entry. However, the form I am creating is not bound to an entity. Therefore, fetching data via the method
$builder->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event){
$form = $event->getForm();
//normally the entity, but NULL in this case
$data = $event->getData();
...
which would normally allow for calling of getter methods on entity properties returns null. So obviously this can't work in this case.
So the question is, is there another way to dynamically generate fields inside of a form that is not tied to an entity?
You can pass options to the form, including data. So something like (from memory but it's untested):
// controller
$this->createForm(SomeForm::class, null, ['fiscalYears' => [2001, 2002]);
// type
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(['fiscalyears' => []);
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$fiscalYears = $options['fiscalYears'];
$builder->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event) use ($fiscalYears) {
$form = $event->getForm();
$form->add('fiscalYear', ChoiceType::class, [
'choices' => $fiscalYears
]);
}
}

Symfony 2: Set field as read only after first save

I have a Symfony 2 entity. When I create a new record, I must fill all the values using a form, but after saving it, one of the values, $amount shouldn't be updatable when I update the others members.
How can I accomplish this? It's possible to mark a form member as a read-only, in runtime?
By using the validation_groups and name options when creating your form, you can change the form.
The name attribute sets the form creation, and the validation_groups takes care of the validation.
For example, in the create/new method of your controller;
public function createAction(Request $request)
{
// Instantiate new Foo object
$client = new Foo();
// create the form (setting validation group)
$form = $this->formFactory->create('foo', $foo, array(
'name' => 'create',
'validation_groups' => array('create')
)
);
// form has been submitted...
if ('POST' === $request->getMethod()) {
// submits the form
$form->handleRequest($request);
// do validation
if ($form->isValid()) {
// do whatever
}
}
// either GET or validation failed, so show the form
return $this->template->renderResponse('FooBundle:foo:add.html.twig', array(
'form' => $form->createView(),
'foo' => $foo
));
}
And in the edit/update function of your controller;
public function updateAction($id, Request $request)
{
// Instantiate Client object
$client = new Foo($id);
// create the form (setting validation group)
$form = $this->formFactory->create('foo', $foo, array(
'name' => 'update',
'validation_groups' => array('update')
));
// form has been submitted...
if ('POST' === $request->getMethod()) {
// submits the form
$form->handleRequest($request);
// do validation
if ($form->isValid()) {
// do whatever
}
}
// either GET or validation failed, so show the form
return $this->template->renderResponse('FooBundle:foo/edit:index.html.twig', array(
'form' => $form->createView(),
'foo' => $foo
));
}
And your Form Type will look something like;
class FooType extends BaseAbstractType
{
protected $options = array(
'data_class' => 'FooBundle\Model\Foo',
'name' => 'foo',
);
private $roleManager;
public function __construct($mergeOptions = null)
{
parent::__construct($mergeOptions);
}
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$this->$options['name']($builder, $options);
}
private function create(FormBuilderInterface $builder, array $options)
{
// ID
$builder->add('Id', 'text', array(
'required' => true,
'label' => 'ID',
'attr' => array(
'placeholder' => 'Format: 2 alphanumeric (e.g. A1)'
)
));
// Name - only show on create
$builder->add('Name', 'text', array(
'required' => true,
'label' => 'Name',
'attr' => array(
'placeholder' => 'Your name'
)
));
// add the submit form button
$builder->add('save', 'submit', array(
'label' => 'Save'
));
}
private function update(FormBuilderInterface $builder, array $options)
{
// ID
$builder->add('Id', 'text', array(
'required' => true,
'label' => 'ID',
'attr' => array(
'placeholder' => 'Format: 2 alphanumeric (e.g. A1)',
)
));
// Name - just for show
$builder->add('Name', 'text', array(
'required' => true,
'label' => 'Name',
'attr' => array(
'readonly' => 'true' // stops it being editable
)
));
// add the submit form button
$builder->add('save', 'submit', array(
'label' => 'Save'
));
}
}
P.S. All my classes are declared as services, so how you call create forms/views/etc may be different.

Symfony empty date field is not required with single_text widget

I created form with date field using single_text widget.
I'm choose this one over choice because I'm using Bootstrap datepicker
Problem I have is that field is always populated with current date instead of being empty.
According to this it should work if i set required to false but id does not.
I tried setting empty_value to empty string same for data but in first case nothing happened probably because this option is for choice fields. In second case I'm getting exception "Expected argument of type "\DateTime", "string" given"
I tried using DataTransformer but didn't make any difference. I found out that for data fields value always goes through DateTimeToLocalizedStringTransformer and if I understand it correctly it is returning empty string if empty value so problem is somewhere further after datatransformers.
One more thing I tried is to set value using attr array and it worked unfortunately the side effect was that populating form with some values doesn't affect date field.
Is there any way to set default value of data field as empty with single_text widget?
Here goes a code
<?php
namespace Psw\AdminBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Doctrine\ORM\EntityRepository;
use Psw\AdminBundle\Form\DataTransformer\EmptyDateTransformer;
use Psw\AdminBundle\Form\DataTransformer\EmptyDateViewTransformer;
class OrdersFilterType extends AbstractType
{
private $admin;
public function __construct($admin=false) {
$this->admin = $admin;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('client', 'entity', array(
'class' => 'PswAdminBundle:User',
'required' => false,
'multiple' => false,
'label' => 'orders.client',
'empty_value' => 'orders.allclients',
'query_builder' => function(EntityRepository $er) {
$qb = $er->createQueryBuilder('u');
return $qb->where($qb->expr()->like('u.roles', '?0'))
->setParameters(array('%ROLE_CLIENT%'));
}
));
if($this->admin) {
$builder->add('staff', 'entity', array(
'class' => 'PswAdminBundle:User',
'required' => false,
'multiple' => false,
'label' => 'orders.staff',
'empty_value' => 'orders.allStaff',
'query_builder' => function(EntityRepository $er) {
$qb = $er->createQueryBuilder('u');
return $qb->where($qb->expr()->like('u.roles', '?0'))
->orWhere($qb->expr()->like('u.roles', '?1'))
->orWhere($qb->expr()->like('u.roles', '?2'))
->setParameters(array('%ROLE_STAFF%','%ROLE_ADMIN%','%ROLE_SUPER_ADMIN%'));
}
));
}
$builder->add('start', 'date', array(
'label' => 'orders.start',
'widget' => 'single_text',
'required' => false,
))
->add('end', 'date', array(
'label' => 'orders.end',
'widget' => 'single_text',
'required' => false,
))
->add('min', null, array(
'label' => 'orders.min',
'required' => false,
))
->add('max', null, array(
'label' => 'orders.max',
'required' => false,
));
}
public function getDefaultOptions(array $options)
{
$options = parent::getDefaultOptions($options);
$options['csrf_protection'] = false;
return $options;
}
public function getName()
{
return 'psw_adminbundle_ordersfiltertype';
}
}

Build a form having a checkbox for each entity in a doctrine collection

I'm displaying an html table for a filtered collection of entities and I want to display a checkbox in each row as part of a form which will add the selected entities to a session var.
I'm thinking that each checkbox should have the entity id as its value and I'll get an array of ids from the form field data (ok, so the value ought to be an indirect ref to the entity, but for the sake of simplicity).
I've tried creating a form Type with a single entity type field, mapped to the id property of the entity and embedded into another form Type which has a collection type field.
class FooEntitySelectByIdentityType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('foo_id', 'entity', array(
'required' => false,
'class' => 'MeMyBundle:FooEntity',
'property' => 'id',
'multiple' => true,
'expanded' => true
));
}
# ...
and
class FooEntitySelectionType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('identity', 'collection', array(
'type' => new FooEntitySelectByIdentityType,
'options' => array(
'required' => false,
'multiple' => true,
'expanded' => true,
'attr' => array('class' => 'foo')
),
));
}
# ...
and in a controller the form is created with a collection of entities as the initial data
$form = $this
->createForm(
new \Me\MyBundle\Form\Type\FooEntitySelectionType,
$collection_of_foo
)
->createView()
;
When the form is rendered there is a single label for the identity field, but no widgets.
Is it even possible to use entity and collection type fields in this particular way?
If so, what might I be doing wrong?
I think this will answer your question.
Forget the FooEntitySelectionType. Add a property_path field option to FooEntitySelectByIdentityType and set the data_class option to null:
class FooEntitySelectByIdentityType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('foo_id', 'entity', array(
'required' => false,
'class' => 'MeMyBundle:FooEntity',
'property' => 'id',
'property_path' => '[id]', # in square brackets!
'multiple' => true,
'expanded' => true
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => null,
'csrf_protection' => false
));
}
# ...
and in your controller, build the FooEntitySelectByIdentityType:
$form = $this
->createForm(
new \Me\MyBundle\Form\Type\FooEntitySelectByIdentityType,
$collection_of_foo
)
->createView()
;
and then in the controller action which receives the POSTed data:
$form = $this
->createForm(new \Me\MyBundle\Form\Type\FooEntitySelectByIdentityType)
;
$form->bind($request);
if ($form->isValid()) {
$data = $form->getData();
$ids = array();
foreach ($data['foo_id'] as $entity) {
$ids[] = $entity->getId();
}
$request->getSession()->set('admin/foo_list/batch', $ids);
}
and finally, in your twig template:
{# ... #}
{% for entity in foo_entity_collection %}
{# ... #}
{{ form_widget(form.foo_id[entity.id]) }}
{# ... #}
If someone is looking for solution for Symfony >=2.3
You have to change this:
$data = $form->getData();
$ids = array();
foreach ($data['foo_id'] as $entity) {
$ids[] = $entity->getId();
}
to this:
$data = $form['foo_id']->getData();
$ids = array();
foreach ($data as $entity) {
$ids[] = $entity->getId();
}
I create a symfony bundle for render a collection of entities as a checkbox table in a configurable way. Although it has been a while since this question was asked, I hope this can help others with this same kind of problem.

Resources