From a453f8a210462e481bc23168dfeac21eee19261f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Tue, 20 Aug 2024 18:13:52 +0200 Subject: [PATCH 01/27] PHPORM-147 Make `id` an alias for `_id` (#3040) * PHPORM-147 Make id an alias for _id * Use id as primary key for DocumentModel * Recursively replace .id with ._id --- CHANGELOG.md | 4 + docs/includes/auth/PersonalAccessToken.php | 1 - .../eloquent-models/PlanetThirdParty.php | 1 - .../write-operations/WriteOperationsTest.php | 18 +- .../includes/usage-examples/DeleteOneTest.php | 2 +- docs/includes/usage-examples/FindManyTest.php | 2 +- docs/includes/usage-examples/FindOneTest.php | 4 +- .../includes/usage-examples/InsertOneTest.php | 2 +- .../includes/usage-examples/UpdateOneTest.php | 2 +- src/Auth/User.php | 1 - src/Eloquent/DocumentModel.php | 17 +- src/Eloquent/Model.php | 7 - src/Query/Builder.php | 134 ++++-- src/Relations/EmbedsMany.php | 10 +- src/Relations/EmbedsOne.php | 6 +- tests/AuthTest.php | 2 +- tests/EmbeddedRelationsTest.php | 48 +-- tests/HybridRelationsTest.php | 24 +- tests/ModelTest.php | 111 ++--- tests/Models/Address.php | 1 - tests/Models/Birthday.php | 1 - tests/Models/Client.php | 1 - tests/Models/Experience.php | 1 - tests/Models/Group.php | 3 +- tests/Models/Guarded.php | 1 - tests/Models/HiddenAnimal.php | 1 - tests/Models/IdIsBinaryUuid.php | 3 +- tests/Models/IdIsInt.php | 3 +- tests/Models/IdIsString.php | 3 +- tests/Models/Item.php | 1 - tests/Models/Label.php | 1 - tests/Models/Location.php | 1 - tests/Models/Photo.php | 1 - tests/Models/Role.php | 1 - tests/Models/Scoped.php | 1 - tests/Models/Skill.php | 1 - tests/Models/Soft.php | 1 - tests/Models/User.php | 5 +- tests/Query/BuilderTest.php | 117 +++--- tests/QueryBuilderTest.php | 67 +-- tests/QueryTest.php | 2 +- tests/QueueTest.php | 4 +- tests/RelationsTest.php | 386 +++++++++--------- tests/SessionTest.php | 33 ++ tests/Ticket/GH2489Test.php | 49 +++ tests/Ticket/GH2783Test.php | 2 +- tests/TransactionTest.php | 24 +- 47 files changed, 635 insertions(+), 476 deletions(-) create mode 100644 tests/SessionTest.php create mode 100644 tests/Ticket/GH2489Test.php diff --git a/CHANGELOG.md b/CHANGELOG.md index a6e7c9144..f0b8a5e27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ # Changelog All notable changes to this project will be documented in this file. +## [5.0.0] - next + +* **BREAKING CHANGE** Use `id` as an alias for `_id` in commands and queries for compatibility with Eloquent packages by @GromNaN in [#3040](https://github.com/mongodb/laravel-mongodb/pull/3040) + ## [4.8.0] - next * Add `Query\Builder::incrementEach()` and `decrementEach()` methods by @SmallRuralDog in [#2550](https://github.com/mongodb/laravel-mongodb/pull/2550) diff --git a/docs/includes/auth/PersonalAccessToken.php b/docs/includes/auth/PersonalAccessToken.php index 165758770..2b08d685f 100644 --- a/docs/includes/auth/PersonalAccessToken.php +++ b/docs/includes/auth/PersonalAccessToken.php @@ -11,6 +11,5 @@ class PersonalAccessToken extends SanctumToken protected $connection = 'mongodb'; protected $table = 'personal_access_tokens'; - protected $primaryKey = '_id'; protected $keyType = 'string'; } diff --git a/docs/includes/eloquent-models/PlanetThirdParty.php b/docs/includes/eloquent-models/PlanetThirdParty.php index 0f3bae638..79d120bba 100644 --- a/docs/includes/eloquent-models/PlanetThirdParty.php +++ b/docs/includes/eloquent-models/PlanetThirdParty.php @@ -10,6 +10,5 @@ class Planet extends CelestialBody use DocumentModel; protected $fillable = ['name', 'diameter']; - protected $primaryKey = '_id'; protected $keyType = 'string'; } diff --git a/docs/includes/fundamentals/write-operations/WriteOperationsTest.php b/docs/includes/fundamentals/write-operations/WriteOperationsTest.php index 39143ac09..b6d54fec4 100644 --- a/docs/includes/fundamentals/write-operations/WriteOperationsTest.php +++ b/docs/includes/fundamentals/write-operations/WriteOperationsTest.php @@ -162,7 +162,7 @@ public function testModelUpdateFluent(): void // begin model update one fluent $concert = Concert::where(['performer' => 'Brad Mehldau']) - ->orderBy('_id') + ->orderBy('id') ->first() ->update(['venue' => 'Manchester Arena', 'ticketsSold' => 9543]); // end model update one fluent @@ -370,7 +370,7 @@ public function testModelDeleteById(): void $data = [ [ - '_id' => 'CH-0401242000', + 'id' => 'CH-0401242000', 'performer' => 'Mitsuko Uchida', 'venue' => 'Carnegie Hall', 'genres' => ['classical'], @@ -378,7 +378,7 @@ public function testModelDeleteById(): void 'performanceDate' => new UTCDateTime(Carbon::create(2024, 4, 1, 20, 0, 0, 'EST')), ], [ - '_id' => 'MSG-0212252000', + 'id' => 'MSG-0212252000', 'performer' => 'Brad Mehldau', 'venue' => 'Philharmonie de Paris', 'genres' => [ 'jazz', 'post-bop' ], @@ -386,7 +386,7 @@ public function testModelDeleteById(): void 'performanceDate' => new UTCDateTime(Carbon::create(2025, 2, 12, 20, 0, 0, 'CET')), ], [ - '_id' => 'MSG-021222000', + 'id' => 'MSG-021222000', 'performer' => 'Billy Joel', 'venue' => 'Madison Square Garden', 'genres' => [ 'rock', 'soft rock', 'pop rock' ], @@ -394,7 +394,7 @@ public function testModelDeleteById(): void 'performanceDate' => new UTCDateTime(Carbon::create(2025, 2, 12, 20, 0, 0, 'CET')), ], [ - '_id' => 'SF-06302000', + 'id' => 'SF-06302000', 'performer' => 'The Rolling Stones', 'venue' => 'Soldier Field', 'genres' => [ 'rock', 'pop', 'blues' ], @@ -478,22 +478,22 @@ public function testModelDeleteMultipleById(): void Concert::truncate(); $data = [ [ - '_id' => 3, + 'id' => 3, 'performer' => 'Mitsuko Uchida', 'venue' => 'Carnegie Hall', ], [ - '_id' => 5, + 'id' => 5, 'performer' => 'Brad Mehldau', 'venue' => 'Philharmonie de Paris', ], [ - '_id' => 7, + 'id' => 7, 'performer' => 'Billy Joel', 'venue' => 'Madison Square Garden', ], [ - '_id' => 9, + 'id' => 9, 'performer' => 'The Rolling Stones', 'venue' => 'Soldier Field', ], diff --git a/docs/includes/usage-examples/DeleteOneTest.php b/docs/includes/usage-examples/DeleteOneTest.php index 1a2acd4e0..b867dfc1f 100644 --- a/docs/includes/usage-examples/DeleteOneTest.php +++ b/docs/includes/usage-examples/DeleteOneTest.php @@ -27,7 +27,7 @@ public function testDeleteOne(): void // begin-delete-one $deleted = Movie::where('title', 'Quiz Show') - ->orderBy('_id') + ->orderBy('id') ->limit(1) ->delete(); diff --git a/docs/includes/usage-examples/FindManyTest.php b/docs/includes/usage-examples/FindManyTest.php index 18324c62d..191ae9719 100644 --- a/docs/includes/usage-examples/FindManyTest.php +++ b/docs/includes/usage-examples/FindManyTest.php @@ -35,7 +35,7 @@ public function testFindMany(): void // begin-find $movies = Movie::where('runtime', '>', 900) - ->orderBy('_id') + ->orderBy('id') ->get(); // end-find diff --git a/docs/includes/usage-examples/FindOneTest.php b/docs/includes/usage-examples/FindOneTest.php index 98452a6a6..8472727be 100644 --- a/docs/includes/usage-examples/FindOneTest.php +++ b/docs/includes/usage-examples/FindOneTest.php @@ -24,13 +24,13 @@ public function testFindOne(): void // begin-find-one $movie = Movie::where('directors', 'Rob Reiner') - ->orderBy('_id') + ->orderBy('id') ->first(); echo $movie->toJson(); // end-find-one $this->assertInstanceOf(Movie::class, $movie); - $this->expectOutputRegex('/^{"_id":"[a-z0-9]{24}","title":"The Shawshank Redemption","directors":\["Frank Darabont","Rob Reiner"\]}$/'); + $this->expectOutputRegex('/^{"_id":"[a-z0-9]{24}","title":"The Shawshank Redemption","directors":\["Frank Darabont","Rob Reiner"\],"id":"[a-z0-9]{24}"}$/'); } } diff --git a/docs/includes/usage-examples/InsertOneTest.php b/docs/includes/usage-examples/InsertOneTest.php index 15eadf419..7e4de48d6 100644 --- a/docs/includes/usage-examples/InsertOneTest.php +++ b/docs/includes/usage-examples/InsertOneTest.php @@ -30,6 +30,6 @@ public function testInsertOne(): void // end-insert-one $this->assertInstanceOf(Movie::class, $movie); - $this->expectOutputRegex('/^{"title":"Marriage Story","year":2019,"runtime":136,"updated_at":".{27}","created_at":".{27}","_id":"[a-z0-9]{24}"}$/'); + $this->expectOutputRegex('/^{"title":"Marriage Story","year":2019,"runtime":136,"updated_at":".{27}","created_at":".{27}","id":"[a-z0-9]{24}"}$/'); } } diff --git a/docs/includes/usage-examples/UpdateOneTest.php b/docs/includes/usage-examples/UpdateOneTest.php index e1f864170..4bf720a75 100644 --- a/docs/includes/usage-examples/UpdateOneTest.php +++ b/docs/includes/usage-examples/UpdateOneTest.php @@ -30,7 +30,7 @@ public function testUpdateOne(): void // begin-update-one $updates = Movie::where('title', 'Carol') - ->orderBy('_id') + ->orderBy('id') ->first() ->update([ 'imdb' => [ diff --git a/src/Auth/User.php b/src/Auth/User.php index a58a898ad..e37fefd3c 100644 --- a/src/Auth/User.php +++ b/src/Auth/User.php @@ -11,6 +11,5 @@ class User extends BaseUser { use DocumentModel; - protected $primaryKey = '_id'; protected $keyType = 'string'; } diff --git a/src/Eloquent/DocumentModel.php b/src/Eloquent/DocumentModel.php index cbc388b22..af3aec3c2 100644 --- a/src/Eloquent/DocumentModel.php +++ b/src/Eloquent/DocumentModel.php @@ -46,6 +46,7 @@ use function str_contains; use function str_starts_with; use function strcmp; +use function strlen; use function trigger_error; use function var_export; @@ -79,9 +80,7 @@ public function getIdAttribute($value = null) { // If we don't have a value for 'id', we will use the MongoDB '_id' value. // This allows us to work with models in a more sql-like way. - if (! $value && array_key_exists('_id', $this->attributes)) { - $value = $this->attributes['_id']; - } + $value ??= $this->attributes['id'] ?? $this->attributes['_id'] ?? null; // Convert ObjectID to string. if ($value instanceof ObjectID) { @@ -248,10 +247,8 @@ public function setAttribute($key, $value) } // Convert _id to ObjectID. - if ($key === '_id' && is_string($value)) { - $builder = $this->newBaseQueryBuilder(); - - $value = $builder->convertKey($value); + if (($key === '_id' || $key === 'id') && is_string($value) && strlen($value) === 24) { + $value = $this->newBaseQueryBuilder()->convertKey($value); } // Support keys in dot notation. @@ -729,12 +726,16 @@ protected function isBSON(mixed $value): bool */ public function save(array $options = []) { - // SQL databases would use autoincrement the id field if set to null. + // SQL databases would autoincrement the id field if set to null. // Apply the same behavior to MongoDB with _id only, otherwise null would be stored. if (array_key_exists('_id', $this->attributes) && $this->attributes['_id'] === null) { unset($this->attributes['_id']); } + if (array_key_exists('id', $this->attributes) && $this->attributes['id'] === null) { + unset($this->attributes['id']); + } + $saved = parent::save($options); // Clear list of unset fields diff --git a/src/Eloquent/Model.php b/src/Eloquent/Model.php index fcb9c4f04..54eef1dc5 100644 --- a/src/Eloquent/Model.php +++ b/src/Eloquent/Model.php @@ -16,13 +16,6 @@ abstract class Model extends BaseModel { use DocumentModel; - /** - * The primary key for the model. - * - * @var string - */ - protected $primaryKey = '_id'; - /** * The primary key type. * diff --git a/src/Query/Builder.php b/src/Query/Builder.php index ddc2413d8..6168159df 100644 --- a/src/Query/Builder.php +++ b/src/Query/Builder.php @@ -298,6 +298,7 @@ public function toMql(): array } $wheres = $this->compileWheres(); + $wheres = $this->aliasIdForQuery($wheres); // Use MongoDB's aggregation framework when using grouping or aggregation functions. if ($this->groups || $this->aggregate) { @@ -375,7 +376,7 @@ public function toMql(): array // Apply order and limit if ($this->orders) { - $pipeline[] = ['$sort' => $this->orders]; + $pipeline[] = ['$sort' => $this->aliasIdForQuery($this->orders)]; } if ($this->offset) { @@ -416,7 +417,7 @@ public function toMql(): array // Normal query // Convert select columns to simple projections. - $projection = array_fill_keys($columns, true); + $projection = $this->aliasIdForQuery(array_fill_keys($columns, true)); // Add custom projections. if ($this->projections) { @@ -431,7 +432,7 @@ public function toMql(): array } if ($this->orders) { - $options['sort'] = $this->orders; + $options['sort'] = $this->aliasIdForQuery($this->orders); } if ($this->offset) { @@ -506,7 +507,7 @@ public function getFresh($columns = [], $returnLazy = false) if ($returnLazy) { return LazyCollection::make(function () use ($result) { foreach ($result as $item) { - yield $item; + yield $this->aliasIdForResult($item); } }); } @@ -515,6 +516,12 @@ public function getFresh($columns = [], $returnLazy = false) $result = $result->toArray(); } + foreach ($result as &$document) { + if (is_array($document)) { + $document = $this->aliasIdForResult($document); + } + } + return new Collection($result); } @@ -593,7 +600,7 @@ public function aggregate($function = null, $columns = ['*']) /** @inheritdoc */ public function exists() { - return $this->first(['_id']) !== null; + return $this->first(['id']) !== null; } /** @inheritdoc */ @@ -682,6 +689,18 @@ public function insert(array $values) $values = [$values]; } + // Compatibility with Eloquent queries that uses "id" instead of MongoDB's _id + foreach ($values as &$document) { + if (isset($document['id'])) { + if (isset($document['_id']) && $document['_id'] !== $document['id']) { + throw new InvalidArgumentException('Cannot insert document with different "id" and "_id" values'); + } + + $document['_id'] = $document['id']; + unset($document['id']); + } + } + $options = $this->inheritConnectionOptions(); $result = $this->collection->insertMany($values, $options); @@ -694,17 +713,18 @@ public function insertGetId(array $values, $sequence = null) { $options = $this->inheritConnectionOptions(); + $values = $this->aliasIdForQuery($values); + $result = $this->collection->insertOne($values, $options); if (! $result->isAcknowledged()) { return null; } - if ($sequence === null || $sequence === '_id') { - return $result->getInsertedId(); - } - - return $values[$sequence]; + return match ($sequence) { + '_id', 'id', null => $result->getInsertedId(), + default => $values[$sequence], + }; } /** @inheritdoc */ @@ -720,7 +740,12 @@ public function update(array $values, array $options = []) unset($values[$key]); } - $options = $this->inheritConnectionOptions($options); + // Since "id" is an alias for "_id", we prevent updating it + foreach ($values as $fields) { + if (array_key_exists('id', $fields)) { + throw new InvalidArgumentException('Cannot update "id" field.'); + } + } return $this->performUpdate($values, $options); } @@ -821,18 +846,6 @@ public function decrementEach(array $columns, array $extra = [], array $options return $this->incrementEach($decrement, $extra, $options); } - /** @inheritdoc */ - public function chunkById($count, callable $callback, $column = '_id', $alias = null) - { - return parent::chunkById($count, $callback, $column, $alias); - } - - /** @inheritdoc */ - public function forPageAfterId($perPage = 15, $lastId = 0, $column = '_id') - { - return parent::forPageAfterId($perPage, $lastId, $column); - } - /** @inheritdoc */ public function pluck($column, $key = null) { @@ -1048,21 +1061,26 @@ public function runPaginationCountQuery($columns = ['*']) /** * Perform an update query. * - * @param array $query - * * @return int */ - protected function performUpdate($query, array $options = []) + protected function performUpdate(array $update, array $options = []) { // Update multiple items by default. if (! array_key_exists('multiple', $options)) { $options['multiple'] = true; } + // Since "id" is an alias for "_id", we prevent updating it + foreach ($update as $operator => $fields) { + if (array_key_exists('id', $fields)) { + throw new InvalidArgumentException('Cannot update "id" field.'); + } + } + $options = $this->inheritConnectionOptions($options); $wheres = $this->compileWheres(); - $result = $this->collection->updateMany($wheres, $query, $options); + $result = $this->collection->updateMany($wheres, $update, $options); if ($result->isAcknowledged()) { return $result->getModifiedCount() ? $result->getModifiedCount() : $result->getUpsertedCount(); } @@ -1155,16 +1173,21 @@ protected function compileWheres(): array // Convert column name to string to use as array key if (isset($where['column'])) { $where['column'] = (string) $where['column']; - } - // Convert id's. - if (isset($where['column']) && ($where['column'] === '_id' || str_ends_with($where['column'], '._id'))) { - if (isset($where['values'])) { - // Multiple values. - $where['values'] = array_map($this->convertKey(...), $where['values']); - } elseif (isset($where['value'])) { - // Single value. - $where['value'] = $this->convertKey($where['value']); + // Compatibility with Eloquent queries that uses "id" instead of MongoDB's _id + if ($where['column'] === 'id') { + $where['column'] = '_id'; + } + + // Convert id's. + if ($where['column'] === '_id' || str_ends_with($where['column'], '._id')) { + if (isset($where['values'])) { + // Multiple values. + $where['values'] = array_map($this->convertKey(...), $where['values']); + } elseif (isset($where['value'])) { + // Single value. + $where['value'] = $this->convertKey($where['value']); + } } } @@ -1604,4 +1627,43 @@ public function orWhereIntegerNotInRaw($column, $values, $boolean = 'and') { throw new BadMethodCallException('This method is not supported by MongoDB'); } + + private function aliasIdForQuery(array $values): array + { + if (array_key_exists('id', $values)) { + $values['_id'] = $values['id']; + unset($values['id']); + } + + foreach ($values as $key => $value) { + if (is_string($key) && str_ends_with($key, '.id')) { + $values[substr($key, 0, -3) . '._id'] = $value; + unset($values[$key]); + } + } + + foreach ($values as &$value) { + if (is_array($value)) { + $value = $this->aliasIdForQuery($value); + } + } + + return $values; + } + + private function aliasIdForResult(array $values): array + { + if (array_key_exists('_id', $values) && ! array_key_exists('id', $values)) { + $values['id'] = $values['_id']; + //unset($values['_id']); + } + + foreach ($values as $key => $value) { + if (is_array($value)) { + $values[$key] = $this->aliasIdForResult($value); + } + } + + return $values; + } } diff --git a/src/Relations/EmbedsMany.php b/src/Relations/EmbedsMany.php index 72c77b598..e4bbf535f 100644 --- a/src/Relations/EmbedsMany.php +++ b/src/Relations/EmbedsMany.php @@ -46,9 +46,9 @@ public function getResults() */ public function performInsert(Model $model) { - // Generate a new key if needed. - if ($model->getKeyName() === '_id' && ! $model->getKey()) { - $model->setAttribute('_id', new ObjectID()); + // Create a new key if needed. + if (($model->getKeyName() === '_id' || $model->getKeyName() === 'id') && ! $model->getKey()) { + $model->setAttribute($model->getKeyName(), new ObjectID()); } // For deeply nested documents, let the parent handle the changes. @@ -249,8 +249,8 @@ public function attach(Model $model) protected function associateNew($model) { // Create a new key if needed. - if ($model->getKeyName() === '_id' && ! $model->getAttribute('_id')) { - $model->setAttribute('_id', new ObjectID()); + if (($model->getKeyName() === '_id' || $model->getKeyName() === 'id') && ! $model->getKey()) { + $model->setAttribute($model->getKeyName(), new ObjectID()); } $records = $this->getEmbedded(); diff --git a/src/Relations/EmbedsOne.php b/src/Relations/EmbedsOne.php index 678141cf1..95d5cc15d 100644 --- a/src/Relations/EmbedsOne.php +++ b/src/Relations/EmbedsOne.php @@ -42,9 +42,9 @@ public function getEager() */ public function performInsert(Model $model) { - // Generate a new key if needed. - if ($model->getKeyName() === '_id' && ! $model->getKey()) { - $model->setAttribute('_id', new ObjectID()); + // Create a new key if needed. + if (($model->getKeyName() === '_id' || $model->getKeyName() === 'id') && ! $model->getKey()) { + $model->setAttribute($model->getKeyName(), new ObjectID()); } // For deeply nested documents, let the parent handle the changes. diff --git a/tests/AuthTest.php b/tests/AuthTest.php index 61710bf74..d2b3a9675 100644 --- a/tests/AuthTest.php +++ b/tests/AuthTest.php @@ -52,7 +52,7 @@ public function testRemindOld() $broker->sendResetLink( ['email' => 'john.doe@example.com'], function ($actualUser, $actualToken) use ($user, &$token) { - $this->assertEquals($user->_id, $actualUser->_id); + $this->assertEquals($user->id, $actualUser->id); // Store token for later use $token = $actualToken; }, diff --git a/tests/EmbeddedRelationsTest.php b/tests/EmbeddedRelationsTest.php index 22e6e8d08..8ee8297f7 100644 --- a/tests/EmbeddedRelationsTest.php +++ b/tests/EmbeddedRelationsTest.php @@ -48,15 +48,15 @@ public function testEmbedsManySave() $this->assertEquals(['London'], $user->addresses->pluck('city')->all()); $this->assertInstanceOf(DateTime::class, $address->created_at); $this->assertInstanceOf(DateTime::class, $address->updated_at); - $this->assertNotNull($address->_id); - $this->assertIsString($address->_id); + $this->assertNotNull($address->id); + $this->assertIsString($address->id); $raw = $address->getAttributes(); - $this->assertInstanceOf(ObjectId::class, $raw['_id']); + $this->assertInstanceOf(ObjectId::class, $raw['id']); $address = $user->addresses()->save(new Address(['city' => 'Paris'])); - $user = User::find($user->_id); + $user = User::find($user->id); $this->assertEquals(['London', 'Paris'], $user->addresses->pluck('city')->all()); $address->setEventDispatcher($events = Mockery::mock(Dispatcher::class)); @@ -82,7 +82,7 @@ public function testEmbedsManySave() $this->assertEquals(2, $user->addresses()->count()); $this->assertEquals(['London', 'New York'], $user->addresses->pluck('city')->all()); - $freshUser = User::find($user->_id); + $freshUser = User::find($user->id); $this->assertEquals(['London', 'New York'], $freshUser->addresses->pluck('city')->all()); $address = $user->addresses->first(); @@ -92,7 +92,7 @@ public function testEmbedsManySave() $this->assertInstanceOf(User::class, $address->user); $this->assertEmpty($address->relationsToArray()); // prevent infinite loop - $user = User::find($user->_id); + $user = User::find($user->id); $user->addresses()->save(new Address(['city' => 'Bruxelles'])); $this->assertEquals(['London', 'New York', 'Bruxelles'], $user->addresses->pluck('city')->all()); @@ -101,7 +101,7 @@ public function testEmbedsManySave() $user->addresses()->save($address); $this->assertEquals(['London', 'Manhattan', 'Bruxelles'], $user->addresses->pluck('city')->all()); - $freshUser = User::find($user->_id); + $freshUser = User::find($user->id); $this->assertEquals(['London', 'Manhattan', 'Bruxelles'], $freshUser->addresses->pluck('city')->all()); } @@ -122,16 +122,16 @@ public function testEmbedsManyAssociate() $user->addresses()->associate($address); $this->assertEquals(['London'], $user->addresses->pluck('city')->all()); - $this->assertNotNull($address->_id); + $this->assertNotNull($address->id); - $freshUser = User::find($user->_id); + $freshUser = User::find($user->id); $this->assertEquals([], $freshUser->addresses->pluck('city')->all()); $address->city = 'Londinium'; $user->addresses()->associate($address); $this->assertEquals(['Londinium'], $user->addresses->pluck('city')->all()); - $freshUser = User::find($user->_id); + $freshUser = User::find($user->id); $this->assertEquals([], $freshUser->addresses->pluck('city')->all()); } @@ -162,7 +162,7 @@ public function testEmbedsManyDuplicate() $this->assertEquals(1, $user->addresses->count()); $this->assertEquals(['Paris'], $user->addresses->pluck('city')->all()); - $user->addresses()->create(['_id' => $address->_id, 'city' => 'Bruxelles']); + $user->addresses()->create(['id' => $address->id, 'city' => 'Bruxelles']); $this->assertEquals(1, $user->addresses->count()); $this->assertEquals(['Bruxelles'], $user->addresses->pluck('city')->all()); } @@ -172,21 +172,21 @@ public function testEmbedsManyCreate() $user = User::create([]); $address = $user->addresses()->create(['city' => 'Bruxelles']); $this->assertInstanceOf(Address::class, $address); - $this->assertIsString($address->_id); + $this->assertIsString($address->id); $this->assertEquals(['Bruxelles'], $user->addresses->pluck('city')->all()); $raw = $address->getAttributes(); - $this->assertInstanceOf(ObjectId::class, $raw['_id']); + $this->assertInstanceOf(ObjectId::class, $raw['id']); $freshUser = User::find($user->id); $this->assertEquals(['Bruxelles'], $freshUser->addresses->pluck('city')->all()); $user = User::create([]); - $address = $user->addresses()->create(['_id' => '', 'city' => 'Bruxelles']); - $this->assertIsString($address->_id); + $address = $user->addresses()->create(['id' => '', 'city' => 'Bruxelles']); + $this->assertIsString($address->id); $raw = $address->getAttributes(); - $this->assertInstanceOf(ObjectId::class, $raw['_id']); + $this->assertInstanceOf(ObjectId::class, $raw['id']); } public function testEmbedsManyCreateMany() @@ -222,7 +222,7 @@ public function testEmbedsManyDestroy() ->once() ->with('eloquent.deleted: ' . $address::class, Mockery::type(Address::class)); - $user->addresses()->destroy($address->_id); + $user->addresses()->destroy($address->id); $this->assertEquals(['Bristol', 'Bruxelles'], $user->addresses->pluck('city')->all()); $address->unsetEventDispatcher(); @@ -237,7 +237,7 @@ public function testEmbedsManyDestroy() $freshUser = User::find($user->id); $this->assertEquals(['Bruxelles', 'Paris', 'San Francisco'], $freshUser->addresses->pluck('city')->all()); - $ids = $user->addresses->pluck('_id'); + $ids = $user->addresses->pluck('id'); $user->addresses()->destroy($ids); $this->assertEquals([], $user->addresses->pluck('city')->all()); @@ -414,13 +414,13 @@ public function testEmbedsManyFindOrContains() $address1 = $user->addresses()->save(new Address(['city' => 'New York'])); $address2 = $user->addresses()->save(new Address(['city' => 'Paris'])); - $address = $user->addresses()->find($address1->_id); + $address = $user->addresses()->find($address1->id); $this->assertEquals($address->city, $address1->city); - $address = $user->addresses()->find($address2->_id); + $address = $user->addresses()->find($address2->id); $this->assertEquals($address->city, $address2->city); - $this->assertTrue($user->addresses()->contains($address2->_id)); + $this->assertTrue($user->addresses()->contains($address2->id)); $this->assertFalse($user->addresses()->contains('123')); } @@ -552,11 +552,11 @@ public function testEmbedsOne() $this->assertEquals('Mark Doe', $user->father->name); $this->assertInstanceOf(DateTime::class, $father->created_at); $this->assertInstanceOf(DateTime::class, $father->updated_at); - $this->assertNotNull($father->_id); - $this->assertIsString($father->_id); + $this->assertNotNull($father->id); + $this->assertIsString($father->id); $raw = $father->getAttributes(); - $this->assertInstanceOf(ObjectId::class, $raw['_id']); + $this->assertInstanceOf(ObjectId::class, $raw['id']); $father->setEventDispatcher($events = Mockery::mock(Dispatcher::class)); $events->shouldReceive('dispatch')->with('eloquent.retrieved: ' . $father::class, Mockery::any()); diff --git a/tests/HybridRelationsTest.php b/tests/HybridRelationsTest.php index 5253784c9..71958d27d 100644 --- a/tests/HybridRelationsTest.php +++ b/tests/HybridRelationsTest.php @@ -83,7 +83,7 @@ public function testSqlRelations() // MongoDB has many $book = new SqlBook(['title' => 'Game of Thrones']); $user->sqlBooks()->save($book); - $user = User::find($user->_id); // refetch + $user = User::find($user->id); // refetch $this->assertCount(1, $user->sqlBooks); // SQL belongs to @@ -93,7 +93,7 @@ public function testSqlRelations() // MongoDB has one $role = new SqlRole(['type' => 'admin']); $user->sqlRole()->save($role); - $user = User::find($user->_id); // refetch + $user = User::find($user->id); // refetch $this->assertEquals('admin', $user->sqlRole->type); // SQL belongs to @@ -239,16 +239,16 @@ public function testHybridBelongsToMany() // sync (pivot is empty) $skill->sqlUsers()->sync([$user->id, $user2->id]); - $check = Skill::query()->find($skill->_id); + $check = Skill::query()->find($skill->id); $this->assertEquals(2, $check->sqlUsers->count()); // sync (pivot is not empty) $skill->sqlUsers()->sync($user); - $check = Skill::query()->find($skill->_id); + $check = Skill::query()->find($skill->id); $this->assertEquals(1, $check->sqlUsers->count()); // Inverse sync (pivot is empty) - $user->skills()->sync([$skill->_id, $skill2->_id]); + $user->skills()->sync([$skill->id, $skill2->id]); $check = SqlUser::find($user->id); $this->assertEquals(2, $check->skills->count()); @@ -288,7 +288,7 @@ public function testHybridMorphToManySqlModelToMongoModel() $label2 = Label::query()->create(['name' => 'MongoDB']); // MorphToMany (pivot is empty) - $user->labels()->sync([$label->_id, $label2->_id]); + $user->labels()->sync([$label->id, $label2->id]); $check = SqlUser::query()->find($user->id); $this->assertEquals(2, $check->labels->count()); @@ -308,12 +308,12 @@ public function testHybridMorphToManySqlModelToMongoModel() // Inverse MorphToMany (pivot is empty) $label->sqlUsers()->sync([$user->id, $user2->id]); - $check = Label::query()->find($label->_id); + $check = Label::query()->find($label->id); $this->assertEquals(2, $check->sqlUsers->count()); // Inverse MorphToMany (pivot is empty) $label->sqlUsers()->sync([$user->id, $user2->id]); - $check = Label::query()->find($label->_id); + $check = Label::query()->find($label->id); $this->assertEquals(2, $check->sqlUsers->count()); } @@ -340,21 +340,21 @@ public function testHybridMorphToManyMongoModelToSqlModel() // MorphToMany (pivot is empty) $experience->sqlUsers()->sync([$user->id, $user2->id]); - $check = Experience::query()->find($experience->_id); + $check = Experience::query()->find($experience->id); $this->assertEquals(2, $check->sqlUsers->count()); // MorphToMany (pivot is not empty) $experience->sqlUsers()->sync([$user->id]); - $check = Experience::query()->find($experience->_id); + $check = Experience::query()->find($experience->id); $this->assertEquals(1, $check->sqlUsers->count()); // Inverse MorphToMany (pivot is empty) - $user->experiences()->sync([$experience->_id, $experience2->_id]); + $user->experiences()->sync([$experience->id, $experience2->id]); $check = SqlUser::query()->find($user->id); $this->assertEquals(2, $check->experiences->count()); // Inverse MorphToMany (pivot is not empty) - $user->experiences()->sync([$experience->_id]); + $user->experiences()->sync([$experience->id]); $check = SqlUser::query()->find($user->id); $this->assertEquals(1, $check->experiences->count()); diff --git a/tests/ModelTest.php b/tests/ModelTest.php index 57e49574f..6903ce64c 100644 --- a/tests/ModelTest.php +++ b/tests/ModelTest.php @@ -63,7 +63,7 @@ public function testNewModel(): void $this->assertInstanceOf(Connection::class, $user->getConnection()); $this->assertFalse($user->exists); $this->assertEquals('users', $user->getTable()); - $this->assertEquals('_id', $user->getKeyName()); + $this->assertEquals('id', $user->getKeyName()); } public function testQualifyColumn(): void @@ -89,14 +89,14 @@ public function testInsert(): void $this->assertTrue($user->exists); $this->assertEquals(1, User::count()); - $this->assertTrue(isset($user->_id)); - $this->assertIsString($user->_id); - $this->assertNotEquals('', (string) $user->_id); - $this->assertNotEquals(0, strlen((string) $user->_id)); + $this->assertTrue(isset($user->id)); + $this->assertIsString($user->id); + $this->assertNotEquals('', (string) $user->id); + $this->assertNotEquals(0, strlen((string) $user->id)); $this->assertInstanceOf(Carbon::class, $user->created_at); $raw = $user->getAttributes(); - $this->assertInstanceOf(ObjectID::class, $raw['_id']); + $this->assertInstanceOf(ObjectID::class, $raw['id']); $this->assertEquals('John Doe', $user->name); $this->assertEquals(35, $user->age); @@ -111,9 +111,9 @@ public function testUpdate(): void $user->save(); $raw = $user->getAttributes(); - $this->assertInstanceOf(ObjectID::class, $raw['_id']); + $this->assertInstanceOf(ObjectID::class, $raw['id']); - $check = User::find($user->_id); + $check = User::find($user->id); $this->assertInstanceOf(User::class, $check); $check->age = 36; $check->save(); @@ -129,16 +129,16 @@ public function testUpdate(): void $user->update(['age' => 20]); $raw = $user->getAttributes(); - $this->assertInstanceOf(ObjectID::class, $raw['_id']); + $this->assertInstanceOf(ObjectID::class, $raw['id']); - $check = User::find($user->_id); + $check = User::find($user->id); $this->assertEquals(20, $check->age); $check->age = 24; $check->fullname = 'Hans Thomas'; // new field $check->save(); - $check = User::find($user->_id); + $check = User::find($user->id); $this->assertEquals(24, $check->age); $this->assertEquals('Hans Thomas', $check->fullname); } @@ -180,46 +180,46 @@ public function testUpsert() public function testManualStringId(): void { $user = new User(); - $user->_id = '4af9f23d8ead0e1d32000000'; + $user->id = '4af9f23d8ead0e1d32000000'; $user->name = 'John Doe'; $user->title = 'admin'; $user->age = 35; $user->save(); $this->assertTrue($user->exists); - $this->assertEquals('4af9f23d8ead0e1d32000000', $user->_id); + $this->assertEquals('4af9f23d8ead0e1d32000000', $user->id); $raw = $user->getAttributes(); - $this->assertInstanceOf(ObjectID::class, $raw['_id']); + $this->assertInstanceOf(ObjectID::class, $raw['id']); $user = new User(); - $user->_id = 'customId'; + $user->id = 'customId'; $user->name = 'John Doe'; $user->title = 'admin'; $user->age = 35; $user->save(); $this->assertTrue($user->exists); - $this->assertEquals('customId', $user->_id); + $this->assertEquals('customId', $user->id); $raw = $user->getAttributes(); - $this->assertIsString($raw['_id']); + $this->assertIsString($raw['id']); } public function testManualIntId(): void { $user = new User(); - $user->_id = 1; + $user->id = 1; $user->name = 'John Doe'; $user->title = 'admin'; $user->age = 35; $user->save(); $this->assertTrue($user->exists); - $this->assertEquals(1, $user->_id); + $this->assertEquals(1, $user->id); $raw = $user->getAttributes(); - $this->assertIsInt($raw['_id']); + $this->assertIsInt($raw['id']); } public function testDelete(): void @@ -267,11 +267,11 @@ public function testFind(): void $user->age = 35; $user->save(); - $check = User::find($user->_id); + $check = User::find($user->id); $this->assertInstanceOf(User::class, $check); $this->assertTrue(Model::isDocumentModel($check)); $this->assertTrue($check->exists); - $this->assertEquals($user->_id, $check->_id); + $this->assertEquals($user->id, $check->id); $this->assertEquals('John Doe', $check->name); $this->assertEquals(35, $check->age); @@ -339,7 +339,7 @@ public function testCreate(): void $check = User::where('name', 'Jane Poe')->first(); $this->assertInstanceOf(User::class, $check); - $this->assertEquals($user->_id, $check->_id); + $this->assertEquals($user->id, $check->id); } public function testDestroy(): void @@ -350,7 +350,7 @@ public function testDestroy(): void $user->age = 35; $user->save(); - User::destroy((string) $user->_id); + User::destroy((string) $user->id); $this->assertEquals(0, User::count()); } @@ -367,7 +367,7 @@ public function testTouch(): void sleep(1); $user->touch(); - $check = User::find($user->_id); + $check = User::find($user->id); $this->assertInstanceOf(User::class, $check); $this->assertNotEquals($old, $check->updated_at); @@ -412,12 +412,12 @@ public function testPrimaryKey(string $model, $id, $expected, bool $expectedFoun $expectedType = get_debug_type($expected); $document = new $model(); - $this->assertEquals('_id', $document->getKeyName()); + $this->assertEquals('id', $document->getKeyName()); - $document->_id = $id; + $document->id = $id; $document->save(); - $this->assertSame($expectedType, get_debug_type($document->_id)); - $this->assertEquals($expected, $document->_id); + $this->assertSame($expectedType, get_debug_type($document->id)); + $this->assertEquals($expected, $document->id); $this->assertSame($expectedType, get_debug_type($document->getKey())); $this->assertEquals($expected, $document->getKey()); @@ -425,8 +425,8 @@ public function testPrimaryKey(string $model, $id, $expected, bool $expectedFoun if ($expectedFound) { $this->assertNotNull($check, 'Not found'); - $this->assertSame($expectedType, get_debug_type($check->_id)); - $this->assertEquals($id, $check->_id); + $this->assertSame($expectedType, get_debug_type($check->id)); + $this->assertEquals($id, $check->id); $this->assertSame($expectedType, get_debug_type($check->getKey())); $this->assertEquals($id, $check->getKey()); } else { @@ -534,10 +534,10 @@ public function testToArray(): void $array = $item->toArray(); $keys = array_keys($array); sort($keys); - $this->assertEquals(['_id', 'created_at', 'name', 'type', 'updated_at'], $keys); + $this->assertEquals(['created_at', 'id', 'name', 'type', 'updated_at'], $keys); $this->assertIsString($array['created_at']); $this->assertIsString($array['updated_at']); - $this->assertIsString($array['_id']); + $this->assertIsString($array['id']); } public function testUnset(): void @@ -559,8 +559,8 @@ public function testUnset(): void $this->assertTrue(isset($user2->note2)); // Re-fetch to be sure - $user1 = User::find($user1->_id); - $user2 = User::find($user2->_id); + $user1 = User::find($user1->id); + $user2 = User::find($user2->id); $this->assertFalse(isset($user1->note1)); $this->assertTrue(isset($user1->note2)); @@ -574,7 +574,7 @@ public function testUnset(): void $this->assertFalse(isset($user2->note2)); // Re-re-fetch to be sure - $user2 = User::find($user2->_id); + $user2 = User::find($user2->id); $this->assertFalse(isset($user2->note1)); $this->assertFalse(isset($user2->note2)); @@ -622,14 +622,14 @@ public function testUnsetAndSet(): void $this->assertSame(['note1' => 'GHI'], $user->getDirty()); // Fetch to be sure the changes are not persisted yet - $userCheck = User::find($user->_id); + $userCheck = User::find($user->id); $this->assertSame('ABC', $userCheck['note1']); // Persist the changes $user->save(); // Re-fetch to be sure - $user = User::find($user->_id); + $user = User::find($user->id); $this->assertTrue(isset($user->note1)); $this->assertSame('GHI', $user->note1); @@ -656,7 +656,7 @@ public function testUnsetDotAttributes(): void $this->assertTrue(isset($user->notes['note2'])); // Re-fetch to be sure - $user = User::find($user->_id); + $user = User::find($user->id); $this->assertFalse(isset($user->notes['note1'])); $this->assertTrue(isset($user->notes['note2'])); @@ -673,7 +673,7 @@ public function testUnsetDotAttributes(): void $this->assertFalse(isset($user->notes)); // Re-fetch to be sure - $user = User::find($user->_id); + $user = User::find($user->id); $this->assertFalse(isset($user->notes)); } @@ -705,7 +705,7 @@ public function testUnsetDotAttributesAndSet(): void $this->assertSame(['note2' => 'DEF', 'note1' => 'ABC'], $user->notes); // Re-fetch to be sure - $user = User::find($user->_id); + $user = User::find($user->id); $this->assertSame(['note2' => 'DEF', 'note1' => 'ABC'], $user->notes); } @@ -722,14 +722,14 @@ public function testDateUseLocalTimeZone(): void $this->assertEquals($tz, $user->birthday->getTimezone()->getName()); $user->save(); - $user = User::find($user->_id); + $user = User::find($user->id); $this->assertEquals($date, $user->birthday); $this->assertEquals($tz, $user->birthday->getTimezone()->getName()); $this->assertSame('1965-03-02T15:30:10+10:00', $user->birthday->format(DATE_ATOM)); $tz = 'America/New_York'; date_default_timezone_set($tz); - $user = User::find($user->_id); + $user = User::find($user->id); $this->assertEquals($date, $user->birthday); $this->assertEquals($tz, $user->birthday->getTimezone()->getName()); $this->assertSame('1965-03-02T00:30:10-05:00', $user->birthday->format(DATE_ATOM)); @@ -748,7 +748,7 @@ public function testDates(): void $user = User::create(['name' => 'John Doe', 'birthday' => new DateTime('1980/1/1')]); $this->assertInstanceOf(Carbon::class, $user->birthday); - $check = User::find($user->_id); + $check = User::find($user->id); $this->assertInstanceOf(Carbon::class, $check->birthday); $this->assertEquals($user->birthday, $check->birthday); @@ -833,7 +833,7 @@ public function testDateNull(): void $user->save(); // Re-fetch to be sure - $user = User::find($user->_id); + $user = User::find($user->id); $this->assertNull($user->birthday); // Nested field with dot notation @@ -845,7 +845,7 @@ public function testDateNull(): void $this->assertNull($user->getAttribute('entry.date')); // Re-fetch to be sure - $user = User::find($user->_id); + $user = User::find($user->id); $this->assertNull($user->getAttribute('entry.date')); } @@ -863,10 +863,10 @@ public function testIdAttribute(): void { $user = User::create(['name' => 'John Doe']); $this->assertInstanceOf(User::class, $user); - $this->assertEquals($user->id, $user->_id); + $this->assertSame($user->id, $user->_id); $user = User::create(['id' => 'custom_id', 'name' => 'John Doe']); - $this->assertNotEquals($user->id, $user->_id); + $this->assertSame($user->id, $user->_id); } public function testPushPull(): void @@ -879,20 +879,20 @@ public function testPushPull(): void $user->push('tags', 'tag2', true); $this->assertEquals(['tag1', 'tag1', 'tag2'], $user->tags); - $user = User::where('_id', $user->_id)->first(); + $user = User::where('id', $user->id)->first(); $this->assertEquals(['tag1', 'tag1', 'tag2'], $user->tags); $user->pull('tags', 'tag1'); $this->assertEquals(['tag2'], $user->tags); - $user = User::where('_id', $user->_id)->first(); + $user = User::where('id', $user->id)->first(); $this->assertEquals(['tag2'], $user->tags); $user->push('tags', 'tag3'); $user->pull('tags', ['tag2', 'tag3']); $this->assertEquals([], $user->tags); - $user = User::where('_id', $user->_id)->first(); + $user = User::where('id', $user->id)->first(); $this->assertEquals([], $user->tags); } @@ -1048,7 +1048,7 @@ public function testFirstOrCreate(): void $check = User::where('name', $name)->first(); $this->assertInstanceOf(User::class, $check); - $this->assertEquals($user->_id, $check->_id); + $this->assertEquals($user->id, $check->id); } public function testEnumCast(): void @@ -1163,6 +1163,7 @@ public function testCreateOrFirst(bool $transaction) } #[TestWith([['_id' => new ObjectID()]])] + #[TestWith([['id' => new ObjectID()]])] #[TestWith([['foo' => 'bar']])] public function testUpdateOrCreate(array $criteria) { @@ -1215,9 +1216,11 @@ public function testUpdateOrCreate(array $criteria) $this->assertEquals($updatedAt, $checkUser->updated_at->getTimestamp()); } - public function testCreateWithNullId() + #[TestWith(['_id'])] + #[TestWith(['id'])] + public function testCreateWithNullId(string $id) { - $user = User::create(['_id' => null, 'email' => 'foo@bar']); + $user = User::create([$id => null, 'email' => 'foo@bar']); $this->assertNotNull(ObjectId::class, $user->id); $this->assertSame(1, User::count()); } diff --git a/tests/Models/Address.php b/tests/Models/Address.php index d94e31d24..60e4d6431 100644 --- a/tests/Models/Address.php +++ b/tests/Models/Address.php @@ -12,7 +12,6 @@ class Address extends Model { use DocumentModel; - protected $primaryKey = '_id'; protected $keyType = 'string'; protected $connection = 'mongodb'; protected static $unguarded = true; diff --git a/tests/Models/Birthday.php b/tests/Models/Birthday.php index ae0e108b1..6c9964da5 100644 --- a/tests/Models/Birthday.php +++ b/tests/Models/Birthday.php @@ -16,7 +16,6 @@ class Birthday extends Model { use DocumentModel; - protected $primaryKey = '_id'; protected $keyType = 'string'; protected $connection = 'mongodb'; protected $table = 'birthday'; diff --git a/tests/Models/Client.php b/tests/Models/Client.php index b0339a0e5..f0118f12f 100644 --- a/tests/Models/Client.php +++ b/tests/Models/Client.php @@ -14,7 +14,6 @@ class Client extends Model { use DocumentModel; - protected $primaryKey = '_id'; protected $keyType = 'string'; protected $connection = 'mongodb'; protected $table = 'clients'; diff --git a/tests/Models/Experience.php b/tests/Models/Experience.php index 6a306afe1..2784cc5dd 100644 --- a/tests/Models/Experience.php +++ b/tests/Models/Experience.php @@ -12,7 +12,6 @@ class Experience extends Model { use DocumentModel; - protected $primaryKey = '_id'; protected $keyType = 'string'; protected $connection = 'mongodb'; protected $table = 'experiences'; diff --git a/tests/Models/Group.php b/tests/Models/Group.php index 57c3af59c..9115b8c3e 100644 --- a/tests/Models/Group.php +++ b/tests/Models/Group.php @@ -12,7 +12,6 @@ class Group extends Model { use DocumentModel; - protected $primaryKey = '_id'; protected $keyType = 'string'; protected $connection = 'mongodb'; protected $table = 'groups'; @@ -20,6 +19,6 @@ class Group extends Model public function users(): BelongsToMany { - return $this->belongsToMany(User::class, 'users', 'groups', 'users', '_id', '_id', 'users'); + return $this->belongsToMany(User::class, 'users', 'groups', 'users', 'id', 'id', 'users'); } } diff --git a/tests/Models/Guarded.php b/tests/Models/Guarded.php index 40d11bea5..57616c4c9 100644 --- a/tests/Models/Guarded.php +++ b/tests/Models/Guarded.php @@ -11,7 +11,6 @@ class Guarded extends Model { use DocumentModel; - protected $primaryKey = '_id'; protected $keyType = 'string'; protected $connection = 'mongodb'; protected $table = 'guarded'; diff --git a/tests/Models/HiddenAnimal.php b/tests/Models/HiddenAnimal.php index a47184fe7..240238da0 100644 --- a/tests/Models/HiddenAnimal.php +++ b/tests/Models/HiddenAnimal.php @@ -22,7 +22,6 @@ final class HiddenAnimal extends Model { use DocumentModel; - protected $primaryKey = '_id'; protected $keyType = 'string'; protected $fillable = [ 'name', diff --git a/tests/Models/IdIsBinaryUuid.php b/tests/Models/IdIsBinaryUuid.php index 2314b4b19..33cc86da3 100644 --- a/tests/Models/IdIsBinaryUuid.php +++ b/tests/Models/IdIsBinaryUuid.php @@ -12,11 +12,10 @@ class IdIsBinaryUuid extends Model { use DocumentModel; - protected $primaryKey = '_id'; protected $keyType = 'string'; protected $connection = 'mongodb'; protected static $unguarded = true; protected $casts = [ - '_id' => BinaryUuid::class, + 'id' => BinaryUuid::class, ]; } diff --git a/tests/Models/IdIsInt.php b/tests/Models/IdIsInt.php index 1f8d1ba88..bf7c9f47f 100644 --- a/tests/Models/IdIsInt.php +++ b/tests/Models/IdIsInt.php @@ -11,9 +11,8 @@ class IdIsInt extends Model { use DocumentModel; - protected $primaryKey = '_id'; protected $keyType = 'int'; protected $connection = 'mongodb'; protected static $unguarded = true; - protected $casts = ['_id' => 'int']; + protected $casts = ['id' => 'int']; } diff --git a/tests/Models/IdIsString.php b/tests/Models/IdIsString.php index 37ba1c424..fef6c36bf 100644 --- a/tests/Models/IdIsString.php +++ b/tests/Models/IdIsString.php @@ -11,9 +11,8 @@ class IdIsString extends Model { use DocumentModel; - protected $primaryKey = '_id'; protected $keyType = 'string'; protected $connection = 'mongodb'; protected static $unguarded = true; - protected $casts = ['_id' => 'string']; + protected $casts = ['id' => 'string']; } diff --git a/tests/Models/Item.php b/tests/Models/Item.php index 2beb40d75..deec7a7cf 100644 --- a/tests/Models/Item.php +++ b/tests/Models/Item.php @@ -15,7 +15,6 @@ class Item extends Model { use DocumentModel; - protected $primaryKey = '_id'; protected $keyType = 'string'; protected $connection = 'mongodb'; protected $table = 'items'; diff --git a/tests/Models/Label.php b/tests/Models/Label.php index b95aa0dcf..5088a1053 100644 --- a/tests/Models/Label.php +++ b/tests/Models/Label.php @@ -17,7 +17,6 @@ class Label extends Model { use DocumentModel; - protected $primaryKey = '_id'; protected $keyType = 'string'; protected $connection = 'mongodb'; protected $table = 'labels'; diff --git a/tests/Models/Location.php b/tests/Models/Location.php index 2c62dbda9..75a40d5f3 100644 --- a/tests/Models/Location.php +++ b/tests/Models/Location.php @@ -11,7 +11,6 @@ class Location extends Model { use DocumentModel; - protected $primaryKey = '_id'; protected $keyType = 'string'; protected $connection = 'mongodb'; protected $table = 'locations'; diff --git a/tests/Models/Photo.php b/tests/Models/Photo.php index be7f3666c..d6786267b 100644 --- a/tests/Models/Photo.php +++ b/tests/Models/Photo.php @@ -12,7 +12,6 @@ class Photo extends Model { use DocumentModel; - protected $primaryKey = '_id'; protected $keyType = 'string'; protected $connection = 'mongodb'; protected $table = 'photos'; diff --git a/tests/Models/Role.php b/tests/Models/Role.php index e9f3fa95d..02551971d 100644 --- a/tests/Models/Role.php +++ b/tests/Models/Role.php @@ -12,7 +12,6 @@ class Role extends Model { use DocumentModel; - protected $primaryKey = '_id'; protected $keyType = 'string'; protected $connection = 'mongodb'; protected $table = 'roles'; diff --git a/tests/Models/Scoped.php b/tests/Models/Scoped.php index 6850dcb21..477c33ded 100644 --- a/tests/Models/Scoped.php +++ b/tests/Models/Scoped.php @@ -12,7 +12,6 @@ class Scoped extends Model { use DocumentModel; - protected $primaryKey = '_id'; protected $keyType = 'string'; protected $connection = 'mongodb'; protected $table = 'scoped'; diff --git a/tests/Models/Skill.php b/tests/Models/Skill.php index 1e2daaf80..c6b3e94bb 100644 --- a/tests/Models/Skill.php +++ b/tests/Models/Skill.php @@ -12,7 +12,6 @@ class Skill extends Model { use DocumentModel; - protected $primaryKey = '_id'; protected $keyType = 'string'; protected $connection = 'mongodb'; protected $table = 'skills'; diff --git a/tests/Models/Soft.php b/tests/Models/Soft.php index cbfa2ef23..f887d05a9 100644 --- a/tests/Models/Soft.php +++ b/tests/Models/Soft.php @@ -18,7 +18,6 @@ class Soft extends Model use SoftDeletes; use MassPrunable; - protected $primaryKey = '_id'; protected $keyType = 'string'; protected $connection = 'mongodb'; protected $table = 'soft'; diff --git a/tests/Models/User.php b/tests/Models/User.php index 22048f282..5b8ac983a 100644 --- a/tests/Models/User.php +++ b/tests/Models/User.php @@ -19,7 +19,7 @@ use MongoDB\Laravel\Eloquent\MassPrunable; /** - * @property string $_id + * @property string $id * @property string $name * @property string $email * @property string $title @@ -38,7 +38,6 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon use Notifiable; use MassPrunable; - protected $primaryKey = '_id'; protected $keyType = 'string'; protected $connection = 'mongodb'; protected $casts = [ @@ -100,7 +99,7 @@ public function clients() public function groups() { - return $this->belongsToMany(Group::class, 'groups', 'users', 'groups', '_id', '_id', 'groups'); + return $this->belongsToMany(Group::class, 'groups', 'users', 'groups', 'id', 'id', 'groups'); } public function photos() diff --git a/tests/Query/BuilderTest.php b/tests/Query/BuilderTest.php index 3ec933499..b081a0557 100644 --- a/tests/Query/BuilderTest.php +++ b/tests/Query/BuilderTest.php @@ -124,10 +124,9 @@ public static function provideQueryBuilderToMql(): iterable ]; // Nested array are not flattened like in the Eloquent builder. MongoDB can compare objects. - $array = [['issue' => 45582], ['id' => 2], [3]]; yield 'whereIn nested array' => [ - ['find' => [['id' => ['$in' => $array]], []]], - fn (Builder $builder) => $builder->whereIn('id', $array), + ['find' => [['_id' => ['$in' => [['issue' => 45582], ['_id' => 2], [3]]]], []]], + fn (Builder $builder) => $builder->whereIn('id', [['issue' => 45582], ['id' => 2], [3]]), ]; yield 'orWhereIn' => [ @@ -135,21 +134,21 @@ public static function provideQueryBuilderToMql(): iterable 'find' => [ [ '$or' => [ - ['id' => 1], - ['id' => ['$in' => [1, 2, 3]]], + ['foo' => 1], + ['foo' => ['$in' => [1, 2, 3]]], ], ], [], // options ], ], - fn (Builder $builder) => $builder->where('id', '=', 1) - ->orWhereIn('id', [1, 2, 3]), + fn (Builder $builder) => $builder->where('foo', '=', 1) + ->orWhereIn('foo', [1, 2, 3]), ]; /** @see DatabaseQueryBuilderTest::testBasicWhereNotIns */ yield 'whereNotIn' => [ - ['find' => [['id' => ['$nin' => [1, 2, 3]]], []]], - fn (Builder $builder) => $builder->whereNotIn('id', [1, 2, 3]), + ['find' => [['foo' => ['$nin' => [1, 2, 3]]], []]], + fn (Builder $builder) => $builder->whereNotIn('foo', [1, 2, 3]), ]; yield 'orWhereNotIn' => [ @@ -157,20 +156,20 @@ public static function provideQueryBuilderToMql(): iterable 'find' => [ [ '$or' => [ - ['id' => 1], - ['id' => ['$nin' => [1, 2, 3]]], + ['foo' => 1], + ['foo' => ['$nin' => [1, 2, 3]]], ], ], [], // options ], ], - fn (Builder $builder) => $builder->where('id', '=', 1) - ->orWhereNotIn('id', [1, 2, 3]), + fn (Builder $builder) => $builder->where('foo', '=', 1) + ->orWhereNotIn('foo', [1, 2, 3]), ]; /** @see DatabaseQueryBuilderTest::testEmptyWhereIns */ yield 'whereIn empty array' => [ - ['find' => [['id' => ['$in' => []]], []]], + ['find' => [['_id' => ['$in' => []]], []]], fn (Builder $builder) => $builder->whereIn('id', []), ]; @@ -220,7 +219,7 @@ public static function provideQueryBuilderToMql(): iterable 'find' => [ [ '$or' => [ - ['id' => 1], + ['age' => 1], ['email' => 'foo'], ], ], @@ -228,7 +227,7 @@ public static function provideQueryBuilderToMql(): iterable ], ], fn (Builder $builder) => $builder - ->where('id', '=', 1) + ->where('age', '=', 1) ->orWhere('email', '=', 'foo'), ]; @@ -497,6 +496,11 @@ public static function provideQueryBuilderToMql(): iterable ->orderBy('age', 'desc'), ]; + yield 'orders by id field' => [ + ['find' => [[], ['sort' => ['_id' => 1]]]], + fn (Builder $builder) => $builder->orderBy('id'), + ]; + yield 'orders = null' => [ ['find' => [[], []]], function (Builder $builder) { @@ -553,12 +557,12 @@ function (Builder $builder) { /** @see DatabaseQueryBuilderTest::testWhereBetweens() */ yield 'whereBetween array of numbers' => [ - ['find' => [['id' => ['$gte' => 1, '$lte' => 2]], []]], + ['find' => [['_id' => ['$gte' => 1, '$lte' => 2]], []]], fn (Builder $builder) => $builder->whereBetween('id', [1, 2]), ]; yield 'whereBetween nested array of numbers' => [ - ['find' => [['id' => ['$gte' => [1], '$lte' => [2, 3]]], []]], + ['find' => [['_id' => ['$gte' => [1], '$lte' => [2, 3]]], []]], fn (Builder $builder) => $builder->whereBetween('id', [[1], [2, 3]]), ]; @@ -579,7 +583,7 @@ function (Builder $builder) { ]; yield 'whereBetween collection' => [ - ['find' => [['id' => ['$gte' => 1, '$lte' => 2]], []]], + ['find' => [['_id' => ['$gte' => 1, '$lte' => 2]], []]], fn (Builder $builder) => $builder->whereBetween('id', collect([1, 2])), ]; @@ -589,16 +593,16 @@ function (Builder $builder) { 'find' => [ [ '$or' => [ - ['id' => 1], - ['id' => ['$gte' => 3, '$lte' => 5]], + ['age' => 1], + ['age' => ['$gte' => 3, '$lte' => 5]], ], ], [], // options ], ], fn (Builder $builder) => $builder - ->where('id', '=', 1) - ->orWhereBetween('id', [3, 5]), + ->where('age', '=', 1) + ->orWhereBetween('age', [3, 5]), ]; /** @link https://www.mongodb.com/docs/manual/reference/bson-type-comparison-order/#arrays */ @@ -607,16 +611,16 @@ function (Builder $builder) { 'find' => [ [ '$or' => [ - ['id' => 1], - ['id' => ['$gte' => [4], '$lte' => [6, 8]]], + ['age' => 1], + ['age' => ['$gte' => [4], '$lte' => [6, 8]]], ], ], [], // options ], ], fn (Builder $builder) => $builder - ->where('id', '=', 1) - ->orWhereBetween('id', [[4], [6, 8]]), + ->where('age', '=', 1) + ->orWhereBetween('age', [[4], [6, 8]]), ]; yield 'orWhereBetween collection' => [ @@ -624,16 +628,16 @@ function (Builder $builder) { 'find' => [ [ '$or' => [ - ['id' => 1], - ['id' => ['$gte' => 3, '$lte' => 4]], + ['age' => 1], + ['age' => ['$gte' => 3, '$lte' => 4]], ], ], [], // options ], ], fn (Builder $builder) => $builder - ->where('id', '=', 1) - ->orWhereBetween('id', collect([3, 4])), + ->where('age', '=', 1) + ->orWhereBetween('age', collect([3, 4])), ]; yield 'whereNotBetween array of numbers' => [ @@ -641,14 +645,14 @@ function (Builder $builder) { 'find' => [ [ '$or' => [ - ['id' => ['$lte' => 1]], - ['id' => ['$gte' => 2]], + ['age' => ['$lte' => 1]], + ['age' => ['$gte' => 2]], ], ], [], // options ], ], - fn (Builder $builder) => $builder->whereNotBetween('id', [1, 2]), + fn (Builder $builder) => $builder->whereNotBetween('age', [1, 2]), ]; /** @see DatabaseQueryBuilderTest::testOrWhereNotBetween() */ @@ -657,11 +661,11 @@ function (Builder $builder) { 'find' => [ [ '$or' => [ - ['id' => 1], + ['age' => 1], [ '$or' => [ - ['id' => ['$lte' => 3]], - ['id' => ['$gte' => 5]], + ['age' => ['$lte' => 3]], + ['age' => ['$gte' => 5]], ], ], ], @@ -670,8 +674,8 @@ function (Builder $builder) { ], ], fn (Builder $builder) => $builder - ->where('id', '=', 1) - ->orWhereNotBetween('id', [3, 5]), + ->where('age', '=', 1) + ->orWhereNotBetween('age', [3, 5]), ]; yield 'orWhereNotBetween nested array of numbers' => [ @@ -679,11 +683,11 @@ function (Builder $builder) { 'find' => [ [ '$or' => [ - ['id' => 1], + ['age' => 1], [ '$or' => [ - ['id' => ['$lte' => [2, 3]]], - ['id' => ['$gte' => [5]]], + ['age' => ['$lte' => [2, 3]]], + ['age' => ['$gte' => [5]]], ], ], ], @@ -692,8 +696,8 @@ function (Builder $builder) { ], ], fn (Builder $builder) => $builder - ->where('id', '=', 1) - ->orWhereNotBetween('id', [[2, 3], [5]]), + ->where('age', '=', 1) + ->orWhereNotBetween('age', [[2, 3], [5]]), ]; yield 'orWhereNotBetween collection' => [ @@ -701,11 +705,11 @@ function (Builder $builder) { 'find' => [ [ '$or' => [ - ['id' => 1], + ['age' => 1], [ '$or' => [ - ['id' => ['$lte' => 3]], - ['id' => ['$gte' => 4]], + ['age' => ['$lte' => 3]], + ['age' => ['$gte' => 4]], ], ], ], @@ -714,8 +718,8 @@ function (Builder $builder) { ], ], fn (Builder $builder) => $builder - ->where('id', '=', 1) - ->orWhereNotBetween('id', collect([3, 4])), + ->where('age', '=', 1) + ->orWhereNotBetween('age', collect([3, 4])), ]; yield 'where like' => [ @@ -1160,6 +1164,21 @@ function (Builder $elemMatchQuery): void { ), ]; + yield 'id alias for _id' => [ + ['find' => [['_id' => 1], []]], + fn (Builder $builder) => $builder->where('id', 1), + ]; + + yield 'id alias for _id with $or' => [ + ['find' => [['$or' => [['_id' => 1], ['_id' => 2]]], []]], + fn (Builder $builder) => $builder->where('id', 1)->orWhere('id', 2), + ]; + + yield 'select colums with id alias' => [ + ['find' => [[], ['projection' => ['name' => 1, 'email' => 1, '_id' => 1]]]], + fn (Builder $builder) => $builder->select('name', 'email', 'id'), + ]; + // Method added in Laravel v10.47.0 if (method_exists(Builder::class, 'whereAll')) { /** @see DatabaseQueryBuilderTest::testWhereAll */ diff --git a/tests/QueryBuilderTest.php b/tests/QueryBuilderTest.php index e1d0ec7f2..6b08a15b7 100644 --- a/tests/QueryBuilderTest.php +++ b/tests/QueryBuilderTest.php @@ -25,6 +25,7 @@ use MongoDB\Laravel\Query\Builder; use MongoDB\Laravel\Tests\Models\Item; use MongoDB\Laravel\Tests\Models\User; +use PHPUnit\Framework\Attributes\TestWith; use Stringable; use function count; @@ -59,7 +60,7 @@ public function testDeleteWithId() $product = DB::table('items')->first(); - $pid = (string) ($product['_id']); + $pid = (string) ($product['id']); DB::table('items')->where('user_id', $userId)->delete($pid); @@ -67,7 +68,7 @@ public function testDeleteWithId() $product = DB::table('items')->first(); - $pid = $product['_id']; + $pid = $product['id']; DB::table('items')->where('user_id', $userId)->delete($pid); @@ -100,7 +101,7 @@ public function testNoDocument() $item = DB::table('items')->where('name', 'nothing')->first(); $this->assertNull($item); - $item = DB::table('items')->where('_id', '51c33d8981fec6813e00000a')->first(); + $item = DB::table('items')->where('id', '51c33d8981fec6813e00000a')->first(); $this->assertNull($item); } @@ -339,37 +340,37 @@ public function testPush() 'messages' => [], ]); - DB::table('users')->where('_id', $id)->push('tags', 'tag1'); + DB::table('users')->where('id', $id)->push('tags', 'tag1'); $user = DB::table('users')->find($id); $this->assertIsArray($user['tags']); $this->assertCount(1, $user['tags']); $this->assertEquals('tag1', $user['tags'][0]); - DB::table('users')->where('_id', $id)->push('tags', 'tag2'); + DB::table('users')->where('id', $id)->push('tags', 'tag2'); $user = DB::table('users')->find($id); $this->assertCount(2, $user['tags']); $this->assertEquals('tag2', $user['tags'][1]); // Add duplicate - DB::table('users')->where('_id', $id)->push('tags', 'tag2'); + DB::table('users')->where('id', $id)->push('tags', 'tag2'); $user = DB::table('users')->find($id); $this->assertCount(3, $user['tags']); // Add unique - DB::table('users')->where('_id', $id)->push('tags', 'tag1', true); + DB::table('users')->where('id', $id)->push('tags', 'tag1', true); $user = DB::table('users')->find($id); $this->assertCount(3, $user['tags']); $message = ['from' => 'Jane', 'body' => 'Hi John']; - DB::table('users')->where('_id', $id)->push('messages', $message); + DB::table('users')->where('id', $id)->push('messages', $message); $user = DB::table('users')->find($id); $this->assertIsArray($user['messages']); $this->assertCount(1, $user['messages']); $this->assertEquals($message, $user['messages'][0]); // Raw - DB::table('users')->where('_id', $id)->push([ + DB::table('users')->where('id', $id)->push([ 'tags' => 'tag3', 'messages' => ['from' => 'Mark', 'body' => 'Hi John'], ]); @@ -377,7 +378,7 @@ public function testPush() $this->assertCount(4, $user['tags']); $this->assertCount(2, $user['messages']); - DB::table('users')->where('_id', $id)->push([ + DB::table('users')->where('id', $id)->push([ 'messages' => [ 'date' => new DateTime(), 'body' => 'Hi John', @@ -406,21 +407,21 @@ public function testPull() 'messages' => [$message1, $message2], ]); - DB::table('users')->where('_id', $id)->pull('tags', 'tag3'); + DB::table('users')->where('id', $id)->pull('tags', 'tag3'); $user = DB::table('users')->find($id); $this->assertIsArray($user['tags']); $this->assertCount(3, $user['tags']); $this->assertEquals('tag4', $user['tags'][2]); - DB::table('users')->where('_id', $id)->pull('messages', $message1); + DB::table('users')->where('id', $id)->pull('messages', $message1); $user = DB::table('users')->find($id); $this->assertIsArray($user['messages']); $this->assertCount(1, $user['messages']); // Raw - DB::table('users')->where('_id', $id)->pull(['tags' => 'tag2', 'messages' => $message2]); + DB::table('users')->where('id', $id)->pull(['tags' => 'tag2', 'messages' => $message2]); $user = DB::table('users')->find($id); $this->assertCount(2, $user['tags']); $this->assertCount(0, $user['messages']); @@ -449,24 +450,24 @@ public function testDistinct() public function testCustomId() { DB::table('items')->insert([ - ['_id' => 'knife', 'type' => 'sharp', 'amount' => 34], - ['_id' => 'fork', 'type' => 'sharp', 'amount' => 20], - ['_id' => 'spoon', 'type' => 'round', 'amount' => 3], + ['id' => 'knife', 'type' => 'sharp', 'amount' => 34], + ['id' => 'fork', 'type' => 'sharp', 'amount' => 20], + ['id' => 'spoon', 'type' => 'round', 'amount' => 3], ]); $item = DB::table('items')->find('knife'); - $this->assertEquals('knife', $item['_id']); + $this->assertEquals('knife', $item['id']); - $item = DB::table('items')->where('_id', 'fork')->first(); - $this->assertEquals('fork', $item['_id']); + $item = DB::table('items')->where('id', 'fork')->first(); + $this->assertEquals('fork', $item['id']); DB::table('users')->insert([ - ['_id' => 1, 'name' => 'Jane Doe'], - ['_id' => 2, 'name' => 'John Doe'], + ['id' => 1, 'name' => 'Jane Doe'], + ['id' => 2, 'name' => 'John Doe'], ]); $item = DB::table('users')->find(1); - $this->assertEquals(1, $item['_id']); + $this->assertEquals(1, $item['id']); } public function testTake() @@ -526,7 +527,7 @@ public function testList() $this->assertCount(3, $list); $this->assertEquals(['knife' => 'sharp', 'fork' => 'sharp', 'spoon' => 'round'], $list); - $list = DB::table('items')->pluck('name', '_id')->toArray(); + $list = DB::table('items')->pluck('name', 'id')->toArray(); $this->assertCount(4, $list); $this->assertEquals(24, strlen(key($list))); } @@ -667,7 +668,7 @@ public function testUpdateSubdocument() { $id = DB::table('users')->insertGetId(['name' => 'John Doe', 'address' => ['country' => 'Belgium']]); - DB::table('users')->where('_id', $id)->update(['address.country' => 'England']); + DB::table('users')->where('id', $id)->update(['address.country' => 'England']); $check = DB::table('users')->find($id); $this->assertEquals('England', $check['address']['country']); @@ -932,7 +933,7 @@ public function testCursor() ]; DB::table('items')->insert($data); - $results = DB::table('items')->orderBy('_id', 'asc')->cursor(); + $results = DB::table('items')->orderBy('id', 'asc')->cursor(); $this->assertInstanceOf(LazyCollection::class, $results); foreach ($results as $i => $result) { @@ -1048,4 +1049,20 @@ public function testIncrementEach() $this->assertEquals(5, $user['note']); $this->assertEquals('foo', $user['extra']); } + + #[TestWith(['id', 'id'])] + #[TestWith(['id', '_id'])] + #[TestWith(['_id', 'id'])] + public function testIdAlias($insertId, $queryId): void + { + DB::collection('items')->insert([$insertId => 'abc', 'name' => 'Karting']); + $item = DB::collection('items')->where($queryId, '=', 'abc')->first(); + $this->assertNotNull($item); + $this->assertSame('abc', $item['id']); + $this->assertSame('Karting', $item['name']); + + DB::collection('items')->where($insertId, '=', 'abc')->update(['name' => 'Bike']); + $item = DB::collection('items')->where($queryId, '=', 'abc')->first(); + $this->assertSame('Bike', $item['name']); + } } diff --git a/tests/QueryTest.php b/tests/QueryTest.php index 2fd66bf70..e228a0f70 100644 --- a/tests/QueryTest.php +++ b/tests/QueryTest.php @@ -405,7 +405,7 @@ public function testCount(): void $this->assertEquals(6, $count); // Test for issue #165 - $count = User::select('_id', 'age', 'title')->where('age', '<>', 35)->count(); + $count = User::select('id', 'age', 'title')->where('age', '<>', 35)->count(); $this->assertEquals(6, $count); } diff --git a/tests/QueueTest.php b/tests/QueueTest.php index 2236fba1b..af31c8a5b 100644 --- a/tests/QueueTest.php +++ b/tests/QueueTest.php @@ -71,7 +71,7 @@ public function testQueueJobExpired(): void $expiry = Carbon::now()->subSeconds(Config::get('queue.connections.database.expire'))->getTimestamp(); Queue::getDatabase() ->table(Config::get('queue.connections.database.table')) - ->where('_id', $id) + ->where('id', $id) ->update(['reserved' => 1, 'reserved_at' => $expiry]); // Expect an attempted older job in the queue @@ -158,7 +158,7 @@ public function testQueueRelease(): void $releasedJob = Queue::getDatabase() ->table(Config::get('queue.connections.database.table')) - ->where('_id', $releasedJobId) + ->where('id', $releasedJobId) ->first(); $this->assertEquals($queue, $releasedJob['queue']); diff --git a/tests/RelationsTest.php b/tests/RelationsTest.php index 02efbc77b..902f0499c 100644 --- a/tests/RelationsTest.php +++ b/tests/RelationsTest.php @@ -40,16 +40,16 @@ public function tearDown(): void public function testHasMany(): void { $author = User::create(['name' => '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]); + 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->assertCount(2, $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' => '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; @@ -59,32 +59,32 @@ public function testHasMany(): void public function testHasManyWithTrashed(): void { $user = User::create(['name' => 'George R. R. Martin']); - $first = Soft::create(['title' => 'A Game of Thrones', 'user_id' => $user->_id]); - $second = Soft::create(['title' => 'The Witcher', 'user_id' => $user->_id]); + $first = Soft::create(['title' => 'A Game of Thrones', 'user_id' => $user->id]); + $second = Soft::create(['title' => 'The Witcher', 'user_id' => $user->id]); self::assertNull($first->deleted_at); - self::assertEquals($user->_id, $first->user->_id); - self::assertEquals([$first->_id, $second->_id], $user->softs->pluck('_id')->toArray()); + self::assertEquals($user->id, $first->user->id); + self::assertEquals([$first->id, $second->id], $user->softs->pluck('id')->toArray()); $first->delete(); $user->refresh(); self::assertNotNull($first->deleted_at); - self::assertEquals([$second->_id], $user->softs->pluck('_id')->toArray()); - self::assertEquals([$first->_id, $second->_id], $user->softsWithTrashed->pluck('_id')->toArray()); + self::assertEquals([$second->id], $user->softs->pluck('id')->toArray()); + self::assertEquals([$first->id, $second->id], $user->softsWithTrashed->pluck('id')->toArray()); } public function testBelongsTo(): void { $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]); + 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]); + $item = Item::create(['type' => 'sword', 'user_id' => $user->id]); $owner = $item->user; $this->assertEquals('John Doe', $owner->name); @@ -96,11 +96,11 @@ public function testBelongsTo(): void public function testHasOne(): void { $user = User::create(['name' => 'John Doe']); - Role::create(['type' => 'admin', 'user_id' => $user->_id]); + Role::create(['type' => 'admin', 'user_id' => $user->id]); $role = $user->role; $this->assertEquals('admin', $role->type); - $this->assertEquals($user->_id, $role->user_id); + $this->assertEquals($user->id, $role->user_id); $user = User::create(['name' => 'Jane Doe']); $role = new Role(['type' => 'user']); @@ -108,20 +108,20 @@ public function testHasOne(): void $role = $user->role; $this->assertEquals('user', $role->type); - $this->assertEquals($user->_id, $role->user_id); + $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); + $this->assertEquals($user->id, $role->user_id); } public function testWithBelongsTo(): void { $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' => '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(); @@ -136,12 +136,12 @@ public function testWithBelongsTo(): void public function testWithHashMany(): void { $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' => '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); + $user = User::with('items')->find($user->id); $items = $user->getRelation('items'); $this->assertCount(3, $items); @@ -151,10 +151,10 @@ public function testWithHashMany(): void public function testWithHasOne(): void { $user = User::create(['name' => 'John Doe']); - Role::create(['type' => 'admin', 'user_id' => $user->_id]); - Role::create(['type' => 'guest', 'user_id' => $user->_id]); + Role::create(['type' => 'admin', 'user_id' => $user->id]); + Role::create(['type' => 'guest', 'user_id' => $user->id]); - $user = User::with('role')->find($user->_id); + $user = User::with('role')->find($user->id); $role = $user->getRelation('role'); $this->assertInstanceOf(Role::class, $role); @@ -168,22 +168,22 @@ public function testEasyRelation(): void $item = Item::create(['type' => 'knife']); $user->items()->save($item); - $user = User::find($user->_id); + $user = User::find($user->id); $items = $user->items; $this->assertCount(1, $items); $this->assertInstanceOf(Item::class, $items[0]); - $this->assertEquals($user->_id, $items[0]->user_id); + $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); + $user = User::find($user->id); $role = $user->role; $this->assertInstanceOf(Role::class, $role); $this->assertEquals('admin', $role->type); - $this->assertEquals($user->_id, $role->user_id); + $this->assertEquals($user->id, $role->user_id); } public function testBelongsToMany(): void @@ -195,7 +195,7 @@ public function testBelongsToMany(): void $user->clients()->create(['name' => 'Buffet Bar Inc.']); // Refetch - $user = User::with('clients')->find($user->_id); + $user = User::with('clients')->find($user->id); $client = Client::with('users')->first(); // Check for relation attributes @@ -228,8 +228,8 @@ public function testBelongsToMany(): void $this->assertInstanceOf(User::class, $user); // Assert they are not attached - $this->assertNotContains($client->_id, $user->client_ids); - $this->assertNotContains($user->_id, $client->user_ids); + $this->assertNotContains($client->id, $user->client_ids); + $this->assertNotContains($user->id, $client->user_ids); $this->assertCount(1, $user->clients); $this->assertCount(1, $client->users); @@ -241,8 +241,8 @@ public function testBelongsToMany(): void $client = Client::Where('name', '=', 'Buffet Bar Inc.')->first(); // Assert they are attached - $this->assertContains($client->_id, $user->client_ids); - $this->assertContains($user->_id, $client->user_ids); + $this->assertContains($client->id, $user->client_ids); + $this->assertContains($user->id, $client->user_ids); $this->assertCount(2, $user->clients); $this->assertCount(2, $client->users); @@ -254,8 +254,8 @@ public function testBelongsToMany(): void $client = Client::Where('name', '=', 'Buffet Bar Inc.')->first(); // Assert they are not attached - $this->assertNotContains($client->_id, $user->client_ids); - $this->assertNotContains($user->_id, $client->user_ids); + $this->assertNotContains($client->id, $user->client_ids); + $this->assertNotContains($user->id, $client->user_ids); $this->assertCount(0, $user->clients); $this->assertCount(1, $client->users); } @@ -265,19 +265,19 @@ public function testBelongsToManyAttachesExistingModels(): void $user = User::create(['name' => 'John Doe', 'client_ids' => ['1234523']]); $clients = [ - Client::create(['name' => 'Pork Pies Ltd.'])->_id, - Client::create(['name' => 'Buffet Bar Inc.'])->_id, + 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, + 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); + $user = User::with('clients')->find($user->id); // Assert non attached ID's are detached successfully $this->assertNotContains('1234523', $user->client_ids); @@ -289,7 +289,7 @@ public function testBelongsToManyAttachesExistingModels(): void $user->clients()->sync($moreClients); // Refetch - $user = User::with('clients')->find($user->_id); + $user = User::with('clients')->find($user->id); // Assert there are now still 2 client objects in the relationship $this->assertCount(2, $user->clients); @@ -307,11 +307,11 @@ public function testBelongsToManySync(): void $client2 = Client::create(['name' => 'Buffet Bar Inc.']); // Sync multiple - $user->clients()->sync([$client1->_id, $client2->_id]); + $user->clients()->sync([$client1->id, $client2->id]); $this->assertCount(2, $user->clients); // Sync single wrapped by an array - $user->clients()->sync([$client1->_id]); + $user->clients()->sync([$client1->id]); $user->load('clients'); $this->assertCount(1, $user->clients); @@ -328,8 +328,8 @@ public function testBelongsToManySync(): void public function testBelongsToManyAttachArray(): void { $user = User::create(['name' => 'John Doe']); - $client1 = Client::create(['name' => 'Test 1'])->_id; - $client2 = Client::create(['name' => 'Test 2'])->_id; + $client1 = Client::create(['name' => 'Test 1'])->id; + $client2 = Client::create(['name' => 'Test 2'])->id; $user->clients()->attach([$client1, $client2]); $this->assertCount(2, $user->clients); @@ -353,27 +353,27 @@ public function testBelongsToManySyncWithCustomKeys(): void $skill1 = Skill::create(['cskill_id' => (string) (new ObjectId()), 'name' => 'PHP']); $skill2 = Skill::create(['cskill_id' => (string) (new ObjectId()), 'name' => 'Laravel']); - $client = Client::query()->find($client->_id); + $client = Client::query()->find($client->id); $client->skillsWithCustomKeys()->sync([$skill1->cskill_id, $skill2->cskill_id]); $this->assertCount(2, $client->skillsWithCustomKeys); self::assertIsString($skill1->cskill_id); self::assertContains($skill1->cskill_id, $client->skillsWithCustomKeys->pluck('cskill_id')); - self::assertNotContains($skill1->_id, $client->skillsWithCustomKeys->pluck('cskill_id')); + self::assertNotContains($skill1->id, $client->skillsWithCustomKeys->pluck('cskill_id')); self::assertIsString($skill2->cskill_id); self::assertContains($skill2->cskill_id, $client->skillsWithCustomKeys->pluck('cskill_id')); - self::assertNotContains($skill2->_id, $client->skillsWithCustomKeys->pluck('cskill_id')); + self::assertNotContains($skill2->id, $client->skillsWithCustomKeys->pluck('cskill_id')); - $check = Skill::query()->find($skill1->_id); + $check = Skill::query()->find($skill1->id); self::assertIsString($check->cskill_id); self::assertContains($check->cskill_id, $client->skillsWithCustomKeys->pluck('cskill_id')); - self::assertNotContains($check->_id, $client->skillsWithCustomKeys->pluck('cskill_id')); + self::assertNotContains($check->id, $client->skillsWithCustomKeys->pluck('cskill_id')); - $check = Skill::query()->find($skill2->_id); + $check = Skill::query()->find($skill2->id); self::assertIsString($check->cskill_id); self::assertContains($check->cskill_id, $client->skillsWithCustomKeys->pluck('cskill_id')); - self::assertNotContains($check->_id, $client->skillsWithCustomKeys->pluck('cskill_id')); + self::assertNotContains($check->id, $client->skillsWithCustomKeys->pluck('cskill_id')); } public function testBelongsToManySyncModelWithCustomKeys(): void @@ -381,18 +381,18 @@ public function testBelongsToManySyncModelWithCustomKeys(): void $client = Client::create(['cclient_id' => (string) (new ObjectId()), 'years' => '5']); $skill1 = Skill::create(['cskill_id' => (string) (new ObjectId()), 'name' => 'PHP']); - $client = Client::query()->find($client->_id); + $client = Client::query()->find($client->id); $client->skillsWithCustomKeys()->sync($skill1); $this->assertCount(1, $client->skillsWithCustomKeys); self::assertIsString($skill1->cskill_id); self::assertContains($skill1->cskill_id, $client->skillsWithCustomKeys->pluck('cskill_id')); - self::assertNotContains($skill1->_id, $client->skillsWithCustomKeys->pluck('cskill_id')); + self::assertNotContains($skill1->id, $client->skillsWithCustomKeys->pluck('cskill_id')); - $check = Skill::query()->find($skill1->_id); - self::assertIsString($check->_id); + $check = Skill::query()->find($skill1->id); + self::assertIsString($check->id); self::assertContains($check->cskill_id, $client->skillsWithCustomKeys->pluck('cskill_id')); - self::assertNotContains($check->_id, $client->skillsWithCustomKeys->pluck('cskill_id')); + self::assertNotContains($check->id, $client->skillsWithCustomKeys->pluck('cskill_id')); } public function testBelongsToManySyncEloquentCollectionWithCustomKeys(): void @@ -402,27 +402,27 @@ public function testBelongsToManySyncEloquentCollectionWithCustomKeys(): void $skill2 = Skill::create(['cskill_id' => (string) (new ObjectId()), 'name' => 'Laravel']); $collection = new Collection([$skill1, $skill2]); - $client = Client::query()->find($client->_id); + $client = Client::query()->find($client->id); $client->skillsWithCustomKeys()->sync($collection); $this->assertCount(2, $client->skillsWithCustomKeys); self::assertIsString($skill1->cskill_id); self::assertContains($skill1->cskill_id, $client->skillsWithCustomKeys->pluck('cskill_id')); - self::assertNotContains($skill1->_id, $client->skillsWithCustomKeys->pluck('cskill_id')); + self::assertNotContains($skill1->id, $client->skillsWithCustomKeys->pluck('cskill_id')); self::assertIsString($skill2->cskill_id); self::assertContains($skill2->cskill_id, $client->skillsWithCustomKeys->pluck('cskill_id')); - self::assertNotContains($skill2->_id, $client->skillsWithCustomKeys->pluck('cskill_id')); + self::assertNotContains($skill2->id, $client->skillsWithCustomKeys->pluck('cskill_id')); - $check = Skill::query()->find($skill1->_id); + $check = Skill::query()->find($skill1->id); self::assertIsString($check->cskill_id); self::assertContains($check->cskill_id, $client->skillsWithCustomKeys->pluck('cskill_id')); - self::assertNotContains($check->_id, $client->skillsWithCustomKeys->pluck('cskill_id')); + self::assertNotContains($check->id, $client->skillsWithCustomKeys->pluck('cskill_id')); - $check = Skill::query()->find($skill2->_id); + $check = Skill::query()->find($skill2->id); self::assertIsString($check->cskill_id); self::assertContains($check->cskill_id, $client->skillsWithCustomKeys->pluck('cskill_id')); - self::assertNotContains($check->_id, $client->skillsWithCustomKeys->pluck('cskill_id')); + self::assertNotContains($check->id, $client->skillsWithCustomKeys->pluck('cskill_id')); } public function testBelongsToManyAttachWithCustomKeys(): void @@ -431,27 +431,27 @@ public function testBelongsToManyAttachWithCustomKeys(): void $skill1 = Skill::create(['cskill_id' => (string) (new ObjectId()), 'name' => 'PHP']); $skill2 = Skill::create(['cskill_id' => (string) (new ObjectId()), 'name' => 'Laravel']); - $client = Client::query()->find($client->_id); + $client = Client::query()->find($client->id); $client->skillsWithCustomKeys()->attach([$skill1->cskill_id, $skill2->cskill_id]); $this->assertCount(2, $client->skillsWithCustomKeys); self::assertIsString($skill1->cskill_id); self::assertContains($skill1->cskill_id, $client->skillsWithCustomKeys->pluck('cskill_id')); - self::assertNotContains($skill1->_id, $client->skillsWithCustomKeys->pluck('cskill_id')); + self::assertNotContains($skill1->id, $client->skillsWithCustomKeys->pluck('cskill_id')); self::assertIsString($skill2->cskill_id); self::assertContains($skill2->cskill_id, $client->skillsWithCustomKeys->pluck('cskill_id')); - self::assertNotContains($skill2->_id, $client->skillsWithCustomKeys->pluck('cskill_id')); + self::assertNotContains($skill2->id, $client->skillsWithCustomKeys->pluck('cskill_id')); - $check = Skill::query()->find($skill1->_id); + $check = Skill::query()->find($skill1->id); self::assertIsString($check->cskill_id); self::assertContains($check->cskill_id, $client->skillsWithCustomKeys->pluck('cskill_id')); - self::assertNotContains($check->_id, $client->skillsWithCustomKeys->pluck('cskill_id')); + self::assertNotContains($check->id, $client->skillsWithCustomKeys->pluck('cskill_id')); - $check = Skill::query()->find($skill2->_id); + $check = Skill::query()->find($skill2->id); self::assertIsString($check->cskill_id); self::assertContains($check->cskill_id, $client->skillsWithCustomKeys->pluck('cskill_id')); - self::assertNotContains($check->_id, $client->skillsWithCustomKeys->pluck('cskill_id')); + self::assertNotContains($check->id, $client->skillsWithCustomKeys->pluck('cskill_id')); } public function testBelongsToManyAttachModelWithCustomKeys(): void @@ -459,18 +459,18 @@ public function testBelongsToManyAttachModelWithCustomKeys(): void $client = Client::create(['cclient_id' => (string) (new ObjectId()), 'years' => '5']); $skill1 = Skill::create(['cskill_id' => (string) (new ObjectId()), 'name' => 'PHP']); - $client = Client::query()->find($client->_id); + $client = Client::query()->find($client->id); $client->skillsWithCustomKeys()->attach($skill1); $this->assertCount(1, $client->skillsWithCustomKeys); self::assertIsString($skill1->cskill_id); self::assertContains($skill1->cskill_id, $client->skillsWithCustomKeys->pluck('cskill_id')); - self::assertNotContains($skill1->_id, $client->skillsWithCustomKeys->pluck('cskill_id')); + self::assertNotContains($skill1->id, $client->skillsWithCustomKeys->pluck('cskill_id')); - $check = Skill::query()->find($skill1->_id); - self::assertIsString($check->_id); + $check = Skill::query()->find($skill1->id); + self::assertIsString($check->id); self::assertContains($check->cskill_id, $client->skillsWithCustomKeys->pluck('cskill_id')); - self::assertNotContains($check->_id, $client->skillsWithCustomKeys->pluck('cskill_id')); + self::assertNotContains($check->id, $client->skillsWithCustomKeys->pluck('cskill_id')); } public function testBelongsToManyAttachEloquentCollectionWithCustomKeys(): void @@ -480,27 +480,27 @@ public function testBelongsToManyAttachEloquentCollectionWithCustomKeys(): void $skill2 = Skill::create(['cskill_id' => (string) (new ObjectId()), 'name' => 'Laravel']); $collection = new Collection([$skill1, $skill2]); - $client = Client::query()->find($client->_id); + $client = Client::query()->find($client->id); $client->skillsWithCustomKeys()->attach($collection); $this->assertCount(2, $client->skillsWithCustomKeys); self::assertIsString($skill1->cskill_id); self::assertContains($skill1->cskill_id, $client->skillsWithCustomKeys->pluck('cskill_id')); - self::assertNotContains($skill1->_id, $client->skillsWithCustomKeys->pluck('cskill_id')); + self::assertNotContains($skill1->id, $client->skillsWithCustomKeys->pluck('cskill_id')); self::assertIsString($skill2->cskill_id); self::assertContains($skill2->cskill_id, $client->skillsWithCustomKeys->pluck('cskill_id')); - self::assertNotContains($skill2->_id, $client->skillsWithCustomKeys->pluck('cskill_id')); + self::assertNotContains($skill2->id, $client->skillsWithCustomKeys->pluck('cskill_id')); - $check = Skill::query()->find($skill1->_id); + $check = Skill::query()->find($skill1->id); self::assertIsString($check->cskill_id); self::assertContains($check->cskill_id, $client->skillsWithCustomKeys->pluck('cskill_id')); - self::assertNotContains($check->_id, $client->skillsWithCustomKeys->pluck('cskill_id')); + self::assertNotContains($check->id, $client->skillsWithCustomKeys->pluck('cskill_id')); - $check = Skill::query()->find($skill2->_id); + $check = Skill::query()->find($skill2->id); self::assertIsString($check->cskill_id); self::assertContains($check->cskill_id, $client->skillsWithCustomKeys->pluck('cskill_id')); - self::assertNotContains($check->_id, $client->skillsWithCustomKeys->pluck('cskill_id')); + self::assertNotContains($check->id, $client->skillsWithCustomKeys->pluck('cskill_id')); } public function testBelongsToManyDetachWithCustomKeys(): void @@ -509,7 +509,7 @@ public function testBelongsToManyDetachWithCustomKeys(): void $skill1 = Skill::create(['cskill_id' => (string) (new ObjectId()), 'name' => 'PHP']); $skill2 = Skill::create(['cskill_id' => (string) (new ObjectId()), 'name' => 'Laravel']); - $client = Client::query()->find($client->_id); + $client = Client::query()->find($client->id); $client->skillsWithCustomKeys()->sync([$skill1->cskill_id, $skill2->cskill_id]); $this->assertCount(2, $client->skillsWithCustomKeys); @@ -519,21 +519,21 @@ public function testBelongsToManyDetachWithCustomKeys(): void self::assertIsString($skill1->cskill_id); self::assertNotContains($skill1->cskill_id, $client->skillsWithCustomKeys->pluck('cskill_id')); - self::assertNotContains($skill1->_id, $client->skillsWithCustomKeys->pluck('cskill_id')); + self::assertNotContains($skill1->id, $client->skillsWithCustomKeys->pluck('cskill_id')); self::assertIsString($skill2->cskill_id); self::assertContains($skill2->cskill_id, $client->skillsWithCustomKeys->pluck('cskill_id')); - self::assertNotContains($skill2->_id, $client->skillsWithCustomKeys->pluck('cskill_id')); + self::assertNotContains($skill2->id, $client->skillsWithCustomKeys->pluck('cskill_id')); - $check = Skill::query()->find($skill1->_id); + $check = Skill::query()->find($skill1->id); self::assertIsString($check->cskill_id); self::assertNotContains($check->cskill_id, $client->skillsWithCustomKeys->pluck('cskill_id')); - self::assertNotContains($check->_id, $client->skillsWithCustomKeys->pluck('cskill_id')); + self::assertNotContains($check->id, $client->skillsWithCustomKeys->pluck('cskill_id')); - $check = Skill::query()->find($skill2->_id); + $check = Skill::query()->find($skill2->id); self::assertIsString($check->cskill_id); self::assertContains($check->cskill_id, $client->skillsWithCustomKeys->pluck('cskill_id')); - self::assertNotContains($check->_id, $client->skillsWithCustomKeys->pluck('cskill_id')); + self::assertNotContains($check->id, $client->skillsWithCustomKeys->pluck('cskill_id')); } public function testBelongsToManyDetachModelWithCustomKeys(): void @@ -542,7 +542,7 @@ public function testBelongsToManyDetachModelWithCustomKeys(): void $skill1 = Skill::create(['cskill_id' => (string) (new ObjectId()), 'name' => 'PHP']); $skill2 = Skill::create(['cskill_id' => (string) (new ObjectId()), 'name' => 'Laravel']); - $client = Client::query()->find($client->_id); + $client = Client::query()->find($client->id); $client->skillsWithCustomKeys()->sync([$skill1->cskill_id, $skill2->cskill_id]); $this->assertCount(2, $client->skillsWithCustomKeys); @@ -552,28 +552,28 @@ public function testBelongsToManyDetachModelWithCustomKeys(): void self::assertIsString($skill1->cskill_id); self::assertNotContains($skill1->cskill_id, $client->skillsWithCustomKeys->pluck('cskill_id')); - self::assertNotContains($skill1->_id, $client->skillsWithCustomKeys->pluck('cskill_id')); + self::assertNotContains($skill1->id, $client->skillsWithCustomKeys->pluck('cskill_id')); self::assertIsString($skill2->cskill_id); self::assertContains($skill2->cskill_id, $client->skillsWithCustomKeys->pluck('cskill_id')); - self::assertNotContains($skill2->_id, $client->skillsWithCustomKeys->pluck('cskill_id')); + self::assertNotContains($skill2->id, $client->skillsWithCustomKeys->pluck('cskill_id')); - $check = Skill::query()->find($skill1->_id); + $check = Skill::query()->find($skill1->id); self::assertIsString($check->cskill_id); self::assertNotContains($check->cskill_id, $client->skillsWithCustomKeys->pluck('cskill_id')); - self::assertNotContains($check->_id, $client->skillsWithCustomKeys->pluck('cskill_id')); + self::assertNotContains($check->id, $client->skillsWithCustomKeys->pluck('cskill_id')); - $check = Skill::query()->find($skill2->_id); + $check = Skill::query()->find($skill2->id); self::assertIsString($check->cskill_id); self::assertContains($check->cskill_id, $client->skillsWithCustomKeys->pluck('cskill_id')); - self::assertNotContains($check->_id, $client->skillsWithCustomKeys->pluck('cskill_id')); + self::assertNotContains($check->id, $client->skillsWithCustomKeys->pluck('cskill_id')); } public function testBelongsToManySyncAlreadyPresent(): void { $user = User::create(['name' => 'John Doe']); - $client1 = Client::create(['name' => 'Test 1'])->_id; - $client2 = Client::create(['name' => 'Test 2'])->_id; + $client1 = Client::create(['name' => 'Test 1'])->id; + $client2 = Client::create(['name' => 'Test 2'])->id; $user->clients()->sync([$client1, $client2]); $this->assertCount(2, $user->clients); @@ -592,18 +592,18 @@ public function testBelongsToManyCustom(): void $group = $user->groups()->create(['name' => 'Admins']); // Refetch - $user = User::find($user->_id); - $group = Group::find($group->_id); + $user = User::find($user->id); + $group = Group::find($group->id); // Check for custom relation attributes $this->assertArrayHasKey('users', $group->getAttributes()); $this->assertArrayHasKey('groups', $user->getAttributes()); // Assert they are attached - $this->assertContains($group->_id, $user->groups->pluck('_id')->toArray()); - $this->assertContains($user->_id, $group->users->pluck('_id')->toArray()); - $this->assertEquals($group->_id, $user->groups()->first()->_id); - $this->assertEquals($user->_id, $group->users()->first()->_id); + $this->assertContains($group->id, $user->groups->pluck('id')->toArray()); + $this->assertContains($user->id, $group->users->pluck('id')->toArray()); + $this->assertEquals($group->id, $user->groups()->first()->id); + $this->assertEquals($user->id, $group->users()->first()->id); } public function testMorph(): void @@ -617,7 +617,7 @@ public function testMorph(): void $this->assertEquals(1, $user->photos->count()); $this->assertEquals($photo->id, $user->photos->first()->id); - $user = User::find($user->_id); + $user = User::find($user->id); $this->assertEquals(1, $user->photos->count()); $this->assertEquals($photo->id, $user->photos->first()->id); @@ -627,14 +627,14 @@ public function testMorph(): void $this->assertNotNull($client->photo); $this->assertEquals($photo->id, $client->photo->id); - $client = Client::find($client->_id); + $client = Client::find($client->id); $this->assertNotNull($client->photo); $this->assertEquals($photo->id, $client->photo->id); $photo = Photo::first(); $this->assertEquals($photo->hasImage->name, $user->name); - $user = User::with('photos')->find($user->_id); + $user = User::with('photos')->find($user->id); $relations = $user->getRelations(); $this->assertArrayHasKey('photos', $relations); $this->assertEquals(1, $relations['photos']->count()); @@ -655,7 +655,7 @@ public function testMorph(): void $this->assertCount(1, $photo->hasImage()->get()); $this->assertInstanceOf(Client::class, $photo->hasImage); - $this->assertEquals($client->_id, $photo->hasImage->_id); + $this->assertEquals($client->id, $photo->hasImage->id); // inverse with custom ownerKey $photo = Photo::query()->create(['url' => 'https://graph.facebook.com/young.gerald/picture']); @@ -665,7 +665,7 @@ public function testMorph(): void $this->assertCount(1, $photo->hasImageWithCustomOwnerKey()->get()); $this->assertInstanceOf(Client::class, $photo->hasImageWithCustomOwnerKey); $this->assertEquals($client->cclient_id, $photo->has_image_with_custom_owner_key_id); - $this->assertEquals($client->_id, $photo->hasImageWithCustomOwnerKey->_id); + $this->assertEquals($client->id, $photo->hasImageWithCustomOwnerKey->id); } public function testMorphToMany(): void @@ -679,10 +679,10 @@ public function testMorphToMany(): void $client->labels()->attach($label); $this->assertEquals(1, $user->labels->count()); - $this->assertContains($label->_id, $user->labels->pluck('_id')); + $this->assertContains($label->id, $user->labels->pluck('id')); $this->assertEquals(1, $client->labels->count()); - $this->assertContains($label->_id, $user->labels->pluck('_id')); + $this->assertContains($label->id, $user->labels->pluck('id')); } public function testMorphToManyAttachEloquentCollection(): void @@ -695,8 +695,8 @@ public function testMorphToManyAttachEloquentCollection(): void $client->labels()->attach(new Collection([$label1, $label2])); $this->assertEquals(2, $client->labels->count()); - $this->assertContains($label1->_id, $client->labels->pluck('_id')); - $this->assertContains($label2->_id, $client->labels->pluck('_id')); + $this->assertContains($label1->id, $client->labels->pluck('id')); + $this->assertContains($label2->id, $client->labels->pluck('id')); } public function testMorphToManyAttachMultipleIds(): void @@ -706,11 +706,11 @@ public function testMorphToManyAttachMultipleIds(): void $label1 = Label::query()->create(['name' => 'stayed solid i never fled']); $label2 = Label::query()->create(['name' => "I've got a lane and I'm in gear"]); - $client->labels()->attach([$label1->_id, $label2->_id]); + $client->labels()->attach([$label1->id, $label2->id]); $this->assertEquals(2, $client->labels->count()); - $this->assertContains($label1->_id, $client->labels->pluck('_id')); - $this->assertContains($label2->_id, $client->labels->pluck('_id')); + $this->assertContains($label1->id, $client->labels->pluck('id')); + $this->assertContains($label2->id, $client->labels->pluck('id')); } public function testMorphToManyDetaching(): void @@ -720,7 +720,7 @@ public function testMorphToManyDetaching(): void $label1 = Label::query()->create(['name' => "I'll never love again"]); $label2 = Label::query()->create(['name' => 'The way I loved you']); - $client->labels()->attach([$label1->_id, $label2->_id]); + $client->labels()->attach([$label1->id, $label2->id]); $this->assertEquals(2, $client->labels->count()); @@ -728,7 +728,7 @@ public function testMorphToManyDetaching(): void $check = $client->withoutRelations(); $this->assertEquals(1, $check->labels->count()); - $this->assertContains($label2->_id, $client->labels->pluck('_id')); + $this->assertContains($label2->id, $client->labels->pluck('id')); } public function testMorphToManyDetachingMultipleIds(): void @@ -739,15 +739,15 @@ public function testMorphToManyDetachingMultipleIds(): void $label2 = Label::query()->create(['name' => "My skin's thick, but I'm not bulletproof"]); $label3 = Label::query()->create(['name' => 'All I can be is myself, go, and tell the truth']); - $client->labels()->attach([$label1->_id, $label2->_id, $label3->_id]); + $client->labels()->attach([$label1->id, $label2->id, $label3->id]); $this->assertEquals(3, $client->labels->count()); - $client->labels()->detach([$label1->_id, $label2->_id]); + $client->labels()->detach([$label1->id, $label2->id]); $client->refresh(); $this->assertEquals(1, $client->labels->count()); - $this->assertContains($label3->_id, $client->labels->pluck('_id')); + $this->assertContains($label3->id, $client->labels->pluck('id')); } public function testMorphToManySyncing(): void @@ -763,12 +763,12 @@ public function testMorphToManySyncing(): void $client->labels()->sync($label2, false); $this->assertEquals(1, $user->labels->count()); - $this->assertContains($label->_id, $user->labels->pluck('_id')); - $this->assertNotContains($label2->_id, $user->labels->pluck('_id')); + $this->assertContains($label->id, $user->labels->pluck('id')); + $this->assertNotContains($label2->id, $user->labels->pluck('id')); $this->assertEquals(2, $client->labels->count()); - $this->assertContains($label->_id, $client->labels->pluck('_id')); - $this->assertContains($label2->_id, $client->labels->pluck('_id')); + $this->assertContains($label->id, $client->labels->pluck('id')); + $this->assertContains($label2->id, $client->labels->pluck('id')); } public function testMorphToManySyncingEloquentCollection(): void @@ -781,8 +781,8 @@ public function testMorphToManySyncingEloquentCollection(): void $client->labels()->sync(new Collection([$label, $label2])); $this->assertEquals(2, $client->labels->count()); - $this->assertContains($label->_id, $client->labels->pluck('_id')); - $this->assertContains($label2->_id, $client->labels->pluck('_id')); + $this->assertContains($label->id, $client->labels->pluck('id')); + $this->assertContains($label2->id, $client->labels->pluck('id')); } public function testMorphToManySyncingMultipleIds(): void @@ -792,11 +792,11 @@ public function testMorphToManySyncingMultipleIds(): void $label = Label::query()->create(['name' => 'They all talk about karma, how it slowly comes']); $label2 = Label::query()->create(['name' => "But life is short, enjoy it while you're young"]); - $client->labels()->sync([$label->_id, $label2->_id]); + $client->labels()->sync([$label->id, $label2->id]); $this->assertEquals(2, $client->labels->count()); - $this->assertContains($label->_id, $client->labels->pluck('_id')); - $this->assertContains($label2->_id, $client->labels->pluck('_id')); + $this->assertContains($label->id, $client->labels->pluck('id')); + $this->assertContains($label2->id, $client->labels->pluck('id')); } public function testMorphToManySyncingWithCustomKeys(): void @@ -809,15 +809,15 @@ public function testMorphToManySyncingWithCustomKeys(): void $client->labelsWithCustomKeys()->sync([$label->clabel_id, $label2->clabel_id]); $this->assertEquals(2, $client->labelsWithCustomKeys->count()); - $this->assertContains($label->_id, $client->labelsWithCustomKeys->pluck('_id')); - $this->assertContains($label2->_id, $client->labelsWithCustomKeys->pluck('_id')); + $this->assertContains($label->id, $client->labelsWithCustomKeys->pluck('id')); + $this->assertContains($label2->id, $client->labelsWithCustomKeys->pluck('id')); $client->labelsWithCustomKeys()->sync($label); $client->load('labelsWithCustomKeys'); $this->assertEquals(1, $client->labelsWithCustomKeys->count()); - $this->assertContains($label->_id, $client->labelsWithCustomKeys->pluck('_id')); - $this->assertNotContains($label2->_id, $client->labelsWithCustomKeys->pluck('_id')); + $this->assertContains($label->id, $client->labelsWithCustomKeys->pluck('id')); + $this->assertNotContains($label2->id, $client->labelsWithCustomKeys->pluck('id')); } public function testMorphToManyLoadAndRefreshing(): void @@ -829,7 +829,7 @@ public function testMorphToManyLoadAndRefreshing(): void $label = Label::query()->create(['name' => 'The greatest gift is knowledge itself']); $label2 = Label::query()->create(['name' => "I made it here all by my lonely, no askin' for help"]); - $client->labels()->sync([$label->_id, $label2->_id]); + $client->labels()->sync([$label->id, $label2->id]); $client->users()->sync($user); $this->assertEquals(2, $client->labels->count()); @@ -842,11 +842,11 @@ public function testMorphToManyLoadAndRefreshing(): void $this->assertEquals(2, $client->labels->count()); - $check = Client::query()->find($client->_id); + $check = Client::query()->find($client->id); $this->assertEquals(2, $check->labels->count()); - $check = Client::query()->with('labels')->find($client->_id); + $check = Client::query()->with('labels')->find($client->id); $this->assertEquals(2, $check->labels->count()); } @@ -860,7 +860,7 @@ public function testMorphToManyHasQuery(): void $label = Label::query()->create(['name' => "I've been digging myself down deeper"]); $label2 = Label::query()->create(['name' => "I won't stop 'til I get where you are"]); - $client->labels()->sync([$label->_id, $label2->_id]); + $client->labels()->sync([$label->id, $label2->id]); $client2->labels()->sync($label); $this->assertEquals(2, $client->labels->count()); @@ -871,12 +871,12 @@ public function testMorphToManyHasQuery(): void $check = Client::query()->has('labels', '>', 1)->get(); $this->assertCount(1, $check); - $this->assertContains($client->_id, $check->pluck('_id')); + $this->assertContains($client->id, $check->pluck('id')); $check = Client::query()->has('labels', '<', 2)->get(); $this->assertCount(2, $check); - $this->assertContains($client2->_id, $check->pluck('_id')); - $this->assertContains($client3->_id, $check->pluck('_id')); + $this->assertContains($client2->id, $check->pluck('id')); + $this->assertContains($client3->id, $check->pluck('id')); } public function testMorphedByMany(): void @@ -891,10 +891,10 @@ public function testMorphedByMany(): void $label->clients()->attach($client); $this->assertEquals(1, $label->users->count()); - $this->assertContains($user->_id, $label->users->pluck('_id')); + $this->assertContains($user->id, $label->users->pluck('id')); $this->assertEquals(1, $label->clients->count()); - $this->assertContains($client->_id, $label->clients->pluck('_id')); + $this->assertContains($client->id, $label->clients->pluck('id')); } public function testMorphedByManyAttachEloquentCollection(): void @@ -908,8 +908,8 @@ public function testMorphedByManyAttachEloquentCollection(): void $label->clients()->attach(new Collection([$client1, $client2])); $this->assertEquals(2, $label->clients->count()); - $this->assertContains($client1->_id, $label->clients->pluck('_id')); - $this->assertContains($client2->_id, $label->clients->pluck('_id')); + $this->assertContains($client1->id, $label->clients->pluck('id')); + $this->assertContains($client2->id, $label->clients->pluck('id')); $client1->refresh(); $this->assertEquals(1, $client1->labels->count()); @@ -923,11 +923,11 @@ public function testMorphedByManyAttachMultipleIds(): void $label = Label::query()->create(['name' => 'Always in the game and never played by the rules']); - $label->clients()->attach([$client1->_id, $client2->_id]); + $label->clients()->attach([$client1->id, $client2->id]); $this->assertEquals(2, $label->clients->count()); - $this->assertContains($client1->_id, $label->clients->pluck('_id')); - $this->assertContains($client2->_id, $label->clients->pluck('_id')); + $this->assertContains($client1->id, $label->clients->pluck('id')); + $this->assertContains($client2->id, $label->clients->pluck('id')); $client1->refresh(); $this->assertEquals(1, $client1->labels->count()); @@ -941,15 +941,15 @@ public function testMorphedByManyDetaching(): void $label = Label::query()->create(['name' => 'Seasons change and our love went cold']); - $label->clients()->attach([$client1->_id, $client2->_id]); + $label->clients()->attach([$client1->id, $client2->id]); $this->assertEquals(2, $label->clients->count()); - $label->clients()->detach($client1->_id); + $label->clients()->detach($client1->id); $check = $label->withoutRelations(); $this->assertEquals(1, $check->clients->count()); - $this->assertContains($client2->_id, $check->clients->pluck('_id')); + $this->assertContains($client2->id, $check->clients->pluck('id')); } public function testMorphedByManyDetachingMultipleIds(): void @@ -960,15 +960,15 @@ public function testMorphedByManyDetachingMultipleIds(): void $label = Label::query()->create(['name' => "Run away, but we're running in circles"]); - $label->clients()->attach([$client1->_id, $client2->_id, $client3->_id]); + $label->clients()->attach([$client1->id, $client2->id, $client3->id]); $this->assertEquals(3, $label->clients->count()); - $label->clients()->detach([$client1->_id, $client2->_id]); + $label->clients()->detach([$client1->id, $client2->id]); $label->load('clients'); $this->assertEquals(1, $label->clients->count()); - $this->assertContains($client3->_id, $label->clients->pluck('_id')); + $this->assertContains($client3->id, $label->clients->pluck('id')); } public function testMorphedByManySyncing(): void @@ -984,9 +984,9 @@ public function testMorphedByManySyncing(): void $label->clients()->sync($client3, false); $this->assertEquals(3, $label->clients->count()); - $this->assertContains($client1->_id, $label->clients->pluck('_id')); - $this->assertContains($client2->_id, $label->clients->pluck('_id')); - $this->assertContains($client3->_id, $label->clients->pluck('_id')); + $this->assertContains($client1->id, $label->clients->pluck('id')); + $this->assertContains($client2->id, $label->clients->pluck('id')); + $this->assertContains($client3->id, $label->clients->pluck('id')); } public function testMorphedByManySyncingEloquentCollection(): void @@ -1000,10 +1000,10 @@ public function testMorphedByManySyncingEloquentCollection(): void $label->clients()->sync(new Collection([$client1, $client2])); $this->assertEquals(2, $label->clients->count()); - $this->assertContains($client1->_id, $label->clients->pluck('_id')); - $this->assertContains($client2->_id, $label->clients->pluck('_id')); + $this->assertContains($client1->id, $label->clients->pluck('id')); + $this->assertContains($client2->id, $label->clients->pluck('id')); - $this->assertNotContains($extra->_id, $label->clients->pluck('_id')); + $this->assertNotContains($extra->id, $label->clients->pluck('id')); } public function testMorphedByManySyncingMultipleIds(): void @@ -1014,13 +1014,13 @@ public function testMorphedByManySyncingMultipleIds(): void $label = Label::query()->create(['name' => "Love ain't patient, it's not kind. true love waits to rob you blind"]); - $label->clients()->sync([$client1->_id, $client2->_id]); + $label->clients()->sync([$client1->id, $client2->id]); $this->assertEquals(2, $label->clients->count()); - $this->assertContains($client1->_id, $label->clients->pluck('_id')); - $this->assertContains($client2->_id, $label->clients->pluck('_id')); + $this->assertContains($client1->id, $label->clients->pluck('id')); + $this->assertContains($client2->id, $label->clients->pluck('id')); - $this->assertNotContains($extra->_id, $label->clients->pluck('_id')); + $this->assertNotContains($extra->id, $label->clients->pluck('id')); } public function testMorphedByManySyncingWithCustomKeys(): void @@ -1034,19 +1034,19 @@ public function testMorphedByManySyncingWithCustomKeys(): void $label->clientsWithCustomKeys()->sync([$client1->cclient_id, $client2->cclient_id]); $this->assertEquals(2, $label->clientsWithCustomKeys->count()); - $this->assertContains($client1->_id, $label->clientsWithCustomKeys->pluck('_id')); - $this->assertContains($client2->_id, $label->clientsWithCustomKeys->pluck('_id')); + $this->assertContains($client1->id, $label->clientsWithCustomKeys->pluck('id')); + $this->assertContains($client2->id, $label->clientsWithCustomKeys->pluck('id')); - $this->assertNotContains($client3->_id, $label->clientsWithCustomKeys->pluck('_id')); + $this->assertNotContains($client3->id, $label->clientsWithCustomKeys->pluck('id')); $label->clientsWithCustomKeys()->sync($client3); $label->load('clientsWithCustomKeys'); $this->assertEquals(1, $label->clientsWithCustomKeys->count()); - $this->assertNotContains($client1->_id, $label->clientsWithCustomKeys->pluck('_id')); - $this->assertNotContains($client2->_id, $label->clientsWithCustomKeys->pluck('_id')); + $this->assertNotContains($client1->id, $label->clientsWithCustomKeys->pluck('id')); + $this->assertNotContains($client2->id, $label->clientsWithCustomKeys->pluck('id')); - $this->assertContains($client3->_id, $label->clientsWithCustomKeys->pluck('_id')); + $this->assertContains($client3->id, $label->clientsWithCustomKeys->pluck('id')); } public function testMorphedByManyLoadAndRefreshing(): void @@ -1072,11 +1072,11 @@ public function testMorphedByManyLoadAndRefreshing(): void $this->assertEquals(3, $label->clients->count()); - $check = Label::query()->find($label->_id); + $check = Label::query()->find($label->id); $this->assertEquals(3, $check->clients->count()); - $check = Label::query()->with('clients')->find($label->_id); + $check = Label::query()->with('clients')->find($label->id); $this->assertEquals(3, $check->clients->count()); } @@ -1100,16 +1100,16 @@ public function testMorphedByManyHasQuery(): void $check = Label::query()->has('clients')->get(); $this->assertCount(2, $check); - $this->assertContains($label->_id, $check->pluck('_id')); - $this->assertContains($label2->_id, $check->pluck('_id')); + $this->assertContains($label->id, $check->pluck('id')); + $this->assertContains($label2->id, $check->pluck('id')); $check = Label::query()->has('users')->get(); $this->assertCount(1, $check); - $this->assertContains($label3->_id, $check->pluck('_id')); + $this->assertContains($label3->id, $check->pluck('id')); $check = Label::query()->has('clients', '>', 1)->get(); $this->assertCount(1, $check); - $this->assertContains($label->_id, $check->pluck('_id')); + $this->assertContains($label->id, $check->pluck('id')); } public function testHasManyHas(): void @@ -1219,18 +1219,18 @@ public function testDoubleSaveOneToMany(): void $author->books()->save($book); $author->save(); $this->assertEquals(1, $author->books()->count()); - $this->assertEquals($author->_id, $book->author_id); + $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); + $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); + $this->assertEquals($author->id, $book->author_id); } public function testDoubleSaveManyToMany(): void @@ -1243,29 +1243,29 @@ public function testDoubleSaveManyToMany(): void $user->save(); $this->assertEquals(1, $user->clients()->count()); - $this->assertEquals([$user->_id], $client->user_ids); - $this->assertEquals([$client->_id], $user->client_ids); + $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); + $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); + $this->assertEquals([$user->id], $client->user_ids); + $this->assertEquals([$client->id], $user->client_ids); } public function testWhereBelongsTo() { $user = User::create(['name' => 'John Doe']); - Item::create(['user_id' => $user->_id]); - Item::create(['user_id' => $user->_id]); - Item::create(['user_id' => $user->_id]); + Item::create(['user_id' => $user->id]); + Item::create(['user_id' => $user->id]); + Item::create(['user_id' => $user->id]); Item::create(['user_id' => null]); $items = Item::whereBelongsTo($user)->get(); diff --git a/tests/SessionTest.php b/tests/SessionTest.php new file mode 100644 index 000000000..7ffbb51f0 --- /dev/null +++ b/tests/SessionTest.php @@ -0,0 +1,33 @@ +getCollection('sessions')->drop(); + + parent::tearDown(); + } + + public function testDatabaseSessionHandler() + { + $sessionId = '123'; + + $handler = new DatabaseSessionHandler( + $this->app['db']->connection('mongodb'), + 'sessions', + 10, + ); + + $handler->write($sessionId, 'foo'); + $this->assertEquals('foo', $handler->read($sessionId)); + + $handler->write($sessionId, 'bar'); + $this->assertEquals('bar', $handler->read($sessionId)); + } +} diff --git a/tests/Ticket/GH2489Test.php b/tests/Ticket/GH2489Test.php new file mode 100644 index 000000000..62ce11d0e --- /dev/null +++ b/tests/Ticket/GH2489Test.php @@ -0,0 +1,49 @@ + 'Location 1', + 'images' => [ + ['_id' => 1, 'uri' => 'image1.jpg'], + ['_id' => 2, 'uri' => 'image2.jpg'], + ], + ], + [ + 'name' => 'Location 2', + 'images' => [ + ['_id' => 3, 'uri' => 'image3.jpg'], + ['_id' => 4, 'uri' => 'image4.jpg'], + ], + ], + ]); + + // With _id + $results = Location::whereIn('images._id', [1])->get(); + + $this->assertCount(1, $results); + $this->assertSame('Location 1', $results->first()->name); + + // With id + $results = Location::whereIn('images.id', [1])->get(); + + $this->assertCount(1, $results); + $this->assertSame('Location 1', $results->first()->name); + } +} diff --git a/tests/Ticket/GH2783Test.php b/tests/Ticket/GH2783Test.php index 73324ddc0..f1580c1e6 100644 --- a/tests/Ticket/GH2783Test.php +++ b/tests/Ticket/GH2783Test.php @@ -32,7 +32,7 @@ public function testMorphToInfersCustomOwnerKey() $queriedImageWithPost = GH2783Image::with('imageable')->find($imageWithPost->getKey()); $this->assertInstanceOf(GH2783Post::class, $queriedImageWithPost->imageable); - $this->assertEquals($post->_id, $queriedImageWithPost->imageable->getKey()); + $this->assertEquals($post->id, $queriedImageWithPost->imageable->getKey()); $queriedImageWithUser = GH2783Image::with('imageable')->find($imageWithUser->getKey()); $this->assertInstanceOf(GH2783User::class, $queriedImageWithUser->imageable); diff --git a/tests/TransactionTest.php b/tests/TransactionTest.php index 190f7487a..bbb45ac05 100644 --- a/tests/TransactionTest.php +++ b/tests/TransactionTest.php @@ -44,7 +44,7 @@ public function testCreateWithCommit(): void $this->assertTrue($klinson->exists); $this->assertEquals('klinson', $klinson->name); - $check = User::find($klinson->_id); + $check = User::find($klinson->id); $this->assertInstanceOf(User::class, $check); $this->assertEquals($klinson->name, $check->name); } @@ -60,7 +60,7 @@ public function testCreateRollBack(): void $this->assertTrue($klinson->exists); $this->assertEquals('klinson', $klinson->name); - $this->assertFalse(User::where('_id', $klinson->_id)->exists()); + $this->assertFalse(User::where('id', $klinson->id)->exists()); } public function testInsertWithCommit(): void @@ -93,7 +93,7 @@ public function testEloquentCreateWithCommit(): void $this->assertTrue($klinson->exists); $this->assertNotNull($klinson->getIdAttribute()); - $check = User::find($klinson->_id); + $check = User::find($klinson->id); $this->assertInstanceOf(User::class, $check); $this->assertEquals($check->name, $klinson->name); } @@ -110,7 +110,7 @@ public function testEloquentCreateWithRollBack(): void $this->assertTrue($klinson->exists); $this->assertNotNull($klinson->getIdAttribute()); - $this->assertFalse(User::where('_id', $klinson->_id)->exists()); + $this->assertFalse(User::where('id', $klinson->id)->exists()); } public function testInsertGetIdWithCommit(): void @@ -132,7 +132,7 @@ public function testInsertGetIdWithRollBack(): void DB::rollBack(); $this->assertInstanceOf(ObjectId::class, $userId); - $this->assertFalse(DB::table('users')->where('_id', (string) $userId)->exists()); + $this->assertFalse(DB::table('users')->where('id', (string) $userId)->exists()); } public function testUpdateWithCommit(): void @@ -176,8 +176,8 @@ public function testEloquentUpdateWithCommit(): void $this->assertEquals(21, $klinson->age); $this->assertEquals(39, $alcaeus->age); - $this->assertTrue(User::where('_id', $klinson->_id)->where('age', 21)->exists()); - $this->assertTrue(User::where('_id', $alcaeus->_id)->where('age', 39)->exists()); + $this->assertTrue(User::where('id', $klinson->id)->where('age', 21)->exists()); + $this->assertTrue(User::where('id', $alcaeus->id)->where('age', 39)->exists()); } public function testEloquentUpdateWithRollBack(): void @@ -197,8 +197,8 @@ public function testEloquentUpdateWithRollBack(): void $this->assertEquals(21, $klinson->age); $this->assertEquals(39, $alcaeus->age); - $this->assertFalse(User::where('_id', $klinson->_id)->where('age', 21)->exists()); - $this->assertFalse(User::where('_id', $alcaeus->_id)->where('age', 39)->exists()); + $this->assertFalse(User::where('id', $klinson->id)->where('age', 21)->exists()); + $this->assertFalse(User::where('id', $alcaeus->id)->where('age', 39)->exists()); } public function testDeleteWithCommit(): void @@ -234,7 +234,7 @@ public function testEloquentDeleteWithCommit(): void $klinson->delete(); DB::commit(); - $this->assertFalse(User::where('_id', $klinson->_id)->exists()); + $this->assertFalse(User::where('id', $klinson->id)->exists()); } public function testEloquentDeleteWithRollBack(): void @@ -246,7 +246,7 @@ public function testEloquentDeleteWithRollBack(): void $klinson->delete(); DB::rollBack(); - $this->assertTrue(User::where('_id', $klinson->_id)->exists()); + $this->assertTrue(User::where('id', $klinson->id)->exists()); } public function testIncrementWithCommit(): void @@ -390,7 +390,7 @@ public function testTransactionRespectsRepetitionLimit(): void $this->assertSame(2, $timesRun); - $check = User::find($klinson->_id); + $check = User::find($klinson->id); $this->assertInstanceOf(User::class, $check); // Age is expected to be 24: the callback is executed twice, incrementing age by 2 every time From bd9ef30f98c92b915e9f657504e9cf15e4a2b046 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Mon, 26 Aug 2024 12:23:20 +0200 Subject: [PATCH 02/27] PHPORM-230 Convert DateTimeInterface to UTCDateTime in queries (#3105) * PHPORM-230 Convert DateTimeInterface to UTCDateTime in queries * Alias id to _id in subdocuments --- src/Query/Builder.php | 65 +++++++++----------------- tests/Query/AggregationBuilderTest.php | 5 +- tests/Query/BuilderTest.php | 6 +++ tests/QueryBuilderTest.php | 12 +++-- 4 files changed, 37 insertions(+), 51 deletions(-) diff --git a/src/Query/Builder.php b/src/Query/Builder.php index 6168159df..fef4eb45c 100644 --- a/src/Query/Builder.php +++ b/src/Query/Builder.php @@ -32,7 +32,6 @@ use function array_map; use function array_merge; use function array_values; -use function array_walk_recursive; use function assert; use function blank; use function call_user_func; @@ -689,17 +688,7 @@ public function insert(array $values) $values = [$values]; } - // Compatibility with Eloquent queries that uses "id" instead of MongoDB's _id - foreach ($values as &$document) { - if (isset($document['id'])) { - if (isset($document['_id']) && $document['_id'] !== $document['id']) { - throw new InvalidArgumentException('Cannot insert document with different "id" and "_id" values'); - } - - $document['_id'] = $document['id']; - unset($document['id']); - } - } + $values = $this->aliasIdForQuery($values); $options = $this->inheritConnectionOptions(); @@ -876,6 +865,7 @@ public function delete($id = null) } $wheres = $this->compileWheres(); + $wheres = $this->aliasIdForQuery($wheres); $options = $this->inheritConnectionOptions(); if (is_int($this->limit)) { @@ -1070,16 +1060,12 @@ protected function performUpdate(array $update, array $options = []) $options['multiple'] = true; } - // Since "id" is an alias for "_id", we prevent updating it - foreach ($update as $operator => $fields) { - if (array_key_exists('id', $fields)) { - throw new InvalidArgumentException('Cannot update "id" field.'); - } - } + $update = $this->aliasIdForQuery($update); $options = $this->inheritConnectionOptions($options); $wheres = $this->compileWheres(); + $wheres = $this->aliasIdForQuery($wheres); $result = $this->collection->updateMany($wheres, $update, $options); if ($result->isAcknowledged()) { return $result->getModifiedCount() ? $result->getModifiedCount() : $result->getUpsertedCount(); @@ -1191,32 +1177,12 @@ protected function compileWheres(): array } } - // Convert DateTime values to UTCDateTime. - if (isset($where['value'])) { - if (is_array($where['value'])) { - array_walk_recursive($where['value'], function (&$item, $key) { - if ($item instanceof DateTimeInterface) { - $item = new UTCDateTime($item); - } - }); - } else { - if ($where['value'] instanceof DateTimeInterface) { - $where['value'] = new UTCDateTime($where['value']); - } - } - } elseif (isset($where['values'])) { - if (is_array($where['values'])) { - array_walk_recursive($where['values'], function (&$item, $key) { - if ($item instanceof DateTimeInterface) { - $item = new UTCDateTime($item); - } - }); - } elseif ($where['values'] instanceof CarbonPeriod) { - $where['values'] = [ - new UTCDateTime($where['values']->getStartDate()), - new UTCDateTime($where['values']->getEndDate()), - ]; - } + // Convert CarbonPeriod to DateTime interval. + if (isset($where['values']) && $where['values'] instanceof CarbonPeriod) { + $where['values'] = [ + $where['values']->getStartDate(), + $where['values']->getEndDate(), + ]; } // In a sequence of "where" clauses, the logical operator of the @@ -1631,12 +1597,21 @@ public function orWhereIntegerNotInRaw($column, $values, $boolean = 'and') private function aliasIdForQuery(array $values): array { if (array_key_exists('id', $values)) { + if (array_key_exists('_id', $values)) { + throw new InvalidArgumentException('Cannot have both "id" and "_id" fields.'); + } + $values['_id'] = $values['id']; unset($values['id']); } foreach ($values as $key => $value) { if (is_string($key) && str_ends_with($key, '.id')) { + $newkey = substr($key, 0, -3) . '._id'; + if (array_key_exists($newkey, $values)) { + throw new InvalidArgumentException(sprintf('Cannot have both "%s" and "%s" fields.', $key, $newkey)); + } + $values[substr($key, 0, -3) . '._id'] = $value; unset($values[$key]); } @@ -1645,6 +1620,8 @@ private function aliasIdForQuery(array $values): array foreach ($values as &$value) { if (is_array($value)) { $value = $this->aliasIdForQuery($value); + } elseif ($value instanceof DateTimeInterface) { + $value = new UTCDateTime($value); } } diff --git a/tests/Query/AggregationBuilderTest.php b/tests/Query/AggregationBuilderTest.php index b3828597d..a355db439 100644 --- a/tests/Query/AggregationBuilderTest.php +++ b/tests/Query/AggregationBuilderTest.php @@ -11,7 +11,6 @@ use InvalidArgumentException; use MongoDB\BSON\Document; use MongoDB\BSON\ObjectId; -use MongoDB\BSON\UTCDateTime; use MongoDB\Builder\BuilderEncoder; use MongoDB\Builder\Expression; use MongoDB\Builder\Pipeline; @@ -33,8 +32,8 @@ public function tearDown(): void public function testCreateAggregationBuilder(): void { User::insert([ - ['name' => 'John Doe', 'birthday' => new UTCDateTime(new DateTimeImmutable('1989-01-01'))], - ['name' => 'Jane Doe', 'birthday' => new UTCDateTime(new DateTimeImmutable('1990-01-01'))], + ['name' => 'John Doe', 'birthday' => new DateTimeImmutable('1989-01-01')], + ['name' => 'Jane Doe', 'birthday' => new DateTimeImmutable('1990-01-01')], ]); // Create the aggregation pipeline from the query builder diff --git a/tests/Query/BuilderTest.php b/tests/Query/BuilderTest.php index b081a0557..666747a46 100644 --- a/tests/Query/BuilderTest.php +++ b/tests/Query/BuilderTest.php @@ -566,6 +566,12 @@ function (Builder $builder) { fn (Builder $builder) => $builder->whereBetween('id', [[1], [2, 3]]), ]; + $date = new DateTimeImmutable('2018-09-30 15:00:00 +02:00'); + yield 'where $lt DateTimeInterface' => [ + ['find' => [['created_at' => ['$lt' => new UTCDateTime($date)]], []]], + fn (Builder $builder) => $builder->where('created_at', '<', $date), + ]; + $period = now()->toPeriod(now()->addMonth()); yield 'whereBetween CarbonPeriod' => [ [ diff --git a/tests/QueryBuilderTest.php b/tests/QueryBuilderTest.php index 6b08a15b7..0495f38aa 100644 --- a/tests/QueryBuilderTest.php +++ b/tests/QueryBuilderTest.php @@ -1053,16 +1053,20 @@ public function testIncrementEach() #[TestWith(['id', 'id'])] #[TestWith(['id', '_id'])] #[TestWith(['_id', 'id'])] + #[TestWith(['_id', '_id'])] public function testIdAlias($insertId, $queryId): void { - DB::collection('items')->insert([$insertId => 'abc', 'name' => 'Karting']); - $item = DB::collection('items')->where($queryId, '=', 'abc')->first(); + DB::table('items')->insert([$insertId => 'abc', 'name' => 'Karting']); + $item = DB::table('items')->where($queryId, '=', 'abc')->first(); $this->assertNotNull($item); $this->assertSame('abc', $item['id']); $this->assertSame('Karting', $item['name']); - DB::collection('items')->where($insertId, '=', 'abc')->update(['name' => 'Bike']); - $item = DB::collection('items')->where($queryId, '=', 'abc')->first(); + DB::table('items')->where($insertId, '=', 'abc')->update(['name' => 'Bike']); + $item = DB::table('items')->where($queryId, '=', 'abc')->first(); $this->assertSame('Bike', $item['name']); + + $result = DB::table('items')->where($queryId, '=', 'abc')->delete(); + $this->assertSame(1, $result); } } From 9c1146c12767d35a75fb7876427ebafd8b7516c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Mon, 26 Aug 2024 17:38:03 +0200 Subject: [PATCH 03/27] PHPORM-216 Remove $collection setting from DocumentModel and Connection::collection(). Use $table and Connection::table() instead (#3104) --- src/Connection.php | 18 ---------- src/Eloquent/DocumentModel.php | 15 -------- src/Schema/Builder.php | 19 ---------- tests/SchemaTest.php | 66 +++++++++++++++++----------------- 4 files changed, 33 insertions(+), 85 deletions(-) diff --git a/src/Connection.php b/src/Connection.php index a0affa56a..cb2bc78de 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -22,9 +22,7 @@ use function is_array; use function preg_match; use function str_contains; -use function trigger_error; -use const E_USER_DEPRECATED; use const FILTER_FLAG_IPV6; use const FILTER_VALIDATE_IP; @@ -77,22 +75,6 @@ public function __construct(array $config) $this->useDefaultQueryGrammar(); } - /** - * Begin a fluent query against a database collection. - * - * @deprecated since mongodb/laravel-mongodb 4.8, use the function table() instead - * - * @param string $collection - * - * @return Query\Builder - */ - public function collection($collection) - { - @trigger_error('Since mongodb/laravel-mongodb 4.8, the method Connection::collection() is deprecated and will be removed in version 5.0. Use the table() method instead.', E_USER_DEPRECATED); - - return $this->table($collection); - } - /** * Begin a fluent query against a database collection. * diff --git a/src/Eloquent/DocumentModel.php b/src/Eloquent/DocumentModel.php index af3aec3c2..fbbc69e49 100644 --- a/src/Eloquent/DocumentModel.php +++ b/src/Eloquent/DocumentModel.php @@ -47,11 +47,8 @@ use function str_starts_with; use function strcmp; use function strlen; -use function trigger_error; use function var_export; -use const E_USER_DEPRECATED; - trait DocumentModel { use HybridRelations; @@ -140,18 +137,6 @@ public function freshTimestamp() return new UTCDateTime(Date::now()); } - /** @inheritdoc */ - public function getTable() - { - if (isset($this->collection)) { - trigger_error('Since mongodb/laravel-mongodb 4.8: Using "$collection" property is deprecated. Use "$table" instead.', E_USER_DEPRECATED); - - return $this->collection; - } - - return parent::getTable(); - } - /** @inheritdoc */ public function getAttribute($key) { diff --git a/src/Schema/Builder.php b/src/Schema/Builder.php index e31a1efe1..630ff4c75 100644 --- a/src/Schema/Builder.php +++ b/src/Schema/Builder.php @@ -17,11 +17,8 @@ use function iterator_to_array; use function sort; use function sprintf; -use function trigger_error; use function usort; -use const E_USER_DEPRECATED; - class Builder extends \Illuminate\Database\Schema\Builder { /** @@ -75,22 +72,6 @@ public function hasTable($table) return $this->hasCollection($table); } - /** - * Modify a collection on the schema. - * - * @deprecated since mongodb/laravel-mongodb 4.8, use the function table() instead - * - * @param string $collection - * - * @return void - */ - public function collection($collection, Closure $callback) - { - @trigger_error('Since mongodb/laravel-mongodb 4.8, the method Schema\Builder::collection() is deprecated and will be removed in version 5.0. Use the function table() instead.', E_USER_DEPRECATED); - - $this->table($collection, $callback); - } - /** @inheritdoc */ public function table($table, Closure $callback) { diff --git a/tests/SchemaTest.php b/tests/SchemaTest.php index baf78d1a5..914b79389 100644 --- a/tests/SchemaTest.php +++ b/tests/SchemaTest.php @@ -61,7 +61,7 @@ public function testBluePrint(): void { $instance = $this; - Schema::collection('newcollection', function ($collection) use ($instance) { + Schema::table('newcollection', function ($collection) use ($instance) { $instance->assertInstanceOf(Blueprint::class, $collection); }); @@ -72,21 +72,21 @@ public function testBluePrint(): void public function testIndex(): void { - Schema::collection('newcollection', function ($collection) { + Schema::table('newcollection', function ($collection) { $collection->index('mykey1'); }); $index = $this->getIndex('newcollection', 'mykey1'); $this->assertEquals(1, $index['key']['mykey1']); - Schema::collection('newcollection', function ($collection) { + Schema::table('newcollection', function ($collection) { $collection->index(['mykey2']); }); $index = $this->getIndex('newcollection', 'mykey2'); $this->assertEquals(1, $index['key']['mykey2']); - Schema::collection('newcollection', function ($collection) { + Schema::table('newcollection', function ($collection) { $collection->string('mykey3')->index(); }); @@ -96,7 +96,7 @@ public function testIndex(): void public function testPrimary(): void { - Schema::collection('newcollection', function ($collection) { + Schema::table('newcollection', function ($collection) { $collection->string('mykey', 100)->primary(); }); @@ -106,7 +106,7 @@ public function testPrimary(): void public function testUnique(): void { - Schema::collection('newcollection', function ($collection) { + Schema::table('newcollection', function ($collection) { $collection->unique('uniquekey'); }); @@ -116,7 +116,7 @@ public function testUnique(): void public function testDropIndex(): void { - Schema::collection('newcollection', function ($collection) { + Schema::table('newcollection', function ($collection) { $collection->unique('uniquekey'); $collection->dropIndex('uniquekey_1'); }); @@ -124,7 +124,7 @@ public function testDropIndex(): void $index = $this->getIndex('newcollection', 'uniquekey'); $this->assertEquals(null, $index); - Schema::collection('newcollection', function ($collection) { + Schema::table('newcollection', function ($collection) { $collection->unique('uniquekey'); $collection->dropIndex(['uniquekey']); }); @@ -132,42 +132,42 @@ public function testDropIndex(): void $index = $this->getIndex('newcollection', 'uniquekey'); $this->assertEquals(null, $index); - Schema::collection('newcollection', function ($collection) { + Schema::table('newcollection', function ($collection) { $collection->index(['field_a', 'field_b']); }); $index = $this->getIndex('newcollection', 'field_a_1_field_b_1'); $this->assertNotNull($index); - Schema::collection('newcollection', function ($collection) { + Schema::table('newcollection', function ($collection) { $collection->dropIndex(['field_a', 'field_b']); }); $index = $this->getIndex('newcollection', 'field_a_1_field_b_1'); $this->assertFalse($index); - Schema::collection('newcollection', function ($collection) { + Schema::table('newcollection', function ($collection) { $collection->index(['field_a' => -1, 'field_b' => 1]); }); $index = $this->getIndex('newcollection', 'field_a_-1_field_b_1'); $this->assertNotNull($index); - Schema::collection('newcollection', function ($collection) { + Schema::table('newcollection', function ($collection) { $collection->dropIndex(['field_a' => -1, 'field_b' => 1]); }); $index = $this->getIndex('newcollection', 'field_a_-1_field_b_1'); $this->assertFalse($index); - Schema::collection('newcollection', function ($collection) { + Schema::table('newcollection', function ($collection) { $collection->index(['field_a', 'field_b'], 'custom_index_name'); }); $index = $this->getIndex('newcollection', 'custom_index_name'); $this->assertNotNull($index); - Schema::collection('newcollection', function ($collection) { + Schema::table('newcollection', function ($collection) { $collection->dropIndex('custom_index_name'); }); @@ -177,7 +177,7 @@ public function testDropIndex(): void public function testDropIndexIfExists(): void { - Schema::collection('newcollection', function (Blueprint $collection) { + Schema::table('newcollection', function (Blueprint $collection) { $collection->unique('uniquekey'); $collection->dropIndexIfExists('uniquekey_1'); }); @@ -185,7 +185,7 @@ public function testDropIndexIfExists(): void $index = $this->getIndex('newcollection', 'uniquekey'); $this->assertEquals(null, $index); - Schema::collection('newcollection', function (Blueprint $collection) { + Schema::table('newcollection', function (Blueprint $collection) { $collection->unique('uniquekey'); $collection->dropIndexIfExists(['uniquekey']); }); @@ -193,28 +193,28 @@ public function testDropIndexIfExists(): void $index = $this->getIndex('newcollection', 'uniquekey'); $this->assertEquals(null, $index); - Schema::collection('newcollection', function (Blueprint $collection) { + Schema::table('newcollection', function (Blueprint $collection) { $collection->index(['field_a', 'field_b']); }); $index = $this->getIndex('newcollection', 'field_a_1_field_b_1'); $this->assertNotNull($index); - Schema::collection('newcollection', function (Blueprint $collection) { + Schema::table('newcollection', function (Blueprint $collection) { $collection->dropIndexIfExists(['field_a', 'field_b']); }); $index = $this->getIndex('newcollection', 'field_a_1_field_b_1'); $this->assertFalse($index); - Schema::collection('newcollection', function (Blueprint $collection) { + Schema::table('newcollection', function (Blueprint $collection) { $collection->index(['field_a', 'field_b'], 'custom_index_name'); }); $index = $this->getIndex('newcollection', 'custom_index_name'); $this->assertNotNull($index); - Schema::collection('newcollection', function (Blueprint $collection) { + Schema::table('newcollection', function (Blueprint $collection) { $collection->dropIndexIfExists('custom_index_name'); }); @@ -226,19 +226,19 @@ public function testHasIndex(): void { $instance = $this; - Schema::collection('newcollection', function (Blueprint $collection) use ($instance) { + Schema::table('newcollection', function (Blueprint $collection) use ($instance) { $collection->index('myhaskey1'); $instance->assertTrue($collection->hasIndex('myhaskey1_1')); $instance->assertFalse($collection->hasIndex('myhaskey1')); }); - Schema::collection('newcollection', function (Blueprint $collection) use ($instance) { + Schema::table('newcollection', function (Blueprint $collection) use ($instance) { $collection->index('myhaskey2'); $instance->assertTrue($collection->hasIndex(['myhaskey2'])); $instance->assertFalse($collection->hasIndex(['myhaskey2_1'])); }); - Schema::collection('newcollection', function (Blueprint $collection) use ($instance) { + Schema::table('newcollection', function (Blueprint $collection) use ($instance) { $collection->index(['field_a', 'field_b']); $instance->assertTrue($collection->hasIndex(['field_a_1_field_b'])); $instance->assertFalse($collection->hasIndex(['field_a_1_field_b_1'])); @@ -247,7 +247,7 @@ public function testHasIndex(): void public function testBackground(): void { - Schema::collection('newcollection', function ($collection) { + Schema::table('newcollection', function ($collection) { $collection->background('backgroundkey'); }); @@ -257,7 +257,7 @@ public function testBackground(): void public function testSparse(): void { - Schema::collection('newcollection', function ($collection) { + Schema::table('newcollection', function ($collection) { $collection->sparse('sparsekey'); }); @@ -267,7 +267,7 @@ public function testSparse(): void public function testExpire(): void { - Schema::collection('newcollection', function ($collection) { + Schema::table('newcollection', function ($collection) { $collection->expire('expirekey', 60); }); @@ -277,11 +277,11 @@ public function testExpire(): void public function testSoftDeletes(): void { - Schema::collection('newcollection', function ($collection) { + Schema::table('newcollection', function ($collection) { $collection->softDeletes(); }); - Schema::collection('newcollection', function ($collection) { + Schema::table('newcollection', function ($collection) { $collection->string('email')->nullable()->index(); }); @@ -291,7 +291,7 @@ public function testSoftDeletes(): void public function testFluent(): void { - Schema::collection('newcollection', function ($collection) { + Schema::table('newcollection', function ($collection) { $collection->string('email')->index(); $collection->string('token')->index(); $collection->timestamp('created_at'); @@ -306,7 +306,7 @@ public function testFluent(): void public function testGeospatial(): void { - Schema::collection('newcollection', function ($collection) { + Schema::table('newcollection', function ($collection) { $collection->geospatial('point'); $collection->geospatial('area', '2d'); $collection->geospatial('continent', '2dsphere'); @@ -324,7 +324,7 @@ public function testGeospatial(): void public function testDummies(): void { - Schema::collection('newcollection', function ($collection) { + Schema::table('newcollection', function ($collection) { $collection->boolean('activated')->default(0); $collection->integer('user_id')->unsigned(); }); @@ -333,7 +333,7 @@ public function testDummies(): void public function testSparseUnique(): void { - Schema::collection('newcollection', function ($collection) { + Schema::table('newcollection', function ($collection) { $collection->sparse_and_unique('sparseuniquekey'); }); @@ -361,7 +361,7 @@ public function testRenameColumn(): void $this->assertArrayNotHasKey('test', $check[2]); $this->assertArrayNotHasKey('newtest', $check[2]); - Schema::collection('newcollection', function (Blueprint $collection) { + Schema::table('newcollection', function (Blueprint $collection) { $collection->renameColumn('test', 'newtest'); }); From ebda1fa0e4a5059cf253c2a0e329b0bd86efcf70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Tue, 27 Aug 2024 23:58:04 +0200 Subject: [PATCH 04/27] PHPORM-229 Make Query\Builder return objects instead of array to match Laravel's behavior (#3107) --- CHANGELOG.md | 1 + .../query-builder/QueryBuilderTest.php | 8 +- src/Query/Builder.php | 47 +++- src/Queue/Failed/MongoFailedJobProvider.php | 10 +- src/Queue/MongoQueue.php | 2 +- tests/AuthTest.php | 6 +- tests/Query/BuilderTest.php | 4 +- tests/QueryBuilderTest.php | 220 +++++++++--------- tests/QueueTest.php | 26 +-- tests/SchemaTest.php | 34 +-- tests/SchemaVersionTest.php | 2 +- tests/TransactionTest.php | 2 +- 12 files changed, 194 insertions(+), 168 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c2b4dddca..2b9b491eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ All notable changes to this project will be documented in this file. ## [5.0.0] - next * **BREAKING CHANGE** Use `id` as an alias for `_id` in commands and queries for compatibility with Eloquent packages by @GromNaN in [#3040](https://github.com/mongodb/laravel-mongodb/pull/3040) +* **BREAKING CHANGE** Make Query\Builder return objects instead of array to match Laravel behavior by @GromNaN in [#3107](https://github.com/mongodb/laravel-mongodb/pull/3107) ## [4.8.0] - 2024-08-27 diff --git a/docs/includes/query-builder/QueryBuilderTest.php b/docs/includes/query-builder/QueryBuilderTest.php index 46822f257..bf92b9a6b 100644 --- a/docs/includes/query-builder/QueryBuilderTest.php +++ b/docs/includes/query-builder/QueryBuilderTest.php @@ -531,11 +531,11 @@ public function testUpsert(): void $this->assertSame(2, $result); - $this->assertSame(119, DB::table('movies')->where('title', 'Inspector Maigret')->first()['runtime']); - $this->assertSame(false, DB::table('movies')->where('title', 'Inspector Maigret')->first()['recommended']); + $this->assertSame(119, DB::table('movies')->where('title', 'Inspector Maigret')->first()->runtime); + $this->assertSame(false, DB::table('movies')->where('title', 'Inspector Maigret')->first()->recommended); - $this->assertSame(true, DB::table('movies')->where('title', 'Petit Maman')->first()['recommended']); - $this->assertSame(72, DB::table('movies')->where('title', 'Petit Maman')->first()['runtime']); + $this->assertSame(true, DB::table('movies')->where('title', 'Petit Maman')->first()->recommended); + $this->assertSame(72, DB::table('movies')->where('title', 'Petit Maman')->first()->runtime); } public function testUpdateUpsert(): void diff --git a/src/Query/Builder.php b/src/Query/Builder.php index b41168b80..9a2cc6cd8 100644 --- a/src/Query/Builder.php +++ b/src/Query/Builder.php @@ -25,6 +25,7 @@ use MongoDB\Driver\Cursor; use Override; use RuntimeException; +use stdClass; use function array_fill_keys; use function array_is_list; @@ -45,6 +46,7 @@ use function func_get_args; use function func_num_args; use function get_debug_type; +use function get_object_vars; use function implode; use function in_array; use function is_array; @@ -52,11 +54,13 @@ use function is_callable; use function is_float; use function is_int; +use function is_object; use function is_string; use function md5; use function preg_match; use function preg_quote; use function preg_replace; +use function property_exists; use function serialize; use function sprintf; use function str_ends_with; @@ -391,7 +395,7 @@ public function toMql(): array } $options = [ - 'typeMap' => ['root' => 'array', 'document' => 'array'], + 'typeMap' => ['root' => 'object', 'document' => 'array'], ]; // Add custom query options @@ -450,8 +454,7 @@ public function toMql(): array $options['projection'] = $projection; } - // Fix for legacy support, converts the results to arrays instead of objects. - $options['typeMap'] = ['root' => 'array', 'document' => 'array']; + $options['typeMap'] = ['root' => 'object', 'document' => 'array']; // Add custom query options if (count($this->options)) { @@ -516,7 +519,7 @@ public function getFresh($columns = [], $returnLazy = false) } foreach ($result as &$document) { - if (is_array($document)) { + if (is_array($document) || is_object($document)) { $document = $this->aliasIdForResult($document); } } @@ -1641,16 +1644,38 @@ private function aliasIdForQuery(array $values): array return $values; } - private function aliasIdForResult(array $values): array + /** + * @psalm-param T $values + * + * @psalm-return T + * + * @template T of array|object + */ + private function aliasIdForResult(array|object $values): array|object { - if (array_key_exists('_id', $values) && ! array_key_exists('id', $values)) { - $values['id'] = $values['_id']; - //unset($values['_id']); + if (is_array($values)) { + if (array_key_exists('_id', $values) && ! array_key_exists('id', $values)) { + $values['id'] = $values['_id']; + //unset($values['_id']); + } + + foreach ($values as $key => $value) { + if (is_array($value) || is_object($value)) { + $values[$key] = $this->aliasIdForResult($value); + } + } } - foreach ($values as $key => $value) { - if (is_array($value)) { - $values[$key] = $this->aliasIdForResult($value); + if ($values instanceof stdClass) { + if (property_exists($values, '_id') && ! property_exists($values, 'id')) { + $values->id = $values->_id; + //unset($values->_id); + } + + foreach (get_object_vars($values) as $key => $value) { + if (is_array($value) || is_object($value)) { + $values->{$key} = $this->aliasIdForResult($value); + } } } diff --git a/src/Queue/Failed/MongoFailedJobProvider.php b/src/Queue/Failed/MongoFailedJobProvider.php index 357f27ddc..102fc98d7 100644 --- a/src/Queue/Failed/MongoFailedJobProvider.php +++ b/src/Queue/Failed/MongoFailedJobProvider.php @@ -43,12 +43,12 @@ public function log($connection, $queue, $payload, $exception) */ public function all() { - $all = $this->getTable()->orderBy('_id', 'desc')->get()->all(); + $all = $this->getTable()->orderBy('id', 'desc')->get()->all(); $all = array_map(function ($job) { - $job['id'] = (string) $job['_id']; + $job->id = (string) $job->id; - return (object) $job; + return $job; }, $all); return $all; @@ -69,9 +69,9 @@ public function find($id) return null; } - $job['id'] = (string) $job['_id']; + $job->id = (string) $job->id; - return (object) $job; + return $job; } /** diff --git a/src/Queue/MongoQueue.php b/src/Queue/MongoQueue.php index 5b91afb6b..7810aab92 100644 --- a/src/Queue/MongoQueue.php +++ b/src/Queue/MongoQueue.php @@ -116,7 +116,7 @@ protected function releaseJobsThatHaveBeenReservedTooLong($queue) ->get(); foreach ($reserved as $job) { - $this->releaseJob($job['_id'], $job['attempts']); + $this->releaseJob($job->id, $job->attempts); } } diff --git a/tests/AuthTest.php b/tests/AuthTest.php index d2b3a9675..ffe3d46e9 100644 --- a/tests/AuthTest.php +++ b/tests/AuthTest.php @@ -61,9 +61,9 @@ function ($actualUser, $actualToken) use ($user, &$token) { $this->assertEquals(1, DB::table('password_reset_tokens')->count()); $reminder = DB::table('password_reset_tokens')->first(); - $this->assertEquals('john.doe@example.com', $reminder['email']); - $this->assertNotNull($reminder['token']); - $this->assertInstanceOf(UTCDateTime::class, $reminder['created_at']); + $this->assertEquals('john.doe@example.com', $reminder->email); + $this->assertNotNull($reminder->token); + $this->assertInstanceOf(UTCDateTime::class, $reminder->created_at); $credentials = [ 'email' => 'john.doe@example.com', diff --git a/tests/Query/BuilderTest.php b/tests/Query/BuilderTest.php index 199935743..49da6fada 100644 --- a/tests/Query/BuilderTest.php +++ b/tests/Query/BuilderTest.php @@ -44,11 +44,11 @@ public function testMql(array $expected, Closure $build, ?string $requiredMethod // Operations that return a Cursor expect a "typeMap" option. if (isset($expected['find'][1])) { - $expected['find'][1]['typeMap'] = ['root' => 'array', 'document' => 'array']; + $expected['find'][1]['typeMap'] = ['root' => 'object', 'document' => 'array']; } if (isset($expected['aggregate'][1])) { - $expected['aggregate'][1]['typeMap'] = ['root' => 'array', 'document' => 'array']; + $expected['aggregate'][1]['typeMap'] = ['root' => 'object', 'document' => 'array']; } // Compare with assertEquals because the query can contain BSON objects. diff --git a/tests/QueryBuilderTest.php b/tests/QueryBuilderTest.php index 436c86996..d34bb5241 100644 --- a/tests/QueryBuilderTest.php +++ b/tests/QueryBuilderTest.php @@ -60,7 +60,7 @@ public function testDeleteWithId() $product = DB::table('items')->first(); - $pid = (string) ($product['id']); + $pid = (string) ($product->id); DB::table('items')->where('user_id', $userId)->delete($pid); @@ -68,7 +68,7 @@ public function testDeleteWithId() $product = DB::table('items')->first(); - $pid = $product['id']; + $pid = $product->id; DB::table('items')->where('user_id', $userId)->delete($pid); @@ -116,8 +116,8 @@ public function testInsert() $this->assertCount(1, $users); $user = $users[0]; - $this->assertEquals('John Doe', $user['name']); - $this->assertIsArray($user['tags']); + $this->assertEquals('John Doe', $user->name); + $this->assertIsArray($user->tags); } public function testInsertGetId() @@ -141,7 +141,7 @@ public function testBatchInsert() $users = DB::table('users')->get(); $this->assertCount(2, $users); - $this->assertIsArray($users[0]['tags']); + $this->assertIsArray($users[0]->tags); } public function testFind() @@ -149,7 +149,7 @@ public function testFind() $id = DB::table('users')->insertGetId(['name' => 'John Doe']); $user = DB::table('users')->find($id); - $this->assertEquals('John Doe', $user['name']); + $this->assertEquals('John Doe', $user->name); } public function testFindWithTimeout() @@ -211,8 +211,8 @@ public function testUpdate() $john = DB::table('users')->where('name', 'John Doe')->first(); $jane = DB::table('users')->where('name', 'Jane Doe')->first(); - $this->assertEquals(100, $john['age']); - $this->assertEquals(20, $jane['age']); + $this->assertEquals(100, $john->age); + $this->assertEquals(20, $jane->age); } public function testUpdateOperators() @@ -239,12 +239,12 @@ public function testUpdateOperators() $john = DB::table('users')->where('name', 'John Doe')->first(); $jane = DB::table('users')->where('name', 'Jane Doe')->first(); - $this->assertArrayNotHasKey('age', $john); - $this->assertTrue($john['ageless']); + $this->assertObjectNotHasProperty('age', $john); + $this->assertTrue($john->ageless); - $this->assertEquals(21, $jane['age']); - $this->assertEquals('she', $jane['pronoun']); - $this->assertFalse($jane['ageless']); + $this->assertEquals(21, $jane->age); + $this->assertEquals('she', $jane->pronoun); + $this->assertFalse($jane->ageless); } public function testDelete() @@ -286,7 +286,7 @@ public function testSubKey() $users = DB::table('users')->where('address.country', 'Belgium')->get(); $this->assertCount(1, $users); - $this->assertEquals('John Doe', $users[0]['name']); + $this->assertEquals('John Doe', $users[0]->name); } public function testInArray() @@ -329,7 +329,7 @@ public function testRaw() $results = DB::table('users')->whereRaw(['age' => 20])->get(); $this->assertCount(1, $results); - $this->assertEquals('Jane Doe', $results[0]['name']); + $this->assertEquals('Jane Doe', $results[0]->name); } public function testPush() @@ -343,31 +343,31 @@ public function testPush() DB::table('users')->where('id', $id)->push('tags', 'tag1'); $user = DB::table('users')->find($id); - $this->assertIsArray($user['tags']); - $this->assertCount(1, $user['tags']); - $this->assertEquals('tag1', $user['tags'][0]); + $this->assertIsArray($user->tags); + $this->assertCount(1, $user->tags); + $this->assertEquals('tag1', $user->tags[0]); DB::table('users')->where('id', $id)->push('tags', 'tag2'); $user = DB::table('users')->find($id); - $this->assertCount(2, $user['tags']); - $this->assertEquals('tag2', $user['tags'][1]); + $this->assertCount(2, $user->tags); + $this->assertEquals('tag2', $user->tags[1]); // Add duplicate DB::table('users')->where('id', $id)->push('tags', 'tag2'); $user = DB::table('users')->find($id); - $this->assertCount(3, $user['tags']); + $this->assertCount(3, $user->tags); // Add unique DB::table('users')->where('id', $id)->push('tags', 'tag1', true); $user = DB::table('users')->find($id); - $this->assertCount(3, $user['tags']); + $this->assertCount(3, $user->tags); $message = ['from' => 'Jane', 'body' => 'Hi John']; DB::table('users')->where('id', $id)->push('messages', $message); $user = DB::table('users')->find($id); - $this->assertIsArray($user['messages']); - $this->assertCount(1, $user['messages']); - $this->assertEquals($message, $user['messages'][0]); + $this->assertIsArray($user->messages); + $this->assertCount(1, $user->messages); + $this->assertEquals($message, $user->messages[0]); // Raw DB::table('users')->where('id', $id)->push([ @@ -375,8 +375,8 @@ public function testPush() 'messages' => ['from' => 'Mark', 'body' => 'Hi John'], ]); $user = DB::table('users')->find($id); - $this->assertCount(4, $user['tags']); - $this->assertCount(2, $user['messages']); + $this->assertCount(4, $user->tags); + $this->assertCount(2, $user->messages); DB::table('users')->where('id', $id)->push([ 'messages' => [ @@ -385,7 +385,7 @@ public function testPush() ], ]); $user = DB::table('users')->find($id); - $this->assertCount(3, $user['messages']); + $this->assertCount(3, $user->messages); } public function testPushRefuses2ndArgumentWhen1stIsAnArray() @@ -410,21 +410,21 @@ public function testPull() DB::table('users')->where('id', $id)->pull('tags', 'tag3'); $user = DB::table('users')->find($id); - $this->assertIsArray($user['tags']); - $this->assertCount(3, $user['tags']); - $this->assertEquals('tag4', $user['tags'][2]); + $this->assertIsArray($user->tags); + $this->assertCount(3, $user->tags); + $this->assertEquals('tag4', $user->tags[2]); DB::table('users')->where('id', $id)->pull('messages', $message1); $user = DB::table('users')->find($id); - $this->assertIsArray($user['messages']); - $this->assertCount(1, $user['messages']); + $this->assertIsArray($user->messages); + $this->assertCount(1, $user->messages); // Raw DB::table('users')->where('id', $id)->pull(['tags' => 'tag2', 'messages' => $message2]); $user = DB::table('users')->find($id); - $this->assertCount(2, $user['tags']); - $this->assertCount(0, $user['messages']); + $this->assertCount(2, $user->tags); + $this->assertCount(0, $user->messages); } public function testDistinct() @@ -456,10 +456,10 @@ public function testCustomId() ]); $item = DB::table('items')->find('knife'); - $this->assertEquals('knife', $item['id']); + $this->assertEquals('knife', $item->id); $item = DB::table('items')->where('id', 'fork')->first(); - $this->assertEquals('fork', $item['id']); + $this->assertEquals('fork', $item->id); DB::table('users')->insert([ ['id' => 1, 'name' => 'Jane Doe'], @@ -467,7 +467,7 @@ public function testCustomId() ]); $item = DB::table('users')->find(1); - $this->assertEquals(1, $item['id']); + $this->assertEquals(1, $item->id); } public function testTake() @@ -481,7 +481,7 @@ public function testTake() $items = DB::table('items')->orderBy('name')->take(2)->get(); $this->assertCount(2, $items); - $this->assertEquals('fork', $items[0]['name']); + $this->assertEquals('fork', $items[0]->name); } public function testSkip() @@ -495,7 +495,7 @@ public function testSkip() $items = DB::table('items')->orderBy('name')->skip(2)->get(); $this->assertCount(2, $items); - $this->assertEquals('spoon', $items[0]['name']); + $this->assertEquals('spoon', $items[0]->name); } public function testPluck() @@ -620,7 +620,7 @@ public function testUpsert() $this->assertSame(2, $result); $this->assertSame(2, DB::table('users')->count()); - $this->assertSame('bar', DB::table('users')->where('email', 'foo')->first()['name']); + $this->assertSame('bar', DB::table('users')->where('email', 'foo')->first()->name); // Update 1 document $result = DB::table('users')->upsert([ @@ -630,7 +630,7 @@ public function testUpsert() $this->assertSame(1, $result); $this->assertSame(2, DB::table('users')->count()); - $this->assertSame('bar2', DB::table('users')->where('email', 'foo')->first()['name']); + $this->assertSame('bar2', DB::table('users')->where('email', 'foo')->first()->name); // If no update fields are specified, all fields are updated // Test single document update @@ -638,7 +638,7 @@ public function testUpsert() $this->assertSame(1, $result); $this->assertSame(2, DB::table('users')->count()); - $this->assertSame('bar3', DB::table('users')->where('email', 'foo')->first()['name']); + $this->assertSame('bar3', DB::table('users')->where('email', 'foo')->first()->name); } public function testUnset() @@ -651,16 +651,16 @@ public function testUnset() $user1 = DB::table('users')->find($id1); $user2 = DB::table('users')->find($id2); - $this->assertArrayNotHasKey('note1', $user1); - $this->assertArrayHasKey('note2', $user1); - $this->assertArrayHasKey('note1', $user2); - $this->assertArrayHasKey('note2', $user2); + $this->assertObjectNotHasProperty('note1', $user1); + $this->assertObjectHasProperty('note2', $user1); + $this->assertObjectHasProperty('note1', $user2); + $this->assertObjectHasProperty('note2', $user2); DB::table('users')->where('name', 'Jane Doe')->unset(['note1', 'note2']); $user2 = DB::table('users')->find($id2); - $this->assertArrayNotHasKey('note1', $user2); - $this->assertArrayNotHasKey('note2', $user2); + $this->assertObjectNotHasProperty('note1', $user2); + $this->assertObjectNotHasProperty('note2', $user2); } public function testUpdateSubdocument() @@ -670,7 +670,7 @@ public function testUpdateSubdocument() DB::table('users')->where('id', $id)->update(['address.country' => 'England']); $check = DB::table('users')->find($id); - $this->assertEquals('England', $check['address']['country']); + $this->assertEquals('England', $check->address['country']); } public function testDates() @@ -685,15 +685,15 @@ public function testDates() $user = DB::table('users') ->where('birthday', new UTCDateTime(Date::parse('1980-01-01 00:00:00'))) ->first(); - $this->assertEquals('John Doe', $user['name']); + $this->assertEquals('John Doe', $user->name); $user = DB::table('users') ->where('birthday', new UTCDateTime(Date::parse('1960-01-01 12:12:12.1'))) ->first(); - $this->assertEquals('Frank White', $user['name']); + $this->assertEquals('Frank White', $user->name); $user = DB::table('users')->where('birthday', '=', new DateTime('1980-01-01 00:00:00'))->first(); - $this->assertEquals('John Doe', $user['name']); + $this->assertEquals('John Doe', $user->name); $start = new UTCDateTime(1000 * strtotime('1950-01-01 00:00:00')); $stop = new UTCDateTime(1000 * strtotime('1981-01-01 00:00:00')); @@ -739,25 +739,25 @@ public function testOperators() $results = DB::table('users')->where('age', 'exists', true)->get(); $this->assertCount(2, $results); - $resultsNames = [$results[0]['name'], $results[1]['name']]; + $resultsNames = [$results[0]->name, $results[1]->name]; $this->assertContains('John Doe', $resultsNames); $this->assertContains('Robert Roe', $resultsNames); $results = DB::table('users')->where('age', 'exists', false)->get(); $this->assertCount(1, $results); - $this->assertEquals('Jane Doe', $results[0]['name']); + $this->assertEquals('Jane Doe', $results[0]->name); $results = DB::table('users')->where('age', 'type', 2)->get(); $this->assertCount(1, $results); - $this->assertEquals('Robert Roe', $results[0]['name']); + $this->assertEquals('Robert Roe', $results[0]->name); $results = DB::table('users')->where('age', 'mod', [15, 0])->get(); $this->assertCount(1, $results); - $this->assertEquals('John Doe', $results[0]['name']); + $this->assertEquals('John Doe', $results[0]->name); $results = DB::table('users')->where('age', 'mod', [29, 1])->get(); $this->assertCount(1, $results); - $this->assertEquals('John Doe', $results[0]['name']); + $this->assertEquals('John Doe', $results[0]->name); $results = DB::table('users')->where('age', 'mod', [14, 0])->get(); $this->assertCount(0, $results); @@ -822,7 +822,7 @@ public function testOperators() $users = DB::table('users')->where('addresses', 'elemMatch', ['city' => 'Brussels'])->get(); $this->assertCount(1, $users); - $this->assertEquals('Jane Doe', $users[0]['name']); + $this->assertEquals('Jane Doe', $users[0]->name); } public function testIncrement() @@ -835,43 +835,43 @@ public function testIncrement() ]); $user = DB::table('users')->where('name', 'John Doe')->first(); - $this->assertEquals(30, $user['age']); + $this->assertEquals(30, $user->age); DB::table('users')->where('name', 'John Doe')->increment('age'); $user = DB::table('users')->where('name', 'John Doe')->first(); - $this->assertEquals(31, $user['age']); + $this->assertEquals(31, $user->age); DB::table('users')->where('name', 'John Doe')->decrement('age'); $user = DB::table('users')->where('name', 'John Doe')->first(); - $this->assertEquals(30, $user['age']); + $this->assertEquals(30, $user->age); DB::table('users')->where('name', 'John Doe')->increment('age', 5); $user = DB::table('users')->where('name', 'John Doe')->first(); - $this->assertEquals(35, $user['age']); + $this->assertEquals(35, $user->age); DB::table('users')->where('name', 'John Doe')->decrement('age', 5); $user = DB::table('users')->where('name', 'John Doe')->first(); - $this->assertEquals(30, $user['age']); + $this->assertEquals(30, $user->age); DB::table('users')->where('name', 'Jane Doe')->increment('age', 10, ['note' => 'adult']); $user = DB::table('users')->where('name', 'Jane Doe')->first(); - $this->assertEquals(20, $user['age']); - $this->assertEquals('adult', $user['note']); + $this->assertEquals(20, $user->age); + $this->assertEquals('adult', $user->note); DB::table('users')->where('name', 'John Doe')->decrement('age', 20, ['note' => 'minor']); $user = DB::table('users')->where('name', 'John Doe')->first(); - $this->assertEquals(10, $user['age']); - $this->assertEquals('minor', $user['note']); + $this->assertEquals(10, $user->age); + $this->assertEquals('minor', $user->note); DB::table('users')->increment('age'); $user = DB::table('users')->where('name', 'John Doe')->first(); - $this->assertEquals(11, $user['age']); + $this->assertEquals(11, $user->age); $user = DB::table('users')->where('name', 'Jane Doe')->first(); - $this->assertEquals(21, $user['age']); + $this->assertEquals(21, $user->age); $user = DB::table('users')->where('name', 'Robert Roe')->first(); - $this->assertNull($user['age']); + $this->assertNull($user->age); $user = DB::table('users')->where('name', 'Mark Moe')->first(); - $this->assertEquals(1, $user['age']); + $this->assertEquals(1, $user->age); } public function testProjections() @@ -885,7 +885,7 @@ public function testProjections() $results = DB::table('items')->project(['tags' => ['$slice' => 1]])->get(); foreach ($results as $result) { - $this->assertEquals(1, count($result['tags'])); + $this->assertEquals(1, count($result->tags)); } } @@ -912,15 +912,15 @@ public function testHintOptions() $results = DB::table('items')->hint(['$natural' => -1])->get(); - $this->assertEquals('spoon', $results[0]['name']); - $this->assertEquals('spork', $results[1]['name']); - $this->assertEquals('fork', $results[2]['name']); + $this->assertEquals('spoon', $results[0]->name); + $this->assertEquals('spork', $results[1]->name); + $this->assertEquals('fork', $results[2]->name); $results = DB::table('items')->hint(['$natural' => 1])->get(); - $this->assertEquals('spoon', $results[2]['name']); - $this->assertEquals('spork', $results[1]['name']); - $this->assertEquals('fork', $results[0]['name']); + $this->assertEquals('spoon', $results[2]->name); + $this->assertEquals('spork', $results[1]->name); + $this->assertEquals('fork', $results[0]->name); } public function testCursor() @@ -936,7 +936,7 @@ public function testCursor() $this->assertInstanceOf(LazyCollection::class, $results); foreach ($results as $i => $result) { - $this->assertEquals($data[$i]['name'], $result['name']); + $this->assertEquals($data[$i]['name'], $result->name); } } @@ -951,52 +951,52 @@ public function testStringableColumn() $this->assertInstanceOf(Stringable::class, $nameColumn, 'Ensure we are testing the feature with a Stringable instance'); $user = DB::table('users')->where($nameColumn, 'John Doe')->first(); - $this->assertEquals('John Doe', $user['name']); + $this->assertEquals('John Doe', $user->name); // Test this other document to be sure this is not a random success to data order $user = DB::table('users')->where($nameColumn, 'Jane Doe')->orderBy('natural')->first(); - $this->assertEquals('Jane Doe', $user['name']); + $this->assertEquals('Jane Doe', $user->name); // With an operator $user = DB::table('users')->where($nameColumn, '!=', 'Jane Doe')->first(); - $this->assertEquals('John Doe', $user['name']); + $this->assertEquals('John Doe', $user->name); // whereIn and whereNotIn $user = DB::table('users')->whereIn($nameColumn, ['John Doe'])->first(); - $this->assertEquals('John Doe', $user['name']); + $this->assertEquals('John Doe', $user->name); $user = DB::table('users')->whereNotIn($nameColumn, ['John Doe'])->first(); - $this->assertEquals('Jane Doe', $user['name']); + $this->assertEquals('Jane Doe', $user->name); $ageColumn = Str::of('age'); // whereBetween and whereNotBetween $user = DB::table('users')->whereBetween($ageColumn, [30, 40])->first(); - $this->assertEquals('Jane Doe', $user['name']); + $this->assertEquals('Jane Doe', $user->name); // whereBetween and whereNotBetween $user = DB::table('users')->whereNotBetween($ageColumn, [30, 40])->first(); - $this->assertEquals('John Doe', $user['name']); + $this->assertEquals('John Doe', $user->name); $birthdayColumn = Str::of('birthday'); // whereDate $user = DB::table('users')->whereDate($birthdayColumn, '1995-01-01')->first(); - $this->assertEquals('John Doe', $user['name']); + $this->assertEquals('John Doe', $user->name); $user = DB::table('users')->whereDate($birthdayColumn, '<', '1990-01-01') ->orderBy($birthdayColumn, 'desc')->first(); - $this->assertEquals('Jane Doe', $user['name']); + $this->assertEquals('Jane Doe', $user->name); $user = DB::table('users')->whereDate($birthdayColumn, '>', '1990-01-01') ->orderBy($birthdayColumn, 'asc')->first(); - $this->assertEquals('John Doe', $user['name']); + $this->assertEquals('John Doe', $user->name); $user = DB::table('users')->whereDate($birthdayColumn, '!=', '1987-01-01')->first(); - $this->assertEquals('John Doe', $user['name']); + $this->assertEquals('John Doe', $user->name); // increment DB::table('users')->where($ageColumn, 28)->increment($ageColumn, 1); $user = DB::table('users')->where($ageColumn, 29)->first(); - $this->assertEquals('John Doe', $user['name']); + $this->assertEquals('John Doe', $user->name); } public function testIncrementEach() @@ -1012,16 +1012,16 @@ public function testIncrementEach() 'note' => 2, ]); $user = DB::table('users')->where('name', 'John Doe')->first(); - $this->assertEquals(31, $user['age']); - $this->assertEquals(7, $user['note']); + $this->assertEquals(31, $user->age); + $this->assertEquals(7, $user->note); $user = DB::table('users')->where('name', 'Jane Doe')->first(); - $this->assertEquals(11, $user['age']); - $this->assertEquals(8, $user['note']); + $this->assertEquals(11, $user->age); + $this->assertEquals(8, $user->note); $user = DB::table('users')->where('name', 'Robert Roe')->first(); - $this->assertSame(1, $user['age']); - $this->assertSame(2, $user['note']); + $this->assertSame(1, $user->age); + $this->assertSame(2, $user->note); DB::table('users')->where('name', 'Jane Doe')->incrementEach([ 'age' => 1, @@ -1029,14 +1029,14 @@ public function testIncrementEach() ], ['extra' => 'foo']); $user = DB::table('users')->where('name', 'Jane Doe')->first(); - $this->assertEquals(12, $user['age']); - $this->assertEquals(10, $user['note']); - $this->assertEquals('foo', $user['extra']); + $this->assertEquals(12, $user->age); + $this->assertEquals(10, $user->note); + $this->assertEquals('foo', $user->extra); $user = DB::table('users')->where('name', 'John Doe')->first(); - $this->assertEquals(31, $user['age']); - $this->assertEquals(7, $user['note']); - $this->assertArrayNotHasKey('extra', $user); + $this->assertEquals(31, $user->age); + $this->assertEquals(7, $user->note); + $this->assertObjectNotHasProperty('extra', $user); DB::table('users')->decrementEach([ 'age' => 1, @@ -1044,9 +1044,9 @@ public function testIncrementEach() ], ['extra' => 'foo']); $user = DB::table('users')->where('name', 'John Doe')->first(); - $this->assertEquals(30, $user['age']); - $this->assertEquals(5, $user['note']); - $this->assertEquals('foo', $user['extra']); + $this->assertEquals(30, $user->age); + $this->assertEquals(5, $user->note); + $this->assertEquals('foo', $user->extra); } #[TestWith(['id', 'id'])] @@ -1058,12 +1058,12 @@ public function testIdAlias($insertId, $queryId): void DB::table('items')->insert([$insertId => 'abc', 'name' => 'Karting']); $item = DB::table('items')->where($queryId, '=', 'abc')->first(); $this->assertNotNull($item); - $this->assertSame('abc', $item['id']); - $this->assertSame('Karting', $item['name']); + $this->assertSame('abc', $item->id); + $this->assertSame('Karting', $item->name); DB::table('items')->where($insertId, '=', 'abc')->update(['name' => 'Bike']); $item = DB::table('items')->where($queryId, '=', 'abc')->first(); - $this->assertSame('Bike', $item['name']); + $this->assertSame('Bike', $item->name); $result = DB::table('items')->where($queryId, '=', 'abc')->delete(); $this->assertSame(1, $result); diff --git a/tests/QueueTest.php b/tests/QueueTest.php index af31c8a5b..04a279640 100644 --- a/tests/QueueTest.php +++ b/tests/QueueTest.php @@ -114,7 +114,7 @@ public function testIncrementAttempts(): void ->get(); $this->assertCount(1, $othersJobs); - $this->assertEquals(0, $othersJobs[0]['attempts']); + $this->assertEquals(0, $othersJobs[0]->attempts); } public function testJobRelease(): void @@ -131,7 +131,7 @@ public function testJobRelease(): void ->get(); $this->assertCount(1, $jobs); - $this->assertEquals(1, $jobs[0]['attempts']); + $this->assertEquals(1, $jobs[0]->attempts); } public function testQueueDeleteReserved(): void @@ -161,15 +161,15 @@ public function testQueueRelease(): void ->where('id', $releasedJobId) ->first(); - $this->assertEquals($queue, $releasedJob['queue']); - $this->assertEquals(1, $releasedJob['attempts']); - $this->assertNull($releasedJob['reserved_at']); + $this->assertEquals($queue, $releasedJob->queue); + $this->assertEquals(1, $releasedJob->attempts); + $this->assertNull($releasedJob->reserved_at); $this->assertEquals( Carbon::now()->addRealSeconds($delay)->getTimestamp(), - $releasedJob['available_at'], + $releasedJob->available_at, ); - $this->assertEquals(Carbon::now()->getTimestamp(), $releasedJob['created_at']); - $this->assertEquals($job->getRawBody(), $releasedJob['payload']); + $this->assertEquals(Carbon::now()->getTimestamp(), $releasedJob->created_at); + $this->assertEquals($job->getRawBody(), $releasedJob->payload); } public function testQueueDeleteAndRelease(): void @@ -194,10 +194,10 @@ public function testFailedJobLogging() $failedJob = Queue::getDatabase()->table(Config::get('queue.failed.table'))->first(); - $this->assertSame('test_connection', $failedJob['connection']); - $this->assertSame('test_queue', $failedJob['queue']); - $this->assertSame('test_payload', $failedJob['payload']); - $this->assertEquals(new UTCDateTime(Carbon::now()), $failedJob['failed_at']); - $this->assertStringStartsWith('Exception: test_exception in ', $failedJob['exception']); + $this->assertSame('test_connection', $failedJob->connection); + $this->assertSame('test_queue', $failedJob->queue); + $this->assertSame('test_payload', $failedJob->payload); + $this->assertEquals(new UTCDateTime(Carbon::now()), $failedJob->failed_at); + $this->assertStringStartsWith('Exception: test_exception in ', $failedJob->exception); } } diff --git a/tests/SchemaTest.php b/tests/SchemaTest.php index 914b79389..82d4a68c6 100644 --- a/tests/SchemaTest.php +++ b/tests/SchemaTest.php @@ -351,15 +351,15 @@ public function testRenameColumn(): void $check = DB::connection()->table('newcollection')->get(); $this->assertCount(3, $check); - $this->assertArrayHasKey('test', $check[0]); - $this->assertArrayNotHasKey('newtest', $check[0]); + $this->assertObjectHasProperty('test', $check[0]); + $this->assertObjectNotHasProperty('newtest', $check[0]); - $this->assertArrayHasKey('test', $check[1]); - $this->assertArrayNotHasKey('newtest', $check[1]); + $this->assertObjectHasProperty('test', $check[1]); + $this->assertObjectNotHasProperty('newtest', $check[1]); - $this->assertArrayHasKey('column', $check[2]); - $this->assertArrayNotHasKey('test', $check[2]); - $this->assertArrayNotHasKey('newtest', $check[2]); + $this->assertObjectHasProperty('column', $check[2]); + $this->assertObjectNotHasProperty('test', $check[2]); + $this->assertObjectNotHasProperty('newtest', $check[2]); Schema::table('newcollection', function (Blueprint $collection) { $collection->renameColumn('test', 'newtest'); @@ -368,18 +368,18 @@ public function testRenameColumn(): void $check2 = DB::connection()->table('newcollection')->get(); $this->assertCount(3, $check2); - $this->assertArrayHasKey('newtest', $check2[0]); - $this->assertArrayNotHasKey('test', $check2[0]); - $this->assertSame($check[0]['test'], $check2[0]['newtest']); + $this->assertObjectHasProperty('newtest', $check2[0]); + $this->assertObjectNotHasProperty('test', $check2[0]); + $this->assertSame($check[0]->test, $check2[0]->newtest); - $this->assertArrayHasKey('newtest', $check2[1]); - $this->assertArrayNotHasKey('test', $check2[1]); - $this->assertSame($check[1]['test'], $check2[1]['newtest']); + $this->assertObjectHasProperty('newtest', $check2[1]); + $this->assertObjectNotHasProperty('test', $check2[1]); + $this->assertSame($check[1]->test, $check2[1]->newtest); - $this->assertArrayHasKey('column', $check2[2]); - $this->assertArrayNotHasKey('test', $check2[2]); - $this->assertArrayNotHasKey('newtest', $check2[2]); - $this->assertSame($check[2]['column'], $check2[2]['column']); + $this->assertObjectHasProperty('column', $check2[2]); + $this->assertObjectNotHasProperty('test', $check2[2]); + $this->assertObjectNotHasProperty('newtest', $check2[2]); + $this->assertSame($check[2]->column, $check2[2]->column); } public function testHasColumn(): void diff --git a/tests/SchemaVersionTest.php b/tests/SchemaVersionTest.php index 0e115a6c2..4a205c77b 100644 --- a/tests/SchemaVersionTest.php +++ b/tests/SchemaVersionTest.php @@ -42,7 +42,7 @@ public function testWithBasicDocument() ->where('name', 'Vador') ->get(); - $this->assertEquals(2, $data[0]['schema_version']); + $this->assertEquals(2, $data[0]->schema_version); } public function testIncompleteImplementation(): void diff --git a/tests/TransactionTest.php b/tests/TransactionTest.php index bbb45ac05..f6a3cd509 100644 --- a/tests/TransactionTest.php +++ b/tests/TransactionTest.php @@ -122,7 +122,7 @@ public function testInsertGetIdWithCommit(): void $this->assertInstanceOf(ObjectId::class, $userId); $user = DB::table('users')->find((string) $userId); - $this->assertEquals('klinson', $user['name']); + $this->assertEquals('klinson', $user->name); } public function testInsertGetIdWithRollBack(): void From d7da552c92225fff0f0c9f386700561beb95fe65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Mon, 2 Sep 2024 14:44:30 +0200 Subject: [PATCH 05/27] Update PR template (#3121) --- .github/PULL_REQUEST_TEMPLATE.md | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index c3aad8477..321d843c0 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -7,4 +7,3 @@ This will help reviewers and should be a good start for the documentation. - [ ] Add tests and ensure they pass - [ ] Add an entry to the CHANGELOG.md file -- [ ] Update documentation for new features From a0b613498b3d7148821566d7774c0655e8629bbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Mon, 2 Sep 2024 14:45:36 +0200 Subject: [PATCH 06/27] PHPORM-231 Remove MongoFailedJobProvider (#3122) --- CHANGELOG.md | 1 + src/MongoDBQueueServiceProvider.php | 68 ---------- src/Query/Builder.php | 10 +- src/Queue/Failed/MongoFailedJobProvider.php | 119 ------------------ tests/QueryTest.php | 11 -- ....php => DatabaseFailedJobProviderTest.php} | 14 ++- tests/QueueTest.php | 4 +- tests/TestCase.php | 2 - 8 files changed, 16 insertions(+), 213 deletions(-) delete mode 100644 src/MongoDBQueueServiceProvider.php delete mode 100644 src/Queue/Failed/MongoFailedJobProvider.php rename tests/Queue/Failed/{MongoFailedJobProviderTest.php => DatabaseFailedJobProviderTest.php} (90%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b9b491eb..c34e9640f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file. * **BREAKING CHANGE** Use `id` as an alias for `_id` in commands and queries for compatibility with Eloquent packages by @GromNaN in [#3040](https://github.com/mongodb/laravel-mongodb/pull/3040) * **BREAKING CHANGE** Make Query\Builder return objects instead of array to match Laravel behavior by @GromNaN in [#3107](https://github.com/mongodb/laravel-mongodb/pull/3107) +* Remove `MongoFailedJobProvider`, replaced by Laravel `DatabaseFailedJobProvider` by @GromNaN in [#3122](https://github.com/mongodb/laravel-mongodb/pull/3122) ## [4.8.0] - 2024-08-27 diff --git a/src/MongoDBQueueServiceProvider.php b/src/MongoDBQueueServiceProvider.php deleted file mode 100644 index ea7a06176..000000000 --- a/src/MongoDBQueueServiceProvider.php +++ /dev/null @@ -1,68 +0,0 @@ -app->singleton('queue.failer', function ($app) { - $config = $app['config']['queue.failed']; - - if (array_key_exists('driver', $config) && ($config['driver'] === null || $config['driver'] === 'null')) { - return new NullFailedJobProvider(); - } - - if (isset($config['driver']) && $config['driver'] === 'mongodb') { - return $this->mongoFailedJobProvider($config); - } - - if (isset($config['driver']) && $config['driver'] === 'dynamodb') { - return $this->dynamoFailedJobProvider($config); - } - - if (isset($config['driver']) && $config['driver'] === 'database-uuids') { - return $this->databaseUuidFailedJobProvider($config); - } - - if (isset($config['table'])) { - return $this->databaseFailedJobProvider($config); - } - - return new NullFailedJobProvider(); - }); - } - - /** - * Create a new MongoDB failed job provider. - */ - protected function mongoFailedJobProvider(array $config): MongoFailedJobProvider - { - if (! isset($config['collection']) && isset($config['table'])) { - trigger_error('Since mongodb/laravel-mongodb 4.4: Using "table" option for the queue is deprecated. Use "collection" instead.', E_USER_DEPRECATED); - $config['collection'] = $config['table']; - } - - return new MongoFailedJobProvider( - $this->app['db'], - $config['database'] ?? null, - $config['collection'] ?? 'failed_jobs', - ); - } -} diff --git a/src/Query/Builder.php b/src/Query/Builder.php index 9a2cc6cd8..486325029 100644 --- a/src/Query/Builder.php +++ b/src/Query/Builder.php @@ -876,11 +876,11 @@ public function delete($id = null) $wheres = $this->aliasIdForQuery($wheres); $options = $this->inheritConnectionOptions(); - if (is_int($this->limit)) { - if ($this->limit !== 1) { - throw new LogicException(sprintf('Delete limit can be 1 or null (unlimited). Got %d', $this->limit)); - } - + /** + * Ignore the limit if it is set to more than 1, as it is not supported by the deleteMany method. + * Required for {@see DatabaseFailedJobProvider::prune()} + */ + if ($this->limit === 1) { $result = $this->collection->deleteOne($wheres, $options); } else { $result = $this->collection->deleteMany($wheres, $options); diff --git a/src/Queue/Failed/MongoFailedJobProvider.php b/src/Queue/Failed/MongoFailedJobProvider.php deleted file mode 100644 index 102fc98d7..000000000 --- a/src/Queue/Failed/MongoFailedJobProvider.php +++ /dev/null @@ -1,119 +0,0 @@ -getTable()->insert([ - 'connection' => $connection, - 'queue' => $queue, - 'payload' => $payload, - 'failed_at' => new UTCDateTime(Carbon::now()), - 'exception' => (string) $exception, - ]); - } - - /** - * Get a list of all of the failed jobs. - * - * @return object[] - */ - public function all() - { - $all = $this->getTable()->orderBy('id', 'desc')->get()->all(); - - $all = array_map(function ($job) { - $job->id = (string) $job->id; - - return $job; - }, $all); - - return $all; - } - - /** - * Get a single failed job. - * - * @param string $id - * - * @return object|null - */ - public function find($id) - { - $job = $this->getTable()->find($id); - - if (! $job) { - return null; - } - - $job->id = (string) $job->id; - - return $job; - } - - /** - * Delete a single failed job from storage. - * - * @param string $id - * - * @return bool - */ - public function forget($id) - { - return $this->getTable()->where('_id', $id)->delete() > 0; - } - - /** - * Get the IDs of all the failed jobs. - * - * @param string|null $queue - * - * @return list - */ - public function ids($queue = null) - { - return $this->getTable() - ->when($queue !== null, static fn ($query) => $query->where('queue', $queue)) - ->orderBy('_id', 'desc') - ->pluck('_id') - ->all(); - } - - /** - * Prune all failed jobs older than the given date. - * - * @param DateTimeInterface $before - * - * @return int - */ - public function prune(DateTimeInterface $before) - { - return $this - ->getTable() - ->where('failed_at', '<', $before) - ->delete(); - } -} diff --git a/tests/QueryTest.php b/tests/QueryTest.php index e228a0f70..1b5746842 100644 --- a/tests/QueryTest.php +++ b/tests/QueryTest.php @@ -6,13 +6,11 @@ use BadMethodCallException; use DateTimeImmutable; -use LogicException; use MongoDB\BSON\Regex; use MongoDB\Laravel\Eloquent\Builder; use MongoDB\Laravel\Tests\Models\Birthday; use MongoDB\Laravel\Tests\Models\Scoped; use MongoDB\Laravel\Tests\Models\User; -use PHPUnit\Framework\Attributes\TestWith; use function str; @@ -662,13 +660,4 @@ public function testDelete(): void User::limit(null)->delete(); $this->assertEquals(0, User::count()); } - - #[TestWith([0])] - #[TestWith([2])] - public function testDeleteException(int $limit): void - { - $this->expectException(LogicException::class); - $this->expectExceptionMessage('Delete limit can be 1 or null (unlimited).'); - User::limit($limit)->delete(); - } } diff --git a/tests/Queue/Failed/MongoFailedJobProviderTest.php b/tests/Queue/Failed/DatabaseFailedJobProviderTest.php similarity index 90% rename from tests/Queue/Failed/MongoFailedJobProviderTest.php rename to tests/Queue/Failed/DatabaseFailedJobProviderTest.php index d0487ffcf..88a7f0e7b 100644 --- a/tests/Queue/Failed/MongoFailedJobProviderTest.php +++ b/tests/Queue/Failed/DatabaseFailedJobProviderTest.php @@ -2,11 +2,11 @@ namespace MongoDB\Laravel\Tests\Queue\Failed; +use Illuminate\Queue\Failed\DatabaseFailedJobProvider; use Illuminate\Support\Facades\Date; use Illuminate\Support\Facades\DB; use MongoDB\BSON\ObjectId; use MongoDB\BSON\UTCDateTime; -use MongoDB\Laravel\Queue\Failed\MongoFailedJobProvider; use MongoDB\Laravel\Tests\TestCase; use OutOfBoundsException; @@ -14,7 +14,10 @@ use function range; use function sprintf; -class MongoFailedJobProviderTest extends TestCase +/** + * Ensure the Laravel class {@see DatabaseFailedJobProvider} works with a MongoDB connection. + */ +class DatabaseFailedJobProviderTest extends TestCase { public function setUp(): void { @@ -57,8 +60,7 @@ public function testLog(): void $this->assertSame('default', $inserted->queue); $this->assertSame('{"foo":"bar"}', $inserted->payload); $this->assertStringContainsString('OutOfBoundsException: This is the error', $inserted->exception); - $this->assertInstanceOf(ObjectId::class, $inserted->_id); - $this->assertSame((string) $inserted->_id, $inserted->id); + $this->assertInstanceOf(ObjectId::class, $inserted->id); } public function testCount(): void @@ -143,8 +145,8 @@ public function testPrune(): void $this->assertEquals(3, $provider->count()); } - private function getProvider(): MongoFailedJobProvider + private function getProvider(): DatabaseFailedJobProvider { - return new MongoFailedJobProvider(DB::getFacadeRoot(), '', 'failed_jobs'); + return new DatabaseFailedJobProvider(DB::getFacadeRoot(), '', 'failed_jobs'); } } diff --git a/tests/QueueTest.php b/tests/QueueTest.php index 04a279640..e149b9ef4 100644 --- a/tests/QueueTest.php +++ b/tests/QueueTest.php @@ -6,12 +6,12 @@ use Carbon\Carbon; use Exception; +use Illuminate\Queue\Failed\DatabaseFailedJobProvider; use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\Queue; use Illuminate\Support\Str; use Mockery; use MongoDB\BSON\UTCDateTime; -use MongoDB\Laravel\Queue\Failed\MongoFailedJobProvider; use MongoDB\Laravel\Queue\MongoJob; use MongoDB\Laravel\Queue\MongoQueue; @@ -87,7 +87,7 @@ public function testFailQueueJob(): void { $provider = app('queue.failer'); - $this->assertInstanceOf(MongoFailedJobProvider::class, $provider); + $this->assertInstanceOf(DatabaseFailedJobProvider::class, $provider); } public function testFindFailJobNull(): void diff --git a/tests/TestCase.php b/tests/TestCase.php index 5f37ea170..2353915ed 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -7,7 +7,6 @@ use Illuminate\Auth\Passwords\PasswordResetServiceProvider as BasePasswordResetServiceProviderAlias; use Illuminate\Foundation\Application; use MongoDB\Laravel\Auth\PasswordResetServiceProvider; -use MongoDB\Laravel\MongoDBQueueServiceProvider; use MongoDB\Laravel\MongoDBServiceProvider; use MongoDB\Laravel\Tests\Models\User; use MongoDB\Laravel\Validation\ValidationServiceProvider; @@ -40,7 +39,6 @@ protected function getPackageProviders($app): array { return [ MongoDBServiceProvider::class, - MongoDBQueueServiceProvider::class, PasswordResetServiceProvider::class, ValidationServiceProvider::class, ]; From af4e73d9d22c19f8e3f8bf649613dff0217c1198 Mon Sep 17 00:00:00 2001 From: Jason Date: Tue, 3 Sep 2024 14:45:23 +0800 Subject: [PATCH 07/27] Remove MongoDBQueueServiceProvider in composer.json (#3131) Class "MongoDB\Laravel\MongoDBQueueServiceProvider" not found due to being removed in this commit https://github.com/mongodb/laravel-mongodb/commit/a0b613498b3d7148821566d7774c0655e8629bbe --- composer.json | 1 - 1 file changed, 1 deletion(-) diff --git a/composer.json b/composer.json index af060bb3c..251f3ad3c 100644 --- a/composer.json +++ b/composer.json @@ -68,7 +68,6 @@ "laravel": { "providers": [ "MongoDB\\Laravel\\MongoDBServiceProvider", - "MongoDB\\Laravel\\MongoDBQueueServiceProvider", "MongoDB\\Laravel\\MongoDBBusServiceProvider" ] } From 5b7ca02fb8fefb94390c782efb02d50f34a286b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Tue, 3 Sep 2024 14:19:02 +0200 Subject: [PATCH 08/27] Remove support for Laravel 10 (#3123) --- .github/workflows/build-ci.yml | 11 ++++------- .github/workflows/static-analysis.yml | 2 +- CHANGELOG.md | 1 + composer.json | 19 ++++++++----------- 4 files changed, 14 insertions(+), 19 deletions(-) diff --git a/.github/workflows/build-ci.yml b/.github/workflows/build-ci.yml index 45833d579..60d96f34f 100644 --- a/.github/workflows/build-ci.yml +++ b/.github/workflows/build-ci.yml @@ -20,15 +20,15 @@ jobs: - "6.0" - "7.0" php: - - "8.1" - "8.2" - "8.3" laravel: - - "10.*" - "11.*" + mode: + - "" include: - - php: "8.1" - laravel: "10.*" + - php: "8.2" + laravel: "11.*" mongodb: "5.0" mode: "low-deps" os: "ubuntu-latest" @@ -37,9 +37,6 @@ jobs: mongodb: "7.0" mode: "ignore-php-req" os: "ubuntu-latest" - exclude: - - php: "8.1" - laravel: "11.*" steps: - uses: "actions/checkout@v4" diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 18ea2014e..331fe22d8 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -21,8 +21,8 @@ jobs: strategy: matrix: php: - - '8.1' - '8.2' + - '8.3' steps: - name: Checkout uses: actions/checkout@v4 diff --git a/CHANGELOG.md b/CHANGELOG.md index c34e9640f..297959410 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. ## [5.0.0] - next +* Remove support for Laravel 10 by @GromNaN in [#3123](https://github.com/mongodb/laravel-mongodb/pull/3123) * **BREAKING CHANGE** Use `id` as an alias for `_id` in commands and queries for compatibility with Eloquent packages by @GromNaN in [#3040](https://github.com/mongodb/laravel-mongodb/pull/3040) * **BREAKING CHANGE** Make Query\Builder return objects instead of array to match Laravel behavior by @GromNaN in [#3107](https://github.com/mongodb/laravel-mongodb/pull/3107) * Remove `MongoFailedJobProvider`, replaced by Laravel `DatabaseFailedJobProvider` by @GromNaN in [#3122](https://github.com/mongodb/laravel-mongodb/pull/3122) diff --git a/composer.json b/composer.json index 251f3ad3c..e7d2f09cd 100644 --- a/composer.json +++ b/composer.json @@ -22,30 +22,27 @@ ], "license": "MIT", "require": { - "php": "^8.1", + "php": "^8.2", "ext-mongodb": "^1.15", "composer-runtime-api": "^2.0.0", - "illuminate/cache": "^10.36|^11", - "illuminate/container": "^10.0|^11", - "illuminate/database": "^10.30|^11", - "illuminate/events": "^10.0|^11", - "illuminate/support": "^10.0|^11", + "illuminate/cache": "^11", + "illuminate/container": "^11", + "illuminate/database": "^11", + "illuminate/events": "^11", + "illuminate/support": "^11", "mongodb/mongodb": "^1.15" }, "require-dev": { "mongodb/builder": "^0.2", "league/flysystem-gridfs": "^3.28", "league/flysystem-read-only": "^3.0", - "phpunit/phpunit": "^10.3", - "orchestra/testbench": "^8.0|^9.0", + "phpunit/phpunit": "^10.5", + "orchestra/testbench": "^9.0", "mockery/mockery": "^1.4.4", "doctrine/coding-standard": "12.0.x-dev", "spatie/laravel-query-builder": "^5.6", "phpstan/phpstan": "^1.10" }, - "conflict": { - "illuminate/bus": "< 10.37.2" - }, "suggest": { "league/flysystem-gridfs": "Filesystem storage in MongoDB with GridFS", "mongodb/builder": "Provides a fluent aggregation builder for MongoDB pipelines" From c710097f0d9e627e868dd32de0b7c04433633349 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Tue, 3 Sep 2024 23:35:13 +0200 Subject: [PATCH 09/27] PHPORM-234 Convert dates in DB Query results (#3119) Use the current timezone when reading an UTCDateTime --- CHANGELOG.md | 1 + src/Eloquent/DocumentModel.php | 22 ++++++++++++++++++---- src/Query/Builder.php | 13 +++++++++++-- tests/AuthTest.php | 4 ++-- tests/QueryBuilderTest.php | 23 ++++++++++++++--------- tests/QueueTest.php | 3 +-- 6 files changed, 47 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 297959410..162bdd010 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file. * Remove support for Laravel 10 by @GromNaN in [#3123](https://github.com/mongodb/laravel-mongodb/pull/3123) * **BREAKING CHANGE** Use `id` as an alias for `_id` in commands and queries for compatibility with Eloquent packages by @GromNaN in [#3040](https://github.com/mongodb/laravel-mongodb/pull/3040) * **BREAKING CHANGE** Make Query\Builder return objects instead of array to match Laravel behavior by @GromNaN in [#3107](https://github.com/mongodb/laravel-mongodb/pull/3107) +* **BREAKING CHANGE** In DB query results, convert BSON `UTCDateTime` objects into `Carbon` date with the default timezone by @GromNaN in [#3119](https://github.com/mongodb/laravel-mongodb/pull/3119) * Remove `MongoFailedJobProvider`, replaced by Laravel `DatabaseFailedJobProvider` by @GromNaN in [#3122](https://github.com/mongodb/laravel-mongodb/pull/3122) ## [4.8.0] - 2024-08-27 diff --git a/src/Eloquent/DocumentModel.php b/src/Eloquent/DocumentModel.php index fbbc69e49..930ed6286 100644 --- a/src/Eloquent/DocumentModel.php +++ b/src/Eloquent/DocumentModel.php @@ -5,12 +5,14 @@ namespace MongoDB\Laravel\Eloquent; use BackedEnum; +use Carbon\Carbon; use Carbon\CarbonInterface; use DateTimeInterface; use DateTimeZone; use Illuminate\Contracts\Queue\QueueableCollection; use Illuminate\Contracts\Queue\QueueableEntity; use Illuminate\Contracts\Support\Arrayable; +use Illuminate\Database\Eloquent\Concerns\HasAttributes; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\Relation; use Illuminate\Support\Arr; @@ -97,8 +99,14 @@ public function getQualifiedKeyName() return $this->getKeyName(); } - /** @inheritdoc */ - public function fromDateTime($value) + /** + * Convert a DateTimeInterface (including Carbon) to a storable UTCDateTime. + * + * @see HasAttributes::fromDateTime() + * + * @param mixed $value + */ + public function fromDateTime($value): UTCDateTime { // If the value is already a UTCDateTime instance, we don't need to parse it. if ($value instanceof UTCDateTime) { @@ -113,8 +121,14 @@ public function fromDateTime($value) return new UTCDateTime($value); } - /** @inheritdoc */ - protected function asDateTime($value) + /** + * Return a timestamp as Carbon object. + * + * @see HasAttributes::asDateTime() + * + * @param mixed $value + */ + protected function asDateTime($value): Carbon { // Convert UTCDateTime instances to Carbon. if ($value instanceof UTCDateTime) { diff --git a/src/Query/Builder.php b/src/Query/Builder.php index 486325029..f4f31b58f 100644 --- a/src/Query/Builder.php +++ b/src/Query/Builder.php @@ -9,11 +9,13 @@ use Carbon\CarbonPeriod; use Closure; use DateTimeInterface; +use DateTimeZone; use Illuminate\Database\Query\Builder as BaseBuilder; use Illuminate\Database\Query\Expression; use Illuminate\Support\Arr; use Illuminate\Support\Carbon; use Illuminate\Support\Collection; +use Illuminate\Support\Facades\Date; use Illuminate\Support\LazyCollection; use InvalidArgumentException; use LogicException; @@ -39,6 +41,7 @@ use function call_user_func_array; use function count; use function ctype_xdigit; +use function date_default_timezone_get; use function dd; use function dump; use function end; @@ -1660,7 +1663,10 @@ private function aliasIdForResult(array|object $values): array|object } foreach ($values as $key => $value) { - if (is_array($value) || is_object($value)) { + if ($value instanceof UTCDateTime) { + $values[$key] = Date::instance($value->toDateTime()) + ->setTimezone(new DateTimeZone(date_default_timezone_get())); + } elseif (is_array($value) || is_object($value)) { $values[$key] = $this->aliasIdForResult($value); } } @@ -1673,7 +1679,10 @@ private function aliasIdForResult(array|object $values): array|object } foreach (get_object_vars($values) as $key => $value) { - if (is_array($value) || is_object($value)) { + if ($value instanceof UTCDateTime) { + $values->{$key} = Date::instance($value->toDateTime()) + ->setTimezone(new DateTimeZone(date_default_timezone_get())); + } elseif (is_array($value) || is_object($value)) { $values->{$key} = $this->aliasIdForResult($value); } } diff --git a/tests/AuthTest.php b/tests/AuthTest.php index ffe3d46e9..98d42832e 100644 --- a/tests/AuthTest.php +++ b/tests/AuthTest.php @@ -4,11 +4,11 @@ namespace MongoDB\Laravel\Tests; +use Carbon\Carbon; use Illuminate\Auth\Passwords\PasswordBroker; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Hash; -use MongoDB\BSON\UTCDateTime; use MongoDB\Laravel\Tests\Models\User; use function bcrypt; @@ -63,7 +63,7 @@ function ($actualUser, $actualToken) use ($user, &$token) { $reminder = DB::table('password_reset_tokens')->first(); $this->assertEquals('john.doe@example.com', $reminder->email); $this->assertNotNull($reminder->token); - $this->assertInstanceOf(UTCDateTime::class, $reminder->created_at); + $this->assertInstanceOf(Carbon::class, $reminder->created_at); $credentials = [ 'email' => 'john.doe@example.com', diff --git a/tests/QueryBuilderTest.php b/tests/QueryBuilderTest.php index d34bb5241..846f48514 100644 --- a/tests/QueryBuilderTest.php +++ b/tests/QueryBuilderTest.php @@ -4,6 +4,7 @@ namespace MongoDB\Laravel\Tests; +use Carbon\Carbon; use DateTime; use DateTimeImmutable; use Illuminate\Support\Facades\Date; @@ -33,7 +34,6 @@ use function md5; use function sort; use function strlen; -use function strtotime; class QueryBuilderTest extends TestCase { @@ -676,27 +676,32 @@ public function testUpdateSubdocument() public function testDates() { DB::table('users')->insert([ - ['name' => 'John Doe', 'birthday' => new UTCDateTime(Date::parse('1980-01-01 00:00:00'))], - ['name' => 'Robert Roe', 'birthday' => new UTCDateTime(Date::parse('1982-01-01 00:00:00'))], - ['name' => 'Mark Moe', 'birthday' => new UTCDateTime(Date::parse('1983-01-01 00:00:00.1'))], - ['name' => 'Frank White', 'birthday' => new UTCDateTime(Date::parse('1960-01-01 12:12:12.1'))], + ['name' => 'John Doe', 'birthday' => Date::parse('1980-01-01 00:00:00')], + ['name' => 'Robert Roe', 'birthday' => Date::parse('1982-01-01 00:00:00')], + ['name' => 'Mark Moe', 'birthday' => Date::parse('1983-01-01 00:00:00.1')], + ['name' => 'Frank White', 'birthday' => Date::parse('1975-01-01 12:12:12.1')], ]); $user = DB::table('users') - ->where('birthday', new UTCDateTime(Date::parse('1980-01-01 00:00:00'))) + ->where('birthday', Date::parse('1980-01-01 00:00:00')) ->first(); $this->assertEquals('John Doe', $user->name); $user = DB::table('users') - ->where('birthday', new UTCDateTime(Date::parse('1960-01-01 12:12:12.1'))) + ->where('birthday', Date::parse('1975-01-01 12:12:12.1')) ->first(); + $this->assertEquals('Frank White', $user->name); + $this->assertInstanceOf(Carbon::class, $user->birthday); + $this->assertSame('1975-01-01 12:12:12.100000', $user->birthday->format('Y-m-d H:i:s.u')); $user = DB::table('users')->where('birthday', '=', new DateTime('1980-01-01 00:00:00'))->first(); $this->assertEquals('John Doe', $user->name); + $this->assertInstanceOf(Carbon::class, $user->birthday); + $this->assertSame('1980-01-01 00:00:00.000000', $user->birthday->format('Y-m-d H:i:s.u')); - $start = new UTCDateTime(1000 * strtotime('1950-01-01 00:00:00')); - $stop = new UTCDateTime(1000 * strtotime('1981-01-01 00:00:00')); + $start = new UTCDateTime(new DateTime('1950-01-01 00:00:00')); + $stop = new UTCDateTime(new DateTime('1981-01-01 00:00:00')); $users = DB::table('users')->whereBetween('birthday', [$start, $stop])->get(); $this->assertCount(2, $users); diff --git a/tests/QueueTest.php b/tests/QueueTest.php index e149b9ef4..efc8f07ff 100644 --- a/tests/QueueTest.php +++ b/tests/QueueTest.php @@ -11,7 +11,6 @@ use Illuminate\Support\Facades\Queue; use Illuminate\Support\Str; use Mockery; -use MongoDB\BSON\UTCDateTime; use MongoDB\Laravel\Queue\MongoJob; use MongoDB\Laravel\Queue\MongoQueue; @@ -197,7 +196,7 @@ public function testFailedJobLogging() $this->assertSame('test_connection', $failedJob->connection); $this->assertSame('test_queue', $failedJob->queue); $this->assertSame('test_payload', $failedJob->payload); - $this->assertEquals(new UTCDateTime(Carbon::now()), $failedJob->failed_at); + $this->assertEquals(Carbon::now(), $failedJob->failed_at); $this->assertStringStartsWith('Exception: test_exception in ', $failedJob->exception); } } From 5f0682fe1180af71f2ad57d97e35d37330267f96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Wed, 4 Sep 2024 10:08:36 +0200 Subject: [PATCH 10/27] PHPORM-157 Remove Blueprint::background() (#3132) --- CHANGELOG.md | 1 + src/Schema/Blueprint.php | 16 ---------------- tests/SchemaTest.php | 10 ---------- 3 files changed, 1 insertion(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 162bdd010..534250191 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file. * **BREAKING CHANGE** Make Query\Builder return objects instead of array to match Laravel behavior by @GromNaN in [#3107](https://github.com/mongodb/laravel-mongodb/pull/3107) * **BREAKING CHANGE** In DB query results, convert BSON `UTCDateTime` objects into `Carbon` date with the default timezone by @GromNaN in [#3119](https://github.com/mongodb/laravel-mongodb/pull/3119) * Remove `MongoFailedJobProvider`, replaced by Laravel `DatabaseFailedJobProvider` by @GromNaN in [#3122](https://github.com/mongodb/laravel-mongodb/pull/3122) +* Remove `Blueprint::background()` method by @GromNaN in [#3132](https://github.com/mongodb/laravel-mongodb/pull/3132) ## [4.8.0] - 2024-08-27 diff --git a/src/Schema/Blueprint.php b/src/Schema/Blueprint.php index 52a5762f5..0ad4535cf 100644 --- a/src/Schema/Blueprint.php +++ b/src/Schema/Blueprint.php @@ -177,22 +177,6 @@ public function unique($columns = null, $name = null, $algorithm = null, $option return $this; } - /** - * Specify a non blocking index for the collection. - * - * @param string|array $columns - * - * @return Blueprint - */ - public function background($columns = null) - { - $columns = $this->fluent($columns); - - $this->index($columns, null, null, ['background' => true]); - - return $this; - } - /** * Specify a sparse index for the collection. * diff --git a/tests/SchemaTest.php b/tests/SchemaTest.php index 82d4a68c6..0f04ab6d4 100644 --- a/tests/SchemaTest.php +++ b/tests/SchemaTest.php @@ -245,16 +245,6 @@ public function testHasIndex(): void }); } - public function testBackground(): void - { - Schema::table('newcollection', function ($collection) { - $collection->background('backgroundkey'); - }); - - $index = $this->getIndex('newcollection', 'backgroundkey'); - $this->assertEquals(1, $index['background']); - } - public function testSparse(): void { Schema::table('newcollection', function ($collection) { From 7551f76b41e70e4883c3005cb557288a1f2a2ff4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Wed, 4 Sep 2024 10:11:34 +0200 Subject: [PATCH 11/27] PHPORM-235 Remove custom DatabaseTokenRepository (#3124) --- CHANGELOG.md | 1 + src/Auth/DatabaseTokenRepository.php | 59 ----------------------- src/Auth/PasswordBrokerManager.php | 23 --------- src/Auth/PasswordResetServiceProvider.php | 22 --------- tests/TestCase.php | 19 -------- 5 files changed, 1 insertion(+), 123 deletions(-) delete mode 100644 src/Auth/DatabaseTokenRepository.php delete mode 100644 src/Auth/PasswordBrokerManager.php delete mode 100644 src/Auth/PasswordResetServiceProvider.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 534250191..0f53f4c22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file. * **BREAKING CHANGE** Make Query\Builder return objects instead of array to match Laravel behavior by @GromNaN in [#3107](https://github.com/mongodb/laravel-mongodb/pull/3107) * **BREAKING CHANGE** In DB query results, convert BSON `UTCDateTime` objects into `Carbon` date with the default timezone by @GromNaN in [#3119](https://github.com/mongodb/laravel-mongodb/pull/3119) * Remove `MongoFailedJobProvider`, replaced by Laravel `DatabaseFailedJobProvider` by @GromNaN in [#3122](https://github.com/mongodb/laravel-mongodb/pull/3122) +* Remove custom `PasswordResetServiceProvider`, use the default `DatabaseTokenRepository` by @GromNaN in [#3124](https://github.com/mongodb/laravel-mongodb/pull/3124) * Remove `Blueprint::background()` method by @GromNaN in [#3132](https://github.com/mongodb/laravel-mongodb/pull/3132) ## [4.8.0] - 2024-08-27 diff --git a/src/Auth/DatabaseTokenRepository.php b/src/Auth/DatabaseTokenRepository.php deleted file mode 100644 index 83ce9bf6d..000000000 --- a/src/Auth/DatabaseTokenRepository.php +++ /dev/null @@ -1,59 +0,0 @@ - $email, - 'token' => $this->hasher->make($token), - 'created_at' => new UTCDateTime(Date::now()), - ]; - } - - /** @inheritdoc */ - protected function tokenExpired($createdAt) - { - $createdAt = $this->convertDateTime($createdAt); - - return parent::tokenExpired($createdAt); - } - - /** @inheritdoc */ - protected function tokenRecentlyCreated($createdAt) - { - $createdAt = $this->convertDateTime($createdAt); - - return parent::tokenRecentlyCreated($createdAt); - } - - private function convertDateTime($createdAt) - { - // Convert UTCDateTime to a date string. - if ($createdAt instanceof UTCDateTime) { - $date = $createdAt->toDateTime(); - $date->setTimezone(new DateTimeZone(date_default_timezone_get())); - $createdAt = $date->format('Y-m-d H:i:s'); - } elseif (is_array($createdAt) && isset($createdAt['date'])) { - $date = new DateTime($createdAt['date'], new DateTimeZone($createdAt['timezone'] ?? 'UTC')); - $date->setTimezone(new DateTimeZone(date_default_timezone_get())); - $createdAt = $date->format('Y-m-d H:i:s'); - } - - return $createdAt; - } -} diff --git a/src/Auth/PasswordBrokerManager.php b/src/Auth/PasswordBrokerManager.php deleted file mode 100644 index 157df3d97..000000000 --- a/src/Auth/PasswordBrokerManager.php +++ /dev/null @@ -1,23 +0,0 @@ -app['db']->connection(), - $this->app['hash'], - $config['table'], - $this->app['config']['app.key'], - $config['expire'], - $config['throttle'] ?? 0, - ); - } -} diff --git a/src/Auth/PasswordResetServiceProvider.php b/src/Auth/PasswordResetServiceProvider.php deleted file mode 100644 index a8aa61da4..000000000 --- a/src/Auth/PasswordResetServiceProvider.php +++ /dev/null @@ -1,22 +0,0 @@ -app->singleton('auth.password', function ($app) { - return new PasswordBrokerManager($app); - }); - - $this->app->bind('auth.password.broker', function ($app) { - return $app->make('auth.password')->broker(); - }); - } -} diff --git a/tests/TestCase.php b/tests/TestCase.php index 2353915ed..5f5bbecdc 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -4,32 +4,14 @@ namespace MongoDB\Laravel\Tests; -use Illuminate\Auth\Passwords\PasswordResetServiceProvider as BasePasswordResetServiceProviderAlias; use Illuminate\Foundation\Application; -use MongoDB\Laravel\Auth\PasswordResetServiceProvider; use MongoDB\Laravel\MongoDBServiceProvider; use MongoDB\Laravel\Tests\Models\User; use MongoDB\Laravel\Validation\ValidationServiceProvider; use Orchestra\Testbench\TestCase as OrchestraTestCase; -use function array_search; - class TestCase extends OrchestraTestCase { - /** - * Get application providers. - * - * @param Application $app - */ - protected function getApplicationProviders($app): array - { - $providers = parent::getApplicationProviders($app); - - unset($providers[array_search(BasePasswordResetServiceProviderAlias::class, $providers)]); - - return $providers; - } - /** * Get package providers. * @@ -39,7 +21,6 @@ protected function getPackageProviders($app): array { return [ MongoDBServiceProvider::class, - PasswordResetServiceProvider::class, ValidationServiceProvider::class, ]; } From 807f5fa587ea68ce38e2bc29fd32b0c807f46625 Mon Sep 17 00:00:00 2001 From: Rea Rustagi <85902999+rustagir@users.noreply.github.com> Date: Thu, 5 Sep 2024 12:55:54 -0400 Subject: [PATCH 12/27] DOCSP-43158: carbon date values db query results (#3133) * DOCSP-43158: carbon date values db query results * add to upgrade guide * wip --- docs/eloquent-models/model-class.txt | 11 +++++++++-- docs/query-builder.txt | 7 +++++++ docs/upgrade.txt | 15 ++++++++++++++- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/docs/eloquent-models/model-class.txt b/docs/eloquent-models/model-class.txt index 8cedb4ece..4e699309a 100644 --- a/docs/eloquent-models/model-class.txt +++ b/docs/eloquent-models/model-class.txt @@ -216,8 +216,8 @@ type, to the Laravel ``datetime`` type. To learn more, see `Attribute Casting `__ in the Laravel documentation. -This conversion lets you use the PHP `DateTime `__ -or the `Carbon class `__ to work with dates +This conversion lets you use the PHP `DateTime +`__ class to work with dates in this field. The following example shows a Laravel query that uses the casting helper on the model to query for planets with a ``discovery_dt`` of less than three years ago: @@ -226,6 +226,13 @@ less than three years ago: Planet::where( 'discovery_dt', '>', new DateTime('-3 years'))->get(); +.. note:: Carbon Date Class + + Starting in {+odm-long+} v5.0, ``UTCDateTime`` BSON values in MongoDB + are returned as `Carbon `__ date + classes in query results. The {+odm-short+} applies the default + timezone when performing this conversion. + To learn more about MongoDB's data types, see :manual:`BSON Types ` in the Server manual. diff --git a/docs/query-builder.txt b/docs/query-builder.txt index 7d33c016d..c3a219aa8 100644 --- a/docs/query-builder.txt +++ b/docs/query-builder.txt @@ -346,6 +346,13 @@ query builder method to retrieve documents from the :start-after: begin query whereDate :end-before: end query whereDate +.. note:: Date Query Result Type + + Starting in {+odm-long+} v5.0, ``UTCDateTime`` BSON values in MongoDB + are returned as `Carbon `__ date + classes in query results. The {+odm-short+} applies the default + timezone when performing this conversion. + .. _laravel-query-builder-pattern: Text Pattern Match Example diff --git a/docs/upgrade.txt b/docs/upgrade.txt index 5d8ca09a3..301a2100e 100644 --- a/docs/upgrade.txt +++ b/docs/upgrade.txt @@ -22,7 +22,7 @@ Overview On this page, you can learn how to upgrade {+odm-long+} to a new major version. This page also includes the changes you must make to your application to upgrade -your object-document mapper (ODM) version without losing functionality, if applicable. +your version of the {+odm-short+} without losing functionality, if applicable. How to Upgrade -------------- @@ -61,6 +61,19 @@ version releases that introduced them. When upgrading library versions, address all the breaking changes between your current version and the planned upgrade version. +- :ref:`laravel-breaking-changes-v5.x` +- :ref:`laravel-breaking-changes-v4.x` + +.. _laravel-breaking-changes-v5.x: + +Version 5.x Breaking Changes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This library version introduces the following breaking changes: + +- In query results, the library converts BSON ``UTCDateTime`` objects to ``Carbon`` + date classes, applying the default timezone. + .. _laravel-breaking-changes-v4.x: Version 4.x Breaking Changes From 837078f8660c4d2846e94f43b26c431741703b89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Thu, 5 Sep 2024 19:54:40 +0200 Subject: [PATCH 13/27] PHPORM-236 Remove _id from query results (#3136) --- CHANGELOG.md | 2 +- docs/includes/usage-examples/FindOneTest.php | 2 +- src/Query/Builder.php | 8 ++++---- tests/QueryBuilderTest.php | 20 +++++++++++++++++-- .../Failed/DatabaseFailedJobProviderTest.php | 8 +++++--- 5 files changed, 29 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f53f4c22..7a175e414 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file. ## [5.0.0] - next * Remove support for Laravel 10 by @GromNaN in [#3123](https://github.com/mongodb/laravel-mongodb/pull/3123) -* **BREAKING CHANGE** Use `id` as an alias for `_id` in commands and queries for compatibility with Eloquent packages by @GromNaN in [#3040](https://github.com/mongodb/laravel-mongodb/pull/3040) +* **BREAKING CHANGE** Use `id` as an alias for `_id` in commands and queries for compatibility with Eloquent packages by @GromNaN in [#3040](https://github.com/mongodb/laravel-mongodb/pull/3040) and [#3136](https://github.com/mongodb/laravel-mongodb/pull/3136) * **BREAKING CHANGE** Make Query\Builder return objects instead of array to match Laravel behavior by @GromNaN in [#3107](https://github.com/mongodb/laravel-mongodb/pull/3107) * **BREAKING CHANGE** In DB query results, convert BSON `UTCDateTime` objects into `Carbon` date with the default timezone by @GromNaN in [#3119](https://github.com/mongodb/laravel-mongodb/pull/3119) * Remove `MongoFailedJobProvider`, replaced by Laravel `DatabaseFailedJobProvider` by @GromNaN in [#3122](https://github.com/mongodb/laravel-mongodb/pull/3122) diff --git a/docs/includes/usage-examples/FindOneTest.php b/docs/includes/usage-examples/FindOneTest.php index 8472727be..e46ba1be4 100644 --- a/docs/includes/usage-examples/FindOneTest.php +++ b/docs/includes/usage-examples/FindOneTest.php @@ -31,6 +31,6 @@ public function testFindOne(): void // end-find-one $this->assertInstanceOf(Movie::class, $movie); - $this->expectOutputRegex('/^{"_id":"[a-z0-9]{24}","title":"The Shawshank Redemption","directors":\["Frank Darabont","Rob Reiner"\],"id":"[a-z0-9]{24}"}$/'); + $this->expectOutputRegex('/^{"title":"The Shawshank Redemption","directors":\["Frank Darabont","Rob Reiner"\],"id":"[a-z0-9]{24}"}$/'); } } diff --git a/src/Query/Builder.php b/src/Query/Builder.php index f4f31b58f..e2f8867b3 100644 --- a/src/Query/Builder.php +++ b/src/Query/Builder.php @@ -1616,7 +1616,7 @@ public function orWhereIntegerNotInRaw($column, $values, $boolean = 'and') private function aliasIdForQuery(array $values): array { if (array_key_exists('id', $values)) { - if (array_key_exists('_id', $values)) { + if (array_key_exists('_id', $values) && $values['id'] !== $values['_id']) { throw new InvalidArgumentException('Cannot have both "id" and "_id" fields.'); } @@ -1627,7 +1627,7 @@ private function aliasIdForQuery(array $values): array foreach ($values as $key => $value) { if (is_string($key) && str_ends_with($key, '.id')) { $newkey = substr($key, 0, -3) . '._id'; - if (array_key_exists($newkey, $values)) { + if (array_key_exists($newkey, $values) && $value !== $values[$newkey]) { throw new InvalidArgumentException(sprintf('Cannot have both "%s" and "%s" fields.', $key, $newkey)); } @@ -1659,7 +1659,7 @@ private function aliasIdForResult(array|object $values): array|object if (is_array($values)) { if (array_key_exists('_id', $values) && ! array_key_exists('id', $values)) { $values['id'] = $values['_id']; - //unset($values['_id']); + unset($values['_id']); } foreach ($values as $key => $value) { @@ -1675,7 +1675,7 @@ private function aliasIdForResult(array|object $values): array|object if ($values instanceof stdClass) { if (property_exists($values, '_id') && ! property_exists($values, 'id')) { $values->id = $values->_id; - //unset($values->_id); + unset($values->_id); } foreach (get_object_vars($values) as $key => $value) { diff --git a/tests/QueryBuilderTest.php b/tests/QueryBuilderTest.php index 846f48514..910adecfc 100644 --- a/tests/QueryBuilderTest.php +++ b/tests/QueryBuilderTest.php @@ -449,18 +449,33 @@ public function testDistinct() public function testCustomId() { + $tags = [['id' => 'sharp', 'name' => 'Sharp']]; DB::table('items')->insert([ - ['id' => 'knife', 'type' => 'sharp', 'amount' => 34], - ['id' => 'fork', 'type' => 'sharp', 'amount' => 20], + ['id' => 'knife', 'type' => 'sharp', 'amount' => 34, 'tags' => $tags], + ['id' => 'fork', 'type' => 'sharp', 'amount' => 20, 'tags' => $tags], ['id' => 'spoon', 'type' => 'round', 'amount' => 3], ]); $item = DB::table('items')->find('knife'); $this->assertEquals('knife', $item->id); + $this->assertObjectNotHasProperty('_id', $item); + $this->assertEquals('sharp', $item->tags[0]['id']); + $this->assertArrayNotHasKey('_id', $item->tags[0]); $item = DB::table('items')->where('id', 'fork')->first(); $this->assertEquals('fork', $item->id); + $item = DB::table('items')->where('_id', 'fork')->first(); + $this->assertEquals('fork', $item->id); + + // tags.id is translated into tags._id in query + $items = DB::table('items')->whereIn('tags.id', ['sharp'])->get(); + $this->assertCount(2, $items); + + // Ensure the field _id is stored in the database + $items = DB::table('items')->whereIn('tags._id', ['sharp'])->get(); + $this->assertCount(2, $items); + DB::table('users')->insert([ ['id' => 1, 'name' => 'Jane Doe'], ['id' => 2, 'name' => 'John Doe'], @@ -468,6 +483,7 @@ public function testCustomId() $item = DB::table('users')->find(1); $this->assertEquals(1, $item->id); + $this->assertObjectNotHasProperty('_id', $item); } public function testTake() diff --git a/tests/Queue/Failed/DatabaseFailedJobProviderTest.php b/tests/Queue/Failed/DatabaseFailedJobProviderTest.php index 88a7f0e7b..01f38b9df 100644 --- a/tests/Queue/Failed/DatabaseFailedJobProviderTest.php +++ b/tests/Queue/Failed/DatabaseFailedJobProviderTest.php @@ -77,8 +77,9 @@ public function testAll(): void $all = $this->getProvider()->all(); $this->assertCount(5, $all); - $this->assertEquals(new ObjectId(sprintf('%024d', 5)), $all[0]->_id); - $this->assertEquals(sprintf('%024d', 5), $all[0]->id, 'id field is added for compatibility with DatabaseFailedJobProvider'); + $this->assertInstanceOf(ObjectId::class, $all[0]->id); + $this->assertEquals(new ObjectId(sprintf('%024d', 5)), $all[0]->id); + $this->assertEquals(sprintf('%024d', 5), (string) $all[0]->id, 'id field is added for compatibility with DatabaseFailedJobProvider'); } public function testFindAndForget(): void @@ -89,7 +90,8 @@ public function testFindAndForget(): void $found = $provider->find($id); $this->assertIsObject($found, 'The job is found'); - $this->assertEquals(new ObjectId($id), $found->_id); + $this->assertInstanceOf(ObjectId::class, $found->id); + $this->assertEquals(new ObjectId($id), $found->id); $this->assertObjectHasProperty('failed_at', $found); // Delete the job From e4248611f148d6ff706e331042edf5a33d313acb Mon Sep 17 00:00:00 2001 From: Nora Reidy Date: Thu, 5 Sep 2024 15:13:31 -0400 Subject: [PATCH 14/27] DOCSP-41335: Id field alias (#3042) Adds information and an example of the ID field alias. --------- Co-authored-by: norareidy Co-authored-by: rustagir --- docs/eloquent-models/model-class.txt | 8 ++------ .../includes/query-builder/QueryBuilderTest.php | 3 ++- docs/query-builder.txt | 17 +++++++++++++++-- docs/upgrade.txt | 5 +++++ 4 files changed, 24 insertions(+), 9 deletions(-) diff --git a/docs/eloquent-models/model-class.txt b/docs/eloquent-models/model-class.txt index 4e699309a..4f81f4663 100644 --- a/docs/eloquent-models/model-class.txt +++ b/docs/eloquent-models/model-class.txt @@ -302,12 +302,8 @@ including this trait, you can make the third-party class compatible with MongoDB. When you apply the ``DocumentModel`` trait to a model class, you must -declare the following properties in your class: - -- ``$primaryKey = '_id'``, because the ``_id`` field uniquely - identifies MongoDB documents -- ``$keyType = 'string'``, because the {+odm-short+} casts MongoDB - ``ObjectId`` values to type ``string`` +set the ``$keyType`` property to ``'string'`` as the {+odm-short+} +casts MongoDB ``ObjectId`` values to type ``string``. Extended Class Example ~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/includes/query-builder/QueryBuilderTest.php b/docs/includes/query-builder/QueryBuilderTest.php index bf92b9a6b..5105e59b5 100644 --- a/docs/includes/query-builder/QueryBuilderTest.php +++ b/docs/includes/query-builder/QueryBuilderTest.php @@ -7,6 +7,7 @@ use Illuminate\Database\Query\Builder; use Illuminate\Pagination\AbstractPaginator; use Illuminate\Support\Facades\DB; +use MongoDB\BSON\ObjectId; use MongoDB\BSON\Regex; use MongoDB\Laravel\Collection; use MongoDB\Laravel\Tests\TestCase; @@ -63,7 +64,7 @@ public function testOrWhere(): void // begin query orWhere $result = DB::connection('mongodb') ->table('movies') - ->where('year', 1955) + ->where('id', new ObjectId('573a1398f29313caabce9682')) ->orWhere('title', 'Back to the Future') ->get(); // end query orWhere diff --git a/docs/query-builder.txt b/docs/query-builder.txt index c3a219aa8..9b1fe65f9 100644 --- a/docs/query-builder.txt +++ b/docs/query-builder.txt @@ -176,8 +176,9 @@ Logical OR Example The following example shows how to chain the ``orWhere()`` query builder method to retrieve documents from the -``movies`` collection that either match the ``year`` -value of ``1955`` or match the ``title`` value ``"Back to the Future"``: +``movies`` collection in which the value of the ``_id`` +field is ``ObjectId('573a1398f29313caabce9682')`` or +the value of the ``title`` field is ``"Back to the Future"``: .. literalinclude:: /includes/query-builder/QueryBuilderTest.php :language: php @@ -185,6 +186,18 @@ value of ``1955`` or match the ``title`` value ``"Back to the Future"``: :start-after: begin query orWhere :end-before: end query orWhere +.. note:: + + You can use the ``id`` alias in your queries to represent the + ``_id`` field in MongoDB documents, as shown in the preceding + code. When you run a find operation using the query builder, {+odm-short+} + automatically converts between ``id`` and ``_id``. This provides better + compatibility with Laravel, as the framework assumes that each record has a + primary key named ``id`` by default. + + Because of this behavior, you cannot have two separate ``id`` and ``_id`` + fields in your documents. + .. _laravel-query-builder-logical-and: Logical AND Example diff --git a/docs/upgrade.txt b/docs/upgrade.txt index 301a2100e..fed27d862 100644 --- a/docs/upgrade.txt +++ b/docs/upgrade.txt @@ -74,6 +74,11 @@ This library version introduces the following breaking changes: - In query results, the library converts BSON ``UTCDateTime`` objects to ``Carbon`` date classes, applying the default timezone. +- ``id`` is an alias for the ``_id`` field in MongoDB documents, and the library + automatically converts between ``id`` and ``_id`` when querying data. Because + of this behavior, you cannot have two separate ``id`` and ``_id`` fields in your + documents. + .. _laravel-breaking-changes-v4.x: Version 4.x Breaking Changes From 74f219c60382ea85eac6cdae54c313a386763c38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Fri, 6 Sep 2024 14:35:22 +0200 Subject: [PATCH 15/27] PHPORM-56 Replace Collection proxy class with Driver monitoring (#3137) --- CHANGELOG.md | 1 + composer.json | 2 +- .../query-builder/QueryBuilderTest.php | 2 +- src/Bus/MongoBatchRepository.php | 2 +- src/Cache/MongoLock.php | 2 +- src/Cache/MongoStore.php | 2 +- src/Collection.php | 80 ------------------- src/CommandSubscriber.php | 53 ++++++++++++ src/Connection.php | 17 ++-- src/Query/AggregationBuilder.php | 5 +- src/Query/Builder.php | 2 +- src/Schema/Blueprint.php | 8 +- tests/Cache/MongoLockTest.php | 2 +- tests/Casts/DecimalTest.php | 2 +- tests/CollectionTest.php | 36 --------- tests/ConnectionTest.php | 25 ++++-- tests/ModelTest.php | 2 +- tests/QueryBuilderTest.php | 2 +- 18 files changed, 97 insertions(+), 148 deletions(-) delete mode 100644 src/Collection.php create mode 100644 src/CommandSubscriber.php delete mode 100644 tests/CollectionTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a175e414..fdedba537 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file. * Remove `MongoFailedJobProvider`, replaced by Laravel `DatabaseFailedJobProvider` by @GromNaN in [#3122](https://github.com/mongodb/laravel-mongodb/pull/3122) * Remove custom `PasswordResetServiceProvider`, use the default `DatabaseTokenRepository` by @GromNaN in [#3124](https://github.com/mongodb/laravel-mongodb/pull/3124) * Remove `Blueprint::background()` method by @GromNaN in [#3132](https://github.com/mongodb/laravel-mongodb/pull/3132) +* Replace `Collection` proxy class with Driver monitoring by @GromNaN in [#3137]((https://github.com/mongodb/laravel-mongodb/pull/3137) ## [4.8.0] - 2024-08-27 diff --git a/composer.json b/composer.json index e7d2f09cd..f03fdc89d 100644 --- a/composer.json +++ b/composer.json @@ -30,7 +30,7 @@ "illuminate/database": "^11", "illuminate/events": "^11", "illuminate/support": "^11", - "mongodb/mongodb": "^1.15" + "mongodb/mongodb": "^1.18" }, "require-dev": { "mongodb/builder": "^0.2", diff --git a/docs/includes/query-builder/QueryBuilderTest.php b/docs/includes/query-builder/QueryBuilderTest.php index 5105e59b5..02d15cc48 100644 --- a/docs/includes/query-builder/QueryBuilderTest.php +++ b/docs/includes/query-builder/QueryBuilderTest.php @@ -9,7 +9,7 @@ use Illuminate\Support\Facades\DB; use MongoDB\BSON\ObjectId; use MongoDB\BSON\Regex; -use MongoDB\Laravel\Collection; +use MongoDB\Collection; use MongoDB\Laravel\Tests\TestCase; use function file_get_contents; diff --git a/src/Bus/MongoBatchRepository.php b/src/Bus/MongoBatchRepository.php index dd0713f97..c6314ba69 100644 --- a/src/Bus/MongoBatchRepository.php +++ b/src/Bus/MongoBatchRepository.php @@ -14,8 +14,8 @@ use Illuminate\Support\Carbon; use MongoDB\BSON\ObjectId; use MongoDB\BSON\UTCDateTime; +use MongoDB\Collection; use MongoDB\Driver\ReadPreference; -use MongoDB\Laravel\Collection; use MongoDB\Laravel\Connection; use MongoDB\Operation\FindOneAndUpdate; use Override; diff --git a/src/Cache/MongoLock.php b/src/Cache/MongoLock.php index e9bd3d607..d273b4d99 100644 --- a/src/Cache/MongoLock.php +++ b/src/Cache/MongoLock.php @@ -6,7 +6,7 @@ use Illuminate\Support\Carbon; use InvalidArgumentException; use MongoDB\BSON\UTCDateTime; -use MongoDB\Laravel\Collection; +use MongoDB\Collection; use MongoDB\Operation\FindOneAndUpdate; use Override; diff --git a/src/Cache/MongoStore.php b/src/Cache/MongoStore.php index e35d0f70d..e37884a93 100644 --- a/src/Cache/MongoStore.php +++ b/src/Cache/MongoStore.php @@ -7,7 +7,7 @@ use Illuminate\Contracts\Cache\Store; use Illuminate\Support\Carbon; use MongoDB\BSON\UTCDateTime; -use MongoDB\Laravel\Collection; +use MongoDB\Collection; use MongoDB\Laravel\Connection; use MongoDB\Operation\FindOneAndUpdate; use Override; diff --git a/src/Collection.php b/src/Collection.php deleted file mode 100644 index 22c0dfa05..000000000 --- a/src/Collection.php +++ /dev/null @@ -1,80 +0,0 @@ -connection = $connection; - $this->collection = $collection; - } - - /** - * Handle dynamic method calls. - * - * @return mixed - */ - public function __call(string $method, array $parameters) - { - $start = microtime(true); - $result = $this->collection->$method(...$parameters); - - // Once we have run the query we will calculate the time that it took to run and - // then log the query, bindings, and execution time so we will report them on - // the event that the developer needs them. We'll log time in milliseconds. - $time = $this->connection->getElapsedTime($start); - - $query = []; - - // Convert the query parameters to a json string. - array_walk_recursive($parameters, function (&$item, $key) { - if ($item instanceof ObjectID) { - $item = (string) $item; - } - }); - - // Convert the query parameters to a json string. - foreach ($parameters as $parameter) { - try { - $query[] = json_encode($parameter, JSON_THROW_ON_ERROR); - } catch (Exception) { - $query[] = '{...}'; - } - } - - $queryString = $this->collection->getCollectionName() . '.' . $method . '(' . implode(',', $query) . ')'; - - $this->connection->logQuery($queryString, [], $time); - - return $result; - } -} diff --git a/src/CommandSubscriber.php b/src/CommandSubscriber.php new file mode 100644 index 000000000..ef282bcac --- /dev/null +++ b/src/CommandSubscriber.php @@ -0,0 +1,53 @@ + */ + private array $commands = []; + + public function __construct(private Connection $connection) + { + } + + public function commandStarted(CommandStartedEvent $event) + { + $this->commands[$event->getOperationId()] = $event; + } + + public function commandFailed(CommandFailedEvent $event) + { + $this->logQuery($event); + } + + public function commandSucceeded(CommandSucceededEvent $event) + { + $this->logQuery($event); + } + + private function logQuery(CommandSucceededEvent|CommandFailedEvent $event): void + { + $startedEvent = $this->commands[$event->getOperationId()]; + unset($this->commands[$event->getOperationId()]); + + $command = []; + foreach (get_object_vars($startedEvent->getCommand()) as $key => $value) { + if ($key[0] !== '$' && ! in_array($key, ['lsid', 'txnNumber'])) { + $command[$key] = $value; + } + } + + $this->connection->logQuery(Document::fromPHP($command)->toCanonicalExtendedJSON(), [], $event->getDurationMicros()); + } +} diff --git a/src/Connection.php b/src/Connection.php index cb2bc78de..a76ddc010 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -8,6 +8,7 @@ use Illuminate\Database\Connection as BaseConnection; use InvalidArgumentException; use MongoDB\Client; +use MongoDB\Collection; use MongoDB\Database; use MongoDB\Driver\Exception\AuthenticationException; use MongoDB\Driver\Exception\ConnectionException; @@ -47,6 +48,8 @@ class Connection extends BaseConnection */ protected $connection; + private ?CommandSubscriber $commandSubscriber; + /** * Create a new database connection instance. */ @@ -62,6 +65,8 @@ public function __construct(array $config) // Create the connection $this->connection = $this->createConnection($dsn, $config, $options); + $this->commandSubscriber = new CommandSubscriber($this); + $this->connection->addSubscriber($this->commandSubscriber); // Select database $this->db = $this->connection->selectDatabase($this->getDefaultDatabaseName($dsn, $config)); @@ -97,9 +102,9 @@ public function table($table, $as = null) * * @return Collection */ - public function getCollection($name) + public function getCollection($name): Collection { - return new Collection($this, $this->db->selectCollection($this->tablePrefix . $name)); + return $this->db->selectCollection($this->tablePrefix . $name); } /** @inheritdoc */ @@ -198,6 +203,8 @@ public function ping(): void /** @inheritdoc */ public function disconnect() { + $this->connection?->removeSubscriber($this->commandSubscriber); + $this->commandSubscriber = null; $this->connection = null; } @@ -264,12 +271,6 @@ protected function getDsn(array $config): string throw new InvalidArgumentException('MongoDB connection configuration requires "dsn" or "host" key.'); } - /** @inheritdoc */ - public function getElapsedTime($start) - { - return parent::getElapsedTime($start); - } - /** @inheritdoc */ public function getDriverName() { diff --git a/src/Query/AggregationBuilder.php b/src/Query/AggregationBuilder.php index ad0c195d4..0d4638731 100644 --- a/src/Query/AggregationBuilder.php +++ b/src/Query/AggregationBuilder.php @@ -10,9 +10,8 @@ use Iterator; use MongoDB\Builder\BuilderEncoder; use MongoDB\Builder\Stage\FluentFactoryTrait; -use MongoDB\Collection as MongoDBCollection; +use MongoDB\Collection; use MongoDB\Driver\CursorInterface; -use MongoDB\Laravel\Collection as LaravelMongoDBCollection; use function array_replace; use function collect; @@ -24,7 +23,7 @@ class AggregationBuilder use FluentFactoryTrait; public function __construct( - private MongoDBCollection|LaravelMongoDBCollection $collection, + private Collection $collection, private readonly array $options = [], ) { } diff --git a/src/Query/Builder.php b/src/Query/Builder.php index e2f8867b3..9b446f8e8 100644 --- a/src/Query/Builder.php +++ b/src/Query/Builder.php @@ -82,7 +82,7 @@ class Builder extends BaseBuilder /** * The database collection. * - * @var \MongoDB\Laravel\Collection + * @var \MongoDB\Collection */ protected $collection; diff --git a/src/Schema/Blueprint.php b/src/Schema/Blueprint.php index 0ad4535cf..f107bd7e5 100644 --- a/src/Schema/Blueprint.php +++ b/src/Schema/Blueprint.php @@ -6,7 +6,7 @@ use Illuminate\Database\Connection; use Illuminate\Database\Schema\Blueprint as SchemaBlueprint; -use MongoDB\Laravel\Collection; +use MongoDB\Collection; use function array_flip; use function implode; @@ -21,14 +21,14 @@ class Blueprint extends SchemaBlueprint /** * The MongoConnection object for this blueprint. * - * @var \MongoDB\Laravel\Connection + * @var Connection */ protected $connection; /** - * The MongoCollection object for this blueprint. + * The Collection object for this blueprint. * - * @var Collection|\MongoDB\Collection + * @var Collection */ protected $collection; diff --git a/tests/Cache/MongoLockTest.php b/tests/Cache/MongoLockTest.php index e3d2568d5..f305061cf 100644 --- a/tests/Cache/MongoLockTest.php +++ b/tests/Cache/MongoLockTest.php @@ -8,8 +8,8 @@ use Illuminate\Support\Facades\DB; use InvalidArgumentException; use MongoDB\BSON\UTCDateTime; +use MongoDB\Collection; use MongoDB\Laravel\Cache\MongoLock; -use MongoDB\Laravel\Collection; use MongoDB\Laravel\Tests\TestCase; use PHPUnit\Framework\Attributes\TestWith; diff --git a/tests/Casts/DecimalTest.php b/tests/Casts/DecimalTest.php index f69d24d62..184abd026 100644 --- a/tests/Casts/DecimalTest.php +++ b/tests/Casts/DecimalTest.php @@ -10,7 +10,7 @@ use MongoDB\BSON\Int64; use MongoDB\BSON\Javascript; use MongoDB\BSON\UTCDateTime; -use MongoDB\Laravel\Collection; +use MongoDB\Collection; use MongoDB\Laravel\Tests\Models\Casting; use MongoDB\Laravel\Tests\TestCase; diff --git a/tests/CollectionTest.php b/tests/CollectionTest.php deleted file mode 100644 index fbdbf3daf..000000000 --- a/tests/CollectionTest.php +++ /dev/null @@ -1,36 +0,0 @@ - 'bar']; - $where = ['id' => new ObjectID('56f94800911dcc276b5723dd')]; - $time = 1.1; - $queryString = 'name-collection.findOne({"id":"56f94800911dcc276b5723dd"})'; - - $mongoCollection = $this->getMockBuilder(MongoCollection::class) - ->disableOriginalConstructor() - ->getMock(); - - $mongoCollection->expects($this->once())->method('findOne')->with($where)->willReturn($return); - $mongoCollection->expects($this->once())->method('getCollectionName')->willReturn('name-collection'); - - $connection = $this->getMockBuilder(Connection::class)->disableOriginalConstructor()->getMock(); - $connection->expects($this->once())->method('getElapsedTime')->willReturn($time); - $connection->expects($this->once())->method('logQuery')->with($queryString, [], $time); - - $collection = new Collection($connection, $mongoCollection); - - $this->assertEquals($return, $collection->findOne($where)); - } -} diff --git a/tests/ConnectionTest.php b/tests/ConnectionTest.php index ac4cc78fc..4f9dfa10c 100644 --- a/tests/ConnectionTest.php +++ b/tests/ConnectionTest.php @@ -7,10 +7,12 @@ use Generator; use Illuminate\Support\Facades\DB; use InvalidArgumentException; +use MongoDB\BSON\ObjectId; use MongoDB\Client; +use MongoDB\Collection; use MongoDB\Database; +use MongoDB\Driver\Exception\BulkWriteException; use MongoDB\Driver\Exception\ConnectionTimeoutException; -use MongoDB\Laravel\Collection; use MongoDB\Laravel\Connection; use MongoDB\Laravel\Query\Builder; use MongoDB\Laravel\Schema\Builder as SchemaBuilder; @@ -225,9 +227,6 @@ public function testCollection() $collection = DB::connection('mongodb')->table('unittests'); $this->assertInstanceOf(Builder::class, $collection); - - $collection = DB::connection('mongodb')->table('unittests'); - $this->assertInstanceOf(Builder::class, $collection); } public function testPrefix() @@ -251,10 +250,12 @@ public function testQueryLog() $this->assertCount(0, DB::getQueryLog()); DB::table('items')->get(); - $this->assertCount(1, DB::getQueryLog()); + $this->assertCount(1, $logs = DB::getQueryLog()); + $this->assertJsonStringEqualsJsonString('{"find":"items","filter":{}}', $logs[0]['query']); - DB::table('items')->insert(['name' => 'test']); - $this->assertCount(2, DB::getQueryLog()); + DB::table('items')->insert(['id' => $id = new ObjectId(), 'name' => 'test']); + $this->assertCount(2, $logs = DB::getQueryLog()); + $this->assertJsonStringEqualsJsonString('{"insert":"items","ordered":true,"documents":[{"name":"test","_id":{"$oid":"' . $id . '"}}]}', $logs[1]['query']); DB::table('items')->count(); $this->assertCount(3, DB::getQueryLog()); @@ -264,6 +265,16 @@ public function testQueryLog() DB::table('items')->where('name', 'test')->delete(); $this->assertCount(5, DB::getQueryLog()); + + // Error + try { + DB::table('items')->where('name', 'test')->update( + ['$set' => ['embed' => ['foo' => 'bar']], '$unset' => ['embed' => ['foo']]], + ); + self::fail('Expected BulkWriteException'); + } catch (BulkWriteException) { + $this->assertCount(6, DB::getQueryLog()); + } } public function testSchemaBuilder() diff --git a/tests/ModelTest.php b/tests/ModelTest.php index 36465ce53..075c0d3ad 100644 --- a/tests/ModelTest.php +++ b/tests/ModelTest.php @@ -15,7 +15,7 @@ use MongoDB\BSON\Binary; use MongoDB\BSON\ObjectID; use MongoDB\BSON\UTCDateTime; -use MongoDB\Laravel\Collection; +use MongoDB\Collection; use MongoDB\Laravel\Connection; use MongoDB\Laravel\Eloquent\Model; use MongoDB\Laravel\Tests\Models\Book; diff --git a/tests/QueryBuilderTest.php b/tests/QueryBuilderTest.php index 910adecfc..523ad3411 100644 --- a/tests/QueryBuilderTest.php +++ b/tests/QueryBuilderTest.php @@ -17,12 +17,12 @@ use MongoDB\BSON\ObjectId; use MongoDB\BSON\Regex; use MongoDB\BSON\UTCDateTime; +use MongoDB\Collection; use MongoDB\Driver\Cursor; use MongoDB\Driver\Monitoring\CommandFailedEvent; use MongoDB\Driver\Monitoring\CommandStartedEvent; use MongoDB\Driver\Monitoring\CommandSubscriber; use MongoDB\Driver\Monitoring\CommandSucceededEvent; -use MongoDB\Laravel\Collection; use MongoDB\Laravel\Query\Builder; use MongoDB\Laravel\Tests\Models\Item; use MongoDB\Laravel\Tests\Models\User; From 2ee0dd99617da56d893cfe1d3cafe7687f51aa91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Fri, 6 Sep 2024 20:40:54 +0200 Subject: [PATCH 16/27] Modernize code with rector (#3139) --- composer.json | 6 ++++-- rector.php | 25 +++++++++++++++++++++++++ src/Bus/MongoBatchRepository.php | 2 +- src/Helpers/QueriesRelationships.php | 7 +++---- src/Query/Builder.php | 2 +- tests/PHPStan/SarifErrorFormatter.php | 6 +++--- 6 files changed, 37 insertions(+), 11 deletions(-) create mode 100644 rector.php diff --git a/composer.json b/composer.json index f03fdc89d..4d679a95c 100644 --- a/composer.json +++ b/composer.json @@ -41,7 +41,8 @@ "mockery/mockery": "^1.4.4", "doctrine/coding-standard": "12.0.x-dev", "spatie/laravel-query-builder": "^5.6", - "phpstan/phpstan": "^1.10" + "phpstan/phpstan": "^1.10", + "rector/rector": "^1.2" }, "suggest": { "league/flysystem-gridfs": "Filesystem storage in MongoDB with GridFS", @@ -73,7 +74,8 @@ "test": "phpunit", "test:coverage": "phpunit --coverage-clover ./coverage.xml", "cs": "phpcs", - "cs:fix": "phpcbf" + "cs:fix": "phpcbf", + "rector": "rector" }, "config": { "allow-plugins": { diff --git a/rector.php b/rector.php new file mode 100644 index 000000000..23afcb2ea --- /dev/null +++ b/rector.php @@ -0,0 +1,25 @@ +withPaths([ + __FILE__, + __DIR__ . '/docs', + __DIR__ . '/src', + __DIR__ . '/tests', + ]) + ->withPhpSets() + ->withTypeCoverageLevel(0) + ->withSkip([ + RemoveExtraParametersRector::class, + ClosureToArrowFunctionRector::class, + NullToStrictStringFuncCallArgRector::class, + MixedTypeRector::class, + AddClosureVoidReturnTypeWhereNoReturnRector::class, + ]); diff --git a/src/Bus/MongoBatchRepository.php b/src/Bus/MongoBatchRepository.php index c6314ba69..2656bbc30 100644 --- a/src/Bus/MongoBatchRepository.php +++ b/src/Bus/MongoBatchRepository.php @@ -28,7 +28,7 @@ // are called by PruneBatchesCommand class MongoBatchRepository extends DatabaseBatchRepository implements PrunableBatchRepository { - private Collection $collection; + private readonly Collection $collection; public function __construct( BatchFactory $factory, diff --git a/src/Helpers/QueriesRelationships.php b/src/Helpers/QueriesRelationships.php index 933b6ec32..1f1ffa34b 100644 --- a/src/Helpers/QueriesRelationships.php +++ b/src/Helpers/QueriesRelationships.php @@ -21,12 +21,11 @@ use function array_map; use function class_basename; use function collect; -use function get_class; use function in_array; use function is_array; use function is_string; use function method_exists; -use function strpos; +use function str_contains; trait QueriesRelationships { @@ -45,7 +44,7 @@ trait QueriesRelationships public function has($relation, $operator = '>=', $count = 1, $boolean = 'and', ?Closure $callback = null) { if (is_string($relation)) { - if (strpos($relation, '.') !== false) { + if (str_contains($relation, '.')) { return $this->hasNested($relation, $operator, $count, $boolean, $callback); } @@ -139,7 +138,7 @@ private function handleMorphToMany($hasQuery, $relation) { // First we select the parent models that have a relation to our related model, // Then extracts related model's ids from the pivot column - $hasQuery->where($relation->getTable() . '.' . $relation->getMorphType(), get_class($relation->getParent())); + $hasQuery->where($relation->getTable() . '.' . $relation->getMorphType(), $relation->getParent()::class); $relations = $hasQuery->pluck($relation->getTable()); $relations = $relation->extractIds($relations->flatten(1)->toArray(), $relation->getForeignPivotKeyName()); diff --git a/src/Query/Builder.php b/src/Query/Builder.php index 9b446f8e8..2bbd5a01a 100644 --- a/src/Query/Builder.php +++ b/src/Query/Builder.php @@ -1079,7 +1079,7 @@ protected function performUpdate(array $update, array $options = []) $wheres = $this->aliasIdForQuery($wheres); $result = $this->collection->updateMany($wheres, $update, $options); if ($result->isAcknowledged()) { - return $result->getModifiedCount() ? $result->getModifiedCount() : $result->getUpsertedCount(); + return $result->getModifiedCount() ?: $result->getUpsertedCount(); } return 0; diff --git a/tests/PHPStan/SarifErrorFormatter.php b/tests/PHPStan/SarifErrorFormatter.php index 1fb814cde..92c0255cc 100644 --- a/tests/PHPStan/SarifErrorFormatter.php +++ b/tests/PHPStan/SarifErrorFormatter.php @@ -26,9 +26,9 @@ class SarifErrorFormatter implements ErrorFormatter private const URI_BASE_ID = 'WORKINGDIR'; public function __construct( - private RelativePathHelper $relativePathHelper, - private string $currentWorkingDirectory, - private bool $pretty, + private readonly RelativePathHelper $relativePathHelper, + private readonly string $currentWorkingDirectory, + private readonly bool $pretty, ) { } From 65072798194fdeabba20ded39be75366bafa0f29 Mon Sep 17 00:00:00 2001 From: Nora Reidy Date: Fri, 6 Sep 2024 16:19:12 -0400 Subject: [PATCH 17/27] DOCSP-43172: Remove DatabaseTokenRepository class (#3130) * DOCSP-43172: Remove DatabaseTokenRepository class * JT feedback * edit * JT feedback 2 --- docs/upgrade.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/upgrade.txt b/docs/upgrade.txt index fed27d862..4d7dca0d8 100644 --- a/docs/upgrade.txt +++ b/docs/upgrade.txt @@ -71,6 +71,16 @@ Version 5.x Breaking Changes This library version introduces the following breaking changes: +- Removes support for the following classes: + + - ``MongoDB\Laravel\Auth\DatabaseTokenRepository``. Instead, use the default + ``Illuminate\Queue\Failed\DatabaseFailedJobProvider`` class and + specify a connection to MongoDB. + + - ``MongoDB\Laravel\Queue\Failed\MongoFailedJobProvider``. Instead, + use the default ``Illuminate\Queue\Failed\DatabaseFailedJobProvider`` + class and specify a connection to MongoDB. + - In query results, the library converts BSON ``UTCDateTime`` objects to ``Carbon`` date classes, applying the default timezone. From 47662745dee06148397d08cc06559be5a71d26a1 Mon Sep 17 00:00:00 2001 From: Rea Rustagi <85902999+rustagir@users.noreply.github.com> Date: Mon, 9 Sep 2024 11:59:40 -0400 Subject: [PATCH 18/27] DOCSP-43159: QB returns objects (#3135) * DOCSP-43159: QB returns objects * add to upgrade guide * add depth layer * JT tech review 2 * wip --- docs/query-builder.txt | 9 +++++++-- docs/upgrade.txt | 22 +++++++++++++++++++++- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/docs/query-builder.txt b/docs/query-builder.txt index 9b1fe65f9..ecd9e7d61 100644 --- a/docs/query-builder.txt +++ b/docs/query-builder.txt @@ -38,7 +38,11 @@ testability. The {+odm-short+} provides the ``DB`` method ``table()`` to access a collection. Chain methods to specify commands and any constraints. Then, chain -the ``get()`` method at the end to run the methods and retrieve the results. +the ``get()`` method at the end to run the methods and retrieve the +results. To retrieve only the first matching result, chain the +``first()`` method instead of the ``get()`` method. Starting in +{+odm-long+} v5.0, the query builder returns results as ``stdClass`` objects. + The following example shows the syntax of a query builder call: .. code-block:: php @@ -46,7 +50,8 @@ The following example shows the syntax of a query builder call: DB::table('') // chain methods by using the "->" object operator ->get(); -.. tip:: + +.. tip:: Set Database Connection Before using the ``DB::table()`` method, ensure that you specify MongoDB as your application's default database connection. For instructions on setting the database connection, diff --git a/docs/upgrade.txt b/docs/upgrade.txt index 4d7dca0d8..a188a9322 100644 --- a/docs/upgrade.txt +++ b/docs/upgrade.txt @@ -14,7 +14,7 @@ Upgrade Library Version .. contents:: On this page :local: :backlinks: none - :depth: 1 + :depth: 2 :class: singlecol Overview @@ -71,6 +71,26 @@ Version 5.x Breaking Changes This library version introduces the following breaking changes: +- The query builder returns results as as ``stdClass`` objects instead + of as arrays. This change requires that you change array access to + property access when interacting with query results. + + The following code shows how to retrieve a query result and access a + property from the result object in older versions compared to v5.0: + + .. code-block:: php + :emphasize-lines: 8-9 + + $document = DB::table('accounts') + ->where('name', 'Anita Charles') + ->first(); + + // older versions + $document['balance']; + + // v5.0 + $document->balance; + - Removes support for the following classes: - ``MongoDB\Laravel\Auth\DatabaseTokenRepository``. Instead, use the default From f65b9e0b0529ceba8985345373a879a06c6e9f40 Mon Sep 17 00:00:00 2001 From: Nora Reidy Date: Tue, 10 Sep 2024 11:18:09 -0400 Subject: [PATCH 19/27] DOCSP-42956: Remove $collection support (#3138) Adds a note about removed $collection and collection() support to the upgrade guide. --- docs/upgrade.txt | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/docs/upgrade.txt b/docs/upgrade.txt index a188a9322..5747cf300 100644 --- a/docs/upgrade.txt +++ b/docs/upgrade.txt @@ -109,6 +109,47 @@ This library version introduces the following breaking changes: of this behavior, you cannot have two separate ``id`` and ``_id`` fields in your documents. +- Removes support for the ``$collection`` property. The following code shows + how to assign a MongoDB collection to a variable in your ``User`` class in + older versions compared to v5.0: + + .. code-block:: php + :emphasize-lines: 10-11 + + use MongoDB\Laravel\Eloquent\Model; + + class User extends Model + { + protected $keyType = 'string'; + + // older versions + protected $collection = 'app_user'; + + // v5.0 + protected $table = 'app_user'; + + ... + } + + This release also modifies the associated ``DB`` and ``Schema`` methods for + accessing a MongoDB collection. The following code shows how to access the + ``app_user`` collection in older versions compared to v5.0: + + .. code-block:: php + :emphasize-lines: 9-11 + + use Illuminate\Support\Facades\Schema; + use Illuminate\Support\Facades\DB; + use MongoDB\Laravel\Schema\Blueprint; + + // older versions + Schema::collection('app_user', function (Blueprint $collection) { ... }); + DB::collection('app_user')->find($id); + + // v5.0 + Schema::table('app_user', function (Blueprint $table) { ... }); + DB::table('app_user')->find($id); + .. _laravel-breaking-changes-v4.x: Version 4.x Breaking Changes From aebf049162a06e0bfa01379de72242193e8a3e15 Mon Sep 17 00:00:00 2001 From: Nora Reidy Date: Tue, 10 Sep 2024 16:50:07 -0400 Subject: [PATCH 20/27] DOCSP-42957: DateTimeInterface in queries (#3140) Adds information & a code example about automatic conversion from DateTimeInterface to UTCDateTime in queries. --- .../query-builder/QueryBuilderTest.php | 17 +++++++-- docs/query-builder.txt | 35 +++++++++++++------ docs/upgrade.txt | 10 ++++++ 3 files changed, 50 insertions(+), 12 deletions(-) diff --git a/docs/includes/query-builder/QueryBuilderTest.php b/docs/includes/query-builder/QueryBuilderTest.php index 02d15cc48..38f001a33 100644 --- a/docs/includes/query-builder/QueryBuilderTest.php +++ b/docs/includes/query-builder/QueryBuilderTest.php @@ -4,6 +4,7 @@ namespace App\Http\Controllers; +use Carbon\Carbon; use Illuminate\Database\Query\Builder; use Illuminate\Pagination\AbstractPaginator; use Illuminate\Support\Facades\DB; @@ -148,14 +149,26 @@ public function testWhereIn(): void $this->assertInstanceOf(\Illuminate\Support\Collection::class, $result); } + public function testWhereCarbon(): void + { + // begin query where date + $result = DB::connection('mongodb') + ->table('movies') + ->where('released', Carbon::create(2010, 1, 15)) + ->get(); + // end query where date + + $this->assertInstanceOf(\Illuminate\Support\Collection::class, $result); + } + public function testWhereDate(): void { - // begin query whereDate + // begin query whereDate string $result = DB::connection('mongodb') ->table('movies') ->whereDate('released', '2010-1-15') ->get(); - // end query whereDate + // end query whereDate string $this->assertInstanceOf(\Illuminate\Support\Collection::class, $result); } diff --git a/docs/query-builder.txt b/docs/query-builder.txt index ecd9e7d61..0a4c878df 100644 --- a/docs/query-builder.txt +++ b/docs/query-builder.txt @@ -353,23 +353,38 @@ query builder method to retrieve documents from the Match Dates Example ^^^^^^^^^^^^^^^^^^^ -The following example shows how to use the ``whereDate()`` +The following example shows how to use the ``where()`` query builder method to retrieve documents from the -``movies`` collection that match the specified date of -``2010-1-15`` in the ``released`` field: +``movies`` collection in which the ``released`` value +is January 15, 2010, specified in a ``Carbon`` object: .. literalinclude:: /includes/query-builder/QueryBuilderTest.php :language: php :dedent: - :start-after: begin query whereDate - :end-before: end query whereDate + :start-after: begin query where date + :end-before: end query where date -.. note:: Date Query Result Type +.. note:: Date Query Filter and Result Type - Starting in {+odm-long+} v5.0, ``UTCDateTime`` BSON values in MongoDB - are returned as `Carbon `__ date - classes in query results. The {+odm-short+} applies the default - timezone when performing this conversion. + Starting in {+odm-long+} v5.0, `Carbon `__ + objects passed as query filters, as shown in the preceding code, are + converted to ``UTCDateTime`` BSON values. + + In query results, ``UTCDateTime`` BSON values in MongoDB are returned as ``Carbon`` + objects. The {+odm-short+} applies the default timezone when performing + this conversion. + +If you want to represent a date as a string in your query filter +rather than as a ``Carbon`` object, use the ``whereDate()`` query +builder method. The following example retrieves documents from +the ``movies`` collection in which the ``released`` value +is January 15, 2010 and specifies the date as a string: + +.. literalinclude:: /includes/query-builder/QueryBuilderTest.php + :language: php + :dedent: + :start-after: begin query whereDate string + :end-before: end query whereDate string .. _laravel-query-builder-pattern: diff --git a/docs/upgrade.txt b/docs/upgrade.txt index 5747cf300..a992197f3 100644 --- a/docs/upgrade.txt +++ b/docs/upgrade.txt @@ -101,6 +101,16 @@ This library version introduces the following breaking changes: use the default ``Illuminate\Queue\Failed\DatabaseFailedJobProvider`` class and specify a connection to MongoDB. +- When using a ``DateTimeInterface`` object, including ``Carbon``, in a query, + the library converts the ``DateTimeInterface`` to a ``MongoDB\BSON\UTCDateTime`` + object. This conversion applies to ``DateTimeInterface`` objects passed as query + filters to the ``where()`` method or as data passed to the ``insert()`` and + ``update()`` methods. + + To view an example that passes a ``Carbon`` object to the + ``DB::where()`` method, see the :ref:`laravel-query-builder-wheredate` + section of the Query Builder guide. + - In query results, the library converts BSON ``UTCDateTime`` objects to ``Carbon`` date classes, applying the default timezone. From 25a6e9e1538d6a01a0f998c7a0b7103b1203d692 Mon Sep 17 00:00:00 2001 From: JaeYeong Choi <80824142+verduck@users.noreply.github.com> Date: Wed, 11 Sep 2024 16:55:58 +0900 Subject: [PATCH 21/27] Add options to countDocuments method (#3142) --- CHANGELOG.md | 1 + src/Query/Builder.php | 2 +- tests/QueryTest.php | 9 +++++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fdedba537..d813edb5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ All notable changes to this project will be documented in this file. * Remove custom `PasswordResetServiceProvider`, use the default `DatabaseTokenRepository` by @GromNaN in [#3124](https://github.com/mongodb/laravel-mongodb/pull/3124) * Remove `Blueprint::background()` method by @GromNaN in [#3132](https://github.com/mongodb/laravel-mongodb/pull/3132) * Replace `Collection` proxy class with Driver monitoring by @GromNaN in [#3137]((https://github.com/mongodb/laravel-mongodb/pull/3137) +* Support options in `count()` queries by @verduck in [#3142](https://github.com/mongodb/laravel-mongodb/pull/3142) ## [4.8.0] - 2024-08-27 diff --git a/src/Query/Builder.php b/src/Query/Builder.php index 2bbd5a01a..43acbcc24 100644 --- a/src/Query/Builder.php +++ b/src/Query/Builder.php @@ -346,7 +346,7 @@ public function toMql(): array $aggregations = blank($this->aggregate['columns']) ? [] : $this->aggregate['columns']; if (in_array('*', $aggregations) && $function === 'count') { - $options = $this->inheritConnectionOptions(); + $options = $this->inheritConnectionOptions($this->options); return ['countDocuments' => [$wheres, $options]]; } diff --git a/tests/QueryTest.php b/tests/QueryTest.php index 1b5746842..78a7b1bee 100644 --- a/tests/QueryTest.php +++ b/tests/QueryTest.php @@ -660,4 +660,13 @@ public function testDelete(): void User::limit(null)->delete(); $this->assertEquals(0, User::count()); } + + public function testLimitCount(): void + { + $count = User::where('age', '>=', 20)->count(); + $this->assertEquals(7, $count); + + $count = User::where('age', '>=', 20)->options(['limit' => 3])->count(); + $this->assertEquals(3, $count); + } } From de037dd35322a75877eba8404115ed7cdf1c10d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Thu, 12 Sep 2024 10:50:20 +0200 Subject: [PATCH 22/27] Update merge-up config for new branch pattern (#3143) --- .github/workflows/merge-up.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/merge-up.yml b/.github/workflows/merge-up.yml index bdd4cfefa..1ddbb7228 100644 --- a/.github/workflows/merge-up.yml +++ b/.github/workflows/merge-up.yml @@ -3,7 +3,7 @@ name: Merge up on: push: branches: - - "[0-9]+.[0-9]+" + - "[0-9]+.[0-9x]+" env: GH_TOKEN: ${{ secrets.MERGE_UP_TOKEN }} @@ -28,4 +28,5 @@ jobs: with: ref: ${{ github.ref_name }} branchNamePattern: '.' + devBranchNamePattern: '.x' enableAutoMerge: true From 3c1aab747ef67e818bb2b4f8191247112f588863 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Thu, 12 Sep 2024 11:44:12 +0200 Subject: [PATCH 23/27] Update changelog (#3144) --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d813edb5a..90c22dfd1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # Changelog All notable changes to this project will be documented in this file. -## [5.0.0] - next +## [5.0.0] - 2024-09-12 * Remove support for Laravel 10 by @GromNaN in [#3123](https://github.com/mongodb/laravel-mongodb/pull/3123) * **BREAKING CHANGE** Use `id` as an alias for `_id` in commands and queries for compatibility with Eloquent packages by @GromNaN in [#3040](https://github.com/mongodb/laravel-mongodb/pull/3040) and [#3136](https://github.com/mongodb/laravel-mongodb/pull/3136) From 3ac795581aadb9152efb0a3923984e2e5d19435c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Fri, 13 Sep 2024 08:47:54 +0200 Subject: [PATCH 24/27] Re-enable support for Laravel 10 (#3148) --- .github/workflows/build-ci.yml | 11 +++++++---- .github/workflows/static-analysis.yml | 1 + CHANGELOG.md | 5 ++++- composer.json | 19 +++++++++++-------- 4 files changed, 23 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build-ci.yml b/.github/workflows/build-ci.yml index 60d96f34f..45833d579 100644 --- a/.github/workflows/build-ci.yml +++ b/.github/workflows/build-ci.yml @@ -20,15 +20,15 @@ jobs: - "6.0" - "7.0" php: + - "8.1" - "8.2" - "8.3" laravel: + - "10.*" - "11.*" - mode: - - "" include: - - php: "8.2" - laravel: "11.*" + - php: "8.1" + laravel: "10.*" mongodb: "5.0" mode: "low-deps" os: "ubuntu-latest" @@ -37,6 +37,9 @@ jobs: mongodb: "7.0" mode: "ignore-php-req" os: "ubuntu-latest" + exclude: + - php: "8.1" + laravel: "11.*" steps: - uses: "actions/checkout@v4" diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 331fe22d8..a66100d93 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -21,6 +21,7 @@ jobs: strategy: matrix: php: + - '8.1' - '8.2' - '8.3' steps: diff --git a/CHANGELOG.md b/CHANGELOG.md index 90c22dfd1..32f7b856b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,12 @@ # Changelog All notable changes to this project will be documented in this file. +## [5.0.1] - 2024-09-13 + +* Restore support for Laravel 10 by @GromNaN in [#3148](https://github.com/mongodb/laravel-mongodb/pull/3148) + ## [5.0.0] - 2024-09-12 -* Remove support for Laravel 10 by @GromNaN in [#3123](https://github.com/mongodb/laravel-mongodb/pull/3123) * **BREAKING CHANGE** Use `id` as an alias for `_id` in commands and queries for compatibility with Eloquent packages by @GromNaN in [#3040](https://github.com/mongodb/laravel-mongodb/pull/3040) and [#3136](https://github.com/mongodb/laravel-mongodb/pull/3136) * **BREAKING CHANGE** Make Query\Builder return objects instead of array to match Laravel behavior by @GromNaN in [#3107](https://github.com/mongodb/laravel-mongodb/pull/3107) * **BREAKING CHANGE** In DB query results, convert BSON `UTCDateTime` objects into `Carbon` date with the default timezone by @GromNaN in [#3119](https://github.com/mongodb/laravel-mongodb/pull/3119) diff --git a/composer.json b/composer.json index 4d679a95c..9c958f1c4 100644 --- a/composer.json +++ b/composer.json @@ -22,28 +22,31 @@ ], "license": "MIT", "require": { - "php": "^8.2", + "php": "^8.1", "ext-mongodb": "^1.15", "composer-runtime-api": "^2.0.0", - "illuminate/cache": "^11", - "illuminate/container": "^11", - "illuminate/database": "^11", - "illuminate/events": "^11", - "illuminate/support": "^11", + "illuminate/cache": "^10.36|^11", + "illuminate/container": "^10.0|^11", + "illuminate/database": "^10.30|^11", + "illuminate/events": "^10.0|^11", + "illuminate/support": "^10.0|^11", "mongodb/mongodb": "^1.18" }, "require-dev": { "mongodb/builder": "^0.2", "league/flysystem-gridfs": "^3.28", "league/flysystem-read-only": "^3.0", - "phpunit/phpunit": "^10.5", - "orchestra/testbench": "^9.0", + "phpunit/phpunit": "^10.3", + "orchestra/testbench": "^8.0|^9.0", "mockery/mockery": "^1.4.4", "doctrine/coding-standard": "12.0.x-dev", "spatie/laravel-query-builder": "^5.6", "phpstan/phpstan": "^1.10", "rector/rector": "^1.2" }, + "conflict": { + "illuminate/bus": "< 10.37.2" + }, "suggest": { "league/flysystem-gridfs": "Filesystem storage in MongoDB with GridFS", "mongodb/builder": "Provides a fluent aggregation builder for MongoDB pipelines" From cf9c9b10900e3c487f021802a7446431ea7405f7 Mon Sep 17 00:00:00 2001 From: Rea Rustagi <85902999+rustagir@users.noreply.github.com> Date: Fri, 13 Sep 2024 10:55:22 -0400 Subject: [PATCH 25/27] DOCSP-43539: v5 release (#3154) * DOCSP-43539: v5 release * toc reshuffle --- docs/compatibility.txt | 2 +- .../framework-compatibility-laravel.rst | 2 +- docs/index.txt | 22 ++++--- docs/upgrade.txt | 58 +++++++++---------- 4 files changed, 44 insertions(+), 40 deletions(-) diff --git a/docs/compatibility.txt b/docs/compatibility.txt index e02bda581..fb253f888 100644 --- a/docs/compatibility.txt +++ b/docs/compatibility.txt @@ -15,7 +15,7 @@ Compatibility :class: singlecol .. meta:: - :keywords: laravel 9, laravel 10, laravel 11, 4.0, 4.1, 4.2 + :keywords: laravel 9, laravel 10, laravel 11, 4.0, 4.1, 4.2, 5.0 Laravel Compatibility --------------------- diff --git a/docs/includes/framework-compatibility-laravel.rst b/docs/includes/framework-compatibility-laravel.rst index 19bcafc1a..bdfbd4d4c 100644 --- a/docs/includes/framework-compatibility-laravel.rst +++ b/docs/includes/framework-compatibility-laravel.rst @@ -7,7 +7,7 @@ - Laravel 10.x - Laravel 9.x - * - 4.2 to 4.8 + * - 4.2 to 5.0 - ✓ - ✓ - diff --git a/docs/index.txt b/docs/index.txt index 12269e0c4..b767d4247 100644 --- a/docs/index.txt +++ b/docs/index.txt @@ -14,8 +14,9 @@ :maxdepth: 1 /quick-start - /usage-examples Release Notes + /upgrade + /usage-examples /fundamentals /eloquent-models /query-builder @@ -27,7 +28,6 @@ /issues-and-help /feature-compatibility /compatibility - /upgrade Introduction ------------ @@ -52,6 +52,17 @@ Learn how to add the {+odm-short+} to a Laravel web application, connect to MongoDB hosted on MongoDB Atlas, and begin working with data in the :ref:`laravel-quick-start` section. +Upgrade Versions +---------------- + +.. important:: + + {+odm-long+} v5.0 introduces breaking changes that might affect how you + upgrade your application from a v4.x version. + +Learn what changes you must make to your application to upgrade between +major versions in the :ref:`laravel-upgrading` section. + Usage Examples -------------- @@ -94,10 +105,3 @@ Compatibility To learn more about which versions of {+odm-long+} and Laravel are compatible, see the :ref:`laravel-compatibility` section. - -Upgrade Versions ----------------- - -Learn what changes you must make to your application to upgrade versions in -the :ref:`laravel-upgrading` section. - diff --git a/docs/upgrade.txt b/docs/upgrade.txt index a992197f3..17d44cbb3 100644 --- a/docs/upgrade.txt +++ b/docs/upgrade.txt @@ -124,41 +124,41 @@ This library version introduces the following breaking changes: older versions compared to v5.0: .. code-block:: php - :emphasize-lines: 10-11 - - use MongoDB\Laravel\Eloquent\Model; - - class User extends Model - { - protected $keyType = 'string'; - - // older versions - protected $collection = 'app_user'; - - // v5.0 - protected $table = 'app_user'; - - ... - } + :emphasize-lines: 10-11 + + use MongoDB\Laravel\Eloquent\Model; + + class User extends Model + { + protected $keyType = 'string'; + + // older versions + protected $collection = 'app_user'; + + // v5.0 + protected $table = 'app_user'; + + ... + } This release also modifies the associated ``DB`` and ``Schema`` methods for accessing a MongoDB collection. The following code shows how to access the ``app_user`` collection in older versions compared to v5.0: .. code-block:: php - :emphasize-lines: 9-11 - - use Illuminate\Support\Facades\Schema; - use Illuminate\Support\Facades\DB; - use MongoDB\Laravel\Schema\Blueprint; - - // older versions - Schema::collection('app_user', function (Blueprint $collection) { ... }); - DB::collection('app_user')->find($id); - - // v5.0 - Schema::table('app_user', function (Blueprint $table) { ... }); - DB::table('app_user')->find($id); + :emphasize-lines: 9-11 + + use Illuminate\Support\Facades\Schema; + use Illuminate\Support\Facades\DB; + use MongoDB\Laravel\Schema\Blueprint; + + // older versions + Schema::collection('app_user', function (Blueprint $collection) { ... }); + DB::collection('app_user')->find($id); + + // v5.0 + Schema::table('app_user', function (Blueprint $table) { ... }); + DB::table('app_user')->find($id); .. _laravel-breaking-changes-v4.x: From 98474c34d90b6f08ff2107eaeb4b7488ae763681 Mon Sep 17 00:00:00 2001 From: Nora Reidy Date: Fri, 13 Sep 2024 11:33:33 -0400 Subject: [PATCH 26/27] DOCSP-43530: Id field in query results (#3149) Adds information about ID field representation in query builder results --- docs/query-builder.txt | 7 ++++--- docs/upgrade.txt | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/docs/query-builder.txt b/docs/query-builder.txt index 0a4c878df..2bb6f75f2 100644 --- a/docs/query-builder.txt +++ b/docs/query-builder.txt @@ -195,9 +195,10 @@ the value of the ``title`` field is ``"Back to the Future"``: You can use the ``id`` alias in your queries to represent the ``_id`` field in MongoDB documents, as shown in the preceding - code. When you run a find operation using the query builder, {+odm-short+} - automatically converts between ``id`` and ``_id``. This provides better - compatibility with Laravel, as the framework assumes that each record has a + code. When you use the query builder to run a find operation, the {+odm-short+} + automatically converts between ``_id`` and ``id`` field names. In query results, + the ``_id`` field is presented as ``id``. This provides better + consistency with Laravel, as the framework assumes that each record has a primary key named ``id`` by default. Because of this behavior, you cannot have two separate ``id`` and ``_id`` diff --git a/docs/upgrade.txt b/docs/upgrade.txt index 17d44cbb3..3032b8e1e 100644 --- a/docs/upgrade.txt +++ b/docs/upgrade.txt @@ -115,9 +115,10 @@ This library version introduces the following breaking changes: date classes, applying the default timezone. - ``id`` is an alias for the ``_id`` field in MongoDB documents, and the library - automatically converts between ``id`` and ``_id`` when querying data. Because - of this behavior, you cannot have two separate ``id`` and ``_id`` fields in your - documents. + automatically converts between ``id`` and ``_id`` when querying data. The query + result object includes an ``id`` field to represent the document's ``_id`` field. + Because of this behavior, you cannot have two separate ``id`` and ``_id`` fields + in your documents. - Removes support for the ``$collection`` property. The following code shows how to assign a MongoDB collection to a variable in your ``User`` class in From b51aeff76c065839da25c56652428421d1b54cad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Tue, 17 Sep 2024 13:05:10 +0200 Subject: [PATCH 27/27] PHPORM-241 Add return type to CommandSubscriber (#3158) --- CHANGELOG.md | 4 ++++ src/CommandSubscriber.php | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32f7b856b..bd353702e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ # Changelog All notable changes to this project will be documented in this file. +## [5.0.2] - 2024-09-17 + +* Fix missing return types in CommandSubscriber by @GromNaN in [#3158](https://github.com/mongodb/laravel-mongodb/pull/3158) + ## [5.0.1] - 2024-09-13 * Restore support for Laravel 10 by @GromNaN in [#3148](https://github.com/mongodb/laravel-mongodb/pull/3148) diff --git a/src/CommandSubscriber.php b/src/CommandSubscriber.php index ef282bcac..569c7c909 100644 --- a/src/CommandSubscriber.php +++ b/src/CommandSubscriber.php @@ -21,17 +21,17 @@ public function __construct(private Connection $connection) { } - public function commandStarted(CommandStartedEvent $event) + public function commandStarted(CommandStartedEvent $event): void { $this->commands[$event->getOperationId()] = $event; } - public function commandFailed(CommandFailedEvent $event) + public function commandFailed(CommandFailedEvent $event): void { $this->logQuery($event); } - public function commandSucceeded(CommandSucceededEvent $event) + public function commandSucceeded(CommandSucceededEvent $event): void { $this->logQuery($event); }