Reputation: 4424
A recent Silverstripe version upgrade to 4.3.1 appears to have broken some functionality where a dataobject is loaded as JSON into a Textfield.
The object looks like this:
class Foo extends DataObject
private static $db = [
'Name' => 'Varchar',
'Description' => 'Text',
'Models' => 'Text',
];
Then there's a function to load the object with JSON generated from a form request:
$data = json_decode($request->getBody(), true);
$foo = new Foo();
$foo->update($data);
Here's an example of the JSON $data
:
"Name":"Test",
"Description":"Wangle fangle blurble wurgle.",
"Models":{
"fish":{"trout":10,"salmon":15,"sturgeon":20},
"vegetable":{"carrot":1,"cabbage":2,"leek":3},
"sauce":{"chipotle":6,"tomato":4,"soy":2}
}
Until very recently the "Models" structure would get saved into the "Models" field as text:
"fish":{"trout":10,"salmon":15,"sturgeon":20},
"vegetable":{"carrot":1,"cabbage":2,"leek":3},
"sauce":{"chipotle":6,"tomato":4,"soy":2}
But now we get the following error:
DataObject::setField: Models only accepts scalars at /var/www/example/vendor/silverstripe/framework/src/ORM/DataObject.php:2648
Line 2640 in DataObject.php says:
If this is a proper database field, we shouldn't be getting non-DBField objects
Has there been a recent security fix that is now blocking the attempt to load a JSON object into a field?
Can anyone help with saving the Models JSON into the text field?
Upvotes: 1
Views: 893
Reputation: 24406
@wmk is bang on here, this is expected behaviour to prevent security vulnerabilities (see SS-2018-021). The change in 4.3.1 is to prevent you from inadvertently allowing users to insert non-scalar values into your data models when you've specified your DB field to be a scalar type.
In your case you're trying to write an array into a text field, which is correctly being blocked by silverstripe/framework.
The easiest workaround for you here is to re-encode the parts of your data array that you know you want to store as a JSON text blob, e.g.:
$data = json_decode($request->getBody(), true);
$data['Models'] = json_encode($data['Models']); // re-encode as JSON before saving
$foo = new Foo();
$foo->update($data);
This will ensure that you're still writing JSON as text into your Text field.
Also as @wmk mentioned, another viable option is to write your own DBField which accepts non-scalar values and takes the responsibility of safely writing them into the database. This would be useful if you're writing a DBField that deals with GIS or spatial data. In your particular example I think you can get away with ensuring it's still encoded as a string as my example.
Upvotes: 3