I have following form in Symfony 2.8:
$form = $this->createFormBuilder()
->add('name', 'text')
->add('email', 'text')
->add('phone', 'text')
->add($this->createFormBuilder()
->create('address', 'form', array('virtual' => true))
->add('street', 'text')
->add('city', 'text')
->add('zip', 'text')
)
->getForm();
And I would like to dynamically add addresses in JS. I can add by CollectionType single input, as per following documentation: https://symfony.com/doc/current/reference/forms/types/collection.html
But I would like to add whole subform address. So I would like to achieve following HTML result:
<input name="form[address][0][street]" />
<input name="form[address][0][city]" />
<input name="form[address][0][zip]" />
not
<input name="form[address][street][0]" />
<input name="form[address][city][0]" />
<input name="form[address][zip][0]" />
Can anybody help? Thanks!
thanks to the comments I solved in in following way:
class Address
{
private $street;
private $city;
public function getStreet()
{
return $this->street;
}
public function setStreet($street)
{
$this->street = $street;
}
public function getCity()
{
return $this->city;
}
public function setCity($city)
{
$this->street = $city;
}
}
class AddressType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('street', 'text')
->add('city', 'text');
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => Address::class,
));
}
}
then in controller:
$data = array(
"addresses" => array(
0 => new Address())
);
// $data is to show first empty subform
$form = $this->createFormBuilder($data)
->add('name', 'text')
->add('email', 'text')
->add('phone', 'text')
->add('addresses',
CollectionType::class,
array(
'entry_type' => AddressType::class,
'empty_data' => true
))
->getForm();
and the twig template looks like this:
{{ form_start(form) }}
{{ form_label(form.name) }}
{{ form_widget(form.name) }}
{{ form_label(form.email) }}
{{ form_widget(form.email) }}
{% for address in form.addresses %}
{{ form_widget(address) }}
{% endfor %}
{{ form_end(form) }}
Related
I'm doing an app with Symfony 5 and there is a problem i don't manage to find a solution, I have no idea.
I want to make a form of an entity "Person".
A Person can add in his family other Person.
So in my entity I made a Many-To-Many self referencing to Person.
class Person
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=50)
*/
private $name;
/**
* #ORM\Column(type="string", length=50)
*/
private $firstname;
/**
* #ORM\Column(type="string", length=255)
*/
private $birthdaydate;
/**
* #ORM\Column(type="string", length=255)
*/
private $gender;
/**
* #ManyToMany(targetEntity="Person")
* #JoinTable(name="family",
* joinColumns={#JoinColumn(name="person__id", referencedColumnName="person__id")},
* inverseJoinColumns={#JoinColumn(name="family_id", referencedColumnName="person__id")}
* )
*/
private $myFamily;
And now, I want to make a form in which I can add new Person, in a person.
I did a CollectionType, like symfony said, but when i want to print it to the page, I get a timeout because of an infinite loop.
It's the "allow_add" which causes the problem.
And i need the prototype variable returned by "allow_add" to add new field in the front.
class PersonType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('name', TextType::class, ['attr' => ['class' => 'form_textfield']])
->add('firstname')
->add('birthdayDate', TextType::class, ['attr' => ['class' => 'form_datetime']])
->add('gender', GenderType::class)
->add('submit', SubmitType::class)
->add('myFamily', CollectionType::class, array('entry_type' => PersonType::class, 'mapped' => false, 'allow_add' => true, 'by_reference' => false, 'allow_delete' => true));
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Person::class,
]);
}
}
Here is my form, but there is nothing interesting, I will add the js necessary when i will be resolve this problem.
{% extends 'base.html.twig' %}
{% block title %}Hello PersonController!
{% endblock %}
{% block body %}
{{ form_start(form) }}
{{ form_row(form.name) }}
{{ form_row(form.firstname) }}
{{ form_row(form.birthdayDate) }}
{{ form_row(form.gender) }}
{{ form_row(form.myFamily) }}
<button type="button" class="add_item_link" data-collection-holder-class="tags">Add a tag</but
{{ form_end(form) }}
{% endblock %}
Thanks everyone in advance.
The answer from Dylan Kas was good, just by adding a new form, it's good.
The Person Form
class PersonType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('name', TextType::class, ['attr' => ['class' => 'form_textfield']])
->add('firstname')
->add('birthdayDate', TextType::class, ['attr' => ['class' => 'form_datetime']])
->add('gender', GenderType::class)
->add('submit', SubmitType::class)
->add('myFamily', CollectionType::class, array('entry_type' => ChildType::class, 'by_reference' => false, 'allow_add' => true, 'allow_delete' => true));
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Person::class,
]);
}
}
The child, referenced by myFamily :
class ChildType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('name', TextType::class, ['attr' => ['class' => 'form_textfield']])
->add('firstname')
->add('birthdayDate', TextType::class, ['attr' => ['class' => 'form_datetime']])
->add('gender', GenderType::class)
->add('submit', SubmitType::class);
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Person::class,
]);
}
}
And the view :
{% block body %}
{{ form_start(form) }}
{{ form_row(form.name) }}
{{ form_row(form.firstname) }}
{{ form_row(form.birthdayDate) }}
{{ form_row(form.gender) }}
<button type="button" class="add_item_link" data-collection-holder-class="myFamily">Add a tag</button>
<ul class="myFamily" data-index="{{ form.myFamily|length > 0 ? form.myFamily|last.vars.name + 1 : 0 }}" data-prototype="{{ form_widget(form.myFamily.vars.prototype)|e('html_attr') }}"></ul>
{{ form_end(form) }}
{% endblock %}
With the js associated
const addFormToCollection = (e) => {
const collectionHolder = document.querySelector(
"." + e.currentTarget.dataset.collectionHolderClass
);
const item = document.createElement("li");
item.innerHTML = collectionHolder.dataset.prototype.replace(
/__name__/g,
collectionHolder.dataset.index
);
collectionHolder.appendChild(item);
collectionHolder.dataset.index++;
};
document
.querySelectorAll(".add_item_link")
.forEach((btn) => btn.addEventListener("click", addFormToCollection));
It still need some work, maybe I can make the child form extending the person form. The front need some work too. But the next people facing this problem will have the solution here.
I am still asking myself how could I do if I've needed to have a form including itself the same form, including itself the same form etc...
The form would be recursivable.
There is an infinite loop because the myFamily property references a Person entity which itself references a myFamily property ...
To keep things simple, one way to manage the family of a person would be to create a separate Family entity.
From the Person point of view, it seems more coherent to have a ManyToOne relationship with a family.
After that, you can add the family of a Person by using the EntityType:class inside the PersonFormType.
Here is the documentation for EntityType : https://symfony.com/doc/current/reference/forms/types/entity.html
I have a simple form:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', null, ['required' => true])
->add('doYouWant', ChoiceType::class, ['choices' => ['no' => 'no', 'yes' => 'yes']])
->add('type')
;
}
I would like the user after the selection doYouWant to "yes" to have a mandatory "type" option, so I am trying:
$builder->addEventListener(
FormEvents::PRE_SUBMIT,
function (FormEvent $event) use ($builder) {
$data = $event->getForm()->getData();
if ($data['doYouWant'] == 'yes') {
$builder->add('type', null, ['required' => true]);
}
}
);
But it does not matter...
I think the simplest way would be to add constraints to each field from your type, and then, in template, by using jquery you can toggle the visibility of your type field based on the selected value from the dropdown.
# AppBundle/Form/ExampleType.php
$builder
->add('name', null, [
'constraints' => [
new NotBlank(['message' => 'This cannot be empty']),
]
])
->add('doYouWant', ChoiceType::class, [
'placeholder' => 'Select',
'choices' => ['no' => 'No', 'yes' => 'Yes'],
'constraints' => [
new NotBlank(['message' => 'This cannot be empty']),
]
])
->add('type', EmailType::class, [
'constraints' => [
new NotBlank(['message' => 'This cannot be empty']),
new Email([
'message' => "The email '{{ value }}' is not a valid email",
])
]
])
;
I've added the type field as being of type email, just for testing purposes.
# Controller/DefaultController.php
/**
* #param Request $request
* #Route("/test", name="test")
* #return Response
*/
public function testAction(Request $request) : Response
{
$form = $this->createForm(ExampleType::class);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
dump($form->getData());die;
}
return $this->render('default/test.html.twig', [
'form' => $form->createView(),
]);
}
# default/test.html.twig (assuming you are using bootstrap and jquery)
{% block body %}
<div class="container">
<div class="row">
<div class="col-xs-12">
{{ form_start(form, { attr: { 'novalidate': 'novalidate' } }) }}
<div class="form-group">
{{ form_label(form.name) }}
{{ form_widget(form.name,{ attr:{ class:'form-control' } }) }}
{{ form_errors(form.name) }}
</div>
<div class="form-group">
{{ form_label(form.doYouWant) }}
{{ form_widget(form.doYouWant,{ attr:{ class:'form-control' } }) }}
{{ form_errors(form.doYouWant) }}
</div>
<div class="form-group type hidden">
{{ form_label(form.type) }}
{{ form_widget(form.type,{ attr:{ class:'form-control' } }) }}
{{ form_errors(form.type) }}
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">Send</button>
</div>
{{ form_end(form) }}
</div>
</div>
</div>
{% endblock %}
{% block javascripts %}
<script>
$(document).ready(function(){
$('#appbundle_example_doYouWant').change(function(){
var choice = $(this).val();
if (choice == 'Yes') {
$('.type').removeClass('hidden');
} else {
$('.type').addClass('hidden');
}
});
});
</script>
{% endblock %}
You can use validation groups and put your assertions inside your entity.
And then you can choose validation groups based on the submitted data like so:
https://symfony.com/doc/current/form/data_based_validation.html
How to add assertion on entity:
class MyEntity {
/**
* #Assert\NotBlank(groups={"YourGroup"})
*/
protected $type;
}
Then on your form:
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'validation_groups' => function (FormInterface $form) {
$data = $form->getData();
$want = $data->getDoYouWant();
if ($want) {
return ['YourGroup'];
}
return ['Default'];
},
));
}
I have 3 tables/entities. I am trying to create form to persist data to all 3 table in one form. Entitities are to long that is why I haven't added here.
Entity Client I made an array collection in Client entity for other 2 entities.
protected $Clientservice;
protected $Hostingaccount;
public function __construct()
{
$this->Clientservice = new ArrayCollection();
$this->Hostingaccount= new ArrayCollection();
}
public function getClientservice()
{
return $this->Clientservice;
}
public function getHostingaccount()
{
return $this->Hostingaccount;
}
public function setClientservice($Clientservice)
{
$this->Clientservice = $Clientservice;
return $this;
}
public function setHostingaccount($Hostingaccount)
{
$this->Hostingaccount = $Hostingaccount;
return $this;
}
And I Have 3 forms:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('CustomerID',IntegerType::class)
->add('Sex',TextType::class)
->add('Name',TextType::class)
->add('FirstName',TextType::class)
->add('LastName',TextType::class)
->add('Email', EmailType::class)
->add('Invoiceemail', EmailType::class)
->add('Iban', TextType::class)
->add('Bic', TextType::class)
->add('SEPAMandateID', TextType::class)
->add('LogoUrl', TextType::class)
->add('CreationDate', DateType::class)
->add('Clientservice', CollectionType::class, array(
'entry_type' => WhmAdminClientserviceType::class
))
->add('Hostingaccount', CollectionType::class, array(
'entry_type' => WhmAdminHostingAccountType::class
))
->getForm();
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => Client::class
));
}
Clientservice form
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('Description',TextType::class);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => Clientservice::class
));
}
Hostingaccount form:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('Domain',TextType::class)
->add('Domainip',IntegerType::class)
->add('UserName',TextType::class)
->add('Owner',TextType::class);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => Hostingaccount::class
));
}
In my controller I have the code like this:
public function AddWhmAdminAction(Request $request)
{
$Client = new Client();
$form = $this->createForm(WhmAdminType::class, $Client);
$form->add('submit', SubmitType::class, array(
'label' => 'Create',
'attr' => array('class' => 'btn btn-default pull-left')
));
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($Client);
$em->flush();
$this->get('session')->getFlashBag()->add('green', 'Product created!');
return $this->redirectToRoute('whmAdmin');
// flash msg
}
//return $form->createView();
$build['form'] = $form->createView();
return $this->render('whm/WhmAdminForm.html.twig', $build);
}
and in my view
{{ form_start(form) }}
{% for flashMessage in app.session.flashbag.get('green') %}
{{ flashMessage }}
{% endfor %}
{{ form_end(form) }}
I followed http://symfony.com/doc/current/form/form_collections.html and this is how far I got.
My form only shows the fields from Client. It does not shows field from Clientservice and hostingaccount. How can I show the fields from clientservice and hostingaccount in the same form as Client.
If the Clientservice and Hostingaccount are in OneToOne or ManyToOne relationship then change:
->add('Clientservice', CollectionType::class, array(
'entry_type' => WhmAdminClientserviceType::class
))
->add('Hostingaccount', CollectionType::class, array(
'entry_type' => WhmAdminHostingAccountType::class
))
to:
->add('Clientservice', WhmAdminClientserviceType::class)
->add('Hostingaccount', WhmAdminHostingAccountType::class)
Otherwise leave your form builders and read about adding and removing items in collection type.
I'm trying to create an embedded form in symfony2 that will consist of a Contact form that will have embedded a ContactPhonenumber form to add phone numbers.
I have followed the instructions in Symfony2 documentation about embedding forms but I can't find a way to show at least an input field for phonenumbers. If I do it without errors, I only get back a label for the embedded phonenumber form.
How can I show an input there?
My code is:
My two entity form classes:
class ContactType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('contactType')
->add('title')
->add('name')
->add('givenName')
->add('middleName')
/*->add(
'contactPhonenumbers',
'collection',
array( 'type' => new ContactPhonenumberType($options) )
)
*/
->add('contactPhonenumbers', new ContactPhonenumberType($options) )
;
}
//.......
//More functions
//.......
}
class ContactPhonenumberType extends AbstractType
{
public function __construct(array $options=null) {
$this->options = $options;
}
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('position')
->add('number')
->add(
$builder->create(
'contact',
'searchable',
array(
'data_class' => '....Bundle\Entity\Contact',
'class' => '....Bundle:Contact',
'dataroute' => '...._contact_searchable',
'classlistview' => new ....Bundle\Component\Listview\ContactList
)
)->addViewTransformer(
new NameToIdTransformer(
$options[ 'em' ],
$options[ 'request' ],
$this,
array(),
'....Bundle:Contact'
)
)
)
->add(
$builder->create(
'phonenumberType',
'searchable',
array(
'data_class' => '....Bundle\Entity\PhonenumberType',
'class' => '.....Bundle:PhonenumberType',
'dataroute' => '...._phonenumbertype_searchable',
'classlistview' => new ....\Component\Listview\PhonenumberTypeList
)
)->addViewTransformer(
new NameToIdTransformer(
$options[ 'em' ],
$options[ 'request' ],
$this,
array(),
'...Bundle:PhonenumberType'
)
)
)
;
}
}
And my function on controller
public function newAction( Request $request )
{
$em = $this->getDoctrine()->getManager();
$entity = new Contact();
// $number = new ArrayCollection();
// $number[] = new ContactPhonenumber();
// $entity->setContactPhonenumbers( $number );
$form = $this->createCreateForm( $entity );
return $this->render(
'...Bundle:Contact:new.html.twig',
array(
'entity' => $entity,
'form' => $form->createView()
)
);
}
And the form in twig:
<div class="widget-body">
{{ form_start( form, { 'attr': { 'class': 'form-horizontal', 'role': 'form', 'novalidate': 'novalidate' } } ) }}
{{ form_errors( form ) }}
{{ form_row( form.contactType ) }}
{{ form_row( form.title ) }}
{{ form_row( form.name ) }}
{{ form_row( form.givenName ) }}
{{ form_row( form.middleName ) }}
------>
{{ form_row(form.contactPhonenumbers) }}
<--------
{{ form_row( form.salutation ) }}
{{ form_row( form.address ) }}
{{ form_row( form.superiorContact ) }}
<div class="form-group">
<hr>
</div>
<div class="form-group">
<div class="col-sm-2"></div>
<div class="col-sm-10">
{{ form_widget( form.submit ) }}
{{ form_widget( form.cancel ) }}
</div>
</div>
{{ form_row( form._token ) }}
{{ form_end( form, { 'render_rest': false } ) }}
I have an entity Game and an entity Player , and each game has 3 players
I'd like to know how to embed PlayerType in GameTpe for 3 times and then display them in form.twig without using javascript
GameType
class GameType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', 'text', array('required' => true))
->add('description', 'text', array('required' => true))
->add('date', 'date', array('required' => true))
->add('players', new PlayerType()); //how to embed playerType 3 times
}
PlayerType
class PlayerType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', 'texet', array('required' => true))
->add('age', 'integer', array('required' => true));
//............
}
form.twig
<form method="post" action="" >
{{ form_widget(form.name) }}
{{ form_widget(form.description) }}
{{ form_widget(form.date) }}
// how to display this form 3 times
{{ form_widget(form.players) }}
<input type="submit" class="btn btn-primary" />
</form>
If your game always have three players why don't you add three fields to Game entity?
Then for example use getter to collect them all.