Symfony 4: Embed form child Create New On Update - forms

This is what I've done so far:
I have a Patient(parent entity) and a File(child entity) each Patient is created with two files when the patient is edited I could edit the two files.
What's needed :
Instead of editing the same files I want that each time The patient is updated the form gets populated with the latest (based on the creation date) files but when submitting it should create new files and keep the old ones in the database
Could someone give me some suggestions and references?
Thanks
//Patient controller Edit Function
public function edit(Request $request, Patient $patient): Response
{
$types=$this->getListData('types');
$form = $this->createForm(PatientType::class, $patient,array(
'types' => $types,
));
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->getDoctrine()->getManager()->flush();
return $this->redirectToRoute('patient_show', [
'id' => $patient->getId(),
]);
}
return $this->render($brand.'/edit.html.twig', [
'patient' => $patient,
'form' => $form->createView(),
]);
}

Related

Insert same entity three times from one form

I'm using symfony4 and In my project I have and entity Bill and it offer two packs:
1) First pack, user can generate just one bill in PDF after filling out a form and saving data in database and it has its own price.
2) Second pack, user can generate Three bills in PDF after filling out a form and saving data in database and this has its own price also.
The first pack is simple and it works fine , I have created BillType and an action in the controller and everything is well.
public function newBillFirstPack(Request $request)
{
$entity = new Bill();
$em = $this->getDoctrine()->getManager();
$form = $this->createForm(BillType::class, $entity);
$form->handleRequest($request);
if ($form->isSubmitted()) {
if ($form->isValid()) {
$em->persist($entity);
$em->flush();
//...............
}
}
return $this->render('frontOffice/bill/new_first_pack.html.twig', array(
'form' => $form->createView()
));
}
The problem is with the second pack , I'd like to know how can I create three bills from one form. I tried to create 3 FormType
- FirstBillType and a twig to render its view.
- SecondtBillType and a twig to render its view.
- ThirdBillType and a twig to render its view also.
And in the controller I created three forms.
I didn't test it yet but even it works I don't like it , I fell it's not a clean solution. Imagine if a day I want to edit an attribute in the formType, so I must edit it in three formsType and three html.twig views, same thing if I want to remove or add an attribute in the forms.
I have see in the documentation "How to Embed a Collection of Forms", but that example is how to embed one attribute many times.
Any good solution ?
If I understand what you need. New action can handle this and depending on your needs form can run different action.
public function newBillThirdPack(Request $request)
{
$entity = new Bill();
$em = $this->getDoctrine()->getManager();
$form = $this->createForm(BillType::class, $entity);
$form->handleRequest($request);
if ($form->isSubmitted()) {
if ($form->isValid()) {
$em->persist($entity);
$em->persist(clone $entity);
$em->persist(clone $entity);
$em->flush();
//...............
}
}
return $this->render('frontOffice/bill/new_third_pack.html.twig', array(
'form' => $form->createView()
));
}

Symfony form that generates another form

