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.
Related
I'm learning php and Symfony. I made Ring entity and RingPhoto entity. I want to add a form for RingPhoto inside a form for Ring entity, so I can edit ring information and photo url; ring information will be under Ring entity and photo url will be under Ring photo.
this is Ring entity
'''
/**
* #ORM\OneToMany(targetEntity="Customize\Entity\RingPhoto", mappedBy="ring")
*/
private $ringPhoto;
public function __construct()
{
$this->ringPhoto = new ArrayCollection();
}
/**
* #return Collection|RingPhoto[]
*/
public function getRingPhoto(): Collection
{
return $this->ringPhoto;
}
'''
and this is ringPhoto Entity
'''
/**
* #ORM\ManyToOne(targetEntity="Customize\Entity\Ring", inversedBy="ringPhoto")
*/
private $ring;
public function getRings(): ?Ring
{
return $this->ring;
}
public function setRings(?Ring $ring): self
{
$this->ring = $ring;
return $this;
}
'''
and this is RingPhotoType
'''
class RingPhotoType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('image1', FileType::class, [
'label' => 'file1'
])
->add('image2', FileType::class, [
'label' => 'file1'
])
->add('image3', FileType::class, [
'label' => 'file1'
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => RingPhoto::class,
]);
}
}
'''
this is Controller
'''
public function UpdateRing($id, Request $request){
$doct = $this->getDoctrine()->getManager();
$rn = $doct->getRepository(Ring::class)->find($id);
if(!$rn){
throw $this->createNotFoundException(
'No Ring found for id.'.$id
);
}
$form = $this->createFormBuilder($rn)
->add('product_group_id', HiddenType::class,['data'=> 1])
->add('ring_name', TextType::class)
->add('ring_type', TextType::class)
->add('ring_shape', TextType::class)
->add('size', ChoiceType::class, array('choices' => array(
'0.5'=>0.5,
'1'=>1,
'1.5'=>1.5,
'2'=>2,
'2.5'=>2.5,
'3'=>3,
'3.5'=>3.5,
'4'=>4,
)))
->add('price', IntegerType::class)
->add('ringPhoto', CollectionType::class,[
'entry_type' => RingPhotoType::class,
'allow_add' => true,
'entry_options' =>[
'label' => false,
],
'allow_add' => true,
'by_reference' => false,
'prototype' => true
])
->add('save', SubmitType::class, ['label' => 'Update'])
->getForm();
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()){
$ring = $form->getData();
$doct = $this->getDoctrine()->getManager();
//tell doctrine to save the product
$doct->persist($ring);
//executes the queries
$doct->flush();
return $this->redirectToRoute('test');
}
else{
return[
'form' => $form->createView(),
];
}
}
'''
and this is the twig
'''
{% extends 'default_frame.twig' %}
{% block main %}
<h3>SamplePage!!</h3>
<div>hello this is Update page</div>
{{form_start(form)}}
{{form_widget(form)}}
{{form_row(form.ringPhoto)}}
{{form_end(form)}}
{% endblock %}
'''
so it's kinda working, having no errors. But in the view, under the price, I can only see the name Ring Photo.
It does not show image1, image2, image3 from RingPhotoType.php that I can fill in :S
how can I fix this issue?
Oh I fixed it.
I added nullable=true to image1, 2, 3 in RingPhoto Entity and somehow it solved the problem
I am using React on the frontend to send data to the controller which uses Symfony Form for validation. The information sent from React is what I expect but I am getting an error that says "This form should not contain extra fields".
I want to create a new Booking using this form, which has a Reservation. A Reservation has many (or none) reservationTripAddOns (TripAddOns). I have figured out that the problem is caused when I select trip add on((TripAddOn::class) reservationTripAddOn) that I want linked to reservation. When I do not select any add on, it works.
Data passed from React when no add ons are selected and works:
{
"reservations”:
[
{
"trip":277,
"date":"10/27/2017”,
"guests":2,
"reservationTripAddOns":[]
}
]
}
Data passed from React when add ons are selected and does not work:
{
"reservations”:
[
{
"trip":277,
"date":"10/26/2017”,
"guests":2,
"reservationTripAddOns”:
[
{
"id":34,
"name":"Additional one guest”,
"price":100,
"trip”:null
}
]
}
]
}
BookingController:
public function newAction(Request $request, GuideProfile $guideProfile)
{
$reservation = new Reservation();
$booking = new Booking();
$booking->addReservation($reservation);
$form = $this->createForm(BookingType::class, $booking, ['guide_profile' => $guideProfile]);
$form->submit(json_decode($request->getContent(), true));
if ($form->isValid()) {
$context = new SerializationContext();
$context->setSerializeNull(true);
$context->setGroups(['Session']);
$this->get('session')->set(
'booking', $this->get('serializer')->serialize($booking, 'json', $context)
);
return $this->createApiResponse($booking, ['Default'], 201);
}
return $this->createApiErrorResponse($form->getErrors(true));
}
BookingType:
class BookingType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('reservations', CollectionType::class, [
'entry_type' => ReservationType::class,
'entry_options' => [
'data_class' => Reservation::class,
'guide_profile' => $options['guide_profile'],
],
'allow_add' => true,
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Booking::class,
'guide_profile' => null,
]);
}
}
ReservationType:
class ReservationType extends AbstractType
{
public function __construct(EntityManager $entityManager)
{
$this->entityManager = $entityManager;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$guideProfileId = $options['guide_profile'] instanceof GuideProfile ? $options['guide_profile']->getId() : 0;
$builder
->add('trip', EntityType::class, [
'class' => Trip::class,
'query_builder' => function (EntityRepository $er) use ($guideProfileId) {
return $er->createQueryBuilder('t')
->where('t.guideProfile = :guide_profile_id')
->orderBy('t.title', 'ASC')
->setParameter('guide_profile_id', $guideProfileId);
},
'choice_label' => 'title',
'placeholder' => 'Select a Trip',
])
->add('date', DateType::class, [
'format' => 'MM/dd/yyyy',
'html5' => false,
'widget' => 'single_text',
])
->add('reservationTripAddOns', 'collection', [
'entry_type' => TripAddOnType::class,
'entry_options' => [
'data_class' => TripAddOn::class,
'mapped' => false,
]
])
->addEventListener(FormEvents::PRE_SET_DATA, [$this, 'addGuests'])
;
}
public function addGuests(FormEvent $event)
{
//assume everything works here
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Reservation::class,
'guide_profile' => null,
]);
}
TripAddOnType:
class TripAddOnType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', TextType::class)
->add('price', TextType::class)
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => 'AppBundle\Entity\TripAddOn'
]);
}
I want to have a form submitted by javascript that contains only one checkbox. But since empty checkboxes don't send their key in request, Symfony doesn't know about the form being submitted. So is there any not so hacky solution or is this kind of a "bug".
form:
class NewsletterType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('subscribingNewsletter', CheckboxType::class, [
'label' => 'form.label.newsletter',
'required' => false,
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => User::class,
]);
}
}
part of the controller:
$newsletterForm = $this->formFactory->create(NewsletterType::class, $userToEdit);
$newsletterForm->handleRequest($request);
if ($this->newsletterFormHandler->handle($newsletterForm)) {
$this->session->getFlashBag()->add('notice', 'flash_message.newsletter_changed');
return $response;
}
handler:
public function handle(FormInterface $form): bool
{
if (!$form->isSubmitted() || !$form->isValid()) {
return false;
}
$this->userManager->update($form->getData());
return true;
}
view:
{{ form_start(NewsletterForm) }}
{{ form_row(NewsletterForm.subscribingNewsletter, {
attr: {class: 'js-newsletter-toggle'}
}) }}
{{ form_end(NewsletterForm) }}
So I finally resolved this issue. The only way I found to get this working is to add your submit button to your code like this:
class NewsletterType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('subscribingNewsletter', CheckboxType::class, [
'label' => 'form.label.newsletter',
'required' => false,
])
->add('submit', SubmitType::class);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => User::class,
]);
}
}
Not in your view. I was submitting the form by javascript so the way to to this is to simulate the button click in js.
I have a trouble with a formtype with mapped=false.
In controller, I called the form with:
$form = $this->createForm(new JurisdictionUserNewType(), $jurisdiction_user);
This is my JurisdictionUserNewType:
class JurisdictionUserNewType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$transformer = new CapitalLetterToLowerCaseTransformer();
$builder
->add('name', 'text')
->add($builder->create('email', 'email')
->addModelTransformer($transformer))
->add('securityUser', new SecurityUserType(), array('mapped' => false))
->add('save', 'submit');
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Radmas\Open010Bundle\Document\JurisdictionUser'
));
}
public function getName()
{
return 'jurisdictionUserNew';
}
}
This is my SecurityUserType :
class SecurityUserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('first_name', null, ['label' => 'profile.edit.labels.first_name', 'icon_class' => 'fa fa-user'])
->add('last_name', null, ['label' => 'profile.edit.labels.last_name', 'icon_class' => 'fa fa-user'])
->add('nickname', null, ['label' => 'profile.edit.labels.nickname',
'attr' => [ 'help_text' => 'profile.edit.labels.nickname_help'], 'icon_class' => 'fa fa-globe']);
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Radmas\SecurityBundle\Document\SecurityUser'
));
}
public function getName()
{
return 'securityUser';
}
}
When I put data in the form, I get the object jurisdictionUser in the modalview but I dont get object securityUser.
If you set 'mapped' => false on any field, you're saying that that field isn't related to your entity, so you don't get it when you retrieve the entity from the submitted form.
You can get it anyway as a single field from the form, as:
$form->handleRequest($request);
if ($form->isValid()) {
$entity = $form->getData();
$securityUser = $form->get('securityUser')->getData();
}
I've run into a problem when following the Symfony cookbook for form collections with add/remove. See: http://symfony.com/doc/current/cookbook/form/form_collections.html
Now, for some reason, if I dynamically add a form row but don't fill in any of its fields, I get the following error:
ContextErrorException: Catchable Fatal Error: Argument 1 passed to Project::addTask() must be an instance of Task, null given in D:\web_workspace\wedding\src\testapp.php line 82
I would like people to be able to have blank rows in the form which will just get ignored. For example, if you click "Add Task" a few times, but don't fill in the last row, the form should still be submitted, and the last row should be ignored.
I've created a very simple Silex demo that fits in just a couple of files. I'll highlight it here, but the full example is here, and can be run by just adding Silex via composer: https://gist.github.com/mattsnowboard/7065865
I have the following Models (just Project which has a description and a collection of Tasks)
class Task
{
protected $name;
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
return $this;
}
}
class Project
{
protected $description;
protected $tasks;
public function __construct()
{
$this->tasks = array();
}
public function getDescription()
{
return $this->description;
}
public function setDescription($description)
{
$this->description = $description;
return $this;
}
public function getTasks()
{
return $this->tasks;
}
public function addTask(Task $task)
{
if (!is_null($task) && !in_array($task, $this->tasks)) {
$this->tasks[] = $task;
}
return $this;
}
public function removeTask(Task $task)
{
if (!is_null($task)) {
$this->tasks = array_diff($this->tasks, array($task));
}
return $this;
}
}
My forms are as follows
class TaskType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('name', 'text', array(
'required' => false
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => '\Task',
));
}
public function getName()
{
return 'task';
}
}
class ProjectType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('description', 'text', array())
->add('tasks', 'collection', array(
'type' => new TaskType(),
'allow_add' => true,
'by_reference' => false,
'allow_delete' => true,
'required' => false
))
->add('submit', 'submit')
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => '\Project',
));
}
public function getName()
{
return 'project';
}
}
The controller adds some test data to the form and just prints the data when it is submitted:
$app->match('/', function (Request $request) use ($app) {
$project = new Project();
// dummy code
$task1 = new Task();
$task1->setName('A Task');
$project->addTask($task1);
// end dummy code
$form = $app['form.factory']->create(new ProjectType(), $project);
$form->handleRequest($request);
if ($form->isValid()) {
$data = $form->getData();
$debug = print_r($data, true);
echo $debug;
}
return $app['twig']->render('test.html.twig', array(
'form' => $form->createView(),
));
})
->method('GET|POST');
The view is mostly this, and the javascript sample from the cookbook
{{ form_start(form) }}
{{ form_row(form.description) }}
<h3>Tags</h3>
<ul class="tasks" data-prototype="{{ form_widget(form.tasks.vars.prototype)|e }}">
{# iterate over each existing tag and render its only field: name #}
{% for task in form.tasks %}
<li>{{ form_row(task.name) }}</li>
{% endfor %}
</ul>
{{ form_widget(form.submit) }}
{{ form_end(form) }}
Am I just missing something? Is this a bug? I feel like this example which is very close to the cookbook should work pretty easily...
Updated: Removing "allow_deleted" doesn't actually fix this.
Sorry, I figured it out, the collection type shouldn't have 'required' => false
$builder->add('description', 'text', array())
->add('tasks', 'collection', array(
'type' => new TaskType(),
'allow_add' => true,
'by_reference' => false,
'allow_delete' => true,
'required' => false
))
Should be
$builder->add('description', 'text', array())
->add('tasks', 'collection', array(
'type' => new TaskType(),
'allow_add' => true,
'by_reference' => false,
'allow_delete' => true
))