johnnyo
johnnyo

Reputation: 23

API Platform: Can't get using a json data type with MySQL to work

I'm new to the API Platform and trying to get a json data attribute to persist to MySQL as a json object. I've looked over the API Platform documentation and googled for an answer, but didn't find anything that answers this.

My setup:

MySQL column is defined as:

`document` json DEFAULT NULL 

I've defined a "MyEntity" PHP object and the json attribute is described as follows:

/**
 * @var json 
 *
 * @ORM\Column(type="json")
 */
private $document;

/**
 * Get document.
 *
 * @return json
 */
public function getDocument()
{
    return $this->document;
}

/**
 * Set document.
 *
 * @param json $document
 *
 * @return MyEntity
 */
public function setDocument($document)
{
    $this->document = $document;

    return $this;
}

Now when up that URL that shows the default interface for MyEntity and execute a POST to create it using this json-ld (simplifying to leave out other columns):

{
    "document": "{"test": "value"}"
}

I get a 400 Bad Request Error

{
    "@context": "/contexts/Error",
    "@type": "hydra:Error",
    "hydra:title": "An error occurred",
    "hydra:description": "Syntax error",
    "trace": [
    {
        "namespace": "",
        "short_class": "",
        "class": "",
        "type": "",
        "function": "",
        "file": "/Library/WebServer/Documents/test/vendor/symfony/symfony/src/Symfony/Component/Serializer/Encoder/JsonDecode.php",
        "line": 78,
        "args": []
    },
...

I escaped the double quotes (which seems strange to me that it you would need to do this since it is a json itself that is describing another json object) like this:

{
    "document": "{\"test\": \"value\"}"
}

and execute a POST again:

I get a 400 Bad Request Error with a different internal error:

{
    "@context": "/contexts/Error",
    "@type": "hydra:Error",
    "hydra:title": "An error occurred",
    "hydra:description": "Could not denormalize object of type AppBundle\\Entity\\json, no supporting normalizer found.",
    "trace": [
    {
        "namespace": "",
        "short_class": "",
        "class": "",
        "type": "",
        "function": "",
        "file": "/Library/WebServer/Documents/test/vendor/symfony/symfony/src/Symfony/Component/Serializer/Serializer.php",
       "line": 295,
       "args": []
    },  
...

So it look like API Platform doesn't recognize the JSON data type and is expecting a custom entity for it...doesn't seem right to me.

I've also looked at types for doctrine, which in my understanding, API Platform uses and found this info:

Some vendors have a native JSON type and Doctrine will use it if possible and otherwise silently fall back to the vendor’s text type to ensure the most efficient storage requirements. If the vendor does not have a native JSON type, this type requires an SQL column comment hint so that it can be reverse engineered from the database. Doctrine cannot map back this type properly on vendors not supporting column comments and will fall back to text type instead.

But since MySQL does support a JSON data type, I thought this would just work, but apparently not. Any help on how API Platform works with a JSON data type using MYSQL would be much appreciated.

Upvotes: 2

Views: 3648

Answers (1)

Iwan Wijaya
Iwan Wijaya

Reputation: 2157

You are getting this error because of your PHPdoc type hints. Api-platform uses PHPdoc metadata during normalization.

There is no such things as "json" data type in PHP, so it will look for a class in the same namespace.

From Doctrine documentation:

Values retrieved from the database are always converted to PHP’s array or null types using PHP’s json_decode() function.

So you should change your PHPDoc typehint to array

/**
 * @var array
 *
 * @ORM\Column(type="json")
 */
private $document;

/**
 * Get document.
 *
 * @return array
 */
public function getDocument()
{
    return $this->document;
}

/**
 * Set document.
 *
 * @param array $document
 *
 * @return MyEntity
 */
public function setDocument($document)
{
    $this->document = $document;

    return $this;
}

Upvotes: 5

Related Questions