Doctrine cascaded entity clearing - php

I have the following problem with clearing entities in doctrine:
I have two entities connected together, one of which is the main-entity so to say and one of which is the sub-entity that belongs to the main-entity. Then when I use the according repository to clear, it only clears the main-entity but leaves the sub-entity.Flushing the entities is actually working fine, because the entities are connected via cascade, so when I flush the main entity, the sub-entity gets flushed also. But this cascading does not seem to work with the clear.
Is there a way to also clear all the sub-entites together with the main entities without creating an extra (and actually not needed for other things) repository for the sub-entities?
Thank you in advance.
EDIT: Ok since I've seemingly been too unspecific, my goal is to clear the entities in doctrine, not delete them in the database. The problem is, that I have a lot of entities to process and doctrine doesn't clear up all entity references in the memory. So is there a way to cascade that, or do I need the repositories for that?
LAST EDIT: Problem has been fixed by doctrine. See accepted answer!

As of the last commit, the issue is fixed. Now cascade clearing works like a charm for me.
Thanks a lot to the developers of doctrine!
Additional info, see here:
https://github.com/doctrine/doctrine2/pull/995#issuecomment-40562699
https://github.com/doctrine/doctrine2/commit/1cd0b26a40dc22b0d11b1860eb058ab9cdc29f36

You need to add cascade={"remove"} to your mapping information in the main entity for field that represent sub-entity.

You can use cascade={"remove"} for each main-entity and sub-entity; it will be do the removal for children on memory which may lead to performance overhead.
If you want to rely on DB for removal which is faster and reliable you can configure each column on delete event onDelete="CASCADE"
(however you did not provide any code but as example see below)
/**
* #ORM\OneToMany(targetEntity="SubEntity", mappedBy="mainEntity")
*/
protected $subEntiy;
and in the other entity you have
/**
* #ORM\ManyToOne(targetEntity="MainEntity", inversedBy="subEntity")
* #ORM\JoinColumn(name="entity_id", referencedColumnName="id", onDelete="CASCADE")
*/
protected $mainEntity;

Related

Create migration from entity file

I have some tables, which were modified. Added some annotation to some relation:
cascade={"persist", "remove"}
In the SQL, the fields have only a reference to another table.
Now, I want to create a migration file, and run bin/console doctrine:migrations:diff, but it does not add the constraint modification for this.
Is it possible to generate a migration file only for this entity?
Cascade persist will not be handled by your database, because it's related to how doctrine handle entities with the UnitOfWork.
Cascade delete can be managed by doctrine or your database.
Using cascade={"remove"} does not affect your database schema, as opposed to using onDelete.
That's the reason you do not see any change because there is no SQL to be executed to update your entity schema.
Basically, cascade={"remove"} will only be used by doctrine and its UnitOfWork when an $entityManager is used toremove an entity.
That's why if you execute a raw query in SQL that remove one of your entry, the cascade option will not do anything which could indeed cause an error if you use raw sql instead of Doctrine DQL.
So if you don't use any raw sql in your code that would remove an entry, it's fine to not do anything.
Otherwise, you can use onDelete which will modify your database structure.
You only need to do it for remove like this:
cascade={"persist"}, onDelete="CASCADE"
Be sure to check the documentation if you use it to understand more about it.

"Real" orphan removal with Doctrine/MySQL

I have two entities linked together by a ManyToMany relationship in a Doctrine/MySQL project.
A Client entity:
class Client
{
[...]
/**
* #ORM\ManyToMany(targetEntity="ClientTag")
* #ORM\JoinTable(name="clients_tags")
*/
protected $tags;
}
And a ClientTag entity:
class ClientTag
{
[...]
/**
* #ORM\Column(type="string", length=45)
*/
protected $label;
/**
* #ORM\Column(type="string", length=7)
*/
protected $color;
}
So I have the ability to associate multiple clients to one tag, and vice-versa, great.
But I can't find a way to automatically remove a tag when there is no more clients referencing it.
I tried to use orphanRemoval on the ManyToMany annotation but it doesn't do what I thought.. Orphan removal should imply exactly what I described above but it removes the tag when the reference to its parent is removed, not considering other entities like I need to.
If a client removes a tag but this tag is still used by 2 other clients, I don't consider it "orphan" as it still has one or more entities referencing it.
Of course I could solve the case by doing a query and removing it myself if I don't find any parent, but I wonder if Doctrine or MySQL have a built in way to do this (that will be far more optimized) ?
Any idea?
Thanks for your help.
Officially orphanRemoval isn't supported for ManyToMany relations in doctrine.
http://docs.doctrine-project.org/en/latest/reference/annotations-reference.html#annref-manytomany
The orphan removal in this case is ambiguous.
You can either just understand the relations (the jointable entries) to the deleted entity as the orphans or the related entity.
From a database point of view it would be the jointable entries.
From an ORM point of view it's the related entities.
Thing is both ways are correct depending on the use case. For example in an Article <-> Category relation you would want to remove the article from all associated categories on deletion, but you wouldn't want to throw away the whole category just because it's empty at this moment.
I'm guessing that's the reason why Doctrine doesn't officially mention the orphanRemoval option for ManyToMany because it's unclear and to fully support both variants the current implementation isn't enough.
Hope that was somehow understandable.
In your case though you'll probably need to clean up unused tags yourself.

