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

Model __set() method for array params

Hi all,

I want to set an array as model's property as below:

class MyModel extends BaseModel
{
    protected foo_bar;

    public function getFooBarr()
    {
        return is_null($this->foo_bar) ? [] : unserialize($this->foo_bar); 
    }

    public function setFooBarr(array $foo_bar)
    {
        $this->foo_bar = serialize($foo_bar); 
    }
}

$model = new MyModel();

$model->foo_bar = ['foo', 'bar'];
var_dump($model->foo_bar); // Result: array (size=0) empty

as this section of source, there is no way to assign array to some model's property without fall in models's relation an lazy loading/setting!

I think this is a bug! the lazy loadings/settings and direct setting/loadings should be separate!

Any idea or workaround to this problem?

Thanks.

The solution is to check getRelationByAlias() before fall in array check as __get() method does: see here

I think the code should be looks like this:

        /**
         * Check if the value is an array
         */
        if typeof value == "array" {

            let lowerProperty = strtolower(property),
                modelName = get_class(this),
                manager = this->getModelsManager();

            /* check relation here */
            let relation = <RelationInterface> manager->getRelationByAlias(modelName, lowerProperty);
            if typeof relation == "object" {

                let related = [];
                for key, item in value {
                    if typeof item == "object" {
                        if item instanceof ModelInterface {
                            let related[] = item;
                        }
                    } else {
                        let lowerKey = strtolower(key),
                            this->{lowerKey} = item,
                            relation = <RelationInterface> manager->getRelationByAlias(modelName, lowerProperty);
                            if typeof relation == "object" {
                                let referencedModel = manager->load(relation->getReferencedModel());
                                referencedModel->writeAttribute(lowerKey, item);
                            }
                    }
                }

                if count(related) > 0 {
                    let this->_related[lowerProperty] = related,
                        this->_dirtyState = self::DIRTY_STATE_TRANSIENT;
                }

                return value;
            }
        }

thanks.



14.8k
Accepted
answer

So this is my workaround in BaseModel class:

class BaseModel extends \Phalcon\Mvc\Model
{
    /**
     * Magic method to assign values to the the model
     *
     * @param string $property
     * @param mixed $value
     * @return mixed|void
     */
    public function __set($property, $value)
    {
        if(!is_object($value)) {
            if(!$this->getModelsManager()->getRelationByAlias(get_class($this), strtolower($property)))
            {
                /**
                 * Check if the property has any setter
                 */
                $method = "set" . PhText::camelize($property);

                if (method_exists($this, $method)) {
                    $this->{$method}($value);
                    return $value;
                }
            }
        }

        return parent::__set($property, $value);
    }
}

In this approach, underscore setters supported as it did in __get() too.

Thanks.

With phalcon v2.0.13 (thanks to \Phalcon\Mvc\Model::_possibleSetter() method) the above workaround could be refactored like this:

class BaseModel extends \Phalcon\Mvc\Model
{
    /**
     * Magic method to assign values to the the model
     *
     * @param string $property
     * @param mixed $value
     * @return mixed|void
     */
    public function __set($property, $value)
    {
        if(!is_object($value)) {
            if(!$this->getModelsManager()->getRelationByAlias(get_class($this), strtolower($property)))
            {
                /**
                 * Check if the property has any setter
                 */
               if ($this->_possibleSetter($property, $value)) {
                    return $value;
                }
            }
        }

        return parent::__set($property, $value);
    }
}