Doctrine - A new entity was found through the relationship - symfony

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.

Related

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.

Doctrine custom repository methods and unmanaged entities

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
}
}

Symfony2 / Doctrine: Reading "deleted" data when using Gedmo's doctrine extensions

I'm building a Symfony2 project and am using gedmo/doctrine-extensions (GitHub) to implement soft delete. My question is whether there's a way to "disable" or "override" softdelete, or even detect if something has been soft deleted.
Here's the situation:
I have a "note" entity that references a "user" entity. A specific note references a user that has been soft deleted. Even though the user has been deleted, it returns true for TWIG's "is defined" logic and can even return the id of the deleted user. However, if I query for any other information (including the "deletedAt" parameter that marks whether or not it is been deleted) I get a 500 "Entity was not found" error.
Since the data is actually still there, and since the note itself hasn't been deleted, I'd still like to say who's written the note, even though the user has been deleted.
Is that possible? If not, how do I properly detect whether something has been soft deleted? Like I said, $note->getUser() still retrieves an object and returns true for any null / "is defined" comparisons.
You can do this by :
$filter = $em->getFilters()->enable('soft-deleteable');
$filter->disableForEntity('Entity\User');
$filter->enableForEntity('Entity\Note');
You need to set the relationship loading to eager, this will prevent lazy loading of objects with just an id and nothing else.
You can find more information on eager loading and it's annotation here:
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/working-with-objects.html#by-eager-loading
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/annotations-reference.html.
As for my code, this is how it looks like when defining a link to a User now:
/**
* #ORM\ManyToOne(targetEntity="User", inversedBy="answers", fetch="EAGER")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
private $user;
In this case, the User entity can have multiple answers. When loading a User from the answer perspective, this will work:
foreach($answers as $answer) {
$user = $answer->getUser();
if (!$user) {
continue;
}
}
You can temporarily disable soft-delete so that deleted items are returned in your results. See the documentation, specifically interesting for you is the section that reads:
This will disable the SoftDeleteable filter, so entities which were
"soft-deleted" will appear in results
$em->getFilters()->disable('soft-deleteable');
So, first run the code above on your Entity Manager $em and then use it to collect your $note.

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.

Delete child entity when modifying parent entity

I'm working in modify file action in my controller. Child entity(StrOrigin) has the following relationship with File entity:
/**
* #ORM\ManyToOne(targetEntity="File" )
* #ORM\JoinColumn(name="STOR_FILE", referencedColumnName="id", onDelete="CASCADE")
*/
Now in my modify action in the controller, I get the file to modify, set the form and do some tests then upload the file, persist file entity and override the StrOrigin (which is many strings from file) with the new modified file. I'm stuck in how to override the StrOrigin. I've tried deleting the old file when submitting and persisting the new one:
$this_file_STROR=$em->getRepository('File')->find(array('id'=>$idfile));
$em->remove($this_file_STROR);
$em->flush();
But that didn't seem to work.
Following up on the comments:
You don't want to remove your actual file. You are misinterpreting the onDelete="CASCADE"! It means that when you delete a file, all StrOrigin will also get deleted. It has nothing to do with what you want to achieve.
What you want is the following:
$this_file_STROR=$em->getRepository('File')->find($idfile);
foreach($this_file_STROR->getStrOrigins() AS $strOrigin){
$em->remove($strOrigin);
}
// now $this_file_STROR as no StrOrigins anymore
$em->flush();
Also note that you don't need to flush at this point. A flush is simply persisting your current objects to the database. As long as you work with the objects, there is no need to flush. Normally you can flush right before your script ends, e.g. before you call render in your controller. If you flush several times, your application may be slow due to interaction with the database.
I found another solution which may be much faster:
It's described here and is called orphan removal. The idea is to simply remove the association and tell doctrine that related entities which are not refered to anymore shall be removed. In your case, you would do the following:
/**
* #ORM\ManyToOne(targetEntity="File" )
* #ORM\JoinColumn(name="STOR_FILE", referencedColumnName="id", orphanRemoval=true)
*/
public function deleteStrOrigins(){
$this->strOrigins = new ArrayCollection(); // you can also try to use = null. I'm using ArrayCollections, so this is my way and I never tried the null approach.
}
Now calling in your code
$this_file_STROR=$em->getRepository('File')->find($idfile);
$this_file_STROR->deleteStrOrigins();
$em->flush();
should delete all related StrOrigins as long as they are not related anywhere else.

Resources