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....
Related
I'm new to symfony and I'm trying to learn but I'm stuck at the moment :
I'm trying to create a form with input already filled, let me explain :
I've got a Session table, linked with an antibiotic (when I create a session, I choose an Antibiotic), Antibiotic table is linked to Result table.
Like this : Tables
What I'm trying to do, is when I click on Valid Result on Result list page, it creates a form of a new result with antibiotic input already filled (because a Session has one Antibiotic)
This is what i've got now (where 6 is the Antibiotic ID from the Session) :
Add Results page
I've tried to look on how edit pages works with no results.
Thanks
/**
* #Route("/session/resultat/{id}", name="addResultat")
*/
public function voirAction($id, Request $request){
//Find the Session object
$em=$this->getDoctrine()->getManager();
$session=$em->getRepository("AppBundle:Session")
->find($id);
if ($session==null)
{
throw new \Symfony\Component\HttpKernel\Exception\NotFoundHttpException("Ouste !");
}
$resultat = new Resultat();
$form = $this->createForm('AppBundle\Form\ResultatType', $resultat);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($resultat);
$em->flush();
return $this->redirectToRoute('resultat_show', array('id' => $resultat->getId()));
}
return $this->render('resultat/addresultats.html.twig', array(
'resultat' => $resultat,
'session' => $session,
'form' => $form->createView(),
));
}
I got it to work finally thanks to ccKep :
Just set my object before creating the form like this :
So after I initialised my new "resultat", i can set his "antibiotique" with setter method and I get it from the $session with the getter method
$resultat->setAntibiotique($session->getAntibiotique());
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
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.
I have a form and after user submits the form and validation goes OK, I want to ask him for his email and nickname in modal window. If user fills and submit an email and nickname, I want to validate it and save it as new record or get id of existing one (in case email was already used in past). If validation is not successful, user should be able to correct values in the same modal. If everything is OK, I want to save the form including create user id.
I already have form saving and user create/find process done. I just do not know, how to put this together, to work in scenario I described above. Could anyone explain, how this should be done in Yii? I am using Yii 1.1.15 and Yii Booster. Thank you.
In Yii the _form.php view file is used both in update.php and create.php views by default.
So, you might need to do smth. similar: insert form with modal in both update.php and create.php views.
Actions and different for these, so you keep logic separate; this is the MVC basic advantage.
public function actionCreate() {
$model = new Users;
if (isset($_POST['Users'])) {
$model->attributes = $_POST['Users'];
if ($model->save()) { // here in the save() method the valadation is included
// ONLY after we validate and successfully saved we go to update action
$this->redirect(array('update', 'id' => $model->id));
}
}
$this->render('create', array(
'model' => $model,
));
}
The main thing is that when you try to save save() method the validation happend automatically.
So if validation is not successful the logic brings back to the same action (create for example) with fields populated in view since model is already having data passed into it: $model->attributes = $_POST['Users'].
If validation is successful we redirect further. Not nessesary ajax form submit, even casual submit fits here.
public function actionUpdate($id) {
$model = $this->loadModel($id);
if (isset($_POST['Users'])) {
$model->attributes = $_POST['Users'];
if ($model->save()) { // after saving EXISTING record we redirect to 'admin' action
$this->redirect(array('admin'));
}
}
$this->render('update', array(
'model' => $model,
));
}
Forms in views(update/create) you keep as originally designed.
Validation for uniqueness is simple in model rules():
array('username, email', 'unique'),
Email valadation for email syntax is seems like this:
array('email', 'email'),
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