Using Symfony, version 2.3 and more recent, I want the user to click on a link to go to the edition page of an already existing entity and that the form which is displayed to be already validated, with each error associated to its corresponding field, i.e. I want
the form to be validated before the form is submitted.
I followed this entry of the cookbook :
$form = $this->container->get('form.factory')->create(new MyEntityFormType, $myEntity, array('validation_groups' => 'my_validation_group'));
$form->submit($request->request->get($form->getName()));
if ($form->isValid()) {
...
}
But the form is not populated with the entity datas : all fields are empty. I tried to replace $request->request->get($form->getName()) with $myEntity, but it triggered an exception :
$myEntity cannot be used as an array in Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php
Does anyone know a method to feed the submit method with properly formatted datas so I can achieve my goal ? Note : I don't want Javascript to be involved.
In place of:
$form->submit($request->request->get($form->getName()));
Try:
$form->submit(array(), false);
You need to bind the the request to the form in order to fill the form with the submitted values, by using: $form->bind($request);
Here is a detailed explanation of what your code should look like:
//Create the form (you can directly use the method createForm() in your controller, it's a shortcut to $this->get('form.factory')->create() )
$form = $this->createForm(new MyEntityFormType, $myEntity, array('validation_groups' => 'my_validation_group'));
// Perform validation if post has been submitted (i.e. detection of HTTP POST method)
if($request->isMethod('POST')){
// Bind the request to the form
$form->bind($request);
// Check if form is valid
if($form->isValid()){
// ... do your magic ...
}
}
// Generate your page with the form inside
return $this->render('YourBundle:yourview.html.twig', array('form' => $form->createView() ) );
Related
Since 3 weeks i try to learn php with the symfony framework.
I want to build an application with which i can track my expanses.
I made good progress but since 2 days i have a little logic problem so maybe someone can help me here.
I want to make a dashboard.(the main side of the project) There the user can monitor the expenditures.
This works. Now i want also a form at the dashboard, so the user can add new expenditures. I already implement a form but with a extra route. So in my ExpenditureController i have the functions dashboard and the function addExpenditure which generate different twig.html templates.
So the user can monitor his expenditures with ...budgetapp/expenditure/dashboard
and he can add new Expenditure with ...budgetapp/expenditure/addexpenditure
My Dashboard-Function
#[Route('/dashboard/', name: 'dashboard')]
public function dashboard(ExpenditureRepository $ar)
{
$user = $this->getUser();
$expenditures = $ar-> findexpendituresOfUser($user);
return $this->render('expenditure/dashboard.html.twig', [
'expenditures' => $expenditures,
]);
}
The expenditure/dashboard.html.twig shows the Expentiures of the current user in a table
My addExpenditure-Function
public function addExpenditure (ManagerRegistry $doctrine, Request $request){
$em = $doctrine->getManager();
$expenditure = new Expenditure();
$form = $this->createForm(ExpenditureType::class, $Expenditure);
$form->handleRequest($request);
if($form->isSubmitted()){
$em->persist($expenditure);
$em->flush();
}
return $this->render('expenditure/addexpenditure.html.twig', [
'addexpenditureForm' => $form->createView()
]);
}
The expenditure/addexpenditure.html.twig looks like this:
{% block body %}
<div class="container">
{{form(eintragenForm)}}
</div>
{% endblock %}
My problem /mistake in thinking:
How can i implement the form to the dashboard? So of course i can take the code from the addexpenditure function and put it 1:1 in the dashboard-funciton. but i dont think this is the right way? I also tried to including template fragments with the offical Embedding Controllers Documentation of Symfony, but this also dont work.
So someone can help me with a suggestion how you would handle this in your project?
Best regards
Markus
There are two possible solutions: put both view and add logic inside one controller's action, or separate them. Since you probably have some validation, it's reasonable to have both add and view code inside one action (otherwise, you'll have to pass errors via sessions, which is not very pretty). So basically, your dashboard/add action will look like this:
#[Route('/dashboard/', name: 'dashboard')]
public function dashboard(Request $request, ExpenditureRepository $ar, ManagerRegistry $doctrine)
{
$expenditure = new Expenditure();
$form = $this->createForm(ExpenditureType::class, $expenditure);
if ($request->isMethod('POST')) {
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $doctrine->getManager();
$em->persist($expenditure);
$em->flush();
return $this->redirectToRoute('dashboard');
}
}
$user = $this->getUser();
$expenditures = $ar-> findexpendituresOfUser($user);
return $this->render(
'expenditure/dashboard.html.twig',
[
'expenditures' => $expenditures,
'addexpenditureForm' => $form->createView(),
]
);
}
What's happening here:
First you check if POST http method was used, if it was - it means
that form has been submitted and you can handle it. There is an alternative solution without checking request method, see comments for more.
If this is a GET request, just show ordinary dashboard
If the form is submitted and it's valid - save data and redirect to
the same page. Otherwise, you'll need to empty all submitted data
from the form, etc. (it's not the right way to do it)
If the form is invalid, do not redirect, just render the page
normally, and all errors will be shown.
Finally, you of course have to render the form on your dashboard.html.twig like you did: {{form(eintragenForm)}}
In my symfony 5 project I would like when submitting a certain form, compare the entity before and after submission.
So keep a copy of the original entity in order to perform processing.
I've :
$parametresAdmin = $entrepriseService->getParametresAdmin();
$form = $this->createForm(ParametresAdminType::class, $parametresAdmin, [
'entreprise' => $this->getUser()->getEntreprise(),
]);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$entrepriseService->updateParametres($parametresAdmin);
return $this->redirectToRoute('admin_parametres');
}
In fact, I want to get a copie of $parametresAdmin->getTypesConges() (which is a collection on OneToMany).
So, when the form is submitted, I want compare the old $parametresAdmin->getTypesConges() and the new $parametresAdmin->getTypesConges().
The "$parametresAdmin->getTypesConges()" part looks like this:
I can add / modify leave types on the fly. Except that I do not want to authorize the possibility of modifying the "Balance type" field for the types of leave that already exist. There is just for those that I add that I leave the possibility in the ChoiceType. So on the front side, it's good. But on the back side, no.
But it doesn't work
What I do :
I change the "Solde initial" for the first line :
But when I submit, I've the same value (the new value : 10 )
EDIT : Currently, I've now :
$parametresAdmin = $entrepriseService->getParametresAdmin();
$typeConges = $parametresAdmin->getTypesConges();
$oldTypesConges = clone $typeConges;
$form = $this->createForm(ParametresAdminType::class, $parametresAdmin, [
'entreprise' => $this->getUser()->getEntreprise(),
]);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$typeConges = $parametresAdmin->getTypesConges(); // update with new data
dd($oldTypesConges, $parametresAdmin->getTypesConges());
$entrepriseService->updateParametres($parametresAdmin);
return $this->redirectToRoute('admin_parametres');
}
You should clone your data like this :
$typeConges = $parametresAdmin->getTypesConges();
$oldTypeConges = clone $typeConges;
// handleRequest(), isValid(), isSubmit() ...
$typeConges = $parametresAdmin->getTypesConges(); // update with new data
dd(oldTypeConges, $parametresAdmin->getTypesConges());
phpdoc says about :
When an object is cloned, PHP will perform a shallow copy of all of the object's properties. Any properties that are references to other variables will remain references.
Take a look at this question on stackoverflow.
Use session variables to store the old data:
To store your data before rendering the form in the controller, use:
$this->get('session')->set('oldTypesConges ', $typesConges );
And after submitting, read the data stored to compare it with the new one:
$oldTypesConges = $this->get('session')->get('oldTypesConges ');
Now, you can compare $typesConges and $oldTypesConges.
This solution was inspired from here: How to set session variables for all the controllers in Symfony2?
$versionsBeforeSubmit = [];
foreach($produit->getVersions()->getIterator() as $version) {
$versionsBeforeSubmit[] = clone $version;
}
$form->handleRequest($request);
if($form->isValid() && !$hasError) {
dump($versionsBeforeSubmit);
}
I have an entity (about 20/25 fields) and i want to edit it with a form.
I just want to edit (and display) few form field.
The problem is, all fields displayed are correctly update, but fields that are not rendered are update with "null" value by default.
My controller :
$em = $this->getDoctrine()->getManager();
$LaboRequest= $em->getRepository('MyBundle:LaboRequest')->find($id);
$form = $this->createForm('MyBundle\Form\LaboRequestType', $LaboRequest);
if ($request->isMethod('POST') && $form->handleRequest($request)->isSubmitted() && $form->isValid()) {
$em->persist($LaboRequest);
$em->flush();
return $this->redirectToRoute(...);
}
return $this->render('...', array(
'LaboRequest' => $LaboRequest,
'form' => $form->createView(),
));
I only render few fields in my view, so i can understand, by default symfony use "null" for fields that are not render.
But is there a way to edit a part of an entity and not affect data of the entity with "null" value ?
I'm not sure you can do that.
But you can extend your original form and call
$builder->remove('xxx')
for each field you want to remove
I wonder whether there is a simple way to reset one of the form fields after form submission and then pass the form to the view - to render the same form but one of the field will be with it's default value. Something like:
$form = $this->createForm('MyForm');
$form->handleRequest($request);
if ($form->isValid()) {
// do something
// ...
// reset one of the form field to it's default value
// something like:
// $form->field->reset() or
// $form->field->setValue('');
}
return array(
'form' => $form->createView(),
);
Thanks!
You can use:
$form->get('field')->submit($defaultValue);
Check out the doc.
I want to make forms
which has three scenes
1.input view (which has 'confirm' button)
2.draft check view (which has 'send' button)
3.send view
in method 1 you can input the data then click 'confirm' button
the system write the data in DB as draft.
in method 2 you see the data and confirm then push 'send' button
the system write the flg 'confirmed'
these are my code.
it works well method 1 but if I push 'send' button in method2.
it doesn't go.
if ($form->isValid()) {
My idea is something wrong??
public function writeEvalStudentAction(Request $request,$keyStr){
...
$form = $this->createFormBuilder($evalStudent)
->add('commentToStudent')
->add('confirm','submit')->getForm();
$form->handleRequest($request);
if ($form->isValid()) {
if ($form->has('send')){
if ($form->get('send')->isClicked()){
//set confirm flg then shows thanks screen.
$evalStudent->setConfirmed(true);
$em->persist($evalStudent);
$em->flush();
return $this->render('AcmeMemberBundle:Default:confirmedEvalStudent.html.twig');
}
}
if ($form->has('confirm')){
if ($form->get('confirm')->isClicked()){
// write in the db as draft.
$evalStudent->setCommentToStudent($form->get('commentToStudent')->getData());
$em->persist($evalStudent);
$em->flush();
$form = $this->createFormBuilder($evalStudent)->add('send','submit')->getForm();
return $this->render('AcmeMemberBundle:Default:checkEvalStudent.html.twig',
array('form' => $form->createView()));
}
}
return $this->render('AcmeMemberBundle:Default:writeEvalStudent.html.twig',
array('form' => $form->createView()));
}
Split it up in 4 controller actions with 4 separate routes. Whereas route2 and route3 require the id of your draft object in the routing parameters.
Make the form1 from step 1 point to step2.
In step2 validate the form and if it is ok insert to db and get your id, then show form2 which points to step 3, if not show form1 with form errors.
Same for step2->step3 and step3->step4.
In step 4 you convert your draft to a final object and persist it and remove the draft.