Render controller in TWIG and show form errors - php

I have indexAction and contactAction
contactAction is a simple form with no mapped fields (FormType) like below:
/**
* #Route("/contact", name="contact")
* #Template()
* #param Request $request
* #return array
*/
public function contactAction(Request $request)
{
$form = $this->createForm(new ContactType());
$form->handleRequest($request);
if ($form->isValid()) {
$firstName = $form->get('first_name')->getData();
$lastName = $form->get('last_name')->getData();
$email = $form->get('email')->getData();
$message = $form->get('message')->getData();
}
return array(
'form' => $form->createView()
);
}
and i render this form in my indexAction with this TWIG command:
{{ render(controller('RusselBundle:Default:contact')) }}
Everything is okey, if page is not reloaded, HTML5 validators works fine, but if form have some errors like: firstName length, error's not show at all, how can i do, so that errors showed up in the form indexAction? Any help would be appreciated. I'm just curious it's possible, and if - how ? Sorry for my english....

Rather than using the request passed into the action you should get the master request from the request stack. As #DebreczeniAndrás says, when you use the render(controller()) you are using a newly created sub-request rather than the request that was actually passed to the page on load (the master request).
public function contactAction(Request $request)
{
$request = $this->get('request_stack')->getMasterRequest();
$form = $this->createForm(new ContactType());
//...
}

On symfony3 use render function like this
{{ render(controller('RusselBundle:Default:contact', {'request':app.request})) }}

If you use the render function in your twig, then that creates a subrequest, thus your original posted (i.e. in your main request) values get lost.
You can pass your main request to your form render action as follows:
{{ render(controller('RusselBundle:Default:contact'), 'request' : app.request ) }}
This will pass all the main request parameters appropriately to your subrequest.

Related

How to handle request from $_POST where no Symfony form is defined on the request, without searching form classname?

How can I handle request data from the $_POST data itself. I mean if I try to handle the form like this: $form->handleRequest($request);, Symfony would try to get the data from $_POST['form_classname'], but I want to fill my form class straight from base $_POST variables.
Actually I want to handle the information from the outer site. And I have to develop something like an API. But without authorization, tokens, etc...
So I decided to build the form with some properties I need. After validation the form might do some logic.
Here is an example of $_POST I have to handle
Function=TransResponse&RESULT=0&RC=00&AUTHCODE=745113
As you can see, there is no form name in request. The $form->handleRequest($request); works only if the request was like an
[form_name][Function]=TransResponse&[form_name][RESULT]=0&[form_name][RC]=00&[form_name][AUTHCODE]=745113
But I can't change the request format.
Just put in your form class
/** #inheritdoc */
function getBlockPrefix() {
return '';
}
Here is the information about this method Documentation
Use
$this->get('form.factory')->createNamed('')
$this->get('form.factory.)->createNamedBuilder('')
to create a Form or FormBuilder respectively that uses the whole $_POST/$_GET array for its parameters.
Example:
/**
* #Route("/testRoute")
* #param Request $request
* #return Response
*/
public function testAction(Request $request): Response
{
$form = $this->get('form.factory')->createNamedBuilder('', FormType::class, null, ['csrf_protection' => false])
->add('text', TextType::class)
->setMethod('GET')
->getForm();
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
return new Response($form['text']->getData());
}
return new Response('Submit me');
}

Is this the correct way to handle forms in two pages?

