From c3d95122e4e0dc66941a3a47e788497fc87806a0 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 16 Mar 2012 11:29:21 -0500 Subject: [PATCH] =?UTF-8?q?Fixing=20bugs=E2=80=A6=20adding=20better=20pivo?= =?UTF-8?q?t=20support.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Taylor Otwell --- laravel/database/eloquent/model.php | 24 ++++++ laravel/database/eloquent/pivot.php | 35 ++++++++ laravel/database/eloquent/query.php | 2 +- .../relationships/has_many_and_belongs_to.php | 85 +++++++++++++++---- 4 files changed, 129 insertions(+), 17 deletions(-) create mode 100644 laravel/database/eloquent/pivot.php diff --git a/laravel/database/eloquent/model.php b/laravel/database/eloquent/model.php index bc60dc1f..00cba31b 100644 --- a/laravel/database/eloquent/model.php +++ b/laravel/database/eloquent/model.php @@ -345,6 +345,18 @@ abstract class Model { return new Query($this); } + /** + * Sync the original attributes with the current attributes. + * + * @return bool + */ + final public function sync() + { + $this->original = $this->attributes; + + return true; + } + /** * Determine if a given attribute has changed from its original state. * @@ -421,6 +433,18 @@ abstract class Model { $this->attributes[$key] = $value; } + /** + * Remove an attribute from the model. + * + * @param string $key + */ + final public function purge($key) + { + unset($this->original[$key]); + + unset($this->attributes[$key]); + } + /** * Handle the dynamic retrieval of attributes and associations. * diff --git a/laravel/database/eloquent/pivot.php b/laravel/database/eloquent/pivot.php new file mode 100644 index 00000000..92ea2e46 --- /dev/null +++ b/laravel/database/eloquent/pivot.php @@ -0,0 +1,35 @@ +pivot_table = $table; + + parent::__construct(array(), true); + } + + /** + * Get the name of the pivot table. + * + * @return string + */ + public function table() + { + return $this->pivot_table; + } + +} \ No newline at end of file diff --git a/laravel/database/eloquent/query.php b/laravel/database/eloquent/query.php index 9a248472..c4848b2e 100644 --- a/laravel/database/eloquent/query.php +++ b/laravel/database/eloquent/query.php @@ -91,7 +91,7 @@ class Query { // any pivot columns that are on the model. if ($this instanceof Relationships\Has_Many_And_Belongs_To) { - $this->clean($results); + $this->pivot($results); } return $results; diff --git a/laravel/database/eloquent/relationships/has_many_and_belongs_to.php b/laravel/database/eloquent/relationships/has_many_and_belongs_to.php index da046add..b4fe924f 100644 --- a/laravel/database/eloquent/relationships/has_many_and_belongs_to.php +++ b/laravel/database/eloquent/relationships/has_many_and_belongs_to.php @@ -1,5 +1,7 @@ other_key(); + $foreign = $this->foreign_key(); - $this->set_select($foreign)->set_join($this->other_key())->set_where($foreign); + $this->set_select($foreign, $other)->set_join($other)->set_where($foreign); } /** * Set the SELECT clause on the query builder for the relationship. * * @param string $foreign + * @param string $other * @return void */ - protected function set_select($foreign) + protected function set_select($foreign, $other) { - $foreign = $this->joining.'.'.$foreign.' as pivot_foreign_key'; + $columns = array($this->model->table().'.*'); - $this->table->select(array($this->model->table().'.*', $foreign)); + $this->with = array_merge($this->with, array($foreign, $other)); + + // Since pivot tables may have extra information on them that the developer + // needs, we allow an extra array of columns to be specified that will be + // fetched from the pivot table and hydrate into the pivot model. + foreach ($this->with as $column) + { + $columns[] = $this->joining.'.'.$column.' as pivot_'.$column; + } + + $this->table->select($columns); return $this; } @@ -201,35 +223,66 @@ class Has_Many_And_Belongs_To extends Relationship { */ public function match($relationship, &$parents, $children) { - $foreign = 'pivot_foreign_key'; + $foreign = $this->foreign_key(); foreach ($children as $key => $child) { - $parents[$child->$foreign]->relationships[$relationship][$child->{$child->key()}] = $child; - - // After matching the child model with its parent, we can remove the foreign key - // from the model, as it was only necessary to allow us to know which parent - // the child belongs to for eager loading and isn't necessary otherwise. - unset($child->attributes[$foreign]); - - unset($child->original[$foreign]); + $parents[$child->pivot->$foreign]->relationships[$relationship][$child->{$child->key()}] = $child; } } /** - * Clean-up any pivot columns that are on the results. + * Hydrate the Pivot model on an array of results. * * @param array $results * @return void */ - protected function clean(&$results) + protected function pivot(&$results) { foreach ($results as &$result) { - + // Every model result for a many-to-many relationship needs a Pivot instance + // to represent the pivot table's columns. Sometimes extra columns are on + // the pivot table that may need to be accessed by the developer. + $pivot = new Pivot($this->joining); + + // If the attribute key starts with "pivot_", we know this is a column on + // the pivot table, so we will move it to the Pivot model and purge it + // from the model since it actually belongs to the pivot. + foreach ($result->attributes as $key => $value) + { + if (starts_with($key, 'pivot_')) + { + $pivot->{substr($key, 6)} = $value; + + $result->purge($key); + } + } + + // Once we have completed hydrating the pivot model instance, we'll set + // it on the result model's relationships array so the developer can + // quickly and easily access any pivot table information. + $result->relationships['pivot'] = $pivot; + + $pivot->sync() and $result->sync(); } } + /** + * Set the columns on the joining table that should be fetched. + * + * @param array $column + * @return Relationship + */ + public function with($columns) + { + $this->with = (is_array($columns)) ? $columns : func_get_args(); + + $this->set_select($this->foreign_key(), $this->other_key()); + + return $this; + } + /** * Get the other or associated key for the relationship. *