[2.X] xPDO::getMany/_getRelatedObjectsByFK hard-caches the first request #60

ReSpawN opened this Issue Feb 6, 2015 · 0 comments


None yet

1 participant

ReSpawN commented Feb 6, 2015

I don't know if this is by design, but we've stumbled upon a weird issue, one I did not expect from xPDO.

On line 1149, function xPDOObject::&getMany(), there is an instant protected call to xPDOObject::&_getRelatedObjectsByFK(). The default usage of getMany, as documented by various docs online, is a simple call on a Car with Wheels or a Box with Crayons.

$wheels = $car->getMany('Wheels');

Alright, seems simple enough. But hey, I'd only like the front wheels please.

$wheels = $car->getMany('Wheels', array('position' => 'front'));

Nice. Works fine as well. Now, I'd like to split them bad boys up and show both the front and the back wheels in one call, one script.

$wheels = array(
    'front' => $car->getMany('Wheels', array('position' => 'front')),
    'rear' => $car->getMany('Wheels', array('position' => 'rear'))

Now, the shit just hit the fan. I've got two identical arrays in there, both showing only the front wheels. What's goin' on here?! Ah, I see.

xPDOQuery::&_getRelatedObjectsByFK() at line #2223 through #2260.

 * Gets related objects by a foreign key and specified criteria.
 * @access protected
 * @param string $alias The alias representing the relationship.
 * @param mixed An optional xPDO criteria expression.
 * @param boolean|integer Indicates if the saved object(s) should
 * be cached and optionally, by specifying an integer value, for how many
 * seconds before expiring.  Overrides the cacheFlag for the object.
 * @return array A collection of objects matching the criteria.
protected function & _getRelatedObjectsByFK($alias, $criteria= null, $cacheFlag= true) {
    $collection= array ();
    if (isset($this->_relatedObjects[$alias]) && (is_object($this->_relatedObjects[$alias]) || (is_array($this->_relatedObjects[$alias]) && !empty ($this->_relatedObjects[$alias])))) {
        $collection= & $this->_relatedObjects[$alias];
    } else {
        /** $criteria logic **/

        if ($collection= $this->xpdo->getCollection($fkMeta['class'], $criteria, $cacheFlag)) {
            $this->_relatedObjects[$alias]= array_diff_key($this->_relatedObjects[$alias], $collection) + $collection;

    return $collection;

As the first getMany iterated with absolutely stunning success, the second call simply jumps into the internal _relatedObjects stack, finds "Wheels" (e.g. $car->_relatedObjects['Wheels']) and returns that.
So why is there a $criteria argument? Why is every call after the first ignored? Why is there an Composite ALIAS name cache in place instead of a xPDOQuery object cache of sorts?

I am at a loss of understanding this. Basically every usage after the first is redundant now. I will not accept this being the final code as this doesn't make any sense in terms of related object fetching which I know works a lot different in ORMs like Doctrine.

Copied from modxcms/revolution#12331

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment