Completed
Pull Request — master (#2116)
by
unknown
14:25 queued 44s
created

Builder::setRewindable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 0
cts 3
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\ODM\MongoDB\Query;
6
7
use BadMethodCallException;
8
use Doctrine\ODM\MongoDB\DocumentManager;
9
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
10
use GeoJson\Geometry\Geometry;
11
use GeoJson\Geometry\Point;
12
use InvalidArgumentException;
13
use MongoDB\BSON\Binary;
14
use MongoDB\BSON\Javascript;
15
use MongoDB\Collection;
16
use MongoDB\Driver\ReadPreference;
17
use function array_filter;
18
use function array_key_exists;
19
use function count;
20
use function func_get_args;
21
use function in_array;
22
use function is_array;
23
use function is_bool;
24
use function is_callable;
25
use function is_string;
26
use function strtolower;
27
28
/**
29
 * Query builder for ODM.
30
 */
31
class Builder
32
{
33
    /**
34
     * The DocumentManager instance for this query
35
     *
36
     * @var DocumentManager
37
     */
38
    private $dm;
39
40
    /**
41
     * The ClassMetadata instance.
42
     *
43
     * @var ClassMetadata
44
     */
45
    private $class;
46
47
    /**
48
     * The current field we are operating on.
49
     *
50
     * @todo Change this to private once ODM requires doctrine/mongodb 1.1+
51
     * @var string
52
     */
53
    protected $currentField;
54
55
    /**
56
     * Whether or not to hydrate the data to documents.
57
     *
58
     * @var bool
59
     */
60
    private $hydrate = true;
61
62
    /**
63
     * Whether or not to refresh the data for documents that are already in the identity map.
64
     *
65
     * @var bool
66
     */
67
    private $refresh = false;
68
69
    /**
70
     * Array of primer Closure instances.
71
     *
72
     * @var array
73
     */
74
    private $primers = [];
75
76
    /**
77
     * Whether or not to register documents in UnitOfWork.
78
     *
79
     * @var bool
80
     */
81
    private $readOnly = false;
82
83
    /**
84
     * @var bool 
85
     */
86
    private $rewindable = true;
87
88
    /**
89
     * The Collection instance.
90
     *
91
     * @var Collection
92
     */
93
    private $collection;
94
95
    /**
96
     * Array containing the query data.
97
     *
98
     * @var array
99
     */
100
    private $query = ['type' => Query::TYPE_FIND];
101
102
    /**
103
     * The Expr instance used for building this query.
104
     *
105
     * This object includes the query criteria and the "new object" used for
106
     * insert and update queries.
107
     *
108
     * @var Expr $expr
109
     */
110
    private $expr;
111
112
    /**
113
     * Construct a Builder
114
     *
115
     * @param string[]|string|null $documentName (optional) an array of document names, the document name, or none
116
     */
117 282
    public function __construct(DocumentManager $dm, $documentName = null)
118
    {
119 282
        $this->dm   = $dm;
120 282
        $this->expr = new Expr($dm);
121 282
        if ($documentName === null) {
122 9
            return;
123
        }
124
125 274
        $this->setDocumentName($documentName);
126 273
    }
127
128 1
    public function __clone()
129
    {
130 1
        $this->expr = clone $this->expr;
131 1
    }
132
133
    /**
134
     * Add one or more $and clauses to the current query.
135
     *
136
     * You can create a new expression using the {@link Builder::expr()} method.
137
     *
138
     * @see Expr::addAnd()
139
     * @see http://docs.mongodb.org/manual/reference/operator/and/
140
     *
141
     * @param array|Expr $expression
142
     * @param array|Expr ...$expressions
143
     */
144 4
    public function addAnd($expression, ...$expressions) : self
0 ignored issues
show
Unused Code introduced by
The parameter $expression is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $expressions is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
145
    {
146 4
        $this->expr->addAnd(...func_get_args());
147
148 4
        return $this;
149
    }
150
151
    /**
152
     * Add one or more $nor clauses to the current query.
153
     *
154
     * You can create a new expression using the {@link Builder::expr()} method.
155
     *
156
     * @see Expr::addNor()
157
     * @see http://docs.mongodb.org/manual/reference/operator/nor/
158
     *
159
     * @param array|Expr $expression
160
     * @param array|Expr ...$expressions
161
     */
162 3
    public function addNor($expression, ...$expressions) : self
0 ignored issues
show
Unused Code introduced by
The parameter $expression is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $expressions is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
163
    {
164 3
        $this->expr->addNor(...func_get_args());
165
166 3
        return $this;
167
    }
168
169
    /**
170
     * Add one or more $or clauses to the current query.
171
     *
172
     * You can create a new expression using the {@link Builder::expr()} method.
173
     *
174
     * @see Expr::addOr()
175
     * @see http://docs.mongodb.org/manual/reference/operator/or/
176
     *
177
     * @param array|Expr $expression
178
     * @param array|Expr ...$expressions
179
     */
180 6
    public function addOr($expression, ...$expressions) : self
0 ignored issues
show
Unused Code introduced by
The parameter $expression is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $expressions is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
181
    {
182 6
        $this->expr->addOr(...func_get_args());
183
184 6
        return $this;
185
    }
186
187
    /**
188
     * Append one or more values to the current array field only if they do not
189
     * already exist in the array.
190
     *
191
     * If the field does not exist, it will be set to an array containing the
192
     * unique value(s) in the argument. If the field is not an array, the query
193
     * will yield an error.
194
     *
195
     * Multiple values may be specified by provided an Expr object and using
196
     * {@link Expr::each()}.
197
     *
198
     * @see Expr::addToSet()
199
     * @see http://docs.mongodb.org/manual/reference/operator/addToSet/
200
     * @see http://docs.mongodb.org/manual/reference/operator/each/
201
     *
202
     * @param mixed|Expr $valueOrExpression
203
     */
204 5
    public function addToSet($valueOrExpression) : self
205
    {
206 5
        $this->expr->addToSet($valueOrExpression);
207
208 5
        return $this;
209
    }
210
211
    /**
212
     * Specify $all criteria for the current field.
213
     *
214
     * @see Expr::all()
215
     * @see http://docs.mongodb.org/manual/reference/operator/all/
216
     */
217 3
    public function all(array $values) : self
218
    {
219 3
        $this->expr->all($values);
220
221 3
        return $this;
222
    }
223
224
    /**
225
     * Apply a bitwise and operation on the current field.
226
     *
227
     * @see Expr::bitAnd()
228
     * @see http://docs.mongodb.org/manual/reference/operator/update/bit/
229
     *
230
     * @return $this
231
     */
232 1
    public function bitAnd(int $value) : self
233
    {
234 1
        $this->expr->bitAnd($value);
235
236 1
        return $this;
237
    }
238
239
    /**
240
     * Apply a bitwise or operation on the current field.
241
     *
242
     * @see Expr::bitOr()
243
     * @see http://docs.mongodb.org/manual/reference/operator/update/bit/
244
     */
245 1
    public function bitOr(int $value) : self
246
    {
247 1
        $this->expr->bitOr($value);
248
249 1
        return $this;
250
    }
251
252
    /**
253
     * Matches documents where all of the bit positions given by the query are
254
     * clear.
255
     *
256
     * @see Expr::bitsAllClear()
257
     * @see https://docs.mongodb.org/manual/reference/operator/query/bitsAllClear/
258
     *
259
     * @param int|array|Binary $value
260
     */
261 1
    public function bitsAllClear($value) : self
262
    {
263 1
        $this->expr->bitsAllClear($value);
264
265 1
        return $this;
266
    }
267
268
    /**
269
     * Matches documents where all of the bit positions given by the query are
270
     * set.
271
     *
272
     * @see Expr::bitsAllSet()
273
     * @see https://docs.mongodb.org/manual/reference/operator/query/bitsAllSet/
274
     *
275
     * @param int|array|Binary $value
276
     */
277 1
    public function bitsAllSet($value) : self
278
    {
279 1
        $this->expr->bitsAllSet($value);
280
281 1
        return $this;
282
    }
283
284
    /**
285
     * Matches documents where any of the bit positions given by the query are
286
     * clear.
287
     *
288
     * @see Expr::bitsAnyClear()
289
     * @see https://docs.mongodb.org/manual/reference/operator/query/bitsAnyClear/
290
     *
291
     * @param int|array|Binary $value
292
     */
293 1
    public function bitsAnyClear($value) : self
294
    {
295 1
        $this->expr->bitsAnyClear($value);
296
297 1
        return $this;
298
    }
299
300
    /**
301
     * Matches documents where any of the bit positions given by the query are
302
     * set.
303
     *
304
     * @see Expr::bitsAnySet()
305
     * @see https://docs.mongodb.org/manual/reference/operator/query/bitsAnySet/
306
     *
307
     * @param int|array|Binary $value
308
     */
309 1
    public function bitsAnySet($value) : self
310
    {
311 1
        $this->expr->bitsAnySet($value);
312
313 1
        return $this;
314
    }
315
316
    /**
317
     * Apply a bitwise xor operation on the current field.
318
     *
319
     * @see Expr::bitXor()
320
     * @see http://docs.mongodb.org/manual/reference/operator/update/bit/
321
     */
322 1
    public function bitXor(int $value) : self
323
    {
324 1
        $this->expr->bitXor($value);
325
326 1
        return $this;
327
    }
328
329
    /**
330
     * A boolean flag to enable or disable case sensitive search for $text
331
     * criteria.
332
     *
333
     * This method must be called after text().
334
     *
335
     * @see Expr::caseSensitive()
336
     * @see http://docs.mongodb.org/manual/reference/operator/text/
337
     *
338
     * @throws BadMethodCallException If the query does not already have $text criteria.
339
     */
340 1
    public function caseSensitive(bool $caseSensitive) : self
341
    {
342 1
        $this->expr->caseSensitive($caseSensitive);
343
344 1
        return $this;
345
    }
346
347
    /**
348
     * Associates a comment to any expression taking a query predicate.
349
     *
350
     * @see Expr::comment()
351
     * @see http://docs.mongodb.org/manual/reference/operator/query/comment/
352
     */
353 1
    public function comment(string $comment) : self
354
    {
355 1
        $this->expr->comment($comment);
356
357 1
        return $this;
358
    }
359
360
    /**
361
     * Change the query type to count.
362
     */
363
    public function count() : self
364
    {
365
        $this->query['type'] = Query::TYPE_COUNT;
366
367
        return $this;
368
    }
369
370
    /**
371
     * Sets the value of the current field to the current date, either as a date or a timestamp.
372
     *
373
     * @see Expr::currentDate()
374
     * @see http://docs.mongodb.org/manual/reference/operator/currentDate/
375
     */
376 3
    public function currentDate(string $type = 'date') : self
377
    {
378 3
        $this->expr->currentDate($type);
379
380 2
        return $this;
381
    }
382
383
    /**
384
     * Return an array of information about the Builder state for debugging.
385
     *
386
     * The $name parameter may be used to return a specific key from the
387
     * internal $query array property. If omitted, the entire array will be
388
     * returned.
389
     *
390
     * @return mixed
391
     */
392 26
    public function debug(?string $name = null)
393
    {
394 26
        return $name !== null ? $this->query[$name] : $this->query;
395
    }
396
397
    /**
398
     * A boolean flag to enable or disable diacritic sensitive search for $text
399
     * criteria.
400
     *
401
     * This method must be called after text().
402
     *
403
     * @see Builder::diacriticSensitive()
404
     * @see http://docs.mongodb.org/manual/reference/operator/text/
405
     *
406
     * @throws BadMethodCallException If the query does not already have $text criteria.
407
     */
408 1
    public function diacriticSensitive(bool $diacriticSensitive) : self
409
    {
410 1
        $this->expr->diacriticSensitive($diacriticSensitive);
411
412 1
        return $this;
413
    }
414
415
    /**
416
     * Change the query type to a distinct command.
417
     *
418
     * @see http://docs.mongodb.org/manual/reference/command/distinct/
419
     */
420 2
    public function distinct(string $field) : self
421
    {
422 2
        $this->query['type']     = Query::TYPE_DISTINCT;
423 2
        $this->query['distinct'] = $field;
424
425 2
        return $this;
426
    }
427
428
    /**
429
     * Specify $elemMatch criteria for the current field.
430
     *
431
     * You can create a new expression using the {@link Builder::expr()} method.
432
     *
433
     * @see Expr::elemMatch()
434
     * @see http://docs.mongodb.org/manual/reference/operator/elemMatch/
435
     *
436
     * @param array|Expr $expression
437
     */
438 6
    public function elemMatch($expression) : self
439
    {
440 6
        $this->expr->elemMatch($expression);
441
442 6
        return $this;
443
    }
444
445
    /**
446
     * Specify an equality match for the current field.
447
     *
448
     * @see Expr::equals()
449
     *
450
     * @param mixed $value
451
     */
452 79
    public function equals($value) : self
453
    {
454 79
        $this->expr->equals($value);
455
456 79
        return $this;
457
    }
458
459
    /**
460
     * Set one or more fields to be excluded from the query projection.
461
     *
462
     * If fields have been selected for inclusion, only the "_id" field may be
463
     * excluded.
464
     *
465
     * @param array|string $fieldName,...
0 ignored issues
show
Documentation introduced by
There is no parameter named $fieldName,.... Did you maybe mean $fieldName?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
466
     */
467 6
    public function exclude($fieldName = null) : self
468
    {
469 6
        if (! isset($this->query['select'])) {
470 6
            $this->query['select'] = [];
471
        }
472
473 6
        $fieldNames = is_array($fieldName) ? $fieldName : func_get_args();
474
475 6
        foreach ($fieldNames as $fieldName) {
476 4
            $this->query['select'][$fieldName] = 0;
477
        }
478
479 6
        return $this;
480
    }
481
482
    /**
483
     * Specify $exists criteria for the current field.
484
     *
485
     * @see Expr::exists()
486
     * @see http://docs.mongodb.org/manual/reference/operator/exists/
487
     */
488 5
    public function exists(bool $bool) : self
489
    {
490 5
        $this->expr->exists($bool);
491
492 5
        return $this;
493
    }
494
495
    /**
496
     * Create a new Expr instance that can be used as an expression with the Builder
497
     */
498 26
    public function expr() : Expr
499
    {
500 26
        $expr = new Expr($this->dm);
501 26
        $expr->setClassMetadata($this->class);
502
503 26
        return $expr;
504
    }
505
506
    /**
507
     * Set the current field to operate on.
508
     */
509 145
    public function field(string $field) : self
510
    {
511 145
        $this->currentField = $field;
512 145
        $this->expr->field($field);
513
514 145
        return $this;
515
    }
516
517
    /**
518
     * Change the query type to find and optionally set and change the class being queried.
519
     */
520 13
    public function find(?string $documentName = null) : self
521
    {
522 13
        $this->setDocumentName($documentName);
523 13
        $this->query['type'] = Query::TYPE_FIND;
524
525 13
        return $this;
526
    }
527
528 1
    public function findAndRemove(?string $documentName = null) : self
529
    {
530 1
        $this->setDocumentName($documentName);
531 1
        $this->query['type'] = Query::TYPE_FIND_AND_REMOVE;
532
533 1
        return $this;
534
    }
535
536 13
    public function findAndUpdate(?string $documentName = null) : self
537
    {
538 13
        $this->setDocumentName($documentName);
539 13
        $this->query['type'] = Query::TYPE_FIND_AND_UPDATE;
540
541 13
        return $this;
542
    }
543
544
    /**
545
     * Add $geoIntersects criteria with a GeoJSON geometry to the query.
546
     *
547
     * The geometry parameter GeoJSON object or an array corresponding to the
548
     * geometry's JSON representation.
549
     *
550
     * @see Expr::geoIntersects()
551
     * @see http://docs.mongodb.org/manual/reference/operator/geoIntersects/
552
     *
553
     * @param array|Geometry $geometry
554
     */
555 1
    public function geoIntersects($geometry) : self
556
    {
557 1
        $this->expr->geoIntersects($geometry);
558
559 1
        return $this;
560
    }
561
562
    /**
563
     * Add $geoWithin criteria with a GeoJSON geometry to the query.
564
     *
565
     * The geometry parameter GeoJSON object or an array corresponding to the
566
     * geometry's JSON representation.
567
     *
568
     * @see Expr::geoWithin()
569
     * @see http://docs.mongodb.org/manual/reference/operator/geoWithin/
570
     *
571
     * @param array|Geometry $geometry
572
     */
573 1
    public function geoWithin($geometry) : self
574
    {
575 1
        $this->expr->geoWithin($geometry);
576
577 1
        return $this;
578
    }
579
580
    /**
581
     * Add $geoWithin criteria with a $box shape to the query.
582
     *
583
     * A rectangular polygon will be constructed from a pair of coordinates
584
     * corresponding to the bottom left and top right corners.
585
     *
586
     * Note: the $box operator only supports legacy coordinate pairs and 2d
587
     * indexes. This cannot be used with 2dsphere indexes and GeoJSON shapes.
588
     *
589
     * @see Expr::geoWithinBox()
590
     * @see http://docs.mongodb.org/manual/reference/operator/box/
591
     */
592 1
    public function geoWithinBox(float $x1, float $y1, float $x2, float $y2) : self
593
    {
594 1
        $this->expr->geoWithinBox($x1, $y1, $x2, $y2);
595
596 1
        return $this;
597
    }
598
599
    /**
600
     * Add $geoWithin criteria with a $center shape to the query.
601
     *
602
     * Note: the $center operator only supports legacy coordinate pairs and 2d
603
     * indexes. This cannot be used with 2dsphere indexes and GeoJSON shapes.
604
     *
605
     * @see Expr::geoWithinCenter()
606
     * @see http://docs.mongodb.org/manual/reference/operator/center/
607
     */
608 1
    public function geoWithinCenter(float $x, float $y, float $radius) : self
609
    {
610 1
        $this->expr->geoWithinCenter($x, $y, $radius);
611
612 1
        return $this;
613
    }
614
615
    /**
616
     * Add $geoWithin criteria with a $centerSphere shape to the query.
617
     *
618
     * Note: the $centerSphere operator supports both 2d and 2dsphere indexes.
619
     *
620
     * @see Expr::geoWithinCenterSphere()
621
     * @see http://docs.mongodb.org/manual/reference/operator/centerSphere/
622
     */
623 1
    public function geoWithinCenterSphere(float $x, float $y, float $radius) : self
624
    {
625 1
        $this->expr->geoWithinCenterSphere($x, $y, $radius);
626
627 1
        return $this;
628
    }
629
630
    /**
631
     * Add $geoWithin criteria with a $polygon shape to the query.
632
     *
633
     * Point coordinates are in x, y order (easting, northing for projected
634
     * coordinates, longitude, latitude for geographic coordinates).
635
     *
636
     * The last point coordinate is implicitly connected with the first.
637
     *
638
     * Note: the $polygon operator only supports legacy coordinate pairs and 2d
639
     * indexes. This cannot be used with 2dsphere indexes and GeoJSON shapes.
640
     *
641
     * @see Expr::geoWithinPolygon()
642
     * @see http://docs.mongodb.org/manual/reference/operator/polygon/
643
     *
644
     * @param array $point1    First point of the polygon
645
     * @param array $point2    Second point of the polygon
646
     * @param array $point3    Third point of the polygon
647
     * @param array ...$points Additional points of the polygon
648
     */
649 1
    public function geoWithinPolygon($point1, $point2, $point3, ...$points) : self
0 ignored issues
show
Unused Code introduced by
The parameter $point1 is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $point2 is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $point3 is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $points is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
650
    {
651 1
        $this->expr->geoWithinPolygon(...func_get_args());
652
653 1
        return $this;
654
    }
655
656
    /**
657
     * Return the expression's "new object".
658
     *
659
     * @see Expr::getNewObj()
660
     */
661 13
    public function getNewObj() : array
662
    {
663 13
        return $this->expr->getNewObj();
664
    }
665
666
    /**
667
     * Gets the Query executable.
668
     */
669 155
    public function getQuery(array $options = []) : Query
670
    {
671 155
        $documentPersister = $this->dm->getUnitOfWork()->getDocumentPersister($this->class->name);
672
673 155
        $query = $this->query;
674
675 155
        $query['query'] = $this->expr->getQuery();
676 155
        $query['query'] = $documentPersister->addDiscriminatorToPreparedQuery($query['query']);
677 155
        $query['query'] = $documentPersister->addFilterToPreparedQuery($query['query']);
678
679 155
        $query['newObj'] = $this->expr->getNewObj();
680
681 155
        if (isset($query['distinct'])) {
682 2
            $query['distinct'] = $documentPersister->prepareFieldName($query['distinct']);
0 ignored issues
show
Documentation introduced by
$query['distinct'] is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
683
        }
684
685 155
        if ($this->class->inheritanceType === ClassMetadata::INHERITANCE_TYPE_SINGLE_COLLECTION && ! empty($query['upsert']) &&
686 155
            (empty($query['query'][$this->class->discriminatorField]) || is_array($query['query'][$this->class->discriminatorField]))) {
687 1
            throw new InvalidArgumentException('Upsert query that is to be performed on discriminated document does not have single ' .
688 1
                'discriminator. Either not use base class or set \'' . $this->class->discriminatorField . '\' field manually.');
689
        }
690
691 154
        if (! empty($query['select'])) {
692 15
            $query['select'] = $documentPersister->prepareProjection($query['select']);
693 15
            if ($this->hydrate && $this->class->inheritanceType === ClassMetadata::INHERITANCE_TYPE_SINGLE_COLLECTION
694 15
                && ! isset($query['select'][$this->class->discriminatorField])) {
695
                $includeMode = 0 < count(array_filter($query['select'], static function ($mode) {
696 2
                    return $mode === 1;
697 2
                }));
698 2
                if ($includeMode && ! isset($query['select'][$this->class->discriminatorField])) {
699 1
                    $query['select'][$this->class->discriminatorField] = 1;
700
                }
701
            }
702
        }
703
704 154
        if (isset($query['sort'])) {
705 20
            $query['sort'] = $documentPersister->prepareSort($query['sort']);
706
        }
707
708 154
        if ($this->class->readPreference && ! array_key_exists('readPreference', $query)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->class->readPreference of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
709 1
            $query['readPreference'] = new ReadPreference($this->class->readPreference, $this->class->readPreferenceTags);
710
        }
711
712 154
        return new Query(
713 154
            $this->dm,
714 154
            $this->class,
715 154
            $this->collection,
716 154
            $query,
717 154
            $options,
718 154
            $this->hydrate,
719 154
            $this->refresh,
720 154
            $this->primers,
721 154
            $this->readOnly,
722 154
            $this->rewindable
723
        );
724
    }
725
726
    /**
727
     * Return the expression's query criteria.
728
     *
729
     * @see Expr::getQuery()
730
     */
731 32
    public function getQueryArray() : array
732
    {
733 32
        return $this->expr->getQuery();
734
    }
735
736
    /**
737
     * Get the type of this query.
738
     */
739
    public function getType() : int
740
    {
741
        return $this->query['type'];
742
    }
743
744
    /**
745
     * Specify $gt criteria for the current field.
746
     *
747
     * @see Expr::gt()
748
     * @see http://docs.mongodb.org/manual/reference/operator/gt/
749
     *
750
     * @param mixed $value
751
     */
752 2
    public function gt($value) : self
753
    {
754 2
        $this->expr->gt($value);
755
756 2
        return $this;
757
    }
758
759
    /**
760
     * Specify $gte criteria for the current field.
761
     *
762
     * @see Expr::gte()
763
     * @see http://docs.mongodb.org/manual/reference/operator/gte/
764
     *
765
     * @param mixed $value
766
     */
767 2
    public function gte($value) : self
768
    {
769 2
        $this->expr->gte($value);
770
771 2
        return $this;
772
    }
773
774
    /**
775
     * Set the index hint for the query.
776
     *
777
     * @param array|string $index
778
     */
779
    public function hint($index) : self
780
    {
781
        $this->query['hint'] = $index;
782
783
        return $this;
784
    }
785
786 17
    public function hydrate(bool $bool = true) : self
787
    {
788 17
        $this->hydrate = $bool;
789
790 17
        return $this;
791
    }
792
793
    /**
794
     * Set the immortal cursor flag.
795
     */
796
    public function immortal(bool $bool = true) : self
797
    {
798
        $this->query['immortal'] = $bool;
799
800
        return $this;
801
    }
802
803
    /**
804
     * Specify $in criteria for the current field.
805
     *
806
     * @see Expr::in()
807
     * @see http://docs.mongodb.org/manual/reference/operator/in/
808
     */
809 24
    public function in(array $values) : self
810
    {
811 24
        $this->expr->in($values);
812
813 24
        return $this;
814
    }
815
816
    /**
817
     * Increment the current field.
818
     *
819
     * If the field does not exist, it will be set to this value.
820
     *
821
     * @see Expr::inc()
822
     * @see http://docs.mongodb.org/manual/reference/operator/inc/
823
     *
824
     * @param float|int $value
825
     */
826 6
    public function inc($value) : self
827
    {
828 6
        $this->expr->inc($value);
829
830 6
        return $this;
831
    }
832
833 6
    public function includesReferenceTo(object $document) : self
834
    {
835 6
        $this->expr->includesReferenceTo($document);
836
837 4
        return $this;
838
    }
839
840 1
    public function insert(?string $documentName = null) : self
841
    {
842 1
        $this->setDocumentName($documentName);
843 1
        $this->query['type'] = Query::TYPE_INSERT;
844
845 1
        return $this;
846
    }
847
848
    /**
849
     * Set the $language option for $text criteria.
850
     *
851
     * This method must be called after text().
852
     *
853
     * @see Expr::language()
854
     * @see http://docs.mongodb.org/manual/reference/operator/text/
855
     */
856 1
    public function language(string $language) : self
857
    {
858 1
        $this->expr->language($language);
859
860 1
        return $this;
861
    }
862
863
    /**
864
     * Set the limit for the query.
865
     *
866
     * This is only relevant for find queries and count commands.
867
     *
868
     * @see Query::prepareCursor()
869
     */
870 2
    public function limit(int $limit) : self
871
    {
872 2
        $this->query['limit'] = $limit;
873
874 2
        return $this;
875
    }
876
877
    /**
878
     * Specify $lt criteria for the current field.
879
     *
880
     * @see Expr::lte()
881
     * @see http://docs.mongodb.org/manual/reference/operator/lte/
882
     *
883
     * @param mixed $value
884
     */
885
    public function lt($value) : self
886
    {
887
        $this->expr->lt($value);
888
889
        return $this;
890
    }
891
892
    /**
893
     * Specify $lte criteria for the current field.
894
     *
895
     * @see Expr::lte()
896
     * @see http://docs.mongodb.org/manual/reference/operator/lte/
897
     *
898
     * @param mixed $value
899
     */
900
    public function lte($value) : self
901
    {
902
        $this->expr->lte($value);
903
904
        return $this;
905
    }
906
907
    /**
908
     * Updates the value of the field to a specified value if the specified value is greater than the current value of the field.
909
     *
910
     * @see Expr::max()
911
     * @see http://docs.mongodb.org/manual/reference/operator/update/max/
912
     *
913
     * @param mixed $value
914
     */
915 1
    public function max($value) : self
916
    {
917 1
        $this->expr->max($value);
918
919 1
        return $this;
920
    }
921
922
    /**
923
     * Specifies a cumulative time limit in milliseconds for processing operations on a cursor.
924
     */
925
    public function maxTimeMS(int $ms) : self
926
    {
927
        $this->query['maxTimeMS'] = $ms;
928
929
        return $this;
930
    }
931
932
    /**
933
     * Updates the value of the field to a specified value if the specified value is less than the current value of the field.
934
     *
935
     * @see Expr::min()
936
     * @see http://docs.mongodb.org/manual/reference/operator/update/min/
937
     *
938
     * @param mixed $value
939
     */
940 1
    public function min($value) : self
941
    {
942 1
        $this->expr->min($value);
943
944 1
        return $this;
945
    }
946
947
    /**
948
     * Specify $mod criteria for the current field.
949
     *
950
     * @see Expr::mod()
951
     * @see http://docs.mongodb.org/manual/reference/operator/mod/
952
     *
953
     * @param float|int $divisor
954
     * @param float|int $remainder
955
     */
956 1
    public function mod($divisor, $remainder = 0) : self
957
    {
958 1
        $this->expr->mod($divisor, $remainder);
959
960 1
        return $this;
961
    }
962
963
    /**
964
     * Multiply the current field.
965
     *
966
     * If the field does not exist, it will be set to 0.
967
     *
968
     * @see Expr::mul()
969
     * @see http://docs.mongodb.org/manual/reference/operator/mul/
970
     *
971
     * @param float|int $value
972
     */
973 1
    public function mul($value) : self
974
    {
975 1
        $this->expr->mul($value);
976
977 1
        return $this;
978
    }
979
980
    /**
981
     * Add $near criteria to the query.
982
     *
983
     * A GeoJSON point may be provided as the first and only argument for
984
     * 2dsphere queries. This single parameter may be a GeoJSON point object or
985
     * an array corresponding to the point's JSON representation.
986
     *
987
     * @see Expr::near()
988
     * @see http://docs.mongodb.org/manual/reference/operator/near/
989
     *
990
     * @param float|array|Point $x
991
     * @param float             $y
992
     */
993 1
    public function near($x, $y = null) : self
994
    {
995 1
        $this->expr->near($x, $y);
996
997 1
        return $this;
998
    }
999
1000
    /**
1001
     * Add $nearSphere criteria to the query.
1002
     *
1003
     * A GeoJSON point may be provided as the first and only argument for
1004
     * 2dsphere queries. This single parameter may be a GeoJSON point object or
1005
     * an array corresponding to the point's JSON representation.
1006
     *
1007
     * @see Expr::nearSphere()
1008
     * @see http://docs.mongodb.org/manual/reference/operator/nearSphere/
1009
     *
1010
     * @param float|array|Point $x
1011
     * @param float             $y
1012
     */
1013 1
    public function nearSphere($x, $y = null) : self
1014
    {
1015 1
        $this->expr->nearSphere($x, $y);
1016
1017 1
        return $this;
1018
    }
1019
1020
    /**
1021
     * Negates an expression for the current field.
1022
     *
1023
     * You can create a new expression using the {@link Builder::expr()} method.
1024
     *
1025
     * @see Expr::not()
1026
     * @see http://docs.mongodb.org/manual/reference/operator/not/
1027
     *
1028
     * @param array|Expr $expression
1029
     */
1030 3
    public function not($expression) : self
1031
    {
1032 3
        $this->expr->not($expression);
1033
1034 3
        return $this;
1035
    }
1036
1037
    /**
1038
     * Specify $ne criteria for the current field.
1039
     *
1040
     * @see Expr::notEqual()
1041
     * @see http://docs.mongodb.org/manual/reference/operator/ne/
1042
     *
1043
     * @param mixed $value
1044
     */
1045 4
    public function notEqual($value) : self
1046
    {
1047 4
        $this->expr->notEqual($value);
1048
1049 4
        return $this;
1050
    }
1051
1052
    /**
1053
     * Specify $nin criteria for the current field.
1054
     *
1055
     * @see Expr::notIn()
1056
     * @see http://docs.mongodb.org/manual/reference/operator/nin/
1057
     *
1058
     * @param array $values
1059
     */
1060 4
    public function notIn(array $values) : self
1061
    {
1062 4
        $this->expr->notIn($values);
1063
1064 4
        return $this;
1065
    }
1066
1067
    /**
1068
     * Remove the first element from the current array field.
1069
     *
1070
     * @see Expr::popFirst()
1071
     * @see http://docs.mongodb.org/manual/reference/operator/pop/
1072
     */
1073 3
    public function popFirst() : self
1074
    {
1075 3
        $this->expr->popFirst();
1076
1077 3
        return $this;
1078
    }
1079
1080
    /**
1081
     * Remove the last element from the current array field.
1082
     *
1083
     * @see Expr::popLast()
1084
     * @see http://docs.mongodb.org/manual/reference/operator/pop/
1085
     */
1086 2
    public function popLast() : self
1087
    {
1088 2
        $this->expr->popLast();
1089
1090 2
        return $this;
1091
    }
1092
1093
    /**
1094
     * Use a primer to eagerly load all references in the current field.
1095
     *
1096
     * If $primer is true or a callable is provided, referenced documents for
1097
     * this field will loaded into UnitOfWork immediately after the query is
1098
     * executed. This will avoid multiple queries due to lazy initialization of
1099
     * Proxy objects.
1100
     *
1101
     * If $primer is false, no priming will take place. That is also the default
1102
     * behavior.
1103
     *
1104
     * If a custom callable is used, its signature should conform to the default
1105
     * Closure defined in {@link ReferencePrimer::__construct()}.
1106
     *
1107
     * @param bool|callable $primer
1108
     *
1109
     * @throws InvalidArgumentException If $primer is not boolean or callable.
1110
     */
1111 22
    public function prime($primer = true) : self
1112
    {
1113 22
        if (! is_bool($primer) && ! is_callable($primer)) {
1114 1
            throw new InvalidArgumentException('$primer is not a boolean or callable');
1115
        }
1116
1117 21
        if ($primer === false) {
1118
            unset($this->primers[$this->currentField]);
1119
1120
            return $this;
1121
        }
1122
1123 21
        $this->primers[$this->currentField] = $primer;
1124
1125 21
        return $this;
1126
    }
1127
1128
    /**
1129
     * Remove all elements matching the given value or expression from the
1130
     * current array field.
1131
     *
1132
     * @see Expr::pull()
1133
     * @see http://docs.mongodb.org/manual/reference/operator/pull/
1134
     *
1135
     * @param mixed|Expr $valueOrExpression
1136
     */
1137 1
    public function pull($valueOrExpression) : self
1138
    {
1139 1
        $this->expr->pull($valueOrExpression);
1140
1141 1
        return $this;
1142
    }
1143
1144
    /**
1145
     * Remove all elements matching any of the given values from the current
1146
     * array field.
1147
     *
1148
     * @see Expr::pullAll()
1149
     * @see http://docs.mongodb.org/manual/reference/operator/pullAll/
1150
     */
1151 1
    public function pullAll(array $values) : self
1152
    {
1153 1
        $this->expr->pullAll($values);
1154
1155 1
        return $this;
1156
    }
1157
1158
    /**
1159
     * Append one or more values to the current array field.
1160
     *
1161
     * If the field does not exist, it will be set to an array containing the
1162
     * value(s) in the argument. If the field is not an array, the query
1163
     * will yield an error.
1164
     *
1165
     * Multiple values may be specified by providing an Expr object and using
1166
     * {@link Expr::each()}. {@link Expr::slice()} and {@link Expr::sort()} may
1167
     * also be used to limit and order array elements, respectively.
1168
     *
1169
     * @see Expr::push()
1170
     * @see http://docs.mongodb.org/manual/reference/operator/push/
1171
     * @see http://docs.mongodb.org/manual/reference/operator/each/
1172
     * @see http://docs.mongodb.org/manual/reference/operator/slice/
1173
     * @see http://docs.mongodb.org/manual/reference/operator/sort/
1174
     *
1175
     * @param mixed|Expr $valueOrExpression
1176
     */
1177 6
    public function push($valueOrExpression) : self
1178
    {
1179 6
        $this->expr->push($valueOrExpression);
1180
1181 6
        return $this;
1182
    }
1183
1184
    /**
1185
     * Specify $gte and $lt criteria for the current field.
1186
     *
1187
     * This method is shorthand for specifying $gte criteria on the lower bound
1188
     * and $lt criteria on the upper bound. The upper bound is not inclusive.
1189
     *
1190
     * @see Expr::range()
1191
     *
1192
     * @param mixed $start
1193
     * @param mixed $end
1194
     */
1195 3
    public function range($start, $end) : self
1196
    {
1197 3
        $this->expr->range($start, $end);
1198
1199 3
        return $this;
1200
    }
1201
1202 2
    public function readOnly(bool $bool = true) : self
1203
    {
1204 2
        $this->readOnly = $bool;
1205
1206 2
        return $this;
1207
    }
1208
1209 10
    public function references(object $document) : self
1210
    {
1211 10
        $this->expr->references($document);
1212
1213 8
        return $this;
1214
    }
1215
1216 5
    public function refresh(bool $bool = true) : self
1217
    {
1218 5
        $this->refresh = $bool;
1219
1220 5
        return $this;
1221
    }
1222
1223 1
    public function remove(?string $documentName = null) : self
1224
    {
1225 1
        $this->setDocumentName($documentName);
1226 1
        $this->query['type'] = Query::TYPE_REMOVE;
1227
1228 1
        return $this;
1229
    }
1230
1231
    /**
1232
     * Rename the current field.
1233
     *
1234
     * @see Expr::rename()
1235
     * @see http://docs.mongodb.org/manual/reference/operator/rename/
1236
     */
1237
    public function rename(string $name) : self
1238
    {
1239
        $this->expr->rename($name);
1240
1241
        return $this;
1242
    }
1243
1244 4
    public function returnNew(bool $bool = true) : self
1245
    {
1246 4
        $this->refresh(true);
1247 4
        $this->query['new'] = $bool;
1248
1249 4
        return $this;
1250
    }
1251
1252
    /**
1253
     * Set one or more fields to be included in the query projection.
1254
     *
1255
     * @param array|string $fieldName,...
0 ignored issues
show
Documentation introduced by
There is no parameter named $fieldName,.... Did you maybe mean $fieldName?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
1256
     */
1257 19
    public function select($fieldName = null) : self
1258
    {
1259 19
        if (! isset($this->query['select'])) {
1260 18
            $this->query['select'] = [];
1261
        }
1262
1263 19
        $fieldNames = is_array($fieldName) ? $fieldName : func_get_args();
1264
1265 19
        foreach ($fieldNames as $fieldName) {
1266 16
            $this->query['select'][$fieldName] = 1;
1267
        }
1268
1269 19
        return $this;
1270
    }
1271
1272
    /**
1273
     * Select only matching embedded documents in an array field for the query
1274
     * projection.
1275
     *
1276
     * @see http://docs.mongodb.org/manual/reference/projection/elemMatch/
1277
     *
1278
     * @param array|Expr $expression
1279
     */
1280 2
    public function selectElemMatch(string $fieldName, $expression) : self
1281
    {
1282 2
        if ($expression instanceof Expr) {
1283 1
            $expression = $expression->getQuery();
1284
        }
1285 2
        $this->query['select'][$fieldName] = ['$elemMatch' => $expression];
1286
1287 2
        return $this;
1288
    }
1289
1290
    /**
1291
     * Select a metadata field for the query projection.
1292
     *
1293
     * @see http://docs.mongodb.org/master/reference/operator/projection/meta/
1294
     */
1295 3
    public function selectMeta(string $fieldName, string $metaDataKeyword) : self
1296
    {
1297 3
        $this->query['select'][$fieldName] = ['$meta' => $metaDataKeyword];
1298
1299 3
        return $this;
1300
    }
1301
1302
    /**
1303
     * Select a slice of an array field for the query projection.
1304
     *
1305
     * The $countOrSkip parameter has two very different meanings, depending on
1306
     * whether or not $limit is provided. See the MongoDB documentation for more
1307
     * information.
1308
     *
1309
     * @see http://docs.mongodb.org/manual/reference/projection/slice/
1310
     */
1311 3
    public function selectSlice(string $fieldName, int $countOrSkip, ?int $limit = null) : self
1312
    {
1313 3
        $slice = $countOrSkip;
1314 3
        if ($limit !== null) {
1315 2
            $slice = [$slice, $limit];
1316
        }
1317 3
        $this->query['select'][$fieldName] = ['$slice' => $slice];
1318
1319 3
        return $this;
1320
    }
1321
1322
    /**
1323
     * Set the current field to a value.
1324
     *
1325
     * This is only relevant for insert, update, or findAndUpdate queries. For
1326
     * update and findAndUpdate queries, the $atomic parameter will determine
1327
     * whether or not a $set operator is used.
1328
     *
1329
     * @see Expr::set()
1330
     * @see http://docs.mongodb.org/manual/reference/operator/set/
1331
     *
1332
     * @param mixed $value
1333
     */
1334 16
    public function set($value, bool $atomic = true) : self
1335
    {
1336 16
        $this->expr->set($value, $atomic && $this->query['type'] !== Query::TYPE_INSERT);
1337
1338 16
        return $this;
1339
    }
1340
1341
    /**
1342
     * Set the expression's "new object".
1343
     *
1344
     * @see Expr::setNewObj()
1345
     */
1346 1
    public function setNewObj(array $newObj) : self
1347
    {
1348 1
        $this->expr->setNewObj($newObj);
1349
1350 1
        return $this;
1351
    }
1352
1353
    /**
1354
     * Set the current field to the value if the document is inserted in an
1355
     * upsert operation.
1356
     *
1357
     * If an update operation with upsert: true results in an insert of a
1358
     * document, then $setOnInsert assigns the specified values to the fields in
1359
     * the document. If the update operation does not result in an insert,
1360
     * $setOnInsert does nothing.
1361
     *
1362
     * @see Expr::setOnInsert()
1363
     * @see https://docs.mongodb.org/manual/reference/operator/update/setOnInsert/
1364
     *
1365
     * @param mixed $value
1366
     */
1367 2
    public function setOnInsert($value) : self
1368
    {
1369 2
        $this->expr->setOnInsert($value);
1370
1371 2
        return $this;
1372
    }
1373
1374
    /**
1375
     * Set the read preference for the query.
1376
     *
1377
     * This is only relevant for read-only queries and commands.
1378
     *
1379
     * @see http://docs.mongodb.org/manual/core/read-preference/
1380
     */
1381 6
    public function setReadPreference(ReadPreference $readPreference) : self
1382
    {
1383 6
        $this->query['readPreference'] = $readPreference;
1384
1385 6
        return $this;
1386
    }
1387
1388
    public function setRewindable(bool $rewindable = true): self
1389
    {
1390
        $this->rewindable = $rewindable;
1391
1392
        return $this;
1393
    }
1394
1395
    /**
1396
     * Set the expression's query criteria.
1397
     *
1398
     * @see Expr::setQuery()
1399
     */
1400 18
    public function setQueryArray(array $query) : self
1401
    {
1402 18
        $this->expr->setQuery($query);
1403
1404 18
        return $this;
1405
    }
1406
1407
    /**
1408
     * Specify $size criteria for the current field.
1409
     *
1410
     * @see Expr::size()
1411
     * @see http://docs.mongodb.org/manual/reference/operator/size/
1412
     */
1413 1
    public function size(int $size) : self
1414
    {
1415 1
        $this->expr->size($size);
1416
1417 1
        return $this;
1418
    }
1419
1420
    /**
1421
     * Set the skip for the query cursor.
1422
     *
1423
     * This is only relevant for find queries, or mapReduce queries that store
1424
     * results in an output collection and return a cursor.
1425
     *
1426
     * @see Query::prepareCursor()
1427
     */
1428
    public function skip(int $skip) : self
1429
    {
1430
        $this->query['skip'] = $skip;
1431
1432
        return $this;
1433
    }
1434
1435
    /**
1436
     * Set the snapshot cursor flag.
1437
     */
1438
    public function snapshot(bool $bool = true) : self
1439
    {
1440
        $this->query['snapshot'] = $bool;
1441
1442
        return $this;
1443
    }
1444
1445
    /**
1446
     * Set one or more field/order pairs on which to sort the query.
1447
     *
1448
     * If sorting by multiple fields, the first argument should be an array of
1449
     * field name (key) and order (value) pairs.
1450
     *
1451
     * @param array|string $fieldName Field name or array of field/order pairs
1452
     * @param int|string   $order     Field order (if one field is specified)
1453
     */
1454 28
    public function sort($fieldName, $order = 1) : self
1455
    {
1456 28
        if (! isset($this->query['sort'])) {
1457 28
            $this->query['sort'] = [];
1458
        }
1459
1460 28
        $fields = is_array($fieldName) ? $fieldName : [$fieldName => $order];
1461
1462 28
        foreach ($fields as $fieldName => $order) {
1463 15
            if (is_string($order)) {
1464 9
                $order = strtolower($order) === 'asc' ? 1 : -1;
1465
            }
1466 15
            $this->query['sort'][$fieldName] = (int) $order;
1467
        }
1468
1469 28
        return $this;
1470
    }
1471
1472
    /**
1473
     * Specify a projected metadata field on which to sort the query.
1474
     *
1475
     * Sort order is not configurable for metadata fields. Sorting by a metadata
1476
     * field requires the same field and $meta expression to exist in the
1477
     * projection document. This method will call {@link Builder::selectMeta()}
1478
     * if the field is not already set in the projection.
1479
     *
1480
     * @see http://docs.mongodb.org/master/reference/operator/projection/meta/#sort
1481
     */
1482 3
    public function sortMeta(string $fieldName, string $metaDataKeyword) : self
1483
    {
1484
        /* It's possible that the field is already projected without the $meta
1485
         * operator. We'll assume that the user knows what they're doing in that
1486
         * case and will not attempt to override the projection.
1487
         */
1488 3
        if (! isset($this->query['select'][$fieldName])) {
1489 2
            $this->selectMeta($fieldName, $metaDataKeyword);
1490
        }
1491
1492 3
        $this->query['sort'][$fieldName] = ['$meta' => $metaDataKeyword];
1493
1494 3
        return $this;
1495
    }
1496
1497
    /**
1498
     * Specify $text criteria for the current field.
1499
     *
1500
     * The $language option may be set with {@link Builder::language()}.
1501
     *
1502
     * @see Expr::text()
1503
     * @see http://docs.mongodb.org/master/reference/operator/query/text/
1504
     */
1505 1
    public function text(string $search) : self
1506
    {
1507 1
        $this->expr->text($search);
1508
1509 1
        return $this;
1510
    }
1511
1512
    /**
1513
     * Specify $type criteria for the current field.
1514
     *
1515
     * @see Expr::type()
1516
     * @see http://docs.mongodb.org/manual/reference/operator/type/
1517
     *
1518
     * @param int|string $type
1519
     */
1520 2
    public function type($type) : self
1521
    {
1522 2
        $this->expr->type($type);
1523
1524 2
        return $this;
1525
    }
1526
1527
    /**
1528
     * Unset the current field.
1529
     *
1530
     * The field will be removed from the document (not set to null).
1531
     *
1532
     * @see Expr::unsetField()
1533
     * @see http://docs.mongodb.org/manual/reference/operator/unset/
1534
     */
1535 4
    public function unsetField() : self
1536
    {
1537 4
        $this->expr->unsetField();
1538
1539 4
        return $this;
1540
    }
1541
1542 23
    public function updateOne(?string $documentName = null) : self
1543
    {
1544 23
        $this->setDocumentName($documentName);
1545 23
        $this->query['type']     = Query::TYPE_UPDATE;
1546 23
        $this->query['multiple'] = false;
1547
1548 23
        return $this;
1549
    }
1550
1551 4
    public function updateMany(?string $documentName = null) : self
1552
    {
1553 4
        $this->setDocumentName($documentName);
1554 4
        $this->query['type']     = Query::TYPE_UPDATE;
1555 4
        $this->query['multiple'] = true;
1556
1557 4
        return $this;
1558
    }
1559
1560
    /**
1561
     * Set the "upsert" option for an update or findAndUpdate query.
1562
     */
1563 7
    public function upsert(bool $bool = true) : self
1564
    {
1565 7
        $this->query['upsert'] = $bool;
1566
1567 7
        return $this;
1568
    }
1569
1570
    /**
1571
     * Specify a JavaScript expression to use for matching documents.
1572
     *
1573
     * @see Expr::where()
1574
     * @see http://docs.mongodb.org/manual/reference/operator/where/
1575
     *
1576
     * @param string|Javascript $javascript
1577
     */
1578 3
    public function where($javascript) : self
1579
    {
1580 3
        $this->expr->where($javascript);
1581
1582 3
        return $this;
1583
    }
1584
1585
    /**
1586
     * Get Discriminator Values
1587
     *
1588
     * @param string[] $classNames
1589
     *
1590
     * @throws InvalidArgumentException If the number of found collections > 1.
1591
     */
1592 2
    private function getDiscriminatorValues($classNames) : array
1593
    {
1594 2
        $discriminatorValues = [];
1595 2
        $collections         = [];
1596 2
        foreach ($classNames as $className) {
1597 2
            $class                 = $this->dm->getClassMetadata($className);
1598 2
            $discriminatorValues[] = $class->discriminatorValue;
1599 2
            $key                   = $this->dm->getDocumentDatabase($className)->getDatabaseName() . '.' . $class->getCollection();
1600 2
            $collections[$key]     = $key;
1601
        }
1602 2
        if (count($collections) > 1) {
1603 1
            throw new InvalidArgumentException('Documents involved are not all mapped to the same database collection.');
1604
        }
1605
1606 1
        return $discriminatorValues;
1607
    }
1608
1609
    /**
1610
     * @param string[]|string|null $documentName an array of document names or just one.
1611
     */
1612 281
    private function setDocumentName($documentName)
1613
    {
1614 281
        if (is_array($documentName)) {
1615 2
            $documentNames = $documentName;
1616 2
            $documentName  = $documentNames[0];
1617
1618 2
            $metadata            = $this->dm->getClassMetadata($documentName);
1619 2
            $discriminatorField  = $metadata->discriminatorField ?? ClassMetadata::DEFAULT_DISCRIMINATOR_FIELD;
1620 2
            $discriminatorValues = $this->getDiscriminatorValues($documentNames);
1621
1622
            // If a defaultDiscriminatorValue is set and it is among the discriminators being queries, add NULL to the list
1623 1
            if ($metadata->defaultDiscriminatorValue && in_array($metadata->defaultDiscriminatorValue, $discriminatorValues)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $metadata->defaultDiscriminatorValue of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1624 1
                $discriminatorValues[] = null;
1625
            }
1626
1627 1
            $this->field($discriminatorField)->in($discriminatorValues);
1628
        }
1629
1630 280
        if ($documentName === null) {
1631 42
            return;
1632
        }
1633
1634 280
        $this->collection = $this->dm->getDocumentCollection($documentName);
1635 280
        $this->class      = $this->dm->getClassMetadata($documentName);
1636
1637
        // Expr also needs to know
1638 280
        $this->expr->setClassMetadata($this->class);
1639 280
    }
1640
}
1641