Symfony2:Two Forms in a Same Page

Symfony2 : Two forms in a same page

You have to treat the forms separately:

if('POST' === $request->getMethod()) {

if ($request->request->has('form1name')) {
// handle the first form
}

if ($request->request->has('form2name')) {
// handle the second form
}
}

This is perfectly explained in Symfony2 Multiple Forms: Different From Embedded Forms (temporarily unavailable - see below)

Update

As the link provided above is temporarily unavailable, you can see an archive of that resource here.

Symfony - Two forms from two entities on the same page

According to your current code, you are only calling your DevisAction.

While doing that, you are not even touching the NewsletterAction and that's why your twig cannot find the formEmail form.

Try including both your forms in one controller action like this and it should render properly.

class DevisController extends Controller
{
public function DevisAction(Request $request)
{
$devis = new Devis();
$form = $this->createForm(new DevisType(), $devis);

$newsletter = new Newsletter();
$formEmail = $this->createForm(new NewsletterType(), $newsletter);

if ($formEmail->handleRequest($request)->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($newsletter);
$em->flush();

$request
->getSession()
->getFlashBag()
->add('info', 'Vous êtes bien inscrit à la newsletter ');

return $this->redirect($this->generateUrl('oah_core_homepage'));
}

if ($form->handleRequest($request)->isValid()) {
$em = $this->getDoctrine()->getManager();

$em->persist($devis);
$em->flush();
$request
->getSession()
->getFlashBag()
->add('info', 'Le devis a bien été envoyé !');

$message = \Swift_Message::newInstance();
$imgUrl = $message->attach(\Swift_Attachment::fromPath('http://img15.hostingpics.net/pics/419370oah.png'));

$message
->setSubject('Demande de devis bien reçue !')
->setFrom('xxx@gmail.com')
->setTo($devis->getEmail())
->setBody(
$this->renderView(
'OAHDevisBundle:Devis:devis_email.html.twig',
array('url' => $imgUrl)
),
'text/html'
);

$this->get('mailer')->send($message);

$message = \Swift_Message::newInstance()
->setSubject('Nouvelle demande de devis')
->setFrom('xxxn@gmail.com')
->setTo('xxxn@gmail.com')
->setBody(
$this->renderView(
'OAHDevisBundle:Devis:devis_recu.html.twig'
),
'text/html'
);

$this->get('mailer')->send($message);

return $this->redirect($this->generateUrl('oah_core_homepage'));
}

// Si on n'est pas en POST, alors on affiche le formulaire
return $this->render('OAHCoreBundle:Core:layout.html.twig', array(
'form' => $form->createView(),
'formEmail' => $formEmail->createView()
));
}
}

EDIT This still needs to be cleaned by providing proper form validation which I have not done but the rendering problem should be solved.

Symfony 2 multiple form in same page Submition

I am assuming that it is your browser that is complaining about blank fields? One of the less amusing things about Symfony 2 forms is that, by default, all fields are required. This is implemented by setting a required attribute on the input element itself. The browser then process the attribute.

Couple of ways to get around this. Set required = false when creating the element:

     $builder->add('name','text', array(
'required' => false, /***** Make it optional *****/
'label' => 'Your Name',
'trim' => true,
'constraints' => array(
new NotBlankConstraint($constraintOptions),
),
'attr' => array('size' => 30),
));

You can also pass this in to your form options when creating the form so everything is optional by default.

You can also add a simple novalidate attribute to the form element itself in your twig template.

By the way, as @Spiro suggested, if you are using the built in security system then you will need to submit each form to a different url. Otherwise the security system will intercept your create user requests.

http://symfony.com/doc/current/reference/forms/types/form.html#required

How to create a page with two forms in Symfony?

Ok, I solved it!

What did I do:

First, I created two forms in project/src/AppBundle/Form/:

AccountGeneralDataType.php

<?php

namespace AppBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;

class AccountGeneralDataType extends AbstractType
{

/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
/**
* todo: Missing avatar upload
*/
$builder
->add('username', TextType::class, [
'label' => 'Username',
'required' => true,
])
->add('email', EmailType::class, [
'label' => 'Email',
'required' => true,
])
->add('submit', SubmitType::class, [
'label' => 'Save changes',
'attr' => [
'class' => 'btn btn-outline-primary float-right',
]
]);
}

/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => 'AppBundle\Entity\Account',
]);
}

/**
* {@inheritdoc}
*/
public function getBlockPrefix()
{
return 'appbundle_general_data_account';
}

}

Don't forget to set a unique label in the getBlockPrefix()!

AccountPasswordType.php

<?php

