Commit e89d2263 authored by Jens Segers's avatar Jens Segers

Slimming down the BelongsToMany class

parent 12e7cb2a
<?php namespace Jenssegers\Mongodb\Relations; <?php namespace Jenssegers\Mongodb\Relations;
use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
...@@ -7,31 +8,16 @@ use Illuminate\Database\Eloquent\Relations\BelongsToMany as EloquentBelongsToMan ...@@ -7,31 +8,16 @@ use Illuminate\Database\Eloquent\Relations\BelongsToMany as EloquentBelongsToMan
class BelongsToMany extends EloquentBelongsToMany { class BelongsToMany extends EloquentBelongsToMany {
/** /**
* Execute the query as a "select" statement. * Hydrate the pivot table relationship on the models.
* *
* @param array $columns * @param array $models
* @return \Illuminate\Database\Eloquent\Collection * @return void
*/ */
public function get($columns = array('*')) protected function hydratePivotRelation(array $models)
{ {
// First we'll add the proper select columns onto the query so it is run with // Do nothing
// the proper columns. Then, we will get the results and hydrate out pivot
// models with the result of those columns as a separate model relation.
$select = $this->getSelectColumns($columns);
$models = $this->query->addSelect($select)->getModels();
// If we actually found models we will also eager load any relationships that
// have been specified as needing to be eager loaded. This will solve the
// n + 1 query problem for the developer and also increase performance.
if (count($models) > 0)
{
$models = $this->query->eagerLoadRelations($models);
}
return $this->related->newCollection($models);
} }
/** /**
* Set the select clause for the relation query. * Set the select clause for the relation query.
* *
...@@ -42,26 +28,6 @@ class BelongsToMany extends EloquentBelongsToMany { ...@@ -42,26 +28,6 @@ class BelongsToMany extends EloquentBelongsToMany {
return $columns; return $columns;
} }
/**
* Get a paginator for the "select" statement.
*
* @param int $perPage
* @param array $columns
* @return \Illuminate\Pagination\Paginator
*/
public function paginate($perPage = null, $columns = array('*'))
{
$this->query->addSelect($this->getSelectColumns($columns));
// When paginating results, we need to add the pivot columns to the query and
// then hydrate into the pivot objects once the results have been gathered
// from the database since this isn't performed by the Eloquent builder.
$pager = $this->query->paginate($perPage, $columns);
return $pager;
}
/** /**
* Set the base constraints on the relation query. * Set the base constraints on the relation query.
* *
...@@ -69,7 +35,7 @@ class BelongsToMany extends EloquentBelongsToMany { ...@@ -69,7 +35,7 @@ class BelongsToMany extends EloquentBelongsToMany {
*/ */
public function addConstraints() public function addConstraints()
{ {
if (static::$constraints) if (static::$constraints)
{ {
// Make sure that the primary key of the parent // Make sure that the primary key of the parent
// is in the relationship array of keys // is in the relationship array of keys
...@@ -77,55 +43,6 @@ class BelongsToMany extends EloquentBelongsToMany { ...@@ -77,55 +43,6 @@ class BelongsToMany extends EloquentBelongsToMany {
} }
} }
/**
* Set the constraints for an eager load of the relation.
*
* @param array $models
* @return void
*/
public function addEagerConstraints(array $models)
{
$this->query->whereIn($this->getForeignKey(), $this->getKeys($models));
}
/**
* Save a new model and attach it to the parent model.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param array $joining
* @param bool $touch
* @return \Illuminate\Database\Eloquent\Model
*/
public function save(Model $model, array $joining = array(), $touch = true)
{
$model->save(array('touch' => false));
$this->attach($model->getKey(), $joining, $touch);
return $model;
}
/**
* Create a new instance of the related model.
*
* @param array $attributes
* @param array $joining
* @param bool $touch
* @return \Illuminate\Database\Eloquent\Model
*/
public function create(array $attributes, array $joining = array(), $touch = true)
{
$instance = $this->related->newInstance($attributes);
// Save the new instance before we attach it to other models
$instance->save(array('touch' => false));
// Attach to the parent instance
$this->attach($instance->_id, $attributes, $touch);
return $instance;
}
/** /**
* Sync the intermediate tables with a list of IDs. * Sync the intermediate tables with a list of IDs.
* *
...@@ -139,13 +56,13 @@ class BelongsToMany extends EloquentBelongsToMany { ...@@ -139,13 +56,13 @@ class BelongsToMany extends EloquentBelongsToMany {
// in this joining table. We'll spin through the given IDs, checking to see // in this joining table. We'll spin through the given IDs, checking to see
// if they exist in the array of current ones, and if not we will insert. // if they exist in the array of current ones, and if not we will insert.
$current = $this->parent->{$this->otherKey}; $current = $this->parent->{$this->otherKey};
// Check if the current array exists or not on the parent model and create it // Check if the current array exists or not on the parent model and create it
// if it does not exist // if it does not exist
if (is_null($current)) $current = array(); if (is_null($current)) $current = array();
$records = $this->formatSyncList($ids); $records = $this->formatSyncList($ids);
$detach = array_diff($current, array_keys($records)); $detach = array_diff($current, array_keys($records));
// Next, we will take the differences of the currents and given IDs and detach // Next, we will take the differences of the currents and given IDs and detach
...@@ -164,29 +81,6 @@ class BelongsToMany extends EloquentBelongsToMany { ...@@ -164,29 +81,6 @@ class BelongsToMany extends EloquentBelongsToMany {
$this->touchIfTouching(); $this->touchIfTouching();
} }
/**
* Format the sync list so that it is keyed by ID.
*
* @param array $records
* @return array
*/
protected function formatSyncList(array $records)
{
$results = array();
foreach ($records as $id => $attributes)
{
if ( ! is_array($attributes))
{
list($id, $attributes) = array($attributes, array());
}
$results[$id] = $attributes;
}
return $results;
}
/** /**
* Attach all of the IDs that aren't in the current array. * Attach all of the IDs that aren't in the current array.
* *
...@@ -220,25 +114,25 @@ class BelongsToMany extends EloquentBelongsToMany { ...@@ -220,25 +114,25 @@ class BelongsToMany extends EloquentBelongsToMany {
public function attach($id, array $attributes = array(), $touch = true) public function attach($id, array $attributes = array(), $touch = true)
{ {
if ($id instanceof Model) $id = $id->getKey(); if ($id instanceof Model) $id = $id->getKey();
// Generate a new parent query instance // Generate a new parent query instance
$parent = $this->newParentQuery(); $parent = $this->newParentQuery();
// Generate a new related query instance // Generate a new related query instance
$related = $this->related->newInstance(); $related = $this->related->newInstance();
// Set contraints on the related query // Set contraints on the related query
$related = $related->where($this->related->getKeyName(), $id); $related = $related->where($this->related->getKeyName(), $id);
$records = $this->createAttachRecords((array) $id, $attributes); $records = $this->createAttachRecords((array) $id, $attributes);
// Get the ID's to attach to the two documents // Get the ID's to attach to the two documents
$otherIds = array_pluck($records, $this->otherKey); $otherIds = array_pluck($records, $this->otherKey);
$foreignIds = array_pluck($records, $this->foreignKey); $foreignIds = array_pluck($records, $this->foreignKey);
// Attach to the parent model // Attach to the parent model
$parent->push($this->otherKey, $otherIds[0])->update(array()); $parent->push($this->otherKey, $otherIds[0])->update(array());
// Attach to the related model // Attach to the related model
$related->push($this->foreignKey, $foreignIds[0])->update(array()); $related->push($this->foreignKey, $foreignIds[0])->update(array());
} }
...@@ -296,54 +190,22 @@ class BelongsToMany extends EloquentBelongsToMany { ...@@ -296,54 +190,22 @@ class BelongsToMany extends EloquentBelongsToMany {
{ {
$query->pull($this->otherKey, $id); $query->pull($this->otherKey, $id);
} }
return count($ids);
}
/**
* If we're touching the parent model, touch.
*
* @return void
*/
public function touchIfTouching()
{
if ($this->touchingParent()) $this->getParent()->touch();
if ($this->getParent()->touches($this->relationName)) $this->touch(); return count($ids);
}
/**
* Determine if we should touch the parent on sync.
*
* @return bool
*/
protected function touchingParent()
{
return $this->getRelated()->touches($this->guessInverseRelation());
}
/**
* Attempt to guess the name of the inverse of the relation.
*
* @return string
*/
protected function guessInverseRelation()
{
return strtolower(str_plural(class_basename($this->getParent())));
} }
/** /**
* Create a new query builder for the parent * Create a new query builder for the parent
* *
* @return Jenssegers\Mongodb\Builder * @return Jenssegers\Mongodb\Builder
*/ */
protected function newParentQuery() protected function newParentQuery()
{ {
$query = $this->parent->newQuery(); $query = $this->parent->newQuery();
return $query->where($this->parent->getKeyName(), '=', $this->parent->getKey()); return $query->where($this->parent->getKeyName(), '=', $this->parent->getKey());
} }
/** /**
* Build model dictionary keyed by the relation's foreign key. * Build model dictionary keyed by the relation's foreign key.
* *
...@@ -370,16 +232,6 @@ class BelongsToMany extends EloquentBelongsToMany { ...@@ -370,16 +232,6 @@ class BelongsToMany extends EloquentBelongsToMany {
return $dictionary; return $dictionary;
} }
/**
* Get the related model's updated at column name.
*
* @return string
*/
public function getRelatedFreshUpdate()
{
return array($this->related->getUpdatedAtColumn() => $this->related->freshTimestamp());
}
/** /**
* Get the fully qualified foreign key for the relation. * Get the fully qualified foreign key for the relation.
* *
...@@ -399,4 +251,4 @@ class BelongsToMany extends EloquentBelongsToMany { ...@@ -399,4 +251,4 @@ class BelongsToMany extends EloquentBelongsToMany {
{ {
return $this->otherKey; return $this->otherKey;
} }
} }
\ No newline at end of file
...@@ -2,214 +2,214 @@ ...@@ -2,214 +2,214 @@
class RelationsTest extends PHPUnit_Framework_TestCase { class RelationsTest extends PHPUnit_Framework_TestCase {
public function setUp() { public function setUp() {
} }
public function tearDown() public function tearDown()
{ {
User::truncate(); User::truncate();
Book::truncate(); Book::truncate();
Item::truncate(); Item::truncate();
Role::truncate(); Role::truncate();
Client::truncate(); Client::truncate();
} }
public function testHasMany() public function testHasMany()
{ {
$author = User::create(array('name' => 'George R. R. Martin')); $author = User::create(array('name' => 'George R. R. Martin'));
Book::create(array('title' => 'A Game of Thrones', 'author_id' => $author->_id)); Book::create(array('title' => 'A Game of Thrones', 'author_id' => $author->_id));
Book::create(array('title' => 'A Clash of Kings', 'author_id' => $author->_id)); Book::create(array('title' => 'A Clash of Kings', 'author_id' => $author->_id));
$books = $author->books; $books = $author->books;
$this->assertEquals(2, count($books)); $this->assertEquals(2, count($books));
$user = User::create(array('name' => 'John Doe')); $user = User::create(array('name' => 'John Doe'));
Item::create(array('type' => 'knife', 'user_id' => $user->_id)); Item::create(array('type' => 'knife', 'user_id' => $user->_id));
Item::create(array('type' => 'shield', 'user_id' => $user->_id)); Item::create(array('type' => 'shield', 'user_id' => $user->_id));
Item::create(array('type' => 'sword', 'user_id' => $user->_id)); Item::create(array('type' => 'sword', 'user_id' => $user->_id));
Item::create(array('type' => 'bag', 'user_id' => null)); Item::create(array('type' => 'bag', 'user_id' => null));
$items = $user->items; $items = $user->items;
$this->assertEquals(3, count($items)); $this->assertEquals(3, count($items));
} }
public function testBelongsTo() public function testBelongsTo()
{ {
$user = User::create(array('name' => 'George R. R. Martin')); $user = User::create(array('name' => 'George R. R. Martin'));
Book::create(array('title' => 'A Game of Thrones', 'author_id' => $user->_id)); Book::create(array('title' => 'A Game of Thrones', 'author_id' => $user->_id));
$book = Book::create(array('title' => 'A Clash of Kings', 'author_id' => $user->_id)); $book = Book::create(array('title' => 'A Clash of Kings', 'author_id' => $user->_id));
$author = $book->author; $author = $book->author;
$this->assertEquals('George R. R. Martin', $author->name); $this->assertEquals('George R. R. Martin', $author->name);
$user = User::create(array('name' => 'John Doe')); $user = User::create(array('name' => 'John Doe'));
$item = Item::create(array('type' => 'sword', 'user_id' => $user->_id)); $item = Item::create(array('type' => 'sword', 'user_id' => $user->_id));
$owner = $item->user; $owner = $item->user;
$this->assertEquals('John Doe', $owner->name); $this->assertEquals('John Doe', $owner->name);
} }
public function testHasOne() public function testHasOne()
{ {
$user = User::create(array('name' => 'John Doe')); $user = User::create(array('name' => 'John Doe'));
Role::create(array('type' => 'admin', 'user_id' => $user->_id)); Role::create(array('type' => 'admin', 'user_id' => $user->_id));
$role = $user->role; $role = $user->role;
$this->assertEquals('admin', $role->type); $this->assertEquals('admin', $role->type);
} }
public function testWithBelongsTo() public function testWithBelongsTo()
{ {
$user = User::create(array('name' => 'John Doe')); $user = User::create(array('name' => 'John Doe'));
Item::create(array('type' => 'knife', 'user_id' => $user->_id)); Item::create(array('type' => 'knife', 'user_id' => $user->_id));
Item::create(array('type' => 'shield', 'user_id' => $user->_id)); Item::create(array('type' => 'shield', 'user_id' => $user->_id));
Item::create(array('type' => 'sword', 'user_id' => $user->_id)); Item::create(array('type' => 'sword', 'user_id' => $user->_id));
Item::create(array('type' => 'bag', 'user_id' => null)); Item::create(array('type' => 'bag', 'user_id' => null));
$items = Item::with('user')->get(); $items = Item::with('user')->get();
$user = $items[0]->getRelation('user'); $user = $items[0]->getRelation('user');
$this->assertInstanceOf('User', $user); $this->assertInstanceOf('User', $user);
$this->assertEquals('John Doe', $user->name); $this->assertEquals('John Doe', $user->name);
$this->assertEquals(1, count($items[0]->getRelations())); $this->assertEquals(1, count($items[0]->getRelations()));
$this->assertEquals(null, $items[3]->getRelation('user')); $this->assertEquals(null, $items[3]->getRelation('user'));
} }
public function testWithHashMany() public function testWithHashMany()
{ {
$user = User::create(array('name' => 'John Doe')); $user = User::create(array('name' => 'John Doe'));
Item::create(array('type' => 'knife', 'user_id' => $user->_id)); Item::create(array('type' => 'knife', 'user_id' => $user->_id));
Item::create(array('type' => 'shield', 'user_id' => $user->_id)); Item::create(array('type' => 'shield', 'user_id' => $user->_id));
Item::create(array('type' => 'sword', 'user_id' => $user->_id)); Item::create(array('type' => 'sword', 'user_id' => $user->_id));
Item::create(array('type' => 'bag', 'user_id' => null)); Item::create(array('type' => 'bag', 'user_id' => null));
$user = User::with('items')->find($user->_id); $user = User::with('items')->find($user->_id);
$items = $user->getRelation('items'); $items = $user->getRelation('items');
$this->assertEquals(3, count($items)); $this->assertEquals(3, count($items));
$this->assertInstanceOf('Item', $items[0]); $this->assertInstanceOf('Item', $items[0]);
} }
public function testWithHasOne() public function testWithHasOne()
{ {
$user = User::create(array('name' => 'John Doe')); $user = User::create(array('name' => 'John Doe'));
Role::create(array('type' => 'admin', 'user_id' => $user->_id)); Role::create(array('type' => 'admin', 'user_id' => $user->_id));
Role::create(array('type' => 'guest', 'user_id' => $user->_id)); Role::create(array('type' => 'guest', 'user_id' => $user->_id));
$user = User::with('role')->find($user->_id); $user = User::with('role')->find($user->_id);
$role = $user->getRelation('role'); $role = $user->getRelation('role');
$this->assertInstanceOf('Role', $role); $this->assertInstanceOf('Role', $role);
$this->assertEquals('admin', $role->type); $this->assertEquals('admin', $role->type);
} }
public function testEasyRelation() public function testEasyRelation()
{ {
// Has Many // Has Many
$user = User::create(array('name' => 'John Doe')); $user = User::create(array('name' => 'John Doe'));
$item = Item::create(array('type' => 'knife')); $item = Item::create(array('type' => 'knife'));
$user->items()->save($item); $user->items()->save($item);
$user = User::find($user->_id); $user = User::find($user->_id);
$items = $user->items; $items = $user->items;
$this->assertEquals(1, count($items)); $this->assertEquals(1, count($items));
$this->assertInstanceOf('Item', $items[0]); $this->assertInstanceOf('Item', $items[0]);
// Has one // Has one
$user = User::create(array('name' => 'John Doe')); $user = User::create(array('name' => 'John Doe'));
$role = Role::create(array('type' => 'admin')); $role = Role::create(array('type' => 'admin'));
$user->role()->save($role); $user->role()->save($role);
$user = User::find($user->_id); $user = User::find($user->_id);
$role = $user->role; $role = $user->role;
$this->assertInstanceOf('Role', $role); $this->assertInstanceOf('Role', $role);
$this->assertEquals('admin', $role->type); $this->assertEquals('admin', $role->type);
}
public function testHasManyAndBelongsTo()
{
$user = User::create(array('name' => 'John Doe'));
$user->clients()->save(new Client(array('name' => 'Pork Pies Ltd.')));
$user->clients()->create(array('name' => 'Buffet Bar Inc.'));
$user = User::with('clients')->find($user->_id);
$client = Client::with('users')->first();
$clients = $client->getRelation('users');
$users = $user->getRelation('clients');
$this->assertInstanceOf('Illuminate\Database\Eloquent\Collection', $users);
$this->assertInstanceOf('Illuminate\Database\Eloquent\Collection', $clients);
$this->assertInstanceOf('Client', $users[0]);
$this->assertInstanceOf('User', $clients[0]);
$this->assertCount(2, $user->clients);
$this->assertCount(1, $client->users);
// Now create a new user to an existing client
$client->users()->create(array('name' => 'Jane Doe'));
$otherClient = User::where('name', '=', 'Jane Doe')->first()->clients()->get();
$this->assertInstanceOf('Illuminate\Database\Eloquent\Collection', $otherClient);
$this->assertInstanceOf('Client', $otherClient[0]);
$this->assertCount(1, $otherClient);
// Now attach an existing client to an existing user
$user = User::where('name', '=', 'Jane Doe')->first();
$client = Client::Where('name', '=', 'Buffet Bar Inc.')->first();
// Check the models are what they should be
$this->assertInstanceOf('Client', $client);
$this->assertInstanceOf('User', $user);
// Assert they are not attached
$this->assertFalse(in_array($client->_id, $user->client_ids));
$this->assertFalse(in_array($user->_id, $client->user_ids));
// Attach the client to the user
$user->clients()->attach($client);
// Get the new user model
$user = User::where('name', '=', 'Jane Doe')->first();
$client = Client::Where('name', '=', 'Buffet Bar Inc.')->first();
// Assert they are attached
$this->assertTrue(in_array($client->_id, $user->client_ids));
$this->assertTrue(in_array($user->_id, $client->user_ids));
}
public function testHasManyAndBelongsToAttachesExistingModels()
{
$user = User::create(array('name' => 'John Doe', 'client_ids' => array('1234523')));
$clients = array(
Client::create(array('name' => 'Pork Pies Ltd.'))->_id,
Client::create(array('name' => 'Buffet Bar Inc.'))->_id
);
$moreClients = array(
Client::create(array('name' => 'Boloni Ltd.'))->_id,
Client::create(array('name' => 'Meatballs Inc.'))->_id
);
// Sync multiple records
$user->clients()->sync($clients);
$user = User::with('clients')->find($user->_id);
// Assert non attached ID's are detached succesfully
$this->assertFalse(in_array('1234523', $user->client_ids));
// Assert there are two client objects in the relationship
$this->assertCount(2, $user->clients);
$user->clients()->sync($moreClients);
$user = User::with('clients')->find($user->_id);
// Assert there are now 4 client objects in the relationship
$this->assertCount(4, $user->clients);
} }
public function testHasManyAndBelongsTo()
{
$user = User::create(array('name' => 'John Doe'));
$user->clients()->save(new Client(array('name' => 'Pork Pies Ltd.')));
$user->clients()->create(array('name' => 'Buffet Bar Inc.'));
$user = User::with('clients')->find($user->_id);
$client = Client::with('users')->first();
$clients = $client->getRelation('users');
$users = $user->getRelation('clients');
$this->assertInstanceOf('Illuminate\Database\Eloquent\Collection', $users);
$this->assertInstanceOf('Illuminate\Database\Eloquent\Collection', $clients);
$this->assertInstanceOf('Client', $users[0]);
$this->assertInstanceOf('User', $clients[0]);
$this->assertCount(2, $user->clients);
$this->assertCount(1, $client->users);
// Now create a new user to an existing client
$client->users()->create(array('name' => 'Jane Doe'));
$otherClient = User::where('name', '=', 'Jane Doe')->first()->clients()->get();
$this->assertInstanceOf('Illuminate\Database\Eloquent\Collection', $otherClient);
$this->assertInstanceOf('Client', $otherClient[0]);
$this->assertCount(1, $otherClient);
// Now attach an existing client to an existing user
$user = User::where('name', '=', 'Jane Doe')->first();
$client = Client::Where('name', '=', 'Buffet Bar Inc.')->first();
// Check the models are what they should be
$this->assertInstanceOf('Client', $client);
$this->assertInstanceOf('User', $user);
// Assert they are not attached
$this->assertFalse(in_array($client->_id, $user->client_ids));
$this->assertFalse(in_array($user->_id, $client->user_ids));
// Attach the client to the user
$user->clients()->attach($client);
// Get the new user model
$user = User::where('name', '=', 'Jane Doe')->first();
$client = Client::Where('name', '=', 'Buffet Bar Inc.')->first();
// Assert they are attached
$this->assertTrue(in_array($client->_id, $user->client_ids));
$this->assertTrue(in_array($user->_id, $client->user_ids));
}
public function testHasManyAndBelongsToAttachesExistingModels()
{
$user = User::create(array('name' => 'John Doe', 'client_ids' => array('1234523')));
$clients = array(
Client::create(array('name' => 'Pork Pies Ltd.'))->_id,
Client::create(array('name' => 'Buffet Bar Inc.'))->_id
);
$moreClients = array(
Client::create(array('name' => 'Boloni Ltd.'))->_id,
Client::create(array('name' => 'Meatballs Inc.'))->_id
);
// Sync multiple records
$user->clients()->sync($clients);
$user = User::with('clients')->find($user->_id);
// Assert non attached ID's are detached succesfully
$this->assertFalse(in_array('1234523', $user->client_ids));
// Assert there are two client objects in the relationship
$this->assertCount(2, $user->clients);
$user->clients()->sync($moreClients);
$user = User::with('clients')->find($user->_id);
// Assert there are now 4 client objects in the relationship
$this->assertCount(4, $user->clients);
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment