How to access the Doctrine replace() method? - symfony

I am doing my first project with Symfony2 + Doctrine, and currently trying to implement replacing records. However when I try to call
$em->save($product);
or
$em->replace($product);
(instead of)
$em->persist($product);
I get fatal errors. So I started digging around to try to find the persist() method so I could see what other methods were available. I searched the entire contents of the vendors/doctrine directory and could not find any references to the persist or flush methods. Where the heck are these located? I tried following the code but quickly got lost.
So the main question: How can I do a replace() with doctrine in Symfony2?
Sub-question: Where are the persist() and flush() methods? Not being able to find them is frustrating in itself.

Incase somebody else is wondering, try looking into:
$em = $service->get('doctrine.orm.entity_manager');
$entity = $em->merge($entity);
$em->flush();
From the docs, Doctrine\ORM\EntityManager::merge():
Merges the state of a detached entity into the persistence context of
this EntityManager and returns the managed copy of the entity. The
entity passed to merge will not become associated/managed with this
EntityManager.
Should do the trick for you.

I don't think that Doctrine's replace() method is supported in Symfony, or at least it is not accessible through EntityManager.
If you need to update your existing entity, then you can simply do it as described here - http://symfony.com/doc/current/book/doctrine.html#updating-an-object.
As for persist() and flush() methods - you can find them in vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectManager.php

Related

Creating new entities in `postFlush` doctrine event