I have an index page which contains a simple form; if the form validation fails the index page is reloaded with errors if not then the action related to the page forwards the request to another action related to page success. The success page uses the form submitted to create a list from DB. Once we are on success page we have another form similar to the first one which the user can use to modify the list on the page. Both forms have same fields.
index page action:
class DefaultController extends Controller {
/**
* #Route("/", name="homepage")
*/
public function indexAction(Request $request) {
$event = new Event();
$form = $this->createForm(EventForm::class, $event);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()) {
// Do some minor modification to the form data
$event->setDate($party->getDate()->modify('+12 hours'));
$cityName = strtolower($event->getPlace()['city']);
// We know the form data is valid, forward it to the action which will use it to query DB
return $this->forward('AppBundle:Frontend/Default:eventsList', array('request' => $request));
}
// If validation fails, reload the index page with the errors
return $this->render('default/frontend/index.html.twig', array('form' => $form->createView()));
}
success page action (where the form data gets forwarded)
/**
* #Route("/success", name="eventList")
*/
public function eventsListAction(Request $request) {
$event = new Party();
// Create a form in the second page and set its action to be the first page, otherwise form submition triggers the FIRST action related to page index and not the one related to page success
$form = $this->createForm(EventForm::class, $event, array('action' => $this->generateUrl('eventList')));
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()) {
$event->setPlace($form["place"]->getData());
$event->setTitle($form["title"]->getData());
$repository = $this->getDoctrine()
->getRepository('AppBundle:Event');
// ....
// Create a list of events using the data from DB
// ....
return $this->render('default/frontend/success.html.twig',
array('events' => $events, 'form' => $form->createView())
);
}
return $this->render('default/frontend/success.html.twig', array('form' => $form->createView()));
}
Although the above implementation "works" I have a couple of issues:
When I submit the first form the url stays the same, that of the first page like:
[HOST]/app_dev.php?place=London&Date=......
But if I submit the second form the URL is correctly:
[HOST]/app_dev.php/success?place=London&date=.....
The implementation feels hacky to me, is there a cleaner way to achieve this?
When the form is submitted, then it's porcessed with the same controller and action. You have to process the submited data and then redirect to success page.
So in your example:
if($form->isSubmitted() && $form->isValid()) {
...
...
return $this->redirectToRoute('eventList');
}
And if you need to transfer posted data from one "page" to another, then you have to store it in session $this->get('session')->set('name', val); and then retrieve data from session in eventList action by $this->get('session')->get('name');
More info how to handle sessions in Symfony: https://symfony.com/doc/current/controller.html#the-request-object-as-a-controller-argument

Submit form to another controller in Symfony

I have a page index.html.twig which contains a Form this form when submitted, get validated and the result is shown in a page success.html.twig. Now I have a new requirement where the page success.html.twig itself contains a Form which should contain the values which were passed by the form from index.html.twig and if the user wants the new form should also allow the user to do search directly from success.twig.html. The requirement is inspired by hostel world.
Questions:
Is there a design pattern which I could use to implement a solution
My current thinking is to create a new Action for success.html.twig and submit the form to thatAction instead of rendering success.html.twig from index.html.twig's Action. Is it correct? How can I implement it?
Code (Partial):
/**
* #Route("/", name="homepage")
*/
public function indexAction(Request $request)
{
$event = new Event();
$form = $this->createForm(MyForm::class, $event);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()) {
$event->setPlace($form["place"]->getData());
$event->setDate($form["date"]->getData()->modify('+12 hours'));
return $this->render('default/frontend/success.html.twig',
array('events' => $events, 'cityName' => $cityName, 'cityImage' => $cityImage)
);
}
return $this->render('default/frontend/index.html.twig', array('form' => $form->createView()));
}
It makes sense to have two different actions - at least for different request types (GET, POST, etc..)
There is just one thing as advice: $form->isValid() has already an isSubmitted() check inside. So there is no need to check whether it is submitted or not.

Creating Contact Form using Symfony

