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

Relationships getRelated, _getRelatedRecords, __get, __call

Since I'm using Phalcon I'm having lot of problems with cached relationships and the lazy loading. Actually I'm triying to fetch a model and get the relationships, but I want the relationships from the cache. I already read the Caching in the ORM documentation, but I don't like the approach, since it implies caching on the ' find' methods...

The use case:

* call a public property alias for the relationship
    * If the alias have results, get them
    * if the alias does not have results, try to get them from cache
        * if no cache, fetch them

I extended the Phalcon Model to use the cache on the relationships, in this way:

    /*
    * Returns related records based on defined relations USING CACHE
    *
    * @param string alias
    * @param array arguments
    * @param int ttl in seconds
    * @return \Phalcon\Mvc\Model\ResultsetInterface
    */
    public function getRelated($alias, $arguments = null, $ttl = null)
    {
        $cache_ttl = ($ttl !== null) ? $ttl : (isset(static::$cache_ttl)) ? static::$cache_ttl : self::$cache_ttl_default;
        $cache_key = $this->getSource() . '.' . $this->getId() . '.' . $alias . '.' . md5(serialize($arguments));
        $rows = $this->getDI()->getCache()->remember($cache_key, $cache_ttl, function () use ($alias, $arguments) {
            return parent::getRelated($alias, $arguments);
        });

        return $rows;
    }

    // remember is just a get if exists and if not, execute callback and save it

The problem with this approach is that I must define getters and call getRelated on them.

The second problem is that every time I call those getters the results are fetched from the cache or the lazy loading, never from the already fetched ones. This means that I must control when I need to call $model->getAliasRelationship() so cache-lazyloading is used, or call $model->aliasRelationship so the already fetched ones are used.

Due to the lazyloading I cannot check if the property is already filled without firing the "automagic" fetch. If I do it I lost the posibility to use the cache.

https://github.com/phalcon/cphalcon/pull/12772 points there. With a method to check if the relationship is already fetched, without fetching it if not, is just as easy as adding the check on the overrided getRelated.

I was looking for a way to redefine the __get so I can avoid the automatic lazyloading and make my own checks on cache etc... and found that those three methods getRelated, _getRelatedRecords, __get make use of the modelManager and I can't find a way to make it more transparent.

As I see it, it would be great that those methods getRelated, _getRelatedRecords, __get make use of a common method to fetch relationships (a proxy for the modelManager) so I can override it and not necesarily be suscribed to this handicap.

Any clue on how to accomplish it?

My unique idea is to make a pull request on cphalcon changing any reference on model.zep so this methods <RelationInterface> manager->getRelationByAlias(className, alias); manager->getRelationRecords(relation, null, this, arguments); does have a proxy on model (where they call the manager methods by default, but I can override them on a model extension.

edited Apr '17

Another idea is to refactor getRelated in model.zep so it uses _getRelatedRecords instead of duplicate code. At the same time, use getRelated on the __get. Seems the way to go, to make the code more robust. But I don't know the implications in this.