I can't create multiple object of an entity at once - symfony

I'm learning Symfony and I have a situation where I need, after a form, to create multiple object of an entity A.
All these objects are quite identical but just the value of one field change which is a relation OneToMany to an entity B. In the form this relation is selected with checkbox (that are EntityType related to B), and so I want to create one object A for each checked boxes.
My issue is that the First's form allows me to create only one object of First type at a time.
It is a project given by my teachers, and I mustn't create a ManyToMany relation.
Can somebody help me?
Here is the code in the Controller:
$A = new A();
$form = $this->createForm(AType::class, $A);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($A);
$entityManager->flush();
}
And here is the code in the FormType:
$builder
->add('anotherField')
->add('idB', EntityType::class, [
'label' => 'B',
'class' => B::class,
'expanded' => true,
'multiple' => true
])
;

If I understand you correctly, with one form submission you wish to save multiple entities to the database. I would clone all the form data into a new object, make changes and then persist all and flush like so:
if ($form->isSubmitted() && $form->isValid()) {
$original = $form->getData();
$copy = clone $original;
// here you can make changes to your copy.
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($original);
$entityManager->persist($copy);
$entityManager->flush();
}

Related

How to persist a collection of forms embedded in a collection of Forms...?

For my Poll Application i created a FormType called CampaignType which holds a CollectionType named blocks which in turn holds a CollectionType named lines, which holds a CollectionType named fields, which holds a CollectionType named pollResults.
In my next code example you can see my code that renders the View to fill a campaign(poll).
public function fillAction(Request $request, $id)
{
$campaign = $this->getDoctrine()->getRepository(Campaign::class)->find($id);
$entityManager = $this->getDoctrine()->getManager();
foreach ($campaign->getBlocks() AS $block){
foreach ($block->getLines() AS $line){
foreach ($line->getFields() AS $field){
$pollResult = new PollResult();
$pollResult->setCampaign($campaign);
$pollResult->setField($field);
$pollResult->setUser($this->getUser());
$entityManager->persist($pollResult);
$field->getPollResults()->add($pollResult);
}
}
}
$form = $this->createForm(CampaignType::class, $campaign);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()){
var_dump("true");
//$entityManager->persist($campaign);
$entityManager->flush();
return $this->redirectToRoute("grappt_poll_campaignShow", ['id' => $id]);
}
return $this->render('GrapptPollBundle:Campaigns:fill.html.twig', [
'campaign' => $campaign,
'form' => $form->createView()
]);
}
The only thing that must be persisted in the database are the PollResults.
Every PollResult has an entry for the campaign_id and the field_id it belongs to, the user_id who filled out the campaign and the value the user chose (and of course its own id, which gets generated automatically).
My Problem is that i don't know how to do that.
Where do i have to call $entityManager->persist($pollResult);.
Right now i put it directly under the initialization-stuff.
Do i have to put it into the if($form->isSubmitted() && $form->isValid())-query and loop through every pollResult?
Do i have to call $entityManager->persist($campaign); although nothing changes there?
Furthermore i wonder if i have to add something for the value-entry of each PollResult?
Thanks in advance for every answer
lxg
What will $form->isValid() return ?
It will depend on the validation constraints of you master form. If your validation constraints are in the annotations of your entity, in your master entity you should have the #Assert\Valid() annotation which will be sure that the nested form is valid :
class Campaign
{
/**
* #ORM\OneToMany(…)
* #Assert\Valid() // <- this line here
*/
private $blocks;
...
If you prefer to put your validation constraints in your CampaignType, you can put it in the options :
public function buildForm (FormBuilderInterface $builder, array $options)
{
$builder
->add('blocks', CollectionType::class,[
'entry_type' => BlockType::class,
'constraints' => array(new Valid()) // <- this line here
...
So, where should you put the persist()?
The best is to have Symfony's form validation (->isValid()) before any persistance, for security and data sanity (don't persist before ensuring csrf protection for instance). If you may add a lot of data (like persisting thousands of entities after one form submission), you can look into Doctrine's batch processing and bulk inserts : https://www.doctrine-project.org/projects/doctrine-orm/en/2.7/reference/batch-processing.html
Should you also persist the Campaign object ?
It depends on the cascade persistence rules you have in your entity.
You can find all the rules to fine-tune the cascade here : https://www.doctrine-project.org/projects/doctrine-orm/en/2.7/reference/working-with-associations.html#transitive-persistence-cascade-operations

Modify multiple entities with one form (bulk edit)

I want to be able to submit a form that modifies multiple entities. Let's say I've got a BulkeditFormType to change the 'active' (Boolean) or 'organisation' (EntityType with App\Entity\Organisation) fields on multiple users at once.
This is my current solution (semi-pseudo-code):
public function bulkedit(Request $request)
{
$form = $this->createForm(BulkeditFormType::class, null, [
//..options
]);
if ($request->isXmlHttpRequest()) {
// fields '_token' and empty values are unset, not shown in this example
$formData = $request->request->get($form->getName());
$entityManager = $this->getDoctrine()->getManager();
$repo = $entityManager->getRepository(App\Entity\User::class);
$entities = $repo->findBy([
'id' => [1,2,3,4,5]
]);
foreach ($entities as $entity) {
$form = $this->createForm(BulkeditFormType::class, $entity, [
//..options
]);
$clearMissing = false;
$form->submit($formData, $clearMissing);
$entityManager->persist($entity);
}
$entityManager->flush();
}
return $this->render('#User/User/bulkedit.html.twig', [
'form' => $form->createView()
]);
}
Note that I've tried to include only relevant parts of my code, so please consider this as pseudo-code, just to get an idea of my current implementation.
While this solution works, it creates a Form object for every entity. Editing 100 users will lead to a large amount of memory usage and many useless database queries.
How can I modify my code in such a way that only one form will be generated which can be re-used by all entities? I've tried to use $form->setData(), but I feel like there must be a better way. A CollectionType will create multiple subforms instead of multiple forms, so in this case it doesn't make a big difference.

Cloning object after getting data from a form in Symfony2

I'm sure I'm missing something very basic here.
I have a form, and when the user updates the fields of the form I don't want to update the underlying entity but want to create a new entity with the new values.
To clone Doctrine entities I followed the indication here.
So my code is (let's say I want to clone the object with id=3:
$id = 3;
$storedBI = $this->getDoctrine()
->getRepository('AppBundle:BenefitItem')
->find($id);
$form = $this->createForm(new BenefitItemFormType(), $storedBI);
$form->handleRequest($request);
if ($form->isValid())
{
$em = $this->getDoctrine()->getManager();
$newBI = clone $form->getData();
$em->persist($newBI);
$em->flush();
}
It simply does not work. It properly creates a new object with the new data passed from the form (which is ok), but also updates the "old" stored object with the same new data.
Any idea?
You have to clone your object during the form creation:
$form = $this->createForm(new BenefitItemFormType(), clone $storedBI);
If this does not work, try to detach your cloned object first.

symfony2: saving form data in a single step

my form got 4 fields after validation when i look for the insert procedure if found i have to do the following which works fine too
if($form->isValid()){
$data = $form->getData();
$entity->setName($data->getName());
$entity->setEmail($data->getEmail());
$entity->setStatus($data->getStatus());
$entity->setSubscribedon($data->getSubscribedon());
$em = $this->getDoctrine()->getManager();
$em->persist($entity);
$em->flush();
$session->getFlashBag()->add('success', 'Subscriber added successfully');
return $this->redirect($this->generateUrl('admin_subscribers'));
}
but do we got any other way like mass assign data to insert ? like in Yii we can assign entire form fields with out setting individual form data , suppose if my form got 15 fields do i have to call set method for each of them ? is there any other way of doing it ?
thank you
You need to pass entity object when you create form, then after form validation just persist that entity
$entity = new Entity();
$em = $this->getDoctrine()->getManager();
$form = $this->createForm(new FormType(), $entity);
$form->handleRequest($request);
if ($form->isValid()) {
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('admin_subscribers'));
}

Choice Multiple = true, create new Entries

My Entity
/**
* Set friend
*
* #param \Frontend\ChancesBundle\Entity\UserFriends $friend
* #return ChanceRequest
*/
public function setFriend(\Frontend\ChancesBundle\Entity\UserFriends $friend = null)
{
$this->friend = $friend;
return $this;
}
My Action
$task = new ChanceRequest();
$form = $this->createFormBuilder($task)
->add('friend', 'choice', array(
'required' => true,
'expanded' => true,
'choices' => $fb_friends,
'multiple' => true,
'mapped' => true
))
->getForm();
Because setFriend is expecting a scalar, I cannot validate this or save it to db. It is an array from how many friends the user want to send a message to somebody. How can I change it?
I have seen here a post:
Symfony2 Choice : Expected argument of type "scalar", "array" given
but this don't work that I put an array in front of \Frontend or $friend. I guess because of the related table.
What do I have to do in Entity to get it work?
If friends could be found in your database (for example it is a User entity), you should declare ManyToMany relationship for these two tables. If not, make friend property to be a doctrine array type. if friends is not a valid entity class, all the rest you have is to make a custom validator and datatransformer. Otherwise you have nothing left to do. All this information you can find in the official symfony and doctrine documentation.
How to create a Custom Validation Constraint
How to use Data Transformers
Association Mapping
Working with Associations

Resources