Doctrine - Remove parent association when deleting child record

I have a FileUpload entity that is child of other entities using the following association:
/**
* #ORM\ManyToOne(targetEntity="FileUpload", cascade={"persist", "remove"} )
* #ORM\JoinColumn(name="image_id", referencedColumnName="id")
*/
protected $image;
The FileUpload entity contains various information about an uploaded file as well as a boolean field to mark it for deletion (handled by a checkbox on the form). I am trying to find a good way of managing this deletion process without having to duplicate the code in every entity that has a FileUpload entity.
I tried creating a service tagged with doctrine.event_listener to remove the FileUpload in postUpdate(), however since there is still an association with the parent of the FileUpload this failed. Does anyone know a way of clearing any associations with the FileUpload when it is deleted? Or any other method of handling this process?
Sorry for the late answer. Now I understand your problem.
In the relation you described on the side has to be the owner of the relation. If let's say A is in relation with B and let's say that A is the owner of the relation, that implies that A has control over all the aspects of the relation so B can't be deleted without A say so.
Think about the foreign key relation of a database. The database won't let you delete the line as long as it is part of a relationship and is not the owner of the relation(this being your current problem).
If you get into a place where you need to delete a FileUpload without knowing the Comment that has a relation to it you may have an architectural problem in your application/database design. If you know the Comment that has a relation to the FileUpload at the point that you want to remove the file then orphanRemoval is what you need. The way you remove it is not by asking the Manager to remove it (cause it can't do it without the approval of the owner of the relation, as explained in the example above). Instead, you ask the owner of the relation to removing it something like this
//for OneToOne relation
$comment->setFileUpload(null);
//for OneToMany relation
$comment->getFileUpload()->removeElement($fileUpload);
After the above statement call flush and it should work. Also for OneToMany make sure that you initialize
$this->fileUpload = new ArrayCollection();
in the Comment entity constructor.
NOTE1: As mentioned before careful with orphan removal cause it doesn't work as you will expect in relation to refresh function of the manager. After an object was market in doctrine unit of work as an orphan, it will get removed even if you called refresh on it, or it's parent. Found a way around this (using the doctrine onFlush event) but is better to not need this and try to avoid the situation.
NOTE2: orphanRemoval has the effect of hard delete in the database. If as some point you need to do add this code to a doctrine subscriber or onFlush listener
public function onFlush(OnFlushEventArgs $args)
{
foreach ($args->getEntityManager()->getUnitOfWork()->getScheduledEntityDeletions() as $entity) {
if ($entity instanceof SoftRemovableInterface) {
$args->getEntityManager()->remove($entity);
$args->getEntityManager()->persist($entity);
$entity->remove();
}
$args->getEntityManager()->getUnitOfWork()->computeChangeSet($args->getEntityManager()->getClassMetadata(get_class($entity)), $entity);
}
}
Where the remove+persist calls are there to take the entity out of the orphan removal list in unit of work (part of the fix for refreshing entities in NOTE1, this is the only place and only way i found that you can stop the removal of an orphan after it was marked as such by doctrine), and $entity->remove(); is the method of the SoftRemovableInterface that handles the soft delete, something like
class Comment implements SoftRemovableInterface
{
/........../
function remove()
{
$this->deleted = true;
}
}
Hope this brings some light to your issue. Happy coding.
Alexandru Cosoi

Optimistic lock in Doctrine2 doesn't work for many to many relation

I have entity User which has many to many relation to roles. I've tried to implement Optimistic lock, everything works fine, just when I changed roles, it doesn't change the version (User entity version), is this proper behaviour?
class User {
/**
* User's roles.
*
* #ORM\ManyToMany(targetEntity="Role")
*/
private $roles;
...
Doctrine 2's locking mechanisms don't take associations into account. They only protect against changes to the entities themselves. IMHO this is expected, because it has no way to know which associations to include and which to ignore. This isn't something you'd want to happen blindly on all associations.
Theoretically Doctrine 2 could achieve this by adding an option to the association mappings, but at this moment that simply isn't supported.
So you have 2 options:
Try to implement such a feature, and submit a PR :)
Implement your own optimistic locking mechanism that does take this specific association into account.
I didn't try, but I think this is proper bahaviour (because flush don't modify User entity) and there is no reason to lock User entity - it's not changed.

Cascade saving with Symfony and Doctrine on demand

I am working with two entities related by a 'one to many' relation but I'm having some problems.
Let say the entity Article has a property with a collection of Post's, and I configure the entities to use cascade. Something like that:
#ORM\OneToMany(targetEntity="Post", mappedBy="article", cascade={"remove", "persist"}, orphanRemoval=true)
When I try to save a instance of Article with his nested instances of Post's works fine but I don't want to save all the Post's anytime I change a property of the Article.
Seems like once you have the cascade relation defined you can only save using the cascade. Is not possible to enable and disable the cascade on demand and save a single entity without having to save all his childrens entities too?
Someone had this problem before and solved that somehow?
Thanks in advance!
This is expected behavior. Try to use events to update/save related entities

Categories