Skip to content

Commit bd9ef30

Browse files
authored
PHPORM-230 Convert DateTimeInterface to UTCDateTime in queries (#3105)
* PHPORM-230 Convert DateTimeInterface to UTCDateTime in queries * Alias id to _id in subdocuments
1 parent d2c6de9 commit bd9ef30

File tree

4 files changed

+37
-51
lines changed

4 files changed

+37
-51
lines changed

src/Query/Builder.php

+21-44
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
use function array_map;
3333
use function array_merge;
3434
use function array_values;
35-
use function array_walk_recursive;
3635
use function assert;
3736
use function blank;
3837
use function call_user_func;
@@ -689,17 +688,7 @@ public function insert(array $values)
689688
$values = [$values];
690689
}
691690

692-
// Compatibility with Eloquent queries that uses "id" instead of MongoDB's _id
693-
foreach ($values as &$document) {
694-
if (isset($document['id'])) {
695-
if (isset($document['_id']) && $document['_id'] !== $document['id']) {
696-
throw new InvalidArgumentException('Cannot insert document with different "id" and "_id" values');
697-
}
698-
699-
$document['_id'] = $document['id'];
700-
unset($document['id']);
701-
}
702-
}
691+
$values = $this->aliasIdForQuery($values);
703692

704693
$options = $this->inheritConnectionOptions();
705694

@@ -876,6 +865,7 @@ public function delete($id = null)
876865
}
877866

878867
$wheres = $this->compileWheres();
868+
$wheres = $this->aliasIdForQuery($wheres);
879869
$options = $this->inheritConnectionOptions();
880870

881871
if (is_int($this->limit)) {
@@ -1070,16 +1060,12 @@ protected function performUpdate(array $update, array $options = [])
10701060
$options['multiple'] = true;
10711061
}
10721062

1073-
// Since "id" is an alias for "_id", we prevent updating it
1074-
foreach ($update as $operator => $fields) {
1075-
if (array_key_exists('id', $fields)) {
1076-
throw new InvalidArgumentException('Cannot update "id" field.');
1077-
}
1078-
}
1063+
$update = $this->aliasIdForQuery($update);
10791064

10801065
$options = $this->inheritConnectionOptions($options);
10811066

10821067
$wheres = $this->compileWheres();
1068+
$wheres = $this->aliasIdForQuery($wheres);
10831069
$result = $this->collection->updateMany($wheres, $update, $options);
10841070
if ($result->isAcknowledged()) {
10851071
return $result->getModifiedCount() ? $result->getModifiedCount() : $result->getUpsertedCount();
@@ -1191,32 +1177,12 @@ protected function compileWheres(): array
11911177
}
11921178
}
11931179

