mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Merge pull request #10295 from creative-commoners/pulls/4.9/manymanythrough-remove-all
FIX Correctly remove relations with ManyManyThroughList::removeall
This commit is contained in:
commit
36df480ee2
@ -389,7 +389,6 @@ class ManyManyList extends RelationList
|
|||||||
*/
|
*/
|
||||||
public function removeAll()
|
public function removeAll()
|
||||||
{
|
{
|
||||||
|
|
||||||
// Remove the join to the join table to avoid MySQL row locking issues.
|
// Remove the join to the join table to avoid MySQL row locking issues.
|
||||||
$query = $this->dataQuery();
|
$query = $this->dataQuery();
|
||||||
$foreignFilter = $query->getQueryParam('Foreign.Filter');
|
$foreignFilter = $query->getQueryParam('Foreign.Filter');
|
||||||
|
@ -130,27 +130,37 @@ class ManyManyThroughList extends RelationList
|
|||||||
// Find has_many row with a local key matching the given id
|
// Find has_many row with a local key matching the given id
|
||||||
$hasManyList = $this->manipulator->getParentRelationship($this->dataQuery());
|
$hasManyList = $this->manipulator->getParentRelationship($this->dataQuery());
|
||||||
$records = $hasManyList->filter($this->manipulator->getLocalKey(), $itemID);
|
$records = $hasManyList->filter($this->manipulator->getLocalKey(), $itemID);
|
||||||
$affectedIds = [];
|
|
||||||
|
|
||||||
// Rather than simple un-associating the record (as in has_many list)
|
// Rather than simple un-associating the record (as in has_many list)
|
||||||
// Delete the actual mapping row as many_many deletions behave.
|
// Delete the actual mapping row as many_many deletions behave.
|
||||||
/** @var DataObject $record */
|
/** @var DataObject $record */
|
||||||
foreach ($records as $record) {
|
foreach ($records as $record) {
|
||||||
$affectedIds[] = $record->ID;
|
|
||||||
$record->delete();
|
$record->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->removeCallbacks && $affectedIds) {
|
if ($this->removeCallbacks && $itemID) {
|
||||||
$this->removeCallbacks->call($this, $affectedIds);
|
$this->removeCallbacks->call($this, [$itemID]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function removeAll()
|
public function removeAll()
|
||||||
{
|
{
|
||||||
// Empty has_many table matching the current foreign key
|
// Get the IDs of records in the current list
|
||||||
$hasManyList = $this->manipulator->getParentRelationship($this->dataQuery());
|
$affectedIds = $this->limit(null)->column('ID');
|
||||||
$affectedIds = $hasManyList->column('ID');
|
if (empty($affectedIds)) {
|
||||||
$hasManyList->removeAll();
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the join records that apply for the current list
|
||||||
|
$records = $this->manipulator->getJoinClass()::get()->filter([
|
||||||
|
$this->manipulator->getForeignIDKey() => $this->getForeignID(),
|
||||||
|
$this->manipulator->getLocalKey() => $affectedIds,
|
||||||
|
]);
|
||||||
|
|
||||||
|
/** @var DataObject $record */
|
||||||
|
foreach ($records as $record) {
|
||||||
|
$record->delete();
|
||||||
|
}
|
||||||
|
|
||||||
if ($this->removeCallbacks && $affectedIds) {
|
if ($this->removeCallbacks && $affectedIds) {
|
||||||
$this->removeCallbacks->call($this, $affectedIds);
|
$this->removeCallbacks->call($this, $affectedIds);
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
namespace SilverStripe\ORM;
|
namespace SilverStripe\ORM;
|
||||||
|
|
||||||
use SilverStripe\Core\Config\Config;
|
|
||||||
use SilverStripe\Core\Injector\Injectable;
|
use SilverStripe\Core\Injector\Injectable;
|
||||||
use SilverStripe\Core\Injector\Injector;
|
use SilverStripe\Core\Injector\Injector;
|
||||||
use SilverStripe\Dev\Deprecation;
|
use SilverStripe\Dev\Deprecation;
|
||||||
|
@ -202,6 +202,64 @@ class ManyManyThroughListTest extends SapphireTest
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testRemoveAll()
|
||||||
|
{
|
||||||
|
$first = $this->objFromFixture(ManyManyThroughListTest\TestObject::class, 'parent1');
|
||||||
|
$first->Items()->add($this->objFromFixture(ManyManyThroughListTest\Item::class, 'child0'));
|
||||||
|
$second = $this->objFromFixture(ManyManyThroughListTest\TestObject::class, 'parent2');
|
||||||
|
|
||||||
|
$firstItems = $first->Items();
|
||||||
|
$secondItems = $second->Items();
|
||||||
|
$initialJoins = ManyManyThroughListTest\JoinObject::get()->count();
|
||||||
|
$initialItems = ManyManyThroughListTest\Item::get()->count();
|
||||||
|
$initialRelations = $firstItems->count();
|
||||||
|
$initialSecondListRelations = $secondItems->count();
|
||||||
|
|
||||||
|
$firstItems->removeAll();
|
||||||
|
|
||||||
|
// Validate all items were removed from the first list, but none were removed from the second list
|
||||||
|
$this->assertEquals(0, count($firstItems));
|
||||||
|
$this->assertEquals($initialSecondListRelations, count($secondItems));
|
||||||
|
|
||||||
|
// Validate that the JoinObjects were actually removed from the database
|
||||||
|
$this->assertEquals($initialJoins - $initialRelations, ManyManyThroughListTest\JoinObject::get()->count());
|
||||||
|
|
||||||
|
// Confirm Item objects were not removed from the database
|
||||||
|
$this->assertEquals($initialItems, ManyManyThroughListTest\Item::get()->count());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRemoveAllIgnoresLimit()
|
||||||
|
{
|
||||||
|
$parent = $this->objFromFixture(ManyManyThroughListTest\TestObject::class, 'parent1');
|
||||||
|
$parent->Items()->add($this->objFromFixture(ManyManyThroughListTest\Item::class, 'child0'));
|
||||||
|
$initialJoins = ManyManyThroughListTest\JoinObject::get()->count();
|
||||||
|
// Validate there are enough items in the relation for this test
|
||||||
|
$this->assertTrue($initialJoins > 1);
|
||||||
|
|
||||||
|
$items = $parent->Items()->Limit(1);
|
||||||
|
$items->removeAll();
|
||||||
|
|
||||||
|
// Validate all items were removed from the list - not only one
|
||||||
|
$this->assertEquals(0, count($items));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFilteredRemoveAll()
|
||||||
|
{
|
||||||
|
$parent = $this->objFromFixture(ManyManyThroughListTest\TestObject::class, 'parent1');
|
||||||
|
$parent->Items()->add($this->objFromFixture(ManyManyThroughListTest\Item::class, 'child0'));
|
||||||
|
$items = $parent->Items();
|
||||||
|
$initialJoins = ManyManyThroughListTest\JoinObject::get()->count();
|
||||||
|
$initialRelations = $items->count();
|
||||||
|
|
||||||
|
$items->filter('Title:not', 'not filtered')->removeAll();
|
||||||
|
|
||||||
|
// Validate only the filtered items were removed
|
||||||
|
$this->assertEquals(1, $items->count());
|
||||||
|
|
||||||
|
// Validate the list only contains the correct remaining item
|
||||||
|
$this->assertEquals(['not filtered'], $items->column('Title'));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test validation
|
* Test validation
|
||||||
*
|
*
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
SilverStripe\ORM\Tests\ManyManyThroughListTest\TestObject:
|
SilverStripe\ORM\Tests\ManyManyThroughListTest\TestObject:
|
||||||
parent1:
|
parent1:
|
||||||
Title: 'my object'
|
Title: 'my object'
|
||||||
|
parent2:
|
||||||
|
Title: 'my object2'
|
||||||
SilverStripe\ORM\Tests\ManyManyThroughListTest\Item:
|
SilverStripe\ORM\Tests\ManyManyThroughListTest\Item:
|
||||||
|
# Having this one first means the IDs of records aren't the same as the IDs of the join objects.
|
||||||
|
child0:
|
||||||
|
Title: 'not filtered'
|
||||||
child1:
|
child1:
|
||||||
Title: 'item 1'
|
Title: 'item 1'
|
||||||
child2:
|
child2:
|
||||||
@ -17,6 +22,14 @@ SilverStripe\ORM\Tests\ManyManyThroughListTest\JoinObject:
|
|||||||
Sort: 2
|
Sort: 2
|
||||||
Parent: =>SilverStripe\ORM\Tests\ManyManyThroughListTest\TestObject.parent1
|
Parent: =>SilverStripe\ORM\Tests\ManyManyThroughListTest\TestObject.parent1
|
||||||
Child: =>SilverStripe\ORM\Tests\ManyManyThroughListTest\Item.child2
|
Child: =>SilverStripe\ORM\Tests\ManyManyThroughListTest\Item.child2
|
||||||
|
join3:
|
||||||
|
Title: 'join 3'
|
||||||
|
Parent: =>SilverStripe\ORM\Tests\ManyManyThroughListTest\TestObject.parent2
|
||||||
|
Child: =>SilverStripe\ORM\Tests\ManyManyThroughListTest\Item.child1
|
||||||
|
join4:
|
||||||
|
Title: 'join 4'
|
||||||
|
Parent: =>SilverStripe\ORM\Tests\ManyManyThroughListTest\TestObject.parent2
|
||||||
|
Child: =>SilverStripe\ORM\Tests\ManyManyThroughListTest\Item.child2
|
||||||
SilverStripe\ORM\Tests\ManyManyThroughListTest\PolyObjectA:
|
SilverStripe\ORM\Tests\ManyManyThroughListTest\PolyObjectA:
|
||||||
obja1:
|
obja1:
|
||||||
Title: 'object A1'
|
Title: 'object A1'
|
||||||
@ -75,4 +88,4 @@ SilverStripe\ORM\Tests\ManyManyThroughListTest\FallbackLocale:
|
|||||||
mexico_argentina:
|
mexico_argentina:
|
||||||
Sort: 1
|
Sort: 1
|
||||||
Parent: =>SilverStripe\ORM\Tests\ManyManyThroughListTest\Locale.mexico
|
Parent: =>SilverStripe\ORM\Tests\ManyManyThroughListTest\Locale.mexico
|
||||||
Locale: =>SilverStripe\ORM\Tests\ManyManyThroughListTest\Locale.argentina
|
Locale: =>SilverStripe\ORM\Tests\ManyManyThroughListTest\Locale.argentina
|
||||||
|
Loading…
Reference in New Issue
Block a user