Matthew Beaumont Thu Jul 07 14:46:24 -0400 2011

Subject: Problem with eager loading and many-to-many

Hi All,

I'm probably doing something wrong here, but I'm having trouble using many-to-many relationships with eager-loading. Here's my table structure:

 1 tbl  `concern` (
 2   `id` int(11) NOT NULL auto_increment,
 3    ...
 4   PRIMARY KEY  (`id`)
 5 )  ;
 6 
 7 tbl  `product` (
 8   `id` int(11) NOT NULL auto_increment,
 9    ...
10   PRIMARY KEY  (`id`)
11 )  ;
12 
13 tbl  `concern_product` (
14   `concern_id` int(11) NOT NULL,
15   `product_id` int(11) NOT NULL,
16 
17 )  ;

then I have the following (partial) model code:

 1 class Concern extends ActiveRecord\Model {
 2 
 3     static $table = 'concern';
 4     static $has_many = array(
 5         array('concern_product'),
 6         array('products', 
 7                    'through' => 'concern_product', 
 8        )
 9  );
10 
11 class ConcernProduct extends ActiveRecord\Model {
12 
13     static $table = 'concern_product';
14 
15     static $belongs_to = array(
16         array('product'),
17         array('concern'),
18     );
19 }

I'm trying to access $concern->products, but it return an empty array. It generates the correct SQL
1 'SELECT  `product`.* FROM `product` INNER JOIN `concern_product` ON(`product`.id = `concern_product`.product_id) WHERE status != ? AND `concern_id` IN(?)'

and seems to pull the correct records, but then they are filtered out by this part of Relationships.php around line 191:

 1 foreach ($related_models as $related)
 2             {
 3                 if ($related->$query_key == $key_to_match)
 4                 {
 5                     $hash = spl_object_hash($related);
 6 
 7                     if (in_array($hash, $used_models))
 8                         $model->set_relationship_from_eager_load(clone($related), $this->attribute_name);
 9                     else
10                         $model->set_relationship_from_eager_load($related, $this->attribute_name);
11 
12                     $used_models[] = $hash;
13                     $matches++;
14                 }
15             }

Maybe I'm crazy, but doesn't this code look for a direct relationship between the model and related model? Obviously there isn't one in my case since I have a many-to-many relationship through a third table, but it seems to be looking for a key in my product model that relates to my concern model, which doesn't exist.

I'm using the nightly build. Any ideas on what I'm doing wrong?


K. LR Mon Jul 11 14:34:40 -0400 2011

Try something like this :

class Concern extends ActiveRecord\Model {
static $table = 'concern';
static $has_many = array(
array('products', 'through' => 'concern_product' )
);
}
class Product extends ActiveRecord\Model
{    
static $table = 'product';
static $has_many = array(
array('concern_product')
);
}
class ConcernProduct extends ActiveRecord\Model
{    
static $table = 'concern_product';
static $belongs_to = array(
array('concern'),
array('product')
);
}
Christopher Coleman Thu Dec 22 12:02:32 -0500 2011

I am also having this problem with eager loading.

The issue is specifically the eager loading, K. LR. I have my models and relationships set up just as you suggest, but it doesn't work.

Or rather, if you just try to access the property it works fine, but if you try to eager load the property via an 'include' during a Model::find(...), it fails.

Christopher Coleman Thu Dec 22 13:27:37 -0500 2011

This appears to be the culprit:
Relationship.php [151-173] with my fix on line 168

 1         if (!empty($options['through'])) {
 2             // save old keys as we will be reseting them below for inner join convenience
 3             $pk = $this->primary_key;
 4             $fk = $this->foreign_key;
 5 
 6             $this->set_keys($this->get_table()->class->getName(), true);
 7 
 8             if (!isset($options['class_name'])) {
 9                 $class = classify($options['through'], true);
10                 $through_table = $class::table();
11             } else {
12                 $class = $options['class_name'];
13                 $relation = $class::table()->get_relationship($options['through']);
14                 $through_table = $relation->get_table();
15             }
16             $options['joins'] = $this->construct_inner_join_sql($through_table, true);
17 
18             $query_key = $fk[0]; <-- FIX: was $this->primary_key[0]
19 
20             // reset keys
21             $this->primary_key = $pk;
22             $this->foreign_key = $fk;
23         }

With this fix, my many to many models eagerly load during a find as expected and I haven't (yet) noticed anything breaking.

(1-3/3)