I have a controller action the prompts the user for some free text input. When submitted the text is parsed into some number of objects that I want to put out on another form for the user to confirm that the initial parsing was done correctly.
Normally after dealing with the response to a form submission we call $this->redirectToRoute() to go off to some other path but I have all these objects laying around that I want to use. If I redirect off someplace else I lose them.
How can I keep them? I tried building my new form right there in the controller action method but then its submission does not seem to be handled properly.
/**
* #Route( "/my_stuff/{id}/text_to_objects", name="text_to_objects" )
*/
public function textToObjects( Request $request, Category $category ) {
$form = $this->createForm( TextToObjectsFormType::class, [
'category' => $category,
]);
$form->handleRequest( $request );
if( $form->isSubmitted() && $form->isValid() ) {
$formData = $form->getData();
$allTheStuff = textParserForStuff( $formData['objectText'] );
$nextForm = $this->createForm( StuffConfirmationFormType::class, $allTheStuff );
return $this->render( 'my_stuff/confirmation.html.twig', [
'form' => $nextForm->createView(),
'category' => $category,
] );
}
return $this->render( 'my_stuff/text.html.twig', [
'form' => $form->createView(),
'category' => $category,
] );
}
This does fine to the point of displaying the confirmation form but when I submit that form I just end up displaying the original TextToObjects form?
To answer albert's question, the TextToObjectsFormType just has three fields, a way to set the date & time for the group of generated objects, a way to select the origin of the objects and a textarea for the textual description. I do not set a data_class so I get an associative array back with the submitted information.
class TextToObjectsFormType extends AbstractType {
public function buildForm( FormBuilderInterface $builder, array $options ) {
$builder
->add( 'textSourceDateTime', DateTimeType::class, [
'widget' => 'single_text',
'invalid_message' => 'Not a valid date and time',
'attr' => [ 'placeholder' => 'mm/dd/yyyy hh:mm',
'class' => 'js-datetimepicker', ],
])
->add( 'objectsOrigin', EntityType::class, [
'class' => ObjectSourcesClass::class,
])
->add( 'objectText', TextareaType::class, [
'label' => 'Copy and paste object description text here',
]);
}
}
How can I get the confirmed, potentially revised, objects back to put them into the database?
Thanks.
Using your current architecture
$nextForm = $this->createForm( StuffConfirmationFormType::class, $allTheStuff );
does not bear enough information. It should contain the action parameter to tell the submission to where route the post request.
In your StuffConfirmationFormType add an 'objectText' field hidden.
Create a confirmation_stuff route
Create an action for this route
In this action if the form is valid => save your stuff ELSE re render TextToObjectsFormType with an action linked to text_to_objects
Be aware that using this technique would not prevent a user to enter non functional data by manually editing the hidden field.
Hope this helps

How to pass default data when creating a form holding a CollectionType?

I am trying to create multi import data, so I need many form collection this same class in one place. I can do this using collection type.
I created pictures Class with have one field: pictures and it is arrayCollection type like this:
class Pictures {
protected $pictures;
public function __construct()
{
$this->pictures = new \Doctrine\Common\Collections\ArrayCollection();
}
public function addPicture(Picture $picture)
{
$this->pictures[] = $picture;
return $this;
}
public function removePicture(Picture $picture)
{
$this->pictures->removeElement($picture);
}
public function getPictures()
{
return $this->pictures;
}
}
Next I created pictures form with collection to pictureType.
$builder
->add('pictures', 'collection', array(
'prototype' => false,
'by_reference' => true,
'entry_type' => PictureType::class,
'options' => array('label' => false))
)
;
Next I have picture Entity with fields for example name, width, height, image (nevermind).
So my PictureType is a simple form fith fields that same like Entity (standard).
Now I try to render this form and add some picture in controller (not from database). Then I create Picture Object and next I add it to pictures->addPicture($picture);
$pictures = new Pictures();
foreach($data as $d){
$picture = new Picture();
$picture->setName($d['title']);
$picture->setImage($d['thumbnail_400_url']);
$picture->setWidth($d['width']);
$picture->setHeight($d['height']);
$pictures->addPicture($picture);
}
$form = $this->createForm('PicturesType', $pictures, array(
'action' => $this->generateUrl('dashboard_fotolia_save'),
'method' => 'POST',
));
After it if rendered form is ok. I can see and change values.
But after submit form data disapear. I hope data will be stored in response but not:(
I want to use it only for create new object. Could you help me?
Form errors: This form should not contain extra fields
Actually it seems that your PicturesType holds a pictures field since you add it with :
$builder->add('pictures', 'collection', ...
So you got a form type with holds an array of data with a key 'pictures' for the corresponding fields.
You may want to set it by :
$form = $this->createForm('PicturesType', array('pictures' => $pictures), ...

Symfony: call form handleRequest but avoid entity persist

I have an entity that models a search form and a form type, I use that form for searching purposes only and I don't want that entity to be modified in database, so, when I do this:
$formModelEntity = $em->getRepository('AppBundle:SearchForm')
->findOneBy(array('name' => 'the_model'));
$formModelForm = $this->createForm(new SearchFormType(), $formModelEntity, array('action' => $this->generateUrl('dosearch'), 'method' => 'POST'));
$formModelForm->handleRequest($request); //or ->submit($request);
if ($formModelForm->isValid())
{
$formInstanceEntity->setFieldsFromModel($formModelEntity);
$em->persist($formInstanceEntity);
$em->flush();
}
The $formModelEntity changes are persisted to database, I want to avoid this but still want to take advantage of handleRequest ability to update the entity with all POST values (for read only purposes).
Is this possible?
In symfony, you only have to persist a new entity. If you update an existing entity found by your entity manager and then flush, your entity will be updated in database even if you didn't persist it.
Edit : you can detach an entity from the entity manager before flushing it using this line of code :
$em->detach($formModelEntity);
the method handleRequest Does not save the changes. it only updates the object within your method.
public function newAction(Request $request)
{
// just setup a fresh $task object (remove the dummy data)
$task = new Task();
$form = $this->createFormBuilder($task)
->add('task', TextType::class)
->add('dueDate', DateType::class)
->add('save', SubmitType::class, array('label' => 'Create Task'))
->getForm();
$form->handleRequest($request);
if ($form->isValid()) {
// ... perform some action, such as saving the task to the database
return $this->redirectToRoute('task_success');
}
return $this->render('default/new.html.twig', array(
'form' => $form->createView(),
));
}
the following snippet exists at http://symfony.com/doc/current/book/forms.html
and as you can see the entity is not persisted.
You're are probably adding a persist/flush and that's what's causing the entities to be updated.

Symfony2 Form Entity Update

Can anyone please show me a specific example of a Symfony2 form entity update? The book only shows how to create a new entity. I need an example of how to update an existing entity where I initially pass the id of the entity on the query string.
I'm having trouble understanding how to access the form again in the code that checks for a post without re-creating the form.
And if I do recreate the form, it means I have to also query for the entity again, which doesn't seem to make much sense.
Here is what I currently have but it doesn't work because it overwrites the entity when the form gets posted.
public function updateAction($id)
{
$em = $this->getDoctrine()->getEntityManager();
$testimonial = $em->getRepository('MyBundle:Testimonial')->find($id);
$form = $this->createForm(new TestimonialType(), $testimonial);
$request = $this->get('request');
if ($request->getMethod() == 'POST') {
$form->bindRequest($request);
echo $testimonial->getName();
if ($form->isValid()) {
// perform some action, such as save the object to the database
//$testimonial = $form->getData();
echo 'testimonial: ';
echo var_dump($testimonial);
$em->persist($testimonial);
$em->flush();
return $this->redirect($this->generateUrl('MyBundle_list_testimonials'));
}
}
return $this->render('MyBundle:Testimonial:update.html.twig', array(
'form' => $form->createView()
));
}
Working now. Had to tweak a few things:
public function updateAction($id)
{
$request = $this->get('request');
if (is_null($id)) {
$postData = $request->get('testimonial');
$id = $postData['id'];
}
$em = $this->getDoctrine()->getEntityManager();
$testimonial = $em->getRepository('MyBundle:Testimonial')->find($id);
$form = $this->createForm(new TestimonialType(), $testimonial);
if ($request->getMethod() == 'POST') {
$form->bindRequest($request);
if ($form->isValid()) {
// perform some action, such as save the object to the database
$em->flush();
return $this->redirect($this->generateUrl('MyBundle_list_testimonials'));
}
}
return $this->render('MyBundle:Testimonial:update.html.twig', array(
'form' => $form->createView()
));
}
This is actually a native function of Symfony 2 :
You can generate automatically a CRUD controller from the command line (via doctrine:generate:crud) and the reuse the generated code.
Documentation here :
http://symfony.com/doc/current/bundles/SensioGeneratorBundle/commands/generate_doctrine_crud.html
A quick look at the auto-generated CRUD code by the Symfony's command generate:doctrine:crudshows the following source code for the edit action
/**
* Displays a form to edit an existing product entity.
*
* #Route("/{id}/edit", name="product_edit")
* #Method({"GET", "POST"})
*/
public function editAction(Request $request, Product $product)
{
$editForm = $this->createForm('AppBundle\Form\ProductType', $product);
$editForm->handleRequest($request);
if ($editForm->isSubmitted() && $editForm->isValid()) {
$this->getDoctrine()->getManager()->flush();
return $this->redirectToRoute('product_edit', array('id' => $product->getId()));
}
return $this->render('product/edit.html.twig', array(
'product' => $product,
'edit_form' => $editForm->createView(),
));
}
Note that a Doctrine entity is passed to the action instead of an id (string or integer). This will make an implicit parameter conversion and saves you from manually fetching the corresponding entity with the given id.
It is mentioned as best practice in the Symfony's documentation