I am trying to create a form using Symfony 2.5 using this tutorial, but this tutorial is using old version of Symfony. I can get the form to display and created the entity as well however I am now working on submitting the form. Following is the code from tutorial and this code is inside default controller contactAction
public function contactAction()
{
$enquiry = new Enquiry();
$form = $this->createForm(new EnquiryType(), $enquiry);
$request = $this->getRequest();
if ($request->getMethod() == 'POST') {
$form->bindRequest($request);
if ($form->isValid()) {
// Perform some action, such as sending an email
// Redirect - This is important to prevent users re-posting
// the form if they refresh the page
return $this->redirect($this->generateUrl('BloggerBlogBundle_contact'));
}
}
return $this->render('BloggerBlogBundle:Page:contact.html.twig', array(
'form' => $form->createView()
));
}
My main concerns is in the following section of above code
$request = $this->getRequest();
if ($request->getMethod() == 'POST') {
$form->bindRequest($request);
As you will notice it is using getRequest() which has been depricated and then my IDE is telling me buildRequest method cannot be found.
I will really appreciate if someone call push me towards the right path of converting the contactAction for symfony verion 2.5, I will really appreciate it.
Declare the action like this:
public function contactAction(Request $request)
{
...
}
And import:
use Symfony\Component\HttpFoundation\Request;
And you will have the request inside your action, so you can remove this line:
$request = $this->getRequest();
Hi there are a few deprecated calls also I would really recommend going to the cookbook in Symfony. But anyway this below will help.
namespace myproject/mybundle/controller;
use Symfony\Component\HttpFoundation\Request;
Class Act //that is me ;) {
/**
* #Route("/contact", name="_lalalalala")
* #Template()
*/
public function contactAction(Request $request){
$enquiry = new Enquiry();
$form = $this->createForm(new EnquiryType(), $enquiry);
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($enquiry);
$em->flush();
return $this->redirect($this->generateUrl('BloggerBlogBundle_contact'));
}
return ['form' => $form->createView()];
}
}
You can shorten this code even further by using symfony services container to inject your form. I would recommend reading this it is pretty awesome. As you can re use forms anywhere :)
You can change the getMethod to isMethod like
if ($request->isMethod('POST'))
And then your can submit the request data to the form using submit like
$form->submit($request->request->get($form->getName()));
Alternatively you can use the handleRequest method that will handle the above 2 method in one go and then you can move on with the rest of your action like
$form->handleRequest($request);
if ($form->isValid())
.. etc
To retrieve the request object, you have two possibilities. Either let Symfony pass the request as an argument to your controller action ...
public function contactAction(Request $request)
{
// ...
}
... or get the request object from the container.
$request = $this->get('request');
For the binding of the request to the form, the manual states the following:
Passing the Request directly to submit() still works, but is deprecated and will be removed in Symfony 3.0. You should use the method handleRequest() instead.
This way, the request binding part would look similar to this:
if ($request->isMethod('POST')) {
$form->handleRequest($request);
if ($form->isValid()) {
// do something
}
}

Processing Symfony Form in Embedded Controller

I am trying to embed a controller in a Twig template like this:
{{ render(controller('IRCGlobalBundle:MailingList:join')) }}
This controller renders a basic form to join a mailing list:
namespace IRC\GlobalBundle\Controller;
use Symfony\Component\HttpFoundation\Request;
use IRC\GlobalBundle\Entity\MailingList;
use IRC\GlobalBundle\Form\Type\MailingListType;
class MailingListController extends BaseController
{
public function joinAction(Request $request) {
$this->getSiteFromRequest($request);
$mailingList = new MailingList;
$mailingList->setSite($this->site);
$form = $this->createForm(new MailingListType(), $mailingList);
$form->handleRequest($request);
if ($form->isSubmitted()) {
echo "submitted form";
} else {
echo "unsubmitted form";
}
return $this->render('IRCGlobalBundle:Forms:join_mailing_list.html.twig', array(
'form' => $form->createView(),
));
}
}
The problem is that the $form->isSubmitted() method never returns true so the form submission cannot be validated/handled. What am I doing wrong? Do I need to change the target of the form to point to my embedded controller?
I guess it's because the form will be rendered like this <form action=""> so it will post the information to the same page but since you are rendering the form using a sub-request the new sub-request will not carry any form data.
The easiest way to solve this issue is by hacking the form action attribute and make it send the request to joinAction and you can redirect or forward the request after handling the form to the page where the user came from.
something like this should work:
$form = $this->createForm(new MailingListType(), $mailingList, array(
'action' => //generate a url to joinAction
));

Categories