BaronGrivet
BaronGrivet

Reputation: 4424

How to load JSON into field of Silverstripe Dataobject

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

Answers (1)

scrowler
scrowler

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

Related Questions