ok, I have submitted serialized data via AJAX using JQuery into a function within my Symfony 2 (2.7.10) app.
I am now having a few issues with trying to bind that data to a new entity and save that entity.
Here is what I have done to date:
public function AJAX_SaveTestAction(Request $request) {
// $GetFormData = $request->get('Data'); <- This is a jquery serialized data
$NewData = new TestInfo(); <- this is the entity I want to save
$form = $this->createForm(new InfoType(), $NewData);
$form->handleRequest($request);
if ( $request->isMethod('POST') ) {
$form->bind( $request->get('Data') );
$em = $this->getDoctrine()->getManager();
$em->persist( $NewData ); <- not saving jquery returns 500 server error?
$em->flush();
}
die(); <- this is just here for testing, I will want to return a json message or something else!
}
All help most welcome.
Thanks.
Don't use form->handleRequest and form->bind
form->bind was the equivalent of form->handleRequest before Symfony 2.6.
If you are using Symfony 2.3 use form->bind
If you are using Symfony 2.6+ use form->handleRequest
Your Controller should look like :
public function AJAX_SaveTestAction(Request $request)
{
$NewData = new TestInfo();
$form = $this->createForm(new InfoType(), $NewData);
$form->handleRequest($request);
if($form->isSubmitted()){ // if you also want to check if your entity is valid, just replace it with "if($form->isValid()){"
$em = $this->getDoctrine()->getManager();
$em->persist( $NewData );
$em->flush();
return new Response('It works');
}
return new Response('error');
}
But your JS should send a POST request to this route (with post parameters).
May be with that :
http://api.jquery.com/jquery.post/
To bind correctly your request data to your entity using handleRequest, Symfony expects an array with your form name as key in your POSTed data.
If you did not override method getName of your form, it is defined by default by "underscoring" your Form class name. So it should be info_type here.
TL;DR: You should submit your data as ['info_type' => [ YOUR DATA... ]] instead of just [ YOUR DATA... ]
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());
This code works just fine :
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
abstract class TableManagerController extends Controller
{
public function listAndAddAction(Request $request)
{
// We get the Entity Manager
$entityManager = $this->getDoctrine()->getManager();
// We get the entity repository
$repository = $entityManager->getRepository($this->entityRepository);
// We build the new form through Form Factory service
$form = $this->get('form.factory')->create($this->entityFormObject, $this->entityObject);
// If user sent the form and sent data is valid
if ($form->handleRequest($request)->isValid())
{
// We set the position of the new entity to the higher existing one + 1
$newPosition = $repository->higherPosition() + 1;
$this->entityObject->setPosition($newPosition);
// We insert the data in DB
$entityManager->persist($this->entityObject);
$entityManager->flush();
// We redirect user to the defined homepage
return $this->redirect($this->generateUrl($this->routeHomePage));
}
return $this->render($this->renderIndexTemplate, array(
'dataList' => $repository->listAll(),
'form' => $form->createView()
));
}
}
But when I just split it in 3 methods, like this :
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
abstract class TableManagerController extends Controller
{
public function listAndAddAction(Request $request)
{
$dataList = $this->listMethod();
$form = $this->addMethod($request);
return $this->render($this->renderIndexTemplate, array(
'dataList' => $dataList,
'form' => $form
));
}
protected function listMethod()
{
// We get the Entity Manager
$entityManager = $this->getDoctrine()->getManager();
// We get the entity repository
$repository = $entityManager->getRepository($this->entityRepository);
// We generate the entity management homepage view (list + add form)
return $repository->listAll();
}
protected function addMethod(Request $request)
{
// We get the Entity Manager
$entityManager = $this->getDoctrine()->getManager();
// We get the entity repository
$repository = $entityManager->getRepository($this->entityRepository);
// We build the new form through Form Factory service
$form = $this->get('form.factory')->create($this->entityFormObject, $this->entityObject);
// If user sent the form and sent data is valid
if ($form->handleRequest($request)->isValid())
{
// We set the position of the new entity to the higher existing one + 1
$newPosition = $repository->higherPosition() + 1;
$this->entityObject->setPosition($newPosition);
// We insert the data in DB
$entityManager->persist($this->entityObject);
$entityManager->flush();
// We redirect user to the defined homepage
return $this->redirect($this->generateUrl($this->routeHomePage));
}
// We return the generated form
return $form->createView();
}
}
I get this error which appears once I've sent the form :
An exception has been thrown during the rendering of a template ("Catchable Fatal Error: Argument 1 passed to Symfony\Component\Form\FormRenderer::renderBlock() must be an instance of Symfony\Component\Form\FormView, instance of Symfony\Component\HttpFoundation\RedirectResponse given, called in D:\Websites\CPG-2015\app\cache\dev\twig\d6\80\0e5eee6c7aa1859cedb4cd0cc7317a0ebbdd61af7e80f217ce1d2cf86771.php on line 61 and defined in D:\Websites\CPG-2015\vendor\symfony\symfony\src\Symfony\Component\Form\FormRenderer.php line 106") in IBCPGAdministrationBundle:CourseLevel:index.html.twig at line 19.
for which I understand there is something wrong with the form. But I really don't get why since this same form, from the same view, appears perfectly well before I send it.
The problem is here in your addMethod:
// We redirect user to the defined homepage
return $this->redirect($this->generateUrl($this->routeHomePage));
which in turn gets used here without any handling of that return possibility:
$form = $this->addMethod($request);
return $this->render($this->renderIndexTemplate, array(
'dataList' => $dataList,
'form' => $form
));
By returning $this->redirect inside of an if-statement, you're giving two potential return values of addMethod, a FormView or a RedirectResponse. As a result, you then try to pass that RedirectResponse through form which Twig attempts to render (which it can't, of course.)
The solution is to re-work your return logic!
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
}
}
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 am trying to add a simple form to allow my users to edit their profile. My problem is:
Since the entity "linked" to the form is the same as the current user object ($user === $entity, see below), if the form validation fails, then the view is rendered with the modified user object (ie. with values of the non-valid form).
Here my (classic) controller:
public function profileAction()
{
$em = $this->getDoctrine()->getEntityManager();
$user = $this->get('security.context')->getToken()->getUser();
$entity = $em->getRepository('AcmeSecurityBundle:User')->find($user->id);
// $user === $entity => true
$form = $this->createForm(new ProfileType(), $entity);
$request = $this->getRequest();
if ($request->getMethod() === 'POST')
{
$form->bindRequest($request);
if ($form->isValid()) {
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('profile'));
}
}
return $this->render('AcmeSecurityBundle:User:profile.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
));
}
So I wondered how to have two distincts objects $user and $entity. I used clone() and it worked well for the view rendering part (the $user object was not modified), but it created a new record in database instead of updating the old one.
PS: I know I should use FOSUserBundle. But I would really like to understand my mistake here :)
I used the same solution as FOSUserBundle, which is calling $em->refresh() on my entity when the form validation fails:
public function profileAction()
{
$em = $this->getDoctrine()->getEntityManager();
$user = $this->get('security.context')->getToken()->getUser();
$entity = $em->getRepository('AcmeSecurityBundle:User')->find($user->id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find User entity.');
}
$form = $this->createForm(new ProfileType(), $entity);
$request = $this->getRequest();
if ($request->getMethod() === 'POST')
{
$form->bindRequest($request);
if ($form->isValid()) {
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('profile'));
}
$em->refresh($user); // Add this line
}
return $this->render('AcmeSecurityBundle:User:profile.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
));
}
Note that if you are using what is called a "virtual" field in "How to handle File Uploads with Doctrine" (in my case "picture_file" you will need to clear it by hand:
$em->refresh($user);
$user->picture_file = null; // here
One approach would be to always redirect:
if ($form->isValid()) {
$em->persist($entity);
$em->flush();
}
return $this->redirect($this->generateUrl('profile'));
Of course you lose error messages and changes.
Another approach would be to define an entity manager just for your UserProvider. $user would no longer be the same as $entity. Bit of extra overhead but it certainly makes the problem go and would prevent similar interactions with other forms that might modify all or part of an user entity.
In a similar fashion, you reduce the overhead by creating an entity manager just for your profile form. With this method, the overhead would only be incurred when editing the profile.
Finally, you could ask yourself if it really mattered that the display data was not quite right in this particular case. Would it really bother anything? Would anyone notice but you?
See How to work with Multiple Entity Managers in Symfony Cookbook
Another idea is to clone your user entity in your user provider. This will divorce it from the entity manager.
You could also use $entityManager->detach($user); to remove the user from the entity manager.
And why is the token user an entity anyways? Consider making a completely independent User class with minimal information pulled from the database by your user provider. That is what I do.