1194-
// Convert DateTime values to UTCDateTime.
1195-
if (isset($where['value'])) {
1196-
if (is_array($where['value'])) {
1197-
array_walk_recursive($where['value'], function (&$item, $key) {
1198-
if ($item instanceof DateTimeInterface) {
1199-
$item = new UTCDateTime($item);
1200-
}
1201-
});
1202-
} else {
1203-
if ($where['value'] instanceof DateTimeInterface) {
1204-
$where['value'] = new UTCDateTime($where['value']);
1205-
}
1206-
}
1207-
} elseif (isset($where['values'])) {
1208-
if (is_array($where['values'])) {
1209-
array_walk_recursive($where['values'], function (&$item, $key) {
1210-
if ($item instanceof DateTimeInterface) {
1211-
$item = new UTCDateTime($item);
1212-
}
1213-
});
1214-
} elseif ($where['values'] instanceof CarbonPeriod) {
1215-
$where['values'] = [
1216-
new UTCDateTime($where['values']->getStartDate()),
1217-
new UTCDateTime($where['values']->getEndDate()),
1218-
];
1219-
}
1180+
// Convert CarbonPeriod to DateTime interval.
1181+
if (isset($where['values']) && $where['values'] instanceof CarbonPeriod) {
1182+
$where['values'] = [
1183+
$where['values']->getStartDate(),
1184+
$where['values']->getEndDate(),
1185+
];
12201186
}
12211187

12221188
// In a sequence of "where" clauses, the logical operator of the
@@ -1631,12 +1597,21 @@ public function orWhereIntegerNotInRaw($column, $values, $boolean = 'and')
16311597
private function aliasIdForQuery(array $values): array
16321598
{
16331599
if (array_key_exists('id', $values)) {
1600+
if (array_key_exists('_id', $values)) {
1601+
throw new InvalidArgumentException('Cannot have both "id" and "_id" fields.');
1602+
}
1603+
16341604
$values['_id'] = $values['id'];
16351605
unset($values['id']);
16361606
}
16371607

16381608
foreach ($values as $key => $value) {
16391609
if (is_string($key) && str_ends_with($key, '.id')) {
1610+
$newkey = substr($key, 0, -3) . '._id';
1611+
if (array_key_exists($newkey, $values)) {
1612+
throw new InvalidArgumentException(sprintf('Cannot have both "%s" and "%s" fields.', $key, $newkey));
1613+
}
1614+
16401615
$values[substr($key, 0, -3) . '._id'] = $value;
16411616
unset($values[$key]);
16421617
}
@@ -1645,6 +1620,8 @@ private function aliasIdForQuery(array $values): array
16451620
foreach ($values as &$value) {
16461621
if (is_array($value)) {
16471622
$value = $this->aliasIdForQuery($value);
1623+
} elseif ($value instanceof DateTimeInterface) {
1624+
$value = new UTCDateTime($value);
16481625
}
16491626
}
16501627

tests/Query/AggregationBuilderTest.php

+2-3
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
use InvalidArgumentException;
1212
use MongoDB\BSON\Document;
1313
use MongoDB\BSON\ObjectId;
14-
use MongoDB\BSON\UTCDateTime;
1514
use MongoDB\Builder\BuilderEncoder;
1615
use MongoDB\Builder\Expression;
1716
use MongoDB\Builder\Pipeline;
@@ -33,8 +32,8 @@ public function tearDown(): void
3332
public function testCreateAggregationBuilder(): void
3433
{
3534
User::insert([
36-
['name' => 'John Doe', 'birthday' => new UTCDateTime(new DateTimeImmutable('1989-01-01'))],
37-
['name' => 'Jane Doe', 'birthday' => new UTCDateTime(new DateTimeImmutable('1990-01-01'))],
35+
['name' => 'John Doe', 'birthday' => new DateTimeImmutable('1989-01-01')],
36+
['name' => 'Jane Doe', 'birthday' => new DateTimeImmutable('1990-01-01')],
3837
]);
3938

4039
// Create the aggregation pipeline from the query builder

tests/Query/BuilderTest.php

+6
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,12 @@ function (Builder $builder) {
566566
fn (Builder $builder) => $builder->whereBetween('id', [[1], [2, 3]]),
567567
];
568568

569+
$date = new DateTimeImmutable('2018-09-30 15:00:00 +02:00');
570+
yield 'where $lt DateTimeInterface' => [
571+
['find' => [['created_at' => ['$lt' => new UTCDateTime($date)]], []]],
572+
fn (Builder $builder) => $builder->where('created_at', '<', $date),
573+
];
574+
569575
$period = now()->toPeriod(now()->addMonth());
570576
yield 'whereBetween CarbonPeriod' => [
571577
[

tests/QueryBuilderTest.php

+8-4
Original file line numberDiff line numberDiff line change
@@ -1053,16 +1053,20 @@ public function testIncrementEach()
10531053
#[TestWith(['id', 'id'])]
10541054
#[TestWith(['id', '_id'])]
10551055
#[TestWith(['_id', 'id'])]
1056+
#[TestWith(['_id', '_id'])]
10561057
public function testIdAlias($insertId, $queryId): void
10571058
{
1058-
DB::collection('items')->insert([$insertId => 'abc', 'name' => 'Karting']);
1059-
$item = DB::collection('items')->where($queryId, '=', 'abc')->first();
1059+
DB::table('items')->insert([$insertId => 'abc', 'name' => 'Karting']);
1060+
$item = DB::table('items')->where($queryId, '=', 'abc')->first();
10601061
$this->assertNotNull($item);
10611062
$this->assertSame('abc', $item['id']);
10621063
$this->assertSame('Karting', $item['name']);
10631064

1064-
DB::collection('items')->where($insertId, '=', 'abc')->update(['name' => 'Bike']);
1065-
$item = DB::collection('items')->where($queryId, '=', 'abc')->first();
1065+
DB::table('items')->where($insertId, '=', 'abc')->update(['name' => 'Bike']);
1066+
$item = DB::table('items')->where($queryId, '=', 'abc')->first();
10661067
$this->assertSame('Bike', $item['name']);
1068+
1069+
$result = DB::table('items')->where($queryId, '=', 'abc')->delete();
1070+
$this->assertSame(1, $result);
10671071
}
10681072
}

0 commit comments

Comments
 (0)