Browse Source

Fix Eloquent eager loading matching.

Taylor Otwell 12 years ago
parent
commit
94948cf675

+ 5 - 27
laravel/database/eloquent/query.php

@@ -66,12 +66,11 @@ class Query {
 	 * Get all of the model results for the query.
 	 *
 	 * @param  array  $columns
-	 * @param  bool   $keyed
 	 * @return array
 	 */
-	public function get($columns = array('*'), $keyed = true)
+	public function get($columns = array('*'))
 	{
-		return $this->hydrate($this->model, $this->table->get($columns), $keyed);
+		return $this->hydrate($this->model, $this->table->get($columns));
 	}
 
 	/**
@@ -100,10 +99,9 @@ class Query {
 	 *
 	 * @param  Model  $model
 	 * @param  array  $results
-	 * @param  bool   $keyed
 	 * @return array
 	 */
-	public function hydrate($model, $results, $keyed = true)
+	public function hydrate($model, $results)
 	{
 		$class = get_class($model);
 
@@ -128,17 +126,7 @@ class Query {
 
 			$new->original = $new->attributes;
 
-			// Typically, the resulting models are keyed by their primary key, but it
-			// may be useful to not do this in some circumstances such as when we
-			// are eager loading a *-to-* relationships which has duplicates.
-			if ($keyed)
-			{
-				$models[$result[$this->model->key()]] = $new;
-			}
-			else
-			{
-				$models[] = $new;
-			}
+			$models[] = $new;
 		}
 
 		if (count($results) > 0)
@@ -199,17 +187,7 @@ class Query {
 
 		$query->initialize($results, $relationship);
 
-		// If we're eager loading a many-to-many relationship we will disable
-		// the primary key indexing on the hydration since there could be
-		// roles shared across users and we don't want to overwrite.
-		if ( ! $query instanceof Has_Many_And_Belongs_To)
-		{
-			$query->match($relationship, $results, $query->get());
-		}
-		else
-		{
-			$query->match($relationship, $results, $query->get(array('*'), false));
-		}
+		$query->match($relationship, $results, $query->get());
 	}
 
 	/**

+ 7 - 2
laravel/database/eloquent/relationships/belongs_to.php

@@ -87,9 +87,14 @@ class Belongs_To extends Relationship {
 
 		foreach ($children as &$child)
 		{
-			if (array_key_exists($child->$foreign, $parents))
+			$parent = array_first($parents, function($k, $v) use ($child, $foreign)
 			{
-				$child->relationships[$relationship] = $parents[$child->$foreign];
+				return $v->get_key() == $child->$foreign;
+			});
+
+			if ( ! is_null($parent))
+			{
+				$child->relationships[$relationship] = $parent;
 			}
 		}
 	}

+ 7 - 2
laravel/database/eloquent/relationships/has_many.php

@@ -91,9 +91,14 @@ class Has_Many extends Has_One_Or_Many {
 	{
 		$foreign = $this->foreign_key();
 
-		foreach ($children as $key => $child)
+		foreach ($parents as &$parent)
 		{
-			$parents[$child->$foreign]->relationships[$relationship][$child->get_key()] = $child;
+			$matching = array_filter($children, function($v) use ($parent, $foreign)
+			{
+				return $v->$foreign == $parent->get_key();
+			});
+
+			$parent->relationships[$relationship] = $matching;
 		}
 	}
 

+ 7 - 7
laravel/database/eloquent/relationships/has_many_and_belongs_to.php

@@ -311,7 +311,7 @@ class Has_Many_And_Belongs_To extends Relationship {
 	 */
 	public function eagerly_constrain($results)
 	{
-		$this->table->where_in($this->joining.'.'.$this->foreign_key(), array_keys($results));
+		$this->table->where_in($this->joining.'.'.$this->foreign_key(), $this->keys($results));
 	}
 
 	/**
@@ -325,14 +325,14 @@ class Has_Many_And_Belongs_To extends Relationship {
 	{
 		$foreign = $this->foreign_key();
 
-		// For each child we'll just get the parent that connects to the child and set the
-		// child model on the relationship array using the keys. Once we're done looping
-		// through the children all of the proper relations will be set.
-		foreach ($children as $key => $child)
+		foreach ($parents as &$parent)
 		{
-			$parent =& $parents[$child->pivot->$foreign];
+			$matching = array_filter($children, function($v) use ($parent, $foreign)
+			{
+				return $v->pivot->$foreign == $parent->get_key();
+			});
 
-			$parent->relationships[$relationship][$child->{$child->key()}] = $child;
+			$parent->relationships[$relationship] = $matching;
 		}
 	}
 

+ 7 - 2
laravel/database/eloquent/relationships/has_one.php

@@ -38,9 +38,14 @@ class Has_One extends Has_One_Or_Many {
 	{
 		$foreign = $this->foreign_key();
 
-		foreach ($children as $key => $child)
+		foreach ($parents as &$parent)
 		{
-			$parents[$child->$foreign]->relationships[$relationship] = $child;
+			$matching = array_first($children, function($k, $v) use ($parent, $foreign)
+			{
+				return $v->$foreign == $parent->get_key();
+			});
+
+			$parent->relationships[$relationship] = $matching;
 		}
 	}
 

+ 1 - 1
laravel/database/eloquent/relationships/has_one_or_many.php

@@ -53,7 +53,7 @@ class Has_One_Or_Many extends Relationship {
 	 */
 	public function eagerly_constrain($results)
 	{
-		$this->table->where_in($this->foreign_key(), array_keys($results));
+		$this->table->where_in($this->foreign_key(), $this->keys($results));
 	}
 
 }

+ 18 - 0
laravel/database/eloquent/relationships/relationship.php

@@ -101,4 +101,22 @@ abstract class Relationship extends Query {
 		return static::foreign($this->base, $this->foreign);
 	}
 
+	/**
+	 * Gather all the primary keys from a result set.
+	 *
+	 * @param  array  $results
+	 * @return array
+	 */
+	public function keys($results)
+	{
+		$keys = array();
+
+		foreach ($results as $result)
+		{
+			$keys[] = $result->get_key();
+		}
+
+		return array_unique($keys);
+	}
+
 }

+ 1 - 1
laravel/error.php

@@ -13,7 +13,7 @@ class Error {
 	{
 		static::log($exception);
 
-		ob_get_level() and ob_end_clean();
+		//ob_get_level() and ob_end_clean();
 
 		// If detailed errors are enabled, we'll just format the exception into
 		// a simple error message and display it on the screen. We don't use a