We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

problem when trying to json_encode a hasMany relationship with NULL column

Hello,

I have a model which is basically

class Products extends \Phalcon\Mvc\Model
{
  /**
   *
   * @var integer
   */
  public $id;

  /**
   *
   * @var string
   */
  public $json;

/**
 * Initialize method for model.
 */
public function initialize()
{
    $this->useDynamicUpdate(true);
    $this->hasMany('id', 'ProductsParts', 'product_id', array('alias' => 'Parts'));
}

the ProductsParts is the same adding the product_id column.

both models implements the following

public function afterFetch()
{
    $this->json = json_decode($this->json, true);
}

I recently updated my volt code from

var productParts = [
    {% if productParts %}{% for pp in productParts %}
        {{ pp | json_encode }}{% if !loop.last %},{% endif %}

    {% endfor %}{% endif %}
];

to

var productsParts = {{ productParts | json_encode }};

as allowed in Phalcon 3.0, and that's why I had my problem which is, if my json column is set to NULL (the real one, not a string), when I'm trying to do

json_encode($product->Parts);

it returns bool(false) with a JSONERRORSYNTAX

As a workaround, I tried with a loop, here is the entire (ugly debug) code

protected function myAction()
{
    $product = Product::findFirst(1);
    echo '<pre>';
    var_dump(json_encode($product->Parts)); // WHAT SHOULD WORK
    $this->lastJson(json_last_error());
    foreach ($product->Parts as $part) {
        var_dump($part->json);
        var_dump(json_encode($part)); // WHAT'S WORKING
        $this->lastJson(json_last_error());
    }
    die;
}

protected function lastJson($error) {
    switch ($error) {
        case JSON_ERROR_NONE:
            echo ' - No errors';
        break;
        case JSON_ERROR_DEPTH:
            echo ' - Maximum stack depth exceeded';
        break;
        case JSON_ERROR_STATE_MISMATCH:
            echo ' - Underflow or the modes mismatch';
        break;
        case JSON_ERROR_CTRL_CHAR:
            echo ' - Unexpected control character found';
        break;
        case JSON_ERROR_SYNTAX:
            echo ' - Syntax error, malformed JSON';
        break;
        case JSON_ERROR_UTF8:
            echo ' - Malformed UTF-8 characters, possibly incorrectly encoded';
        break;
        default:
            echo ' - Unknown error';
        break;
    }
    echo '<br/>';
    echo '<br/>';
}

and the result :

bool(false)
 - Syntax error, malformed JSON

array(1) {
  ["test"]=>
  string(4) "test"
}
string(80) "{"id":"1","product_id":"1","json":{"test":"test"}}"
 - No errors

NULL
string(69) "{"id":"2","product_id":"3","json":null}"
 - No errors

In my data set, there's a NULL json and another one with the string "{"test":"test"}"

I think this is a framework problem because if my line $this->json = json_decode($this->json, true); was faulty, it shouldn't work when looping through the "Parts" either.

I just tried the following

$this->json = !is_null($this->json) ? json_decode($this->json, true) : NULL;

and it actually worked !

what difference can it be between the NULL returned by json_decode($this->json, true) and an actual NULL ?

If anybody sees, you're welcome ! Thank you



3.4k
edited Mar '17

When you do var productsParts = {{ productParts | json_encode }}; in your volt file, you're making null become (string) "null". You can't loop over a string like you tried.

I'd do something like this;

public function afterFetch()
{
    $json = json_decode($this->json, true);
    if( json_last_error() ) {
        $json = json_decode([]);
    }
    $this->json = $json;
}

Now when you try to use json property, you just need to check the length of it.

{% if productParts|length > 0 %}
    {% for product in productParts %}
       <p>Product Id: {{ product.id }} <br /></p>
    {% endfor %}
{% else %}
    <p>No product parts</p>
{% endif %}