diff --git a/model/ManyManyList.php b/model/ManyManyList.php index 677b49a2d..52aa3d275 100644 --- a/model/ManyManyList.php +++ b/model/ManyManyList.php @@ -168,11 +168,27 @@ class ManyManyList extends RelationList { * Remove all items from this many-many join. To remove a subset of items, filter it first. */ public function removeAll() { - $query = $this->dataQuery()->query(); - $query->setDelete(true); - $query->setSelect(array('*')); - $query->setFrom("\"$this->joinTable\""); - $query->execute(); + $base = ClassInfo::baseDataClass($this->dataClass()); + + // Remove the join to the join table to avoid MySQL row locking issues. + $query = $this->dataQuery(); + $query->removeFilterOn($query->getQueryParam('Foreign.Filter')); + + $query = $query->query(); + $query->setSelect("\"$base\".\"ID\""); + + $from = $query->getFrom(); + unset($from[$this->joinTable]); + $query->setFrom($from); + + // Use a sub-query as SQLite does not support setting delete targets in + // joined queries. + $delete = new SQLQuery(); + $delete->setDelete(true); + $delete->setFrom("\"$this->joinTable\""); + $delete->addWhere($this->foreignIDFilter()); + $delete->addWhere("\"$this->joinTable\".\"$this->localKey\" IN ({$query->sql()})"); + $delete->execute(); } /** diff --git a/tests/model/ManyManyListTest.php b/tests/model/ManyManyListTest.php index 1979e0577..b8be17d42 100644 --- a/tests/model/ManyManyListTest.php +++ b/tests/model/ManyManyListTest.php @@ -161,4 +161,53 @@ class ManyManyListTest extends SapphireTest { $this->assertEquals($teamTwoID, $teamsWithoutTheCaptain->first()->ID, 'The ManyManyList contains the wrong team'); } + + public function testRemoveAll() { + $first = new DataObjectTest_Team(); + $first->write(); + + $second = new DataObjectTest_Team(); + $second->write(); + + $firstPlayers = $first->Players(); + $secondPlayers = $second->Players(); + + $a = new DataObjectTest_Player(); + $a->ShirtNumber = 'a'; + $a->write(); + + $b = new DataObjectTest_Player(); + $b->ShirtNumber = 'b'; + $b->write(); + + $firstPlayers->add($a); + $firstPlayers->add($b); + + $secondPlayers->add($a); + $secondPlayers->add($b); + + $this->assertEquals(array('a', 'b'), $firstPlayers->sort('ShirtNumber')->column('ShirtNumber')); + $this->assertEquals(array('a', 'b'), $secondPlayers->sort('ShirtNumber')->column('ShirtNumber')); + + $firstPlayers->removeAll(); + + $this->assertEquals(0, count($firstPlayers)); + $this->assertEquals(2, count($secondPlayers)); + + $firstPlayers->removeAll(); + + $firstPlayers->add($a); + $firstPlayers->add($b); + + $this->assertEquals(array('a', 'b'), $firstPlayers->sort('ShirtNumber')->column('ShirtNumber')); + + $firstPlayers->filter('ShirtNumber', 'b')->removeAll(); + + $this->assertEquals(array('a'), $firstPlayers->column('ShirtNumber')); + $this->assertEquals(array('a', 'b'), $secondPlayers->sort('ShirtNumber')->column('ShirtNumber')); + + $this->assertNotNull(DataObjectTest_Player::get()->byID($a->ID)); + $this->assertNotNull(DataObjectTest_Player::get()->byID($b->ID)); + } + }