39

I need to insert an entity which has associations.

If I already have the FK's of the associated entities, is there a way to insert the primary entity into the db with just the FK's populated?

Or do I always have to

  • retrieve the associated entities via the FK's,
  • populate the primary entity's properties referring to the assocations,
  • and then invoke the persist method.
j0k
  • 22,600
  • 28
  • 79
  • 90
blacktie24
  • 4,985
  • 6
  • 41
  • 52
  • 2
    So you want to manually create a relationship by specifying the ID of an existing entity to be related? Could you please add some example code of what you are trying to achieve? – Cobby Mar 22 '11 at 01:03
  • hey Cobby, appreciate the response. I have a create user form, where you can choose from a select option list which role to give a user (user can only have one role in this case). So when I process the form submission, I should have a username and a roleId. If I want to save the user, do I need to retrieve the associated role entity and set it within my user object in order to save the user object? Or can I simply set the roleId within the user object and save it? – blacktie24 Mar 22 '11 at 01:16

4 Answers4

65

You want a reference proxy

Let's say I have Posts and Tags. A Post hasMany Tags. I get a bunch of tags from the user, who checked a bunch of checkboxes.

The following would add tags to an existing post, without fetching each tag entity first. It does this by using reference proxies, generated by EntityManager::getReference():

$tag_ids = $_POST['tag_id']; // an array of integers representing tag IDs.
$post = $em->getRepository('Post')->find($post_id); // returns a Post entity.

foreach($tags_ids as $tid){
   $post->addTag($em->getReference('Tag',$tid));
}
$em->persist($post);
$em->flush();
j0k
  • 22,600
  • 28
  • 79
  • 90
timdev
  • 61,857
  • 6
  • 82
  • 92
  • ahhh got it, that makes a lot of sense, but what actually happens in the backend? When doctrine persists the post, with its newly added tags, does it end up replacing the proxies with the actual entities, and then adding these associations to the join table? – blacktie24 Mar 24 '11 at 00:19
  • I'm not sure what Doctrine does internally, but I expect it's efficient -- it simply inserts and/or updates as necessary. If you create a reference proxy with an id that doesn't exist, you probably get an exception when you try to flush(), and the transaction gets rolled back. But don't rely on my word, test it yourself. – timdev Mar 24 '11 at 02:37
  • ya i'll try it out. Thx again for your time, timdev, I really appreciate it. Cheers. – blacktie24 Mar 24 '11 at 03:03
  • Totally didn't know about the EntityManager::getReference() function, mega fail. Should probably re-read the API docs more closely :P – Cobby May 11 '11 at 11:37
  • +1 awesome reply @timdev. If the id does not exist, it is still generating a reference proxy, I have posted this question [here](http://stackoverflow.com/questions/16456090/getreference-of-doctrine-entity-manager). I still don't understand why doctrine generates a proxy for an entity that does not exist... – Mick May 09 '13 at 07:19
  • nice answer. it works fine but I had to remove the `method type hinting` because of the difference between entity and proxy. is this the best solution regarding this issue? – user1236048 Jun 03 '13 at 10:54
  • @alex.dominte Proxies extend the entity classes, so if you're type-hinting an entity class, substituting a proxy should work fine. – timdev Jun 05 '13 at 21:00
4

In regards to using a reference proxy
In my investigations this is only partly a solution, as follows:

Yes, you do not have to pro-actively retrieve the related record (because you create a proxy record), but when you flush (commit) the update transaction it still first executes a select statement to retrieve the related record, and then only does the update (all in one hit to the db).
This is inefficient and should not be necessary (we have the foreign-key id, why retrieve the record..?)

So while not a complete solution, what you do gain is only a single connection to the database (which is good) and slightly simplified code.

I am not sure if there is a solution to this at the moment...??
Hopefully the doctrine bods will update in the future and if using the proxy logic we should gain an automatic performance enhancement...

MarkOfSine
  • 275
  • 3
  • 11
0

You should retrieve the entity to be related and the make the relationship.

I assume you could manually specify the relationship by directly accessing the database through the DBAL layer but I wouldn't not recommend this, nor have I tried it.

Cobby
  • 5,273
  • 4
  • 28
  • 41
  • Cobby, yeah that's what I was expecting, but why exactly would this not be recommended, as it would technically save an extra query? Thanks again for taking time out to follow up on my question, I really appreciate it. – blacktie24 Mar 22 '11 at 04:59
  • 1
    Going by your example, you should already have your roles cached (ensure this in the previous request when they are loaded for the drop down list). So it's not really an extra query. By doing it this way, you maintain a cleaner domain layer at expense of an extremely small performance hit. – Cobby Mar 22 '11 at 05:35
-1

You can do this using entityManager::merge

$post = new Post();
$post->setPostCategory(['id' => 1]);
$em->persist($em->merge($post));
$em->flush();
Fábio Paiva
  • 569
  • 4
  • 7
  • Does not work for me: Argument 1 passed to App\Entity\Post::setPostCategory() must be an instance of App\Entity\PostCategory or null, array given – Nicodemuz Jun 09 '18 at 05:12