diff --git a/lib/Model.php b/lib/Model.php index 0c20ac9ab..c87019599 100644 --- a/lib/Model.php +++ b/lib/Model.php @@ -446,6 +446,30 @@ public function __set($name, $value) return $this->$item['to']->$delegated_name = $value; } + $table = static::table(); + if ($relationship = $table->get_relationship($name) + ) + { + if (is_null($value)) + { + $this->__relationships[$name] = $value; + return $this->assign_attribute($relationship->foreign_key[0], $value); + } + elseif (($value instanceof \ActiveRecord\Model) && + $value instanceof $relationship->class_name) + { + $this->__relationships[$name] = $value; + $pk = $value->get_primary_key(0); + return $this->assign_attribute( + $relationship->foreign_key[0], + $value->is_new_record() ? null : $value->{$pk[0]} + ); + } + else + { + throw new RelationshipException(); + } + } throw new UndefinedPropertyException(get_called_class(),$name); } @@ -837,7 +861,7 @@ public static function table() * @param boolean $guard_attributes Set to true to guard protected/non-accessible attributes * @return Model */ - public static function create($attributes, $validate=true, $guard_attributes=true) + public static function create(array $attributes=array(), $validate=true, $guard_attributes=true) { $class_name = get_called_class(); $model = new $class_name($attributes, $guard_attributes); diff --git a/lib/Relationship.php b/lib/Relationship.php index 297d89438..526071160 100644 --- a/lib/Relationship.php +++ b/lib/Relationship.php @@ -184,24 +184,36 @@ protected function query_and_attach_related_models_eagerly(Table $table, $models $model_values_key = $inflector->variablize($model_values_key); $query_key = $inflector->variablize($query_key); + $all_related_with_value = null; foreach ($models as $model) { + if(!isset($all_related_with_value)) + { + $all_related_with_value = array(); + foreach ($related_models as $related) + { + $all_related_with_value[$related->$query_key][] = $related; + } + } $matches = 0; $key_to_match = $model->$model_values_key; - foreach ($related_models as $related) + if(array_key_exists($key_to_match,$all_related_with_value)) { - if ($related->$query_key == $key_to_match) + foreach ($all_related_with_value[$key_to_match] as $related) { - $hash = spl_object_hash($related); - - if (in_array($hash, $used_models)) - $model->set_relationship_from_eager_load(clone($related), $this->attribute_name); - else - $model->set_relationship_from_eager_load($related, $this->attribute_name); - - $used_models[] = $hash; - $matches++; + if ($related->$query_key == $key_to_match) + { + $hash = spl_object_hash($related); + + if (array_key_exists($hash, $used_models)) + $model->set_relationship_from_eager_load(clone($related), $this->attribute_name); + else + $model->set_relationship_from_eager_load($related, $this->attribute_name); + + $used_models[$hash] = null; + $matches++; + } } } diff --git a/lib/Utils.php b/lib/Utils.php index af5840cd6..3db34514c 100644 --- a/lib/Utils.php +++ b/lib/Utils.php @@ -63,13 +63,15 @@ function array_flatten(array $array) /** * Somewhat naive way to determine if an array is a hash. */ -function is_hash(&$array) +function is_hash($array) { - if (!is_array($array)) + if (!is_array($array)) { return false; + } $keys = array_keys($array); - return @is_string($keys[0]) ? true : false; + + return isset($keys[0]) && is_string($keys[0]); } /** @@ -173,7 +175,7 @@ public static function extract_options($options) return is_array(end($options)) ? end($options) : array(); } - public static function add_condition(&$conditions=array(), $condition, $conjuction='AND') + public static function add_condition(&$conditions=array(), $condition, $conjunction='AND') { if (is_array($condition)) { @@ -181,12 +183,12 @@ public static function add_condition(&$conditions=array(), $condition, $conjucti $conditions = array_flatten($condition); else { - $conditions[0] .= " $conjuction " . array_shift($condition); + $conditions[0] = "({$conditions[0]}) $conjunction (" . array_shift($condition) . ")"; $conditions[] = array_flatten($condition); } } elseif (is_string($condition)) - $conditions[0] .= " $conjuction $condition"; + $conditions[0] = "({$conditions[0]}) {$conjunction} ($condition)"; return $conditions; } @@ -367,4 +369,4 @@ public static function add_irregular($singular, $plural) { self::$irregular[$singular] = $plural; } -} \ No newline at end of file +} diff --git a/test/RelationshipTest.php b/test/RelationshipTest.php index e726a6b71..8d1049462 100644 --- a/test/RelationshipTest.php +++ b/test/RelationshipTest.php @@ -540,7 +540,7 @@ public function test_gh93_and_gh100_eager_loading_respects_association_options() Venue::$has_many = array(array('events', 'class_name' => 'Event', 'order' => 'id asc', 'conditions' => array('length(title) = ?', 14))); $venues = Venue::find(array(2, 6), array('include' => 'events')); - $this->assert_sql_has("WHERE length(title) = ? AND venue_id IN(?,?) ORDER BY id asc",ActiveRecord\Table::load('Event')->last_sql); + $this->assert_sql_has("WHERE (length(title) = ?) AND (venue_id IN(?,?)) ORDER BY id asc",ActiveRecord\Table::load('Event')->last_sql); $this->assert_equals(1, count($venues[0]->events)); } @@ -732,4 +732,64 @@ public function test_dont_attempt_eager_load_when_record_does_not_exist() { Author::find(999999, array('include' => array('books'))); } + + public function test_gh_125_auto_setter_for_relation() + { + $event = new Event(array( + 'venue' => new Venue() + )); + + $this->assert_not_null($event->venue); + $this->assert_null($event->venue_id); + } + + public function test_gh_125_auto_setter_for_relation_with_id() + { + $event = new Event(array( + 'venue' => Venue::create() + )); + + $this->assert_not_null($event->venue); + $this->assert_not_null($event->venue_id); + $event->venue->delete(); + } + + /** + * @expectedException ActiveRecord\RelationshipException + */ + public function test_gh_125_auto_setter_for_relation_raises_exception() + { + $event = new Event(array( + 'venue' => new Book() + )); + } + + public function test_gh_125_auto_setter_for_relation_using_subclass() + { + $event = new Event(array( + 'venue' => new SubVenue() + )); + + $this->assert_not_null($event->venue); + $this->assert_null($event->venue_id); + } + + public function test_gh_125_unset_existing_relation() + { + $event = new Event(array( + 'venue' => Venue::create() + )); + + $event->venue->delete(); + $event->venue = null; + $this->assert_null($event->venue); + $this->assert_null($event->venue_id); + } + + public function test_gh_125_add_model_to_has_many_relation() + { + $venue = Venue::find('first'); + $venue->events = null; + // $venue->events = array(new Event()); + } } diff --git a/test/UtilsTest.php b/test/UtilsTest.php index 20074bbc9..37d8bf151 100644 --- a/test/UtilsTest.php +++ b/test/UtilsTest.php @@ -103,5 +103,19 @@ public function test_wrap_strings_in_arrays() $x = '1'; $this->assert_equals(array(array('1')),ActiveRecord\wrap_strings_in_arrays($x)); } -} + public function test_is_hash() + { + $hash = array('key' => 'value'); + $this->assert_true(ActiveRecord\is_hash($hash)); + + $notHash = array(0 => 'value'); + $this->assert_false(ActiveRecord\is_hash($notHash)); + } + + public function test_is_hash_empty_array() + { + $notHash = array(); + $this->assert_false(ActiveRecord\is_hash($notHash)); + } +}; diff --git a/test/models/SubVenue.php b/test/models/SubVenue.php new file mode 100644 index 000000000..9de87a2c1 --- /dev/null +++ b/test/models/SubVenue.php @@ -0,0 +1,6 @@ + diff --git a/test/models/Venue.php b/test/models/Venue.php index 3c13adeee..6d1e3f6ce 100644 --- a/test/models/Venue.php +++ b/test/models/Venue.php @@ -6,7 +6,7 @@ class Venue extends ActiveRecord\Model static $has_many = array( - 'events', + array('events'), array('hosts', 'through' => 'events') );