Deleting many_many objects


#1

Silverstripe Version: 4.1

I read here https://docs.silverstripe.org/en/4/developer_guides/model/relations/#adding-relations that there is a remove() method per item. It doesn’t actually delete the related record, just the relationship.

How do you delete both?

I saw a removeAll method in ManyManyList, that also doesn’t delete the actual record.

I did it with:


# Get existing links
$Links = $Event->EventLinks();

# Use the result so it's actually there
$ids = [];
foreach ($Links as $Link) $ids[] = $Link->ID;

if ( ! empty($ids))
{
	# Remove all the relations
	$Event->EventLinks()->removeAll();

	# Remove each EventLink record
	foreach ($ids as $ID)
	{
		EventLink::delete_by_id('EventLink', $ID);
	}
}

is there a better way?


#2

A little further down that page you linked. ‘Cascade Deletions’
https://docs.silverstripe.org/en/4/developer_guides/model/relations/#cascading-deletions


#3

I’ll try that. I did read that but assumed it meant when deleting the “parent” record, delete the many relations. That’s not what I’m looking for. I just want to clear out the parents relations and add some more. Parent doesn’t get deleted.


#4

Got you, sorry I misunderstood what you were asking. Yes, the cascade deletes work when you delete the “parent” record.

The reason it works the way it does is that in a many_many situation, the “child” records could be linked in a number of places, so removing the relation only is the safe way to work (else you could inadvertently be removing records which are still linked elsewhere).

To manually delete the associated records, you could probably simplify your code a little, by not needing the intermediate array:

foreach ($Event->EventLinks() as $Link) {
  $Link->delete();
}

//$Event->EventLinks()->removeAll();

#5

I did try that (amongst a grip of other things) and I think the removeAll() didn’t work when the children have already been deleted as it EventLinks() doesn’t have any results. I could be mistaken on that - will try.


#6

I wasn’t sure about that… but that’s what you want anyway, isn’t it? You can just get rid of that last line and you’re done.


#8

Have you checked that is the case? It makes sense that deleting the records would also remove the relations. If it doesn’t, I’d consider that a bug


#7

Nah, it’ll remove the child Link records but leave the orphaned records in the many_many join table. You don’t ever see the orphans and they don’t have any effect on the system but no point polluting the table…


#9

I just tried and yeah it doesn’t delete the join table row when using your code.

I’ve set these up right?

Event

private static $many_many[
    'EventLinks' => EventLink::class
];

EventLink

private static $belongs_many_many[
    'Events' => Event::class
];

If I do the removeAll() before the $Link->delete, the joining table rows get deleted correctly but leaves the Link record. $Link->delete() has no effect.

The docs do say the function is remove() not delete so I tried that too. Same behaviour happens as delete.


#10

OK, so I’ve dug a bit deeper. It is an issue: [ORM] [2009-06-05] When you delete a DataObject that is has a many_many join, entries in the join table aren’t deleted. · Issue #1903 · silverstripe/silverstripe-framework · GitHub

One of the solutions would be to add an onAfterDelete() method to the ‘child’ object which removed the entries from the relation table. It’s not especially pretty, but it would work.


#11

Jeepers an old one at that. Thanks for looking Tim.