Open th3hamm0r opened 6 years ago
I don't think this is an impact/high. The worst thing is a slight slow down / unnecessary work.
@th3hamm0r what if you swapped the code around. This should not write (since the last write has no changes)
old:
$form->saveInto($this->record);
$this->record->write();
$extraData = $this->getExtraSavedData($this->record, $list);
$list->add($this->record, $extraData);
new:
$form->saveInto($this->record);
$extraData = $this->getExtraSavedData($this->record, $list);
$list->add($this->record, $extraData);
$this->record->write(); // no-op if already saved due to change detection
Oh, in 4.1.x ManyManyList::add() works with unsaved records. I guess it doesn't work in 3.x / 4.0.
4.1: ManyManyList::add()
// Ensure record is saved
if (!$item->isInDB()) {
$item->write();
}
$itemID = $item->ID;
4.0 and below:
if (empty($itemID)) {
throw new InvalidArgumentException("ManyManyList::add() doesn't accept unsaved records");
}
@tractorcow Yes, your suggestion above works too (tested it with 4.0.0).
yeah but it will break 4.0 with manymanylist. In 4.1 it'll safely lazy-save as needed.
Affected Version
We've first found it in 3.6 (I think it affects all 3.x version) and in 4.x it is still present.
Description
If you have a model that contains a "has_many"-relation and you use a
GridField
to manage it, saving one of these related objects actually triggers twowrite()
calls on that object. We found this issue, because if the object isVersioned
a "save" operation in the CMS actually creates two versions!This happens since the
GridFieldDetailForm_ItemRequest
first callswrite()
on the edited record and afterwards theadd()
operation of theHasManyList
again callswrite()
on that object. It looks like for some reason the checks inwrite()
which should protect from multiple db writes do not work in this case.In SilverStripe 3.6 we've quick-fixed it by wrapping https://github.com/silverstripe/silverstripe-framework/blob/3.6/forms/gridfield/GridFieldDetailForm.php#L557 with:
In SilverStripe 4.x the code is here and could be fixed similarly.
But this is more like a quick-fix, because there may be still some other code that bypasses the checks in
DataObject
'swrite()
and triggers additional db writes.