Solved thread

This post is marked as solved. If you think the information contained on this thread must be part of the official documentation, please contribute submitting a pull request to its repository.

Caching many to many relation

I have a few many-to-many relationships that I would like to cache.

Is there a way to override the function that generates the query to check for a cache key first? (Then set/get as needed).

Thanks!

There is always a way to using cache :) but which function you want to override? did you mean getRobotsParts() in following example?

$robot = Robots::findFirst();
$robotsParts = $robot->getRobotsParts(); // all the related records in RobotsParts

Here's how I'm accessing the relation:

$this->hasManyToMany(
            'user_id',
            '\NS\UserRole',
            'user_id', 'role_id',
            '\NS\Role',
            'role_id',
            array('alias'=>'roles')
        );

$user = User::findFirst();

$roles = $user->roles; // This is what I want to cache 

Of course, this is just one example, I have several classes that use relations like this, so if I could write a generic caching function/class, that'd be great.



7.6k
Accepted
answer
edited Mar '15

There are two methods in Phalcon Mvc Model that handle related rows in model relations as below:

        /**
         * Returns related records based on defined relations
         *
         * @param string alias
         * @param array arguments
         * @return \Phalcon\Mvc\Model\ResultsetInterface
         */
        public function getRelated($alias, $arguments=null){ }
        /**
         * Returns related records defined relations depending on the method name
         *
         * @param string modelName
         * @param string method
         * @param array arguments
         * @return mixed
         */
        protected function _getRelatedRecords($modelName, $method, $arguments){ }

You should make your own Model for example App\Model\AbstractModel.php that overrided these methods and you should extend other models from these abstract model too. your methods could be something like this:

namesapce App\Model

class AbstractModel extends \Phalcon\Mvc\Model
{
        /*
         * Returns related records based on defined relations
         *
         * @param string alias
         * @param array arguments
         * @return \Phalcon\Mvc\Model\ResultsetInterface
         */
        public function getRelated($alias, $arguments=null){
            $rows = $this->cache->get("{$alis}CacheKey");
            if ($rows === null) {
              $rows = parent::getRelated($alias, $arguments);
              $this->cache->save("{$alis}CacheKey", $rows);
            }
            return $rows
        }

        // EXTEND ANY METHOD ....
}

Cool, thanks. I'll give that a shot and see what happens. I have a similar class that overrides the find and findFirst methods, but the getRelated function is what i was looking for.

edited Mar '15

Ok, one final question -- I've overridden the getRelated($alias, $arguments = NULL) function.

Here's what I've got:

// This works great.
$roles = $user->getRelated('roles'). 

// this does not (the data gets populated correctly, but doesn't run the new function)
$roles = $user->roles

However, when I run $roles = $user->roles, my user defined function does not run, so must be another function that runs when accessing a relationship directly via alias. It's not a terribly big deal, but I think $user->roles looks nicer and reads more easily.

edited Mar '15

I don't know exactly which function is responsible for relation handling but its better to check Phalcon Mvc Model class and find the correct method.

Check this link:

https://github.com/phalcon/phalcon-devtools/blob/master/ide/2.0.0/Phalcon/Mvc/Model.php

Why you dont use cache parameter? Its not so big effort, or is it?

$roles = $user->getRoles(array(
    'cache' => array(
            'key'=> $key, 
            'lifetime' => $lifetime
    )
));

No -- it's not a terribly big effort to do as you suggested. However, the software I'm writing may or may not have access to memcache, it'll be a config option. So overriding a method in the model itself means I only have to check the config in one place, rather than all the different places I would call $user->getRelated() or $user->getRoles().

Here's what I found:

$user->roles uses the magic method, so I just overrode that method in the model

public function __get($property){
    // do config checking here, set / get from cache as needed
    $data = parent::__get($property);
    return $data;
}
edited Jan '16

It's not working for me using this code:

// on user model
....
 public function getRoles() {
        return $this->getRelated('roles', [
            'cache' => [
                'lifetime' => 1600,
                'key'      => 'user.roles.' . $this->id,
            ]
        ]);
}

The cache never hits, and the database is queried X times.