namespace AppBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\Validator\Constraints\NotBlank;

class AccountPasswordType extends AbstractType
{

/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('old-password', PasswordType::class, [
'label' => 'Old password',
'mapped' => false,
])
->add('new-password', RepeatedType::class, [
'label' => 'New password',
'mapped' => false,
'type' => PasswordType::class,
'invalid_message' => 'The new password fields must match.',
'options' => ['attr' => ['class' => 'password-field']],
'required' => true,
'first_options' => [
'label' => 'New password'
],
'second_options' => [
'label' => 'Repeat password'
],
'constraints' => [
new NotBlank(),
],
])
->add('submit', SubmitType::class, [
'label' => 'Update password',
'attr' => [
'class' => 'btn btn-outline-primary float-right'
]
]);
}

/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'allow_extra_fields' => true,
]);
}

/**
* {@inheritdoc}
*/
public function getBlockPrefix()
{
return 'appbundle_change_password';
}

}

Don't forget to set a unique label in the getBlockPrefix()!

The AccountPasswordType does not use a data_class (which is set in the configureOptions() method) because I don't fill automatically an object with it.

Second, I called them in my controller:

AccountController.php

<?php

namespace AppBundle\Controller;

use AppBundle\Entity\Account;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Form\FormError;

/**
* Account controller.
*
* @Route("account")
*/
class AccountController extends Controller
{
/**
* Displays forms to edit an existing account entity.
*
* @Route("/edit/{username}", name="account_edit")
* @Method({"GET", "POST"})
*/
public function editAction(Request $request, Account $account)
{
/**
* Form to edit general information
*/
$editAccountGeneralInformationForm = $this->createForm('AppBundle\Form\AccountGeneralDataType', $account);
$editAccountGeneralInformationForm->handleRequest($request);

/**
* Form to edit password
*/
$editAccountPasswordForm = $this->createForm('AppBundle\Form\AccountPasswordType', []);
$editAccountPasswordForm->handleRequest($request);

// Handle the account form
if ($editAccountGeneralInformationForm->isSubmitted() && !$editAccountGeneralInformationForm->isValid())
{
$session->getFlashBag()->add('danger', 'Your iinformation have not been updated, please retry.');
}

if ($editAccountGeneralInformationForm->isSubmitted() && $editAccountGeneralInformationForm->isValid())
{
$this->getDoctrine()->getManager()->flush();

$session->getFlashBag()->add('success', 'Your information have been updated.');
}

// Handle the password form
if ($editAccountPasswordForm->isSubmitted() && !$editAccountPasswordForm->isValid())
{
$session->getFlashBag()->add('danger', 'Your password have not been updated, please retry.');
}

if ($editAccountPasswordForm->isSubmitted() && $editAccountPasswordForm->isValid())
{
$oldUserPassword = $account->getPassword();
$oldPasswordSubmitted = $editAccountPasswordForm['old-password']->getData();
$newPasswordSubmitted = $editAccountPasswordForm['new-password']->getData();

if (password_verify($oldPasswordSubmitted, $oldUserPassword))
{
$newPassword = password_hash($newPasswordSubmitted, PASSWORD_BCRYPT, ['cost' => 15]);
$account->setPassword($newPassword);
$this->getDoctrine()->getManager()->flush();

$session->getFlashBag()->add('success', 'Your password have been updated.');
}
else
{
$editAccountPasswordForm->get('old-password')->addError(new FormError('Incorrect password.'));
$session->getFlashBag()->add('danger', 'Your password have not been updated, please retry.');
}
}

return $this->render('account/edit.html.twig', [
'account' => $account,
'edit_form' => $editAccountGeneralInformationForm->createView(),
'edit_password_form' => $editAccountPasswordForm->createView(),
]);
}
}

Using $foobarForm->isSubmitted(), we can know if the form have been submitted.

My biggest problem was that the two Type classes for my forms had the same name (defined in getBlockPrefix()) so when I submitted the second one, it was the first one which thrown errors.

Multiple form include symfony (same page)

Thanks for your response !

Finaly, the problem was not here. I used a specific twig view for my comment form form-comment.html.twig (include in the index) but I did not specify a specific route path.

Two responses elements :

First : I use {{ render(url('comment-add')) }} instead of render(controller("AppBundle:Default:method") for include the twig view.

Second : I specify a path for action comment post with my route-name (comment-add). {{ form_start(form_comment, {'attr': {'method' : 'post', 'action' : path('comment-add', {'post_id': post_id, 'groupe_id' : groupe_id})}}) }}

(I'm sorry because I had not specified that I used a form with include twig view.)



Related Topics



Leave a reply



Submit