diff --git a/README.md b/README.md index d90fdb00a..9aa83a38e 100644 --- a/README.md +++ b/README.md @@ -73,31 +73,32 @@ Change your default database connection name in `app/config/database.php`: And add a new mongodb connection: ```php -'mongodb' => array( +'mongodb' => [ 'driver' => 'mongodb', 'host' => env('DB_HOST', 'localhost'), 'port' => env('DB_PORT', 27017), 'database' => env('DB_DATABASE', ''), 'username' => env('DB_USERNAME', ''), 'password' => env('DB_PASSWORD', ''), - 'options' => array( - 'db' => 'admin' // sets the authentication database required by mongo 3 - ) -), + 'options' => [ + 'db' => 'admin', // sets the authentication database required by mongo 3 + ] + 'mongoid' => true, // sets to use mongoID object instead of mongoID as string in relations. +], ``` You can connect to multiple servers or replica sets with the following configuration: ```php -'mongodb' => array( +'mongodb' => [ 'driver' => 'mongodb', - 'host' => array('server1', 'server2'), + 'host' => ['server1', 'server2'], 'port' => env('DB_PORT', 27017), 'database' => env('DB_DATABASE', ''), 'username' => env('DB_USERNAME', ''), 'password' => env('DB_PASSWORD', ''), - 'options' => array('replicaSet' => 'replicaSetName') -), + 'options' => ['replicaSet' => 'replicaSetName'] +], ``` Eloquent diff --git a/phpunit.xml b/phpunit.xml index 3f99f229f..aa7d616d7 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -31,6 +31,7 @@ tests/ModelTest.php tests/RelationsTest.php + tests/ObjectIdRelationsTest.php tests/RelationsTest.php diff --git a/src/Jenssegers/Mongodb/Eloquent/Builder.php b/src/Jenssegers/Mongodb/Eloquent/Builder.php index 3b2424709..88fed3114 100644 --- a/src/Jenssegers/Mongodb/Eloquent/Builder.php +++ b/src/Jenssegers/Mongodb/Eloquent/Builder.php @@ -171,7 +171,16 @@ protected function addHasWhere(EloquentBuilder $hasQuery, Relation $relation, $o $query = $hasQuery->getQuery(); // Get the number of related objects for each possible parent. - $relationCount = array_count_values($query->lists($relation->getHasCompareKey())); + $lists = array_map(function ($value) { + if($value instanceof \MongoId){ + + return (string) $value; + } + + return $value; + }, $query->lists($relation->getHasCompareKey())); + + $relationCount = array_count_values($lists); // Remove unwanted related objects based on the operator and count. $relationCount = array_filter($relationCount, function ($counted) use ($count, $operator) diff --git a/src/Jenssegers/Mongodb/Model.php b/src/Jenssegers/Mongodb/Model.php index 449d77529..ffa47e0e1 100644 --- a/src/Jenssegers/Mongodb/Model.php +++ b/src/Jenssegers/Mongodb/Model.php @@ -55,7 +55,8 @@ public function getIdAttribute($value) } // Convert MongoId's to string. - if ($value instanceof MongoId) + $isObject = config('database.connections.mongodb.mongoid', false); + if ($value instanceof MongoId && !$isObject) { return (string) $value; } diff --git a/src/Jenssegers/Mongodb/Relations/BelongsTo.php b/src/Jenssegers/Mongodb/Relations/BelongsTo.php index 12ab4581e..ae0c4de55 100644 --- a/src/Jenssegers/Mongodb/Relations/BelongsTo.php +++ b/src/Jenssegers/Mongodb/Relations/BelongsTo.php @@ -1,5 +1,7 @@ query->whereIn($key, $this->getEagerModelKeys($models)); } + /** + * Match the eagerly loaded results to their parents. + * + * @param array $models + * @param \Illuminate\Database\Eloquent\Collection $results + * @param string $relation + * @return array + */ + public function match(array $models, Collection $results, $relation) + { + $foreign = $this->foreignKey; + + $other = $this->otherKey; + + // First we will get to build a dictionary of the child models by their primary + // key of the relationship, then we can easily match the children back onto + // the parents using that dictionary and the primary key of the children. + $dictionary = []; + + foreach ($results as $result) { + $dictionary[(string) $result->getAttribute($other)] = $result; + } + + // Once we have the dictionary constructed, we can loop through all the parents + // and match back onto their children using these keys of the dictionary and + // the primary key of the children to map them onto the correct instances. + foreach ($models as $model) { + if(\MongoId::isValid((string) $model->$foreign)){ + $modelForeign = (string) $model->$foreign; + }else{ + $modelForeign = $model->$foreign; + } + if (isset($dictionary[$modelForeign])) { + $model->setRelation($relation, $dictionary[$modelForeign]); + } + } + + return $models; + } + } diff --git a/src/Jenssegers/Mongodb/Relations/BelongsToMany.php b/src/Jenssegers/Mongodb/Relations/BelongsToMany.php index c53d73ea6..42ff04944 100644 --- a/src/Jenssegers/Mongodb/Relations/BelongsToMany.php +++ b/src/Jenssegers/Mongodb/Relations/BelongsToMany.php @@ -126,7 +126,16 @@ public function sync($ids, $detaching = true) { $this->detach($detach); - $changes['detached'] = (array) array_map(function ($v) { return (int) $v; }, $detach); + $changes['detached'] = (array) array_map(function ($v) { + + $isObject = config('database.connections.mongodb.mongoid', false); + if($isObject && $v instanceof \MongoId){ + + return (string) $v; + } + + return (int) $v; + }, $detach); } // Now we are finally ready to attach the new records. Note that we'll disable @@ -169,7 +178,7 @@ public function attach($id, array $attributes = [], $touch = true) { $model = $id; - $id = $model->getKey(); + $id = [$model->getKey()]; // Attach the new parent id to the related model. $model->push($this->foreignKey, $this->parent->getKey(), true); @@ -244,7 +253,7 @@ protected function buildDictionary(Collection $results) { foreach ($result->$foreign as $item) { - $dictionary[$item][] = $result; + $dictionary[(string) $item][] = $result; } } @@ -280,4 +289,51 @@ public function getForeignKey() { return $this->foreignKey; } + + /** + * Match the eagerly loaded results to their parents. + * + * @param array $models + * @param \Illuminate\Database\Eloquent\Collection $results + * @param string $relation + * @return array + */ + public function match(array $models, Collection $results, $relation) + { + $dictionary = $this->buildDictionary($results); + + // Once we have an array dictionary of child objects we can easily match the + // children back to their parent using the dictionary and the keys on the + // the parent models. Then we will return the hydrated models back out. + foreach ($models as $model) { + if (isset($dictionary[$key = (string) $model->getKey()])) { + $collection = $this->related->newCollection($dictionary[$key]); + + $model->setRelation($relation, $collection); + } + } + + return $models; + } + + /** + * Format the sync list so that it is keyed by ID. + * + * @param array $records + * @return array + */ + protected function formatSyncList(array $records) + { + $results = []; + + foreach ($records as $id => $attributes) { + if (! is_array($attributes)) { + list($id, $attributes) = [$attributes, []]; + } + + $results[(string) $id] = $attributes; + } + + return $results; + } } diff --git a/src/Jenssegers/Mongodb/Relations/EmbedsOneOrMany.php b/src/Jenssegers/Mongodb/Relations/EmbedsOneOrMany.php index db6066082..16af47fa5 100644 --- a/src/Jenssegers/Mongodb/Relations/EmbedsOneOrMany.php +++ b/src/Jenssegers/Mongodb/Relations/EmbedsOneOrMany.php @@ -7,6 +7,7 @@ use Jenssegers\Mongodb\Eloquent\Collection; abstract class EmbedsOneOrMany extends Relation { + use RelationKeysTrait; /** * The local key of the parent model. @@ -401,9 +402,11 @@ public function getQualifiedParentKeyName() * * @return string */ - protected function getParentKey() + public function getParentKey() { - return $this->parent->getKey(); + $value = $this->parent->getKey(); + + return $this->evaluateObjectID($value); } } diff --git a/src/Jenssegers/Mongodb/Relations/HasMany.php b/src/Jenssegers/Mongodb/Relations/HasMany.php index c59065639..19a86d245 100644 --- a/src/Jenssegers/Mongodb/Relations/HasMany.php +++ b/src/Jenssegers/Mongodb/Relations/HasMany.php @@ -4,6 +4,8 @@ use Illuminate\Database\Eloquent\Relations\HasMany as EloquentHasMany; class HasMany extends EloquentHasMany { + use RelationKeysTrait; + use HasOneOrManyTrait; /** * Add the constraints for a relationship count query. diff --git a/src/Jenssegers/Mongodb/Relations/HasOne.php b/src/Jenssegers/Mongodb/Relations/HasOne.php index fb62a8386..51662ce3b 100644 --- a/src/Jenssegers/Mongodb/Relations/HasOne.php +++ b/src/Jenssegers/Mongodb/Relations/HasOne.php @@ -4,6 +4,8 @@ use Illuminate\Database\Eloquent\Relations\HasOne as EloquentHasOne; class HasOne extends EloquentHasOne { + use RelationKeysTrait; + use HasOneOrManyTrait; /** * Add the constraints for a relationship count query. diff --git a/src/Jenssegers/Mongodb/Relations/HasOneOrManyTrait.php b/src/Jenssegers/Mongodb/Relations/HasOneOrManyTrait.php new file mode 100644 index 000000000..3a6e5f532 --- /dev/null +++ b/src/Jenssegers/Mongodb/Relations/HasOneOrManyTrait.php @@ -0,0 +1,57 @@ +getPlainForeignKey(); + + foreach ($results as $result) { + $dictionary[(string) $result->{$foreign}][] = $result; + } + + return $dictionary; + } + + /** + * Match the eagerly loaded results to their many parents. + * + * @param array $models + * @param \Illuminate\Database\Eloquent\Collection $results + * @param string $relation + * @param string $type + * @return array + */ + protected function matchOneOrMany(array $models, Collection $results, $relation, $type) + { + $dictionary = $this->buildDictionary($results); + + // Once we have the dictionary we can simply spin through the parent models to + // link them up with their children using the keyed dictionary to make the + // matching very convenient and easy work. Then we'll just return them. + foreach ($models as $model) { + $key = (string) $model->getAttribute($this->localKey); + + if (isset($dictionary[$key])) { + $value = $this->getRelationValue($dictionary, $key, $type); + + $model->setRelation($relation, $value); + } + } + + return $models; + } + +} diff --git a/src/Jenssegers/Mongodb/Relations/RelationKeysTrait.php b/src/Jenssegers/Mongodb/Relations/RelationKeysTrait.php new file mode 100644 index 000000000..7a431f6ef --- /dev/null +++ b/src/Jenssegers/Mongodb/Relations/RelationKeysTrait.php @@ -0,0 +1,40 @@ +parent->getAttribute($this->localKey); + + return $this->evaluateObjectID($value); + } + + /** + * Get the key value of the parent's local key. + * + * @param $value + * @return mixed + */ + public function evaluateObjectID($value) + { + $isObject = config('database.connections.mongodb.mongoid', false); + if ($isObject && \MongoId::isValid($value)) { + return new \MongoId($value); + } + + return $value; + } +} diff --git a/tests/ObjectIdRelationsTest.php b/tests/ObjectIdRelationsTest.php new file mode 100644 index 000000000..49d3c80cc --- /dev/null +++ b/tests/ObjectIdRelationsTest.php @@ -0,0 +1,531 @@ + 'George R. R. Martin']); + Book::create(['title' => 'A Game of Thrones', 'author_id' => $author->_id]); + Book::create(['title' => 'A Clash of Kings', 'author_id' => $author->_id]); + + $books = $author->books; + $this->assertEquals(2, count($books)); + + $user = User::create(['name' => 'John Doe']); + Item::create(['type' => 'knife', 'user_id' => $user->_id]); + Item::create(['type' => 'shield', 'user_id' => $user->_id]); + Item::create(['type' => 'sword', 'user_id' => $user->_id]); + Item::create(['type' => 'bag', 'user_id' => null]); + + $items = $user->items; + $this->assertEquals(3, count($items)); + } + + public function testBelongsTo() + { + $user = User::create(['name' => 'George R. R. Martin']); + Book::create(['title' => 'A Game of Thrones', 'author_id' => $user->_id]); + $book = Book::create(['title' => 'A Clash of Kings', 'author_id' => $user->_id]); + + $author = $book->author; + $this->assertEquals('George R. R. Martin', $author->name); + + $user = User::create(['name' => 'John Doe']); + $item = Item::create(['type' => 'sword', 'user_id' => $user->_id]); + + $owner = $item->user; + $this->assertEquals('John Doe', $owner->name); + + $book = Book::create(['title' => 'A Clash of Kings']); + $this->assertEquals(null, $book->author); + } + + public function testHasOne() + { + $user = User::create(['name' => 'John Doe']); + Role::create(['type' => 'admin', 'user_id' => $user->_id]); + + $role = $user->role; + $this->assertEquals('admin', $role->type); + $this->assertEquals($user->_id, $role->user_id); + + $user = User::create(['name' => 'Jane Doe']); + $role = new Role(['type' => 'user']); + $user->role()->save($role); + + $role = $user->role; + $this->assertEquals('user', $role->type); + $this->assertEquals($user->_id, $role->user_id); + + $user = User::where('name', 'Jane Doe')->first(); + $role = $user->role; + $this->assertEquals('user', $role->type); + $this->assertEquals($user->_id, $role->user_id); + } + + public function testWithBelongsTo() + { + $user = User::create(['name' => 'John Doe']); + Item::create(['type' => 'knife', 'user_id' => $user->_id]); + Item::create(['type' => 'shield', 'user_id' => $user->_id]); + Item::create(['type' => 'sword', 'user_id' => $user->_id]); + Item::create(['type' => 'bag', 'user_id' => null]); + + $items = Item::with('user')->orderBy('user_id', 'desc')->get(); + + $user = $items[0]->getRelation('user'); + $this->assertInstanceOf('User', $user); + $this->assertEquals('John Doe', $user->name); + $this->assertEquals(1, count($items[0]->getRelations())); + $this->assertEquals(null, $items[3]->getRelation('user')); + } + + public function testWithHashMany() + { + $user = User::create(['name' => 'John Doe']); + Item::create(['type' => 'knife', 'user_id' => $user->_id]); + Item::create(['type' => 'shield', 'user_id' => $user->_id]); + Item::create(['type' => 'sword', 'user_id' => $user->_id]); + Item::create(['type' => 'bag', 'user_id' => null]); + + $user = User::with('items')->find($user->_id); + + $items = $user->getRelation('items'); + $this->assertEquals(3, count($items)); + $this->assertInstanceOf('Item', $items[0]); + } + + public function testWithHasOne() + { + $user = User::create(['name' => 'John Doe']); + Role::create(['type' => 'admin', 'user_id' => $user->_id]); + Role::create(['type' => 'guest', 'user_id' => $user->_id]); + + $user = User::with('role')->find($user->_id); + + $role = $user->getRelation('role'); + $this->assertInstanceOf('Role', $role); + $this->assertEquals('admin', $role->type); + } + + public function testEasyRelation() + { + // Has Many + $user = User::create(['name' => 'John Doe']); + $item = Item::create(['type' => 'knife']); + $user->items()->save($item); + + $user = User::find($user->_id); + $items = $user->items; + $this->assertEquals(1, count($items)); + $this->assertInstanceOf('Item', $items[0]); + $this->assertEquals($user->_id, $items[0]->user_id); + + // Has one + $user = User::create(['name' => 'John Doe']); + $role = Role::create(['type' => 'admin']); + $user->role()->save($role); + + $user = User::find($user->_id); + $role = $user->role; + $this->assertInstanceOf('Role', $role); + $this->assertEquals('admin', $role->type); + $this->assertEquals($user->_id, $role->user_id); + } + + public function testBelongsToMany() + { + $user = User::create(['name' => 'John Doe']); + + // Add 2 clients + $user->clients()->save(new Client(['name' => 'Pork Pies Ltd.'])); + $user->clients()->create(['name' => 'Buffet Bar Inc.']); + + // Refetch + $user = User::with('clients')->find($user->_id); + $client = Client::with('users')->first(); + + // Check for relation attributes + $this->assertTrue(array_key_exists('user_ids', $client->getAttributes())); + $this->assertTrue(array_key_exists('client_ids', $user->getAttributes())); + + $clients = $user->getRelation('clients'); + $users = $client->getRelation('users'); + + $this->assertInstanceOf('Illuminate\Database\Eloquent\Collection', $users); + $this->assertInstanceOf('Illuminate\Database\Eloquent\Collection', $clients); + $this->assertInstanceOf('Client', $clients[0]); + $this->assertInstanceOf('User', $users[0]); + $this->assertCount(2, $user->clients); + $this->assertCount(1, $client->users); + + // Now create a new user to an existing client + $user = $client->users()->create(['name' => 'Jane Doe']); + + $this->assertInstanceOf('Illuminate\Database\Eloquent\Collection', $user->clients); + $this->assertInstanceOf('Client', $user->clients->first()); + $this->assertCount(1, $user->clients); + + // Get user and unattached client + $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)); + $this->assertCount(1, $user->clients); + $this->assertCount(1, $client->users); + + // 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)); + $this->assertCount(2, $user->clients); + $this->assertCount(2, $client->users); + + // Detach clients from user + $user->clients()->sync([]); + + // Get the new user model + $user = User::where('name', '=', 'Jane Doe')->first(); + $client = Client::Where('name', '=', 'Buffet Bar Inc.')->first(); + + // Assert they are not attached + $this->assertFalse(in_array($client->_id, $user->client_ids)); + $this->assertFalse(in_array($user->_id, $client->user_ids)); + $this->assertCount(0, $user->clients); + $this->assertCount(1, $client->users); + } + + public function testBelongsToManyAttachesExistingModels() + { + $user = User::create(['name' => 'John Doe', 'client_ids' => ['1234523']]); + + $clients = [ + Client::create(['name' => 'Pork Pies Ltd.'])->_id, + Client::create(['name' => 'Buffet Bar Inc.'])->_id, + ]; + + $moreClients = [ + Client::create(['name' => 'synced Boloni Ltd.'])->_id, + Client::create(['name' => 'synced 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); + + // Add more clients + $user->clients()->sync($moreClients); + + // Refetch + $user = User::with('clients')->find($user->_id); + + // Assert there are now still 2 client objects in the relationship + $this->assertCount(2, $user->clients); + + // Assert that the new relationships name start with synced + $this->assertStringStartsWith('synced', $user->clients[0]->name); + $this->assertStringStartsWith('synced', $user->clients[1]->name); + } + + public function testBelongsToManySync() + { + // create test instances + $user = User::create(['name' => 'John Doe']); + $client1 = Client::create(['name' => 'Pork Pies Ltd.'])->_id; + $client2 = Client::create(['name' => 'Buffet Bar Inc.'])->_id; + + // Sync multiple + $user->clients()->sync([$client1, $client2]); + $this->assertCount(2, $user->clients); + + // Refresh user + $user = User::where('name', '=', 'John Doe')->first(); + + // Sync single + $user->clients()->sync([$client1]); + $this->assertCount(1, $user->clients); + } + + public function testBelongsToManyAttachArray() + { + $user = User::create(['name' => 'John Doe']); + $client1 = Client::create(['name' => 'Test 1'])->_id; + $client2 = Client::create(['name' => 'Test 2'])->_id; + + $user = User::where('name', '=', 'John Doe')->first(); + $user->clients()->attach([$client1, $client2]); + $this->assertCount(2, $user->clients); + } + + public function testBelongsToManySyncAlreadyPresent() + { + $user = User::create(['name' => 'John Doe']); + $client1 = Client::create(['name' => 'Test 1'])->_id; + $client2 = Client::create(['name' => 'Test 2'])->_id; + + $user->clients()->sync([$client1, $client2]); + $this->assertCount(2, $user->clients); + + $user = User::where('name', '=', 'John Doe')->first(); + $user->clients()->sync([$client1]); + $this->assertCount(1, $user->clients); + + $user = User::where('name', '=', 'John Doe')->first()->toArray(); + $this->assertCount(1, $user['client_ids']); + } + + public function testBelongsToManyCustom() + { + $user = User::create(['name' => 'John Doe']); + $group = $user->groups()->create(['name' => 'Admins']); + + // Refetch + $user = User::find($user->_id); + $group = Group::find($group->_id); + + // Check for custom relation attributes + $this->assertTrue(array_key_exists('users', $group->getAttributes())); + $this->assertTrue(array_key_exists('groups', $user->getAttributes())); + + // Assert they are attached + $this->assertTrue(in_array($group->_id, $user->groups)); + $this->assertTrue(in_array($user->_id, $group->users)); + $this->assertEquals($group->_id, $user->groups()->first()->_id); + $this->assertEquals($user->_id, $group->users()->first()->_id); + } + + public function testMorph() + { + $user = User::create(['name' => 'John Doe']); + $client = Client::create(['name' => 'Jane Doe']); + + $photo = Photo::create(['url' => 'http://graph.facebook.com/john.doe/picture']); + $photo = $user->photos()->save($photo); + + $this->assertEquals(1, $user->photos->count()); + $this->assertEquals($photo->id, $user->photos->first()->id); + + $user = User::find($user->_id); + $this->assertEquals(1, $user->photos->count()); + $this->assertEquals($photo->id, $user->photos->first()->id); + + $photo = Photo::create(['url' => 'http://graph.facebook.com/jane.doe/picture']); + $client->photo()->save($photo); + + $this->assertNotNull($client->photo); + $this->assertEquals($photo->id, $client->photo->id); + + $client = Client::find($client->_id); + $this->assertNotNull($client->photo); + $this->assertEquals($photo->id, $client->photo->id); + + $photo = Photo::first(); + $this->assertEquals($photo->imageable->name, $user->name); + + //todo need to fix the morph relations +// $user = User::with('photos')->find($user->_id); +// $relations = $user->getRelations(); +// $this->assertTrue(array_key_exists('photos', $relations)); +// $this->assertEquals(1, $relations['photos']->count()); + +// $photos = Photo::with('imageable')->get(); +// $relations = $photos[0]->getRelations(); +// $this->assertTrue(array_key_exists('imageable', $relations)); +// $this->assertInstanceOf('User', $relations['imageable']); +// +// $relations = $photos[1]->getRelations(); +// $this->assertTrue(array_key_exists('imageable', $relations)); +// $this->assertInstanceOf('Client', $relations['imageable']); + } + + public function testHasManyHas() + { + $author1 = User::create(['name' => 'George R. R. Martin']); + $author1->books()->create(['title' => 'A Game of Thrones', 'rating' => 5]); + $author1->books()->create(['title' => 'A Clash of Kings', 'rating' => 5]); + $author2 = User::create(['name' => 'John Doe']); + $author2->books()->create(['title' => 'My book', 'rating' => 2]); + User::create(['name' => 'Anonymous author']); + Book::create(['title' => 'Anonymous book', 'rating' => 1]); + + $authors = User::has('books')->get(); + $this->assertCount(2, $authors); + $this->assertEquals('George R. R. Martin', $authors[0]->name); + $this->assertEquals('John Doe', $authors[1]->name); + + $authors = User::has('books', '>', 1)->get(); + $this->assertCount(1, $authors); + + $authors = User::has('books', '<', 5)->get(); + $this->assertCount(3, $authors); + + $authors = User::has('books', '>=', 2)->get(); + $this->assertCount(1, $authors); + + $authors = User::has('books', '<=', 1)->get(); + $this->assertCount(2, $authors); + + $authors = User::has('books', '=', 2)->get(); + $this->assertCount(1, $authors); + + $authors = User::has('books', '!=', 2)->get(); + $this->assertCount(2, $authors); + + $authors = User::has('books', '=', 0)->get(); + $this->assertCount(1, $authors); + + $authors = User::has('books', '!=', 0)->get(); + $this->assertCount(2, $authors); + + $authors = User::whereHas('books', function ($query) + { + $query->where('rating', 5); + + })->get(); + $this->assertCount(1, $authors); + + $authors = User::whereHas('books', function ($query) + { + $query->where('rating', '<', 5); + + })->get(); + $this->assertCount(1, $authors); + } + + public function testHasOneHas() + { + $user1 = User::create(['name' => 'John Doe']); + $user1->role()->create(['title' => 'admin']); + $user2 = User::create(['name' => 'Jane Doe']); + $user2->role()->create(['title' => 'reseller']); + User::create(['name' => 'Mark Moe']); + Role::create(['title' => 'Customer']); + + $users = User::has('role')->get(); + $this->assertCount(2, $users); + $this->assertEquals('John Doe', $users[0]->name); + $this->assertEquals('Jane Doe', $users[1]->name); + + $users = User::has('role', '=', 0)->get(); + $this->assertCount(1, $users); + + $users = User::has('role', '!=', 0)->get(); + $this->assertCount(2, $users); + } + + public function testNestedKeys() + { + $client = Client::create([ + 'data' => [ + 'client_id' => 35298, + 'name' => 'John Doe', + ], + ]); + + $address = $client->addresses()->create([ + 'data' => [ + 'address_id' => 1432, + 'city' => 'Paris', + ], + ]); + + $client = Client::where('data.client_id', 35298)->first(); + $this->assertEquals(1, $client->addresses->count()); + + $address = $client->addresses->first(); + $this->assertEquals('Paris', $address->data['city']); + + $client = Client::with('addresses')->first(); + $this->assertEquals('Paris', $client->addresses->first()->data['city']); + } + + public function testDoubleSaveOneToMany() + { + $author = User::create(['name' => 'George R. R. Martin']); + $book = Book::create(['title' => 'A Game of Thrones']); + + $author->books()->save($book); + $author->books()->save($book); + $author->save(); + $this->assertEquals(1, $author->books()->count()); + $this->assertEquals($author->_id, $book->author_id); + + $author = User::where('name', 'George R. R. Martin')->first(); + $book = Book::where('title', 'A Game of Thrones')->first(); + $this->assertEquals(1, $author->books()->count()); + $this->assertEquals($author->_id, $book->author_id); + + $author->books()->save($book); + $author->books()->save($book); + $author->save(); + $this->assertEquals(1, $author->books()->count()); + $this->assertEquals($author->_id, $book->author_id); + } + + public function testDoubleSaveManyToMany() + { + $user = User::create(['name' => 'John Doe']); + $client = Client::create(['name' => 'Admins']); + + $user->clients()->save($client); + $user->clients()->save($client); + $user->save(); + + $this->assertEquals(1, $user->clients()->count()); + $this->assertEquals([$user->_id], $client->user_ids); + $this->assertEquals([$client->_id], $user->client_ids); + + $user = User::where('name', 'John Doe')->first(); + $client = Client::where('name', 'Admins')->first(); + $this->assertEquals(1, $user->clients()->count()); + $this->assertEquals([$user->_id], $client->user_ids); + $this->assertEquals([$client->_id], $user->client_ids); + + $user->clients()->save($client); + $user->clients()->save($client); + $user->save(); + $this->assertEquals(1, $user->clients()->count()); + $this->assertEquals([$user->_id], $client->user_ids); + $this->assertEquals([$client->_id], $user->client_ids); + } + +}