I'm struggling to find a way to perform a persist() and flush() methods after the final flush (I mainly want to do it in postFlush event).
I collect the necessary entities in onFLush event (with changeSets) and wait up until all entities (which I collected) got flushed to get their id's (auto incremented).
So that I have at this point an array with all needed entities and their change sets and their id's set.
Then I want to create new entities (let's call them "traces") based on fields of previously collected entities and persist & flush "traces" in database.
But I'm really stuck here as I can't know entities id's in onFlush event, and I can't persist & flush them in postFlush when they already have their id's set.
Currently Doctrine documentation states following:
postFlush is called at the end of EntityManager#flush(). EntityManager#flush() can NOT be called safely inside its listeners.
And if I dare do this, it ends up in a recursion and php fails with an error.
Which approach may I take here?
I believe you could do a check if you aren't flushing "traces" entity and then perform your "traces" creation. That shouldn't loop.
Also you might want to look at symfony's eventDispatcher . You could dispatch your events manually, since it might be cleaner.
More details on "traces" would be helpful, from what I can imagine it is some kind of a changelog, history; so for that I might suggest EntityAuditBundle. It works pretty good with doctrine and is not hard to set up, I am using it myself.

Why does Doctrine return me an associative array with a gap in the keys when fetching?

I am building an app with Symfony.
I've got a Doctrine entity that contains (among other properties) a collection of another Entity (forming a OneToMany association).
Class OuterEntity
{
/**
* #ORM\OneToMany(targetEntity="InnerEntity", mappedBy="outer", cascade={"persist", "remove"})
*/
private $inners;
}
In my OuterEntityController, in the updateOuterEntityAction method, i do the following :
parse the request
get the updated Outer from db
modify it according to the request
call flush() on the entity manager
call findAll() on the OuterEntity repository
return the OuterEntity list to the client in a JsonResponse in order to notify it of the new state of the db. Since I use a JsonResponse, I let Symfony do the serialization (very probably with json_encode).
Everything is allright except when I delete one or more InnerEntity in the association. The issue does not come from the removal, but from the returned json format. Most of the times, the inners I get are under the form of a simple array:
{"inners":[{inner1},{inner2},...]}
, which is fine for me. But after a removal from this association (in the example, I assume the 2nd one was deleted), I get an array like this:
{"inners":{"0":{inner1},"2":{inner2},"3":{...},...}
Furthermore, this issue doesn't happen if the last Inner is deleted (or several Inners all located at the end of the array).
My supposition is that Doctrine places the association in an associative array and when json_encode serializes this array, it renders in the first format if the indexation is normal (0, 1, 2...), or in the second format if the indexation is broken (0, 2, 3...).
So my question is now : why doctrine does not place the result in a "normally indexed" array after a removal?
I think there's something going on with Doctrine's caching mechanism, but I can't figure exactly what. Maybe it's because the entity manager still considers the deleted entities. But I thought that the entity manager was cleared after the flush, isn't it?
I tried to call clear() on the entity manager, but I've had some strange behaviour and quickly gave up.
I'd be glad if one of you could point the mistake I am making.
It seems that the problem is on my side of the keyboard.
I don't know why, but I retried to clear the entity manager today, and everything works just fine as I first expected. I must have made a really dumb move at my first try.
Thanks everybody for the help, and sorry for the inconvenience.

Insert entity in preUpdate event

I'm trying to persist a History entity whenever a Message gets updated. I have too much going on behind the scenes to post all the code here and for it to make sense, but I've basically tracked the issue down to the UnitOfWork::commit method. There, the UOW first loops through the entityInsertions, and finding nothing, continues on to the entityUpdates. There the UOW's entityInsertions gets updated, but since it's already past that loop, it doesn't pick up that it still needs to persist some entities. Is there any way to force the UOW to "restart" this process? If so, how? I'm using Doctrine 2.4.
Thanks for any help!
This might be the dirtiest solution ever, but what I ended up doing was basically the following...
Create an onFlush event subscriber
Inject the entire container into the subscriber (seeing as injecting only the entity manager will result in a circular reference error)
Loop through the UnitOfWork's scheduledEntityUpdates and scheduledEntityInserts (I wasn't interested in deletes)
Handle each scheduled update or insert which you are interested in (in my case, I marked each entity I was interested in with a LoggableInterface, just to know which entities are loggable)
Handle the relevant object with a handler chain (This was just my own algorithm, yours may not require this. This was set up to handle logging of different LoggableInterface objects in different ways)
Persist the entity (the actual history event) via the entity manager, and do the following:
$classMeta = $this->entityManager->getClassMetadata(get_class($historyEntity));
$this->entityManager->getUnitOfWork()->computeChangeSet($classMeta, $historyEntity);
Profit
Hope this helps somebody!

Doctrine2 - A new entity was found through the relationship [...]

Wow, this is very strange:
$bar = new Bar();
$foo = $entity->getFoo();
$bar->setFoo($foo);
$em->persist($bar);
$em->flush();
and I obtain the following message
A new entity was found through the relationship [....]
By reading on the net, I undertand that if fetch two object from two different entity managers, one manager will doesn't know about entities fetched from other manager. Since here I'm into a "db hook" (postPersist) and I've manager injected from DIC, I've tried to change my code in agreement:
$bar = new Bar();
$foo_id = $entity->getFoo()->getId();
$foo = $em->getRepository('MyBundleName:Foo')->findOneBy(array('id'=>$foo_id));
$bar->setFoo($foo);
$em->persist($bar);
$em->flush();
but nothing changed.
Can someone point me to right direction and, most important, explain what's going on here?
UPDATE
I've migrated all code outside db hooks and I've created a new service that will do pretty much same things of hooks but has to called explicitly after flush() Is very strange, however, that nothing changed. I've also tried to use spl_obj_hash onto entity manager for retrieve information about the entity manager who flush object into db and "other one" that will retrieve $foo from db and try to persist $bar.
This is the result:
SPL_OBJ ENTITY MANAGER - 1: 0000000021770e03000000001eda54f7
SPL_OBJ ENTITY MANAGER - 2: 0000000021770e03000000001eda54f7
if I'm not wrong, they're the same entity manager object. I'm pretty confused actually...
UPDATE - WORKING CODE
I've found a "code combination" that will do the work but I'm still pretty confused about it.
If I use first snippet of code (where $foo is retrieved from $entity itself) and flush only $bar, error is still there.
If I use second snippet of code (where $foo is retrieved from entity manager) and persist only $bar, all works properly (obviosuly if I call $flush() without parameters, issue is still there)
I had the same problem and resolved it, I'm posting it because I haven't seen it mentioned anywhere and it might help someone.
I have a service that parses data & persists it to entities, service to which I'm feeding the entityManager & child entities. And I started to have the same error on persist. Using merge() didn't help as it just threw me "A managed dirty+entity [...]" error instead.
After a little digging it seems that as far as Doctrine is concerned the entities I was feeding my service came from a different entityManager, so you actually have to get the entities from the entityManager inside the service if you are using it to persist.
I fixed mine by removing clear
$this->getDoctrine()->getManager()->clear();
I'm not sure since I never had this issue before and right now I'm not in the position to replicate it. But I will give you some options I would try:
If the two entitymanagers are using the same database you can merge (http://docs.doctrine-project.org/en/2.0.x/reference/working-with-objects.html#merging-entities) the entity into the other entitymanager. In this case that will look something like this:
$em->merge($foo);
This makes sure the entity $foo is managed by your entitymanager.
Also, remember that a postPersist is executed after the database operations. I think you have better chances if you try your modified code in a prepersist method.
As the documentation says:
prePersist - The prePersist event occurs for a given entity before the respective
EntityManager persist operation for that entity is executed.
postPersist - The postPersist event occurs for an entity after the entity has
been made persistent. It will be invoked after the database insert
operations. Generated primary key values are available in the
postPersist event.
Full documentation can be found here: http://docs.doctrine-project.org/en/2.0.x/reference/events.html#lifecycle-events
Just try this and let me know the result
Try : $em->merge($bar);
$bar = new Bar();
$foo = $entity->getFoo();
$bar->setFoo($foo);
$em->merge($bar);
$em->flush();
[1] Doctrine - A new entity was found through the relationship
It's not so clear from your code, but I think you create a new $foo, but you don't save it before to save $bar.
Try this:
$em->persist($foo);
$em->flush();
$bar->setFoo($foo);
$em->persist($bar);
$em->flush();
In general you can escape this by using cascade=persist on the association of your entities.

Doctrine and Symfony: magic methods and caching

I'm currently implementing the doctrine result cache so I've set
result_cache_driver: apc
into my configuration.
Then I've correctly got query cache working inside the Repository, using for example
->setResultCacheId(sprintf('posts_for_user_%d', $userId))
First problem come when I used these things in doctrine:
$repository->findOneBy(array)
$repository->findBy(array)
which can maybe easily overridden in the repository.
The problem which I can't get past is to use the ParamConverter to use doctrine caching and also entities association.
For example, if I have a Team entity with a OneToMany relation to Player I usually do
$team->getPlayers()
I don't have the control over the caching of that query. Is that possible in some way?
When you run methods like find/findBy or process over PersistentCollection doing $team->getPlayers(), there is UoW which loads the data using EntityPersister and ObjectHydrator to hydrate an object. These objects have no support of result cache driver.
In the other hand, when you use DQL or QueryBuilder, your code products Query object that extends AbstractQuery. If you look inside AbstractQuery::execute you will see this pretty piece of code which makes using of result cache driver possible
$cache = $queryCacheProfile->getResultCacheDriver();
$result = $cache->fetch($cacheKey);
if (isset($result[$realCacheKey])) {
return $result[$realCacheKey];
}
So my suggestion - try load your entities using QueryBuilder and leftJoins on children Collections.
$this->createQueryBuilder('x')->select('x, p')->leftJoin('x.players', 'p')->....;
It'll create the possibility of using result cache driver.

Resources