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();
}
Related
(Symfony 5.2, doctrine, PHP 8.0)
Take a look at this sample code:
more described here: https://symfony.com/doc/current/form/form_collections.html
class TaskType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder->add('description');
$builder->add('tags', CollectionType::class, [
'entry_type' => TagType::class,
'entry_options' => ['label' => false],
'prototype' => true,
'allow_add' => true,
'allow_delete' => true,
]);
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Task::class,
]);
}
}
class TagType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder->add('name');
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
$tag = $event->getData();
$form = $event->getForm();
dd($tag); // empty because of prototype: true (as I said need it for adding new t
});
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Tag::class,
]);
}
}
TABLES:
TASK: id, name
TAG: id, task_id, name
my problem:
I would like to set - HTML attribute disabled for input name that meets the condition.
I need to get Task ID inside TagType so I can check if this id is used in another table and if so - disable changing tag name.
I tried multiple things but nothing worked for me.
The main problem is its a prototype (need it for adding/removing) and I don't receive tag id inside $tag = $event->getData(); in TagType so I can't get task id.
any idea? I didn't found the same problem on the internet and I'm not able to figure it out alone.
Something you can try is to use the PRE_SET_DATA event on the task form side to get the ID, and pass that ID to your tag form with the entry_options. Something like (not tested, but you'll get the idea) :
class TaskType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder->add('description');
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
$task = $event->getData();
$form = $event->getForm();
$form->add('tags', CollectionType::class, [
'entry_type' => TagType::class,
'entry_options' => [
'label' => false,
'task_id' => !empty($task) ? $task->getId() : null,
],
'prototype' => true,
'allow_add' => true,
'allow_delete' => true,
]);
});
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Task::class,
]);
}
}
class TagType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
// $options['task_id']
// Use your taskId here. The value can be null, on the task creation form.
$builder->add('name');
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Tag::class,
'task_id' => null,
]);
}
}
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 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 have 2 entities, User and Account. An Account can be own by multiple users and a User can own multiple Account. So I have this entity UserHasAccount that is handling this relation, plus some few fields that have personalised values for each User - Account relation, such as position, showInMenu, showInSideBar.
In the settings of the account, when a user is logged in I want to display those options but only for the current logged user.
So I end up with 2 form type:
AccountEditType:
class AccountEditType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', null, array(
'label' => 'account.form.name',
'translation_domain' => 'AcmeAccountBundle',
))
->add('timezone', 'genemu_jqueryselect2_timezone', array(
'label' => 'account.form.timezone',
'translation_domain' => 'AcmeAccountBundle',
))
->add('accountUser', new UserAccountEditType($options['user']))
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Acme\AccountBundle\Entity\Account',
'user' => null,
'validation_groups' => array('AcmeUpdateAccount'),
'cascade_validation' => true,
));
}
public function getName()
{
return 'acme_account_edit_settings';
}
}
UserAccountEditType:
class UserAccountEditType extends AbstractType
{
protected $user;
public function __construct($user)
{
$this->user = $user;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('showInMenu', null, array(
'label' => 'account.user_account.form.show_in_menu',
'translation_domain' => 'AcmeUserBundle',
'required' => false,
))
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Acme\UserBundle\Entity\UserHasAccount',
));
}
public function getName()
{
return 'acme_general_account_user';
}
}
The problem is that Account.accountUser is a collection of user and so I cannot use UserAccountEditType which is mapping only one relation (not the collection). If I leave it like this I have the following error:
The form's view data is expected to be an instance of class Acme\UserBundle\Entity\UserHasAccount, but is an instance of class Doctrine\ORM\PersistentCollection
How can I possibly select one of the relation in the form type?
Cheers,
Maxime