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

ORM cannot save relationships when toArray is being overwritten

I have recently discovered a strange ?bug? Lets say my model has category and subcategory like this:

<?php

namespace XXX\Inventory\Models;

use Phalcon\Mvc\Model;

class Inventory extends Model
{
    public $id;
    public $categoryId;
    public $subCategoryId;

    // other fields here

    public function initialize()
    {
        $this->setSource('inventory');

        $this->belongsTo('categoryId', InventoryCategory::class, 'id', [
            'alias' => 'category',
        ]);
        $this->belongsTo('subCategoryId', InventorySubCategory::class, 'id', [
            'alias' => 'subCategory',
        ]);

        $this::setup(['castOnHydrate' => true]);
    }

    public function toArray($columns = null)
    {
        $result = parent::toArray($columns);

        $result['id'] = (int)$this->id;

        if (!empty($this->category)) {
            $result['category'] = $this->category->toArray();
        }

        if (!empty($this->subCategory)) {
            $result['subCategory'] = $this->subCategory->toArray();
        }

        return $result;
    }

}

It becomes impossible to save relationships by setting

$entity = Inventory::findOneById(12);
$entity->categoryId = 1;
$entity->subCategoryId = 2;
$entity->save();

Every other field saves properly but not categoryId nor subCategoryId. There are 2 workarounds of this problem:

  1. Comment out toArray those lines:

        if (!empty($this->category)) {
            $result['category'] = $this->category->toArray();
        }
    
        if (!empty($this->subCategory)) {
            $result['subCategory'] = $this->subCategory->toArray();
        }
  2. Remove relationships:
        $this->belongsTo('categoryId', InventoryCategory::class, 'id', [
            'alias' => 'category',
        ]);
        $this->belongsTo('subCategoryId', InventorySubCategory::class, 'id', [
            'alias' => 'subCategory',
        ]);

Is it suppose to be working like this or am I missing something?

edited Mar '18

There's one line in the model source where toArray is used, this may be the culprit.

https://github.com/phalcon/cphalcon/blob/master/phalcon/mvc/model.zep#L4630

I'm not sure what the exact problem may be (might not be related to this at all, since it's inside the serializer), but it may help debugging ;]

edited Mar '18

Well I don't see it inside save() function.

Exact problem with this code:

$entity = Inventory::findOneById(12);
$entity->categoryId = 1;
$entity->subCategoryId = 2;
var_dump($entity->categoryId); // 1
var_dump($entity->subCategoryId); // 2
$entity->save();
var_dump($entity->categoryId); // old categoryId
var_dump($entity->subCategoryId); // old subCategoryId

Of course database values don't change either. Removing relationships OR commenting out toArray modifications resolves the problem but thats not a solution.



4.8k
Accepted
answer
edited Mar '18

The dumbest solution I couldn't even dreamt on worked flawlessly:

Before:

    if (!empty($this->category)) {
        $result['category'] = $this->category->toArray();
    }

This fixes it:

        if ($relCategory = $this->getRelated('category')) {
            $result['category'] = $relCategory->toArray();
        }

Anyone can explain why previous solution didn't work? What exactly is happening under the hood that checking empty doesn't work?



12.1k

Try with $this->getCategory()