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.
Related
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');
}
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
Hi I am a new to the Symfony2 MVC framework. What I have achieved so far is rendering a form in a twig template using the twig template. What I want to do next is create second (separate) controller to deal with form submission. Can you share with me how to achieve this.
I have read the symfony2 documentation however, it is not working.
Many thanks:)
You need to set an action on the form you are generating like so:
public function generateSearchBarAction()
{
$form = $this->createFormBuilder()
//This is where we are defining the target route
->setAction($this->generateUrl('route_to_catch_the_request'))
->setMethod('POST')
->add('keyword')
->getForm()
;
return $this->render('search_bar.html.twig', array(
'form' => $form->createView()
));
}
The controller that is provided at route_to_catch_the_request can then catch the request.
public function showSearchKeywordsAction(Request $request)
{
$form->handleRequest($request);
if ($form->isValid()) {
//do whatever...
}
}
I am new at Zend2:
I have a form, and in the first stage I create a new ViewModel and return it:
return new ViewModel(array('form' => $form, 'messages' => $messages));
In the post stage when the data comes back from the browser, how can I connect this same form to a new View (which has the same elements maybe less, maybe more) or create another form and rassign it the old form's data and relate it to a new view to show?
Any help would be appreciated.
EDIT:
I tried to do the following:
$form->setAttribute('action', $this->url('auth/index/login-post.phtml'));
But still shows the old one.
When I do this:
return $this->redirect()->toRoute('auth/default', array('controller' => 'index', 'action' => 'login-post'));
I get error page: The requested controller was unable to dispatch the request.
When I get the post of the request I need to load another view, I mean how do I specify which view is connected to which form?
The forms do not themselves have any knowledge of the view. If you wish to change the view after completing the form submission; where this new view provides perhaps a different form, this is something that should be done within the controller.
A (non-working) example with a few options on how a different view could be returned.
class FooController extends AbstractActionController
{
public function getFooForm()
{
return $this->getServiceLocator()->get('Form\Foo');
}
public function getBarForm()
{
return $this->getServiceLocator()->get('Form\Bar')
}
public function fooAction()
{
$request = $this->getRequest();
$form = $this->getFooForm();
if ($request->isPost()) {
$form->setData($request->getPost());
// Is the posted form vaild
if ($form->isValid()) {
// Forms validated data
$data = $form->getData();
// Now there are a few options
// 1. Return a new view with the data
$view = new ViewModel($data);
$view->setTemplate('path/to/file');
return $view;
// OR Option 2 - Redirect
return $this->redirect()->toRoute('bar', $someRouteParams);
// Option 3 - Dispatch a new controller action
// and then return it's view model/response
// We can also pass on the posted data so the controller
// action that is dispathed will already have our POSTed data in the
// request
$request->setPost(new \Zend\Stdlib\Parameters($data));
return $this->forward()->dispatch('App\Controller\Foo', array('action' => 'bar'));
}
}
// Render default foo.phtml or use $view->setTemplate('path/to/view')
// and render the form, which will post back to itself (fooAction)
return new ViewModel(array('form' => $form));
}
public function barAction()
{
$request = $this->getRequest();
$form = $this->getBarForm();
if ($request->isPost()) {
$form->setData($request->getPost());
// ....
}
// Renders the bar.phtml view
return $this->viewModel(array('form' => $form));
}
}
From what I understand form your question, you would need to be using option 3 as the new view should populate a second form with it's already validated data.
If you are referring to something like an edit view then you just need to bind your object to the form.
$form->bind($yourObject);
http://zf2.readthedocs.org/en/latest/modules/zend.form.quick-start.html#binding-an-object
Otherwise you can make the form post to any controller action using by setting it:
$form->setAttribute('action', $this->url('contact/process'));
Maybe post what code you have and more specifics and I'm sure you will get some more detailed answers
I have a form in Symfony2 framework. On successful submission of the page it renders another twig template file and returns the values by passing the parameters in an array. But after submission, if I refresh the page, again it is submitting the form and the table entry is created. Here is the code that is executed after submission in the controller,
$this->get('session')->setFlash('info', $this->get('translator')->trans('flash.marca'));
return $this->render('NewBundle:Backend:marca.html.twig', array(
'active' => 1,
'marca' => $marca,
'data' => $dataCamp,
'dataMarca' => $this->getMarcas($admin->getId()),
'admin' => $admin,
));
I want the form to be redirected to the twig files mentioned there with the parameters and the alert message mentioned above. But I don't want the form to be submitted on page refresh.
Thanks
This worked for me:
return $this->redirectToRoute("route_name");
You should save submitted data in session and redirect user. Then you will be able to refresh page as much as you want without additional submission.
Example code - your action algorithm should be similar:
...
/**
* #Route("/add" , name="acme_app_entity_add")
*/
public function addAction()
{
$entity = new Entity();
$form = $this->createForm(new EntityType(), $entity);
$session = $this->get('session');
// Check if data already was submitted and validated
if ($session->has('submittedData')) {
$submittedData = $session->get('submittedData');
// There you can remove saved data from session or not and leave it for addition request like save entity in DB
// $session->remove('submittedData');
// There your second template
return $this->render('AcmeAppBundle:Entity:preview.html.twig', array(
'submittedData' => $submittedData
// other data which you need in this template
));
}
if ($request->isMethod('POST')) {
$form->bindRequest($request);
if ($form->isValid()) {
$this->get('session')->setFlash('success', 'Provided data is valid.');
// Data is valid so save it in session for another request
$session->set('submittedData', $form->getData()); // in this point may be you need serialize saved data, depends of your requirements
// Redirect user to this action again
return $this->redirect($this->generateUrl('acme_app_entity_add'));
} else {
// provide form errors in session storage
$this->get('session')->setFlash('error', $form->getErrorsAsString());
}
}
return $this->render('AcmeAppBundle:Entity:add.html.twig', array(
'form' => $form->createView()
));
}
Redirect to same page is preventing additional data submission. So lean of this example modify your action and you will be fine.
Also instead save data in session you can pass it through redirect request. But I think this approach is more difficult.
Save your data (session/db/wherever you want it saved)
redirect to a new action, retreiving the new data in that action, and rendering the template
this way, refreshing the new action, will only refresh the template, as the saving of your data happened in the previous action
understand ?
so basically replace your
return $this->render....
by
return $this->redirect($this->generateUrl('ROUTE_TO_NEW_ACTION')));
and in this new action, you put your
return $this->render....