Doctrine custom repository methods and unmanaged entities - symfony

I've got custom entity repository (let's say CategoryRepository) that returns Doctrine entities. I also have newly created entity (let's say Product) that I want to persist.
Product is related to Category and, in that case, Product is the owning side of the relationship so I've got following code:
$category = $categoryRepository->customGetCategory($someCriteria);
$product = new Product();
$product->setCategory($category);
$em->persist($product);
and result is
[Doctrine\ORM\ORMInvalidArgumentException]
A new entity was found through the relationship
'Acme\SomethingBundle\Entity\Product#category' that was not configured
to cascade persist operations for entity: blahblah. To solve this
issue: Either explicitly call EntityManager#persist() on this unknown
entity or configure cascade persist this association in the mapping
for example #ManyToOne(..,cascade={"persist"})
For now I'm aware that all entities returned by custom repository methods using \Doctrine\ORM\Query::getResult() method where Query object is returned by EntityManager::createQuery($dql) factory method are detached by default. So I've got entity returned by repository that exists in database and I can't find a way for doctrine to have it managed just like any entity returned by f. ex. $repository->findBy() method.
Could anyone point me in right direction with this? I'd really like to solve that, it's killing me.

This is probably one of the top 5 Doctrine questions asked. Just difficult to search for. Could try searching on the error message.
The problem is that Category::setProduct is never being called. Update your Product entity with:
class Product
{
public function setCategory($category);
{
$this->category = $category;
$category->setProduct($this); // *** Add this
}
}

Related

Symfony2 EntityManager Updating without persist

As you can see below, I do not "persist" at all. But the changes I make are registering. I would appreciate your help.
$entityManager = $this->getDoctrine()->getManager();
$entity = $entityManager->getRepository(City::class)->find(1);
$entity->setName("debug name");
$entityManager->flush();
You have to call method persist() when initializing a new object, like new City(). When you fetch object from database with find() it already has some metadata.
From doctrine website:
Doctrine uses the Identity Map pattern to track objects. Whenever you
fetch an object from the database, Doctrine will keep a reference to
this object inside its UnitOfWork. The array holding all the entity
references is two-levels deep and has the keys “root entity name” and
“id”.
Here is example from doctrine:
When you call EntityManager#flush Doctrine will ask the identity map
for all objects that are currently managed. This means you don’t have
to call EntityManager#persist over and over again to pass known
objects to the EntityManager. This is a NO-OP for known entities, but
leads to much code written that is confusing to other developers.
The following code WILL update your database with the changes made to
the Person object, even if you did not call EntityManager#persist:
<?php
$user = $entityManager->find("Person", 1);
$user->setName("Guilherme");
$entityManager->flush();
just detach your found entity after you load it from repository
$entity = $entityManager->getRepository(City::class)->find(1);
$entityManager->detach($entity);

Symfony2 - New entity was found through relationship - but it has been persisted [duplicate]

since 2 weeks, we are having this problem while trying to flush new elements:
CRITICAL: Doctrine\ORM\ORMInvalidArgumentException:
A new entity was found through the relationship 'Comment#capture' that was not configured to cascade persist operations for entity
But the capture is already in the database, and we are getting it by a findOneBy, so if we cascade persist it, or persist it, we get a
Table constraint violation: duplicate entry.
The comments are created in a loop with differents captures, with a new, and all required field are set.
With all of the entities persisted and / or got by a findOne (and all valid), the flush still fails.
I'm on this issue since a while, so please help me
I had the same problem and it was the same EntityManager. I wanted to insert an object related ManyToOne. And I don't want a cascade persist.
Example :
$category = $em->find("Category", 10);
$product = new Product();
$product->setCategory($category)
$em->persist($product);
$em->flush();
This throws the same exception for me.
So the solution is :
$category = $em->find("Category", 10);
$product = new Product();
$product->setCategory($category)
$em->merge($product);
$em->flush();
In my case a too early call of
$this->entityManager->clear();
caused the problem. It also disappeared by only doing a clear on the recent object, like
$this->entityManager->clear($capture);
My answer is relevant for topic, but not very relevant for your particular case, so for those googling I post this, as the answers above did not help me.
In my case, I had the same error with batch-processing entities that had a relation and that relation was set to the very same entity.
WHAT I DID WRONG:
When I did $this->entityManager->clear(); while processing batch of entities I would get this error, because next batch of entities would point to the detached related entity.
WHAT WENT WRONG:
I did not know that $this->entityManager->clear(); works the same as $this->entityManager->detach($entity); only detaches ALL of the repositorie`s entities.
I thought that $this->entityManager->clear(); also detaches related entities.
WHAT I SHOULD HAVE DONE:
I should have iterated over entities and detach them one by one - that would not detach the related entity that the future entities pointed to.
I hope this helps someone.
First of all, you should take better care of your code, I see like 3 differents indentations in your entity and controller - this is hard to read, and do not fit the Symfony2 coding standards.
The code you show for your controller is not complete, we have no idea from where $this->activeCapture is coming. Inside you have a $people['capture'] which contains a Capture object I presume. This is very important.
If the Capture in $people['capture'] is persisted / fetched from another EntityManager than $this->entityManager (which, again, we do not know from where it come), Doctrine2 have no idea that the object is already persisted.
You should make sure to use the same instance of the Doctrine Entity Manager for all those operations (use spl_object_hash on the EM object to make sure they are the same instance).
You can also tell the EntityManager what to do with the Capture object.
// Refreshes the persistent state of an entity from the database
$this->entityManager->refresh($captureEntity);
// Or
// Merges the state of a detached entity into the
// persistence context of this EntityManager and returns the managed copy of the entity.
$captureEntity = $this->entityManager->merge($captureEntity);
If this does not help, you should provide more code.
The error:
'Comment#capture' that was not configured to cascade persist operations for entity
The problem:
/**
* #ORM\ManyToOne(targetEntity="Capture", inversedBy="comments")
* #ORM\JoinColumn(name="capture_id", referencedColumnName="id",nullable=true)
*/
protected $capture;
dont configured the cascade persist
try with this:
/**
* #ORM\ManyToOne(targetEntity="Capture", inversedBy="comments", cascade={"persist", "remove" })
* #ORM\JoinColumn(name="capture_id", referencedColumnName="id",nullable=true)
*/
protected $capture;
Refreshing the entity in question helped my case.
/* $item->getProduct() is already set */
/* Add these 3 lines anyway */
$id = $item->getProduct()->getId();
$reference = $this->getDoctrine()->getReference(Product::class, $id);
$item->setProduct($reference);
/* Original code as follows */
$quote->getItems()->add($item);
$this->getDoctrine()->persist($quote);
$this->getDoctrine()->flush();
Despite my $item already having a Product set elsewhere, I was still getting the error.
Turns out it was set via a different instance of EntityManager.
So this is a hack of sorts, by retrieving id of the existing product, and then retrieving a reference of it, and using setProduct to "refresh" the whatever connection. I later fixed it by ensuring I have and use only a single instance of EntityManager in my codebase.
I got this error too when tried to add new entity.
A new entity was found through the relationship 'Application\Entity\User#chats'
that was not configured to cascade persist operations for entity: ###.
To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity or
configure cascade persist this association in the mapping for example #ManyToOne(..,cascade={"persist"}).
My case was that I tried to save entity, that shouldn't be saved. Entity relations was filled and tried to be saved (User has Chat in Many2Many, but Chat was a temporary entity), but there were some collisions.
So If I use cascade={"persist"} I get unwanted behaviour - trash entity is saved. My solution was to remove non-saving entity out of any saving entities:
// User entity code
public function removeFromChats(Chat $c = null){
if ($c and $this->chats->contains($c)) {
$this->chats->removeElement($c);
}
}
Saving code
/* some code witch $chat entity */
$chat->addUser($user);
// saving
$user->removeFromChats($chat);
$this->getEntityManager()->persist($user);
$this->getEntityManager()->flush();
I want to tell about my case as that might be helpful to somebody.
Given two entities: AdSet and AdSetPlacemnt. AdSet has the following property:
/**
* #ORM\OneToOne(targetEntity="AdSetPlacement", mappedBy="adSet", cascade={"persist"})
*
* #JMS\Expose
*/
protected $placement;
Then error appears when I try to delete some AdSet objects in a cycle after 1st iteration
foreach($adSetIds as $adSetId) {
/** #var AdSet $adSet */
$adSet = $this->adSetRepository->findOneBy(["id" => $adSetId]);
$this->em->remove($adSet);
$this->em->flush();
}
Error
A new entity was found through the relationship 'AppBundle\Entity\AdSetPlacement#adSet' that was not configured to cascade persist operations for entity: AppBundle\Entity\AdSet#00000000117d7c930000000054c81ae1. To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity or configure cascade persist this association in the mapping for example #ManyToOne(..,cascade={"persist"}). If you cannot find out which entity causes the problem implement 'AppBundle\Entity\AdSet#__toString()' to get a clue.
Solution
The solution was to add "remove" to $placement cascade options to be:
cascade={"persist","remove"}. This guarantees that Placement also becomes detached. Entity manager will "forget" about Placement object thinking of it as "removed" once AdSet is removed.
Bad alternative
When trying to figure out what's going on I've seen a couple answers or recommendations to simply use entity manager's clear method to completely clear persistence context.
foreach($adSetIds as $adSetId) {
/** #var AdSet $adSet */
$adSet = $this->adSetRepository->findOneBy(["id" => $adSetId]);
$this->em->remove($adSet);
$this->em->flush();
$this->em->clear();
}
So that code also works, the issue gets solved but it's not always what you really wanna do. Indeed it's happens quite rarely that you actually need to clear entity manager.

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 - A new entity was found through the relationship

since 2 weeks, we are having this problem while trying to flush new elements:
CRITICAL: Doctrine\ORM\ORMInvalidArgumentException:
A new entity was found through the relationship 'Comment#capture' that was not configured to cascade persist operations for entity
But the capture is already in the database, and we are getting it by a findOneBy, so if we cascade persist it, or persist it, we get a
Table constraint violation: duplicate entry.
The comments are created in a loop with differents captures, with a new, and all required field are set.
With all of the entities persisted and / or got by a findOne (and all valid), the flush still fails.
I'm on this issue since a while, so please help me
I had the same problem and it was the same EntityManager. I wanted to insert an object related ManyToOne. And I don't want a cascade persist.
Example :
$category = $em->find("Category", 10);
$product = new Product();
$product->setCategory($category)
$em->persist($product);
$em->flush();
This throws the same exception for me.
So the solution is :
$category = $em->find("Category", 10);
$product = new Product();
$product->setCategory($category)
$em->merge($product);
$em->flush();
In my case a too early call of
$this->entityManager->clear();
caused the problem. It also disappeared by only doing a clear on the recent object, like
$this->entityManager->clear($capture);
My answer is relevant for topic, but not very relevant for your particular case, so for those googling I post this, as the answers above did not help me.
In my case, I had the same error with batch-processing entities that had a relation and that relation was set to the very same entity.
WHAT I DID WRONG:
When I did $this->entityManager->clear(); while processing batch of entities I would get this error, because next batch of entities would point to the detached related entity.
WHAT WENT WRONG:
I did not know that $this->entityManager->clear(); works the same as $this->entityManager->detach($entity); only detaches ALL of the repositorie`s entities.
I thought that $this->entityManager->clear(); also detaches related entities.
WHAT I SHOULD HAVE DONE:
I should have iterated over entities and detach them one by one - that would not detach the related entity that the future entities pointed to.
I hope this helps someone.
First of all, you should take better care of your code, I see like 3 differents indentations in your entity and controller - this is hard to read, and do not fit the Symfony2 coding standards.
The code you show for your controller is not complete, we have no idea from where $this->activeCapture is coming. Inside you have a $people['capture'] which contains a Capture object I presume. This is very important.
If the Capture in $people['capture'] is persisted / fetched from another EntityManager than $this->entityManager (which, again, we do not know from where it come), Doctrine2 have no idea that the object is already persisted.
You should make sure to use the same instance of the Doctrine Entity Manager for all those operations (use spl_object_hash on the EM object to make sure they are the same instance).
You can also tell the EntityManager what to do with the Capture object.
// Refreshes the persistent state of an entity from the database
$this->entityManager->refresh($captureEntity);
// Or
// Merges the state of a detached entity into the
// persistence context of this EntityManager and returns the managed copy of the entity.
$captureEntity = $this->entityManager->merge($captureEntity);
If this does not help, you should provide more code.
The error:
'Comment#capture' that was not configured to cascade persist operations for entity
The problem:
/**
* #ORM\ManyToOne(targetEntity="Capture", inversedBy="comments")
* #ORM\JoinColumn(name="capture_id", referencedColumnName="id",nullable=true)
*/
protected $capture;
dont configured the cascade persist
try with this:
/**
* #ORM\ManyToOne(targetEntity="Capture", inversedBy="comments", cascade={"persist", "remove" })
* #ORM\JoinColumn(name="capture_id", referencedColumnName="id",nullable=true)
*/
protected $capture;
Refreshing the entity in question helped my case.
/* $item->getProduct() is already set */
/* Add these 3 lines anyway */
$id = $item->getProduct()->getId();
$reference = $this->getDoctrine()->getReference(Product::class, $id);
$item->setProduct($reference);
/* Original code as follows */
$quote->getItems()->add($item);
$this->getDoctrine()->persist($quote);
$this->getDoctrine()->flush();
Despite my $item already having a Product set elsewhere, I was still getting the error.
Turns out it was set via a different instance of EntityManager.
So this is a hack of sorts, by retrieving id of the existing product, and then retrieving a reference of it, and using setProduct to "refresh" the whatever connection. I later fixed it by ensuring I have and use only a single instance of EntityManager in my codebase.
I got this error too when tried to add new entity.
A new entity was found through the relationship 'Application\Entity\User#chats'
that was not configured to cascade persist operations for entity: ###.
To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity or
configure cascade persist this association in the mapping for example #ManyToOne(..,cascade={"persist"}).
My case was that I tried to save entity, that shouldn't be saved. Entity relations was filled and tried to be saved (User has Chat in Many2Many, but Chat was a temporary entity), but there were some collisions.
So If I use cascade={"persist"} I get unwanted behaviour - trash entity is saved. My solution was to remove non-saving entity out of any saving entities:
// User entity code
public function removeFromChats(Chat $c = null){
if ($c and $this->chats->contains($c)) {
$this->chats->removeElement($c);
}
}
Saving code
/* some code witch $chat entity */
$chat->addUser($user);
// saving
$user->removeFromChats($chat);
$this->getEntityManager()->persist($user);
$this->getEntityManager()->flush();
I want to tell about my case as that might be helpful to somebody.
Given two entities: AdSet and AdSetPlacemnt. AdSet has the following property:
/**
* #ORM\OneToOne(targetEntity="AdSetPlacement", mappedBy="adSet", cascade={"persist"})
*
* #JMS\Expose
*/
protected $placement;
Then error appears when I try to delete some AdSet objects in a cycle after 1st iteration
foreach($adSetIds as $adSetId) {
/** #var AdSet $adSet */
$adSet = $this->adSetRepository->findOneBy(["id" => $adSetId]);
$this->em->remove($adSet);
$this->em->flush();
}
Error
A new entity was found through the relationship 'AppBundle\Entity\AdSetPlacement#adSet' that was not configured to cascade persist operations for entity: AppBundle\Entity\AdSet#00000000117d7c930000000054c81ae1. To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity or configure cascade persist this association in the mapping for example #ManyToOne(..,cascade={"persist"}). If you cannot find out which entity causes the problem implement 'AppBundle\Entity\AdSet#__toString()' to get a clue.
Solution
The solution was to add "remove" to $placement cascade options to be:
cascade={"persist","remove"}. This guarantees that Placement also becomes detached. Entity manager will "forget" about Placement object thinking of it as "removed" once AdSet is removed.
Bad alternative
When trying to figure out what's going on I've seen a couple answers or recommendations to simply use entity manager's clear method to completely clear persistence context.
foreach($adSetIds as $adSetId) {
/** #var AdSet $adSet */
$adSet = $this->adSetRepository->findOneBy(["id" => $adSetId]);
$this->em->remove($adSet);
$this->em->flush();
$this->em->clear();
}
So that code also works, the issue gets solved but it's not always what you really wanna do. Indeed it's happens quite rarely that you actually need to clear entity manager.

Multiple Entity managers

I have also posted this question before and I am posting it again as it is not resolved yet.
I am using Symfony2 for my application and I need to create two database connections i.e Read and Write for this I search and found easily that we can create different entity managers and i have created like that :
Making object in controller
$emWrite = $this->getDoctrine()->getEntityManager('write');
$em = $this->getDoctrine()->getEntityManager();
when I persist the entity It gives me the following error:
A new entity was found through the relationship '
AppBundle\Entity\Follower#user' that was not configured to cascade persist
operations for entity: adeel. Explicitly persist the new entity or configure
cascading persist operations on the relationship. If you cannot find out which
entity causes the problem implement AppBundle\Entity\User#__toString()
to get a clue. (500 Internal Server Error)
I have already tried many things like giving persist property in both sides of entities involved.
Your problem isn't related to multiple entity managers but to options that you haven't setted onto your class, fore relationship purpose.
I haven't much details about your entities, but I could suggest you to modify relationship in that way
<?php
class User
{
//...
/**
* Bidirectional - One-To-Many (INVERSE SIDE)
*
* #OneToMany(targetEntity="Comment", mappedBy="author",
cascade={"persist","remove"})
*/
private $commentsAuthored;
//...
}
Where cascade={"persist","remove"} is what you're looking for.

Resources