a tiny mvc framework for php using php-activerecord

GH-27 fix for incorrect key references for inner joins using has_many_thru with explicit keys

+102 -22
+20 -22
lib/php-activerecord/lib/Relationship.php
··· 96 96 $this->foreign_key = is_array($this->options['foreign_key']) ? $this->options['foreign_key'] : array($this->options['foreign_key']); 97 97 } 98 98 99 + protected function get_table() 100 + { 101 + return Table::load($this->class_name); 102 + } 103 + 99 104 /** 100 105 * What is this relationship's cardinality? 101 106 * ··· 292 297 $join_table = $from_table; 293 298 $join_table_name = $from_table->get_fully_qualified_table_name(); 294 299 $from_table_name = Table::load($this->class_name)->get_fully_qualified_table_name(); 295 - 296 - } 300 + } 297 301 else 298 302 { 299 303 $join_table = Table::load($this->class_name); ··· 307 311 $this->set_keys($from_table->class->getName()); 308 312 309 313 if ($using_through) 314 + { 315 + $foreign_key = $this->primary_key[0]; 310 316 $join_primary_key = $this->keyify($this->class_name); 317 + } 311 318 else 319 + { 312 320 $join_primary_key = $this->foreign_key[0]; 313 - 314 - $foreign_key = $this->primary_key[0]; 321 + $foreign_key = $this->primary_key[0]; 322 + } 315 323 } 316 324 else 317 325 { ··· 416 424 $this->set_inferred_class_name(); 417 425 } 418 426 419 - private function get_table() 420 - { 421 - return Table::load($this->class_name); 422 - } 423 - 424 - protected function set_keys($model_class_name) 427 + protected function set_keys($model_class_name, $override=false) 425 428 { 426 429 //infer from class_name 427 - if (!$this->foreign_key) 430 + if (!$this->foreign_key || $override) 428 431 $this->foreign_key = array($this->keyify($model_class_name)); 429 432 430 - if (!$this->primary_key) 433 + if (!$this->primary_key || $override) 431 434 $this->primary_key = Table::load($model_class_name)->pk; 432 435 } 433 436 434 437 public function load(Model $model) 435 438 { 436 439 $class_name = $this->class_name; 437 - $this->set_keys(get_class($model)); 438 440 439 441 // since through relationships depend on other relationships we can't do 440 442 // this initiailization in the constructor since the other relationship ··· 450 452 if (!($through_relationship instanceof HasMany) && !($through_relationship instanceof BelongsTo)) 451 453 throw new HasManyThroughAssociationException('has_many through can only use a belongs_to or has_many association'); 452 454 453 - $through_table = Table::load(classify($this->through, true)); 454 - $through_table_name = $through_table->get_fully_qualified_table_name(); 455 + $this->set_keys($this->get_table()->class->getName()); 455 456 457 + $through_table = Table::load(classify($this->through, true)); 456 458 $this->options['joins'] = $this->construct_inner_join_sql($through_table, true); 457 - $conn = $this->get_table()->conn; 458 - 459 - foreach ($this->foreign_key as $index => &$key) 460 - { 461 - $k = $key; 462 - $key = "$through_table_name." . $conn->quote_name($k); 463 - } 464 459 } 465 460 466 461 $this->initialized = true; 467 462 } 468 463 464 + $this->set_keys(get_class($model), true); 465 + 469 466 if (!($conditions = $this->create_conditions_from_keys($model, $this->foreign_key, $this->primary_key))) 470 467 return null; 468 + 471 469 $options = $this->unset_non_finder_options($this->options); 472 470 $options['conditions'] = $conditions; 473 471 return $class_name::find($this->poly_relationship ? 'all' : 'first',$options);
+8
lib/php-activerecord/test/RelationshipTest.php
··· 274 274 $this->assert_equals(3,$hosts[1]->id); 275 275 } 276 276 277 + public function test_gh27_has_many_through_with_explicit_keys() 278 + { 279 + $property = Property::first(); 280 + 281 + $this->assert_equals(1, $property->amenities[0]->amenity_id); 282 + $this->assert_equals(2, $property->amenities[1]->amenity_id); 283 + } 284 + 277 285 public function test_gh16_has_many_through_inside_a_loop_should_not_cause_an_exception() 278 286 { 279 287 $count = 0;
+4
lib/php-activerecord/test/fixtures/amenities.csv
··· 1 + amenity_id, type 2 + 1, "Test #1" 3 + 2, "Test #2" 4 + 3, "Test #3"
lib/php-activerecord/test/fixtures/property.csv

This is a binary file and will not be displayed.

+5
lib/php-activerecord/test/fixtures/property_amenities.csv
··· 1 + id, amenity_id, property_id 2 + 257117, 1, 28840 3 + 257118, 2, 28840 4 + 257119, 2, 28841 5 + 257120, 3, 28841
+11
lib/php-activerecord/test/models/Amenity.php
··· 1 + <?php 2 + class Amenity extends ActiveRecord\Model 3 + { 4 + static $table_name = 'amenities'; 5 + static $primary_key = 'amenity_id'; 6 + 7 + static $has_many = array( 8 + array('property_amenities') 9 + ); 10 + }; 11 + ?>
+12
lib/php-activerecord/test/models/Property.php
··· 1 + <?php 2 + class Property extends ActiveRecord\Model 3 + { 4 + static $table_name = 'property'; 5 + static $primary_key = 'property_id'; 6 + 7 + static $has_many = array( 8 + array('property_amenities'), 9 + array('amenities', 'through' => 'property_amenities') 10 + ); 11 + }; 12 + ?>
+12
lib/php-activerecord/test/models/PropertyAmenity.php
··· 1 + <?php 2 + class PropertyAmenity extends ActiveRecord\Model 3 + { 4 + static $table_name = 'property_amenities'; 5 + static $primary_key = 'id'; 6 + 7 + static $belongs_to = array( 8 + array('amenity'), 9 + array('property') 10 + ); 11 + }; 12 + ?>
+15
lib/php-activerecord/test/sql/mysql.sql
··· 70 70 author_id int, 71 71 is_awesome int default 1 72 72 ); 73 + 74 + CREATE TABLE amenities( 75 + `amenity_id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, 76 + `type` varchar(40) NOT NULL DEFAULT '' 77 + ); 78 + 79 + CREATE TABLE property( 80 + `property_id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY 81 + ); 82 + 83 + CREATE TABLE property_amenities( 84 + `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, 85 + `amenity_id` int(11) NOT NULL DEFAULT '0', 86 + `property_id` int(11) NOT NULL DEFAULT '0' 87 + );
+15
lib/php-activerecord/test/sql/sqlite.sql
··· 69 69 author_id int, 70 70 is_awesome int default 1 71 71 ); 72 + 73 + CREATE TABLE amenities( 74 + `amenity_id` INTEGER NOT NULL PRIMARY KEY, 75 + `type` varchar(40) DEFAULT NULL 76 + ); 77 + 78 + CREATE TABLE property( 79 + `property_id` INTEGER NOT NULL PRIMARY KEY 80 + ); 81 + 82 + CREATE TABLE property_amenities( 83 + `id` INTEGER NOT NULL PRIMARY KEY, 84 + `amenity_id` INT NOT NULL, 85 + `property_id` INT NOT NULL 86 + );