Builder::readOnly()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 1
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
    /** @var bool */
84
    private $rewindable = true;
85
86
    /**
87
     * The Collection instance.
88
     *
89
     * @var Collection
90
     */
91
    private $collection;
92
93
    /**
94
     * Array containing the query data.
95
     *
96
     * @var array
97
     */
98
    private $query = ['type' => Query::TYPE_FIND];
99
100
    /**
101
     * The Expr instance used for building this query.
102
     *
103
     * This object includes the query criteria and the "new object" used for
104
     * insert and update queries.
105
     *
106
     * @var Expr $expr
107
     */
108
    private $expr;
109
110
    /**
111
     * Construct a Builder
112
     *
113
     * @param string[]|string|null $documentName (optional) an array of document names, the document name, or none
114
     */
115 285
    public function __construct(DocumentManager $dm, $documentName = null)
116
    {
117 285
        $this->dm   = $dm;
118 285
        $this->expr = new Expr($dm);
119 285
        if ($documentName === null) {
120 9
            return;
121
        }
122
123 277
        $this->setDocumentName($documentName);
124 276
    }
125
126 1
    public function __clone()
127
    {
128 1
        $this->expr = clone $this->expr;
129 1
    }
130
131
    /**
132
     * Add one or more $and clauses to the current query.
133
     *
134
     * You can create a new expression using the {@link Builder::expr()} method.
135
     *
136
     * @see Expr::addAnd()
137
     * @see http://docs.mongodb.org/manual/reference/operator/and/
138
     *
139
     * @param array|Expr $expression
140
     * @param array|Expr ...$expressions
141
     */
142 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...
143
    {
144 4
        $this->expr->addAnd(...func_get_args());
145
146 4
        return $this;
147
    }
148
149
    /**
150
     * Add one or more $nor clauses to the current query.
151
     *
152
     * You can create a new expression using the {@link Builder::expr()} method.
153
     *
154
     * @see Expr::addNor()
155
     * @see http://docs.mongodb.org/manual/reference/operator/nor/
156
     *
157
     * @param array|Expr $expression
158
     * @param array|Expr ...$expressions
159
     */
160 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...
161
    {
162 3
        $this->expr->addNor(...func_get_args());
163
164 3
        return $this;
165
    }
166
167
    /**
168
     * Add one or more $or clauses to the current query.
169
     *
170
     * You can create a new expression using the {@link Builder::expr()} method.
171
     *
172
     * @see Expr::addOr()
173
     * @see http://docs.mongodb.org/manual/reference/operator/or/
174
     *
175
     * @param array|Expr $expression
176
     * @param array|Expr ...$expressions
177
     */
178 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...
179
    {
180 6
        $this->expr->addOr(...func_get_args());
181
182 6
        return $this;
183
    }
184
185
    /**
186
     * Append one or more values to the current array field only if they do not
187
     * already exist in the array.
188
     *
189
     * If the field does not exist, it will be set to an array containing the
190
     * unique value(s) in the argument. If the field is not an array, the query
191
     * will yield an error.
192
     *
193
     * Multiple values may be specified by provided an Expr object and using
194
     * {@link Expr::each()}.
195
     *
196
     * @see Expr::addToSet()
197
     * @see http://docs.mongodb.org/manual/reference/operator/addToSet/
198
     * @see http://docs.mongodb.org/manual/reference/operator/each/
199
     *
200
     * @param mixed|Expr $valueOrExpression
201
     */
202 5
    public function addToSet($valueOrExpression) : self
203
    {
204 5
        $this->expr->addToSet($valueOrExpression);
205
206 5
        return $this;
207
    }
208
209
    /**
210
     * Specify $all criteria for the current field.
211
     *
212
     * @see Expr::all()
213
     * @see http://docs.mongodb.org/manual/reference/operator/all/
214
     */
215 3
    public function all(array $values) : self
216
    {
217 3
        $this->expr->all($values);
218
219 3
        return $this;
220
    }
221
222
    /**
223
     * Apply a bitwise and operation on the current field.
224
     *
225
     * @see Expr::bitAnd()
226
     * @see http://docs.mongodb.org/manual/reference/operator/update/bit/
227
     *
228
     * @return $this
229
     */
230 1
    public function bitAnd(int $value) : self
231
    {
232 1
        $this->expr->bitAnd($value);
233
234 1
        return $this;
235
    }
236
237
    /**
238
     * Apply a bitwise or operation on the current field.
239
     *
240
     * @see Expr::bitOr()
241
     * @see http://docs.mongodb.org/manual/reference/operator/update/bit/
242
     */
243 1
    public function bitOr(int $value) : self
244
    {
245 1
        $this->expr->bitOr($value);
246
247 1
        return $this;
248
    }
249
250
    /**
251
     * Matches documents where all of the bit positions given by the query are
252
     * clear.
253
     *
254
     * @see Expr::bitsAllClear()
255
     * @see https://docs.mongodb.org/manual/reference/operator/query/bitsAllClear/
256
     *
257
     * @param int|array|Binary $value
258
     */
259 1
    public function bitsAllClear($value) : self
260
    {
261 1
        $this->expr->bitsAllClear($value);
262
263 1
        return $this;
264
    }
265
266
    /**
267
     * Matches documents where all of the bit positions given by the query are
268
     * set.
269
     *
270
     * @see Expr::bitsAllSet()
271
     * @see https://docs.mongodb.org/manual/reference/operator/query/bitsAllSet/
272
     *
273
     * @param int|array|Binary $value
274
     */
275 1
    public function bitsAllSet($value) : self
276
    {
277 1
        $this->expr->bitsAllSet($value);
278
279 1
        return $this;
280
    }
281
282
    /**
283
     * Matches documents where any of the bit positions given by the query are
284
     * clear.
285
     *
286
     * @see Expr::bitsAnyClear()
287
     * @see https://docs.mongodb.org/manual/reference/operator/query/bitsAnyClear/
288
     *
289
     * @param int|array|Binary $value
290
     */
291 1
    public function bitsAnyClear($value) : self
292
    {
293 1
        $this->expr->bitsAnyClear($value);
294
295 1
        return $this;
296
    }
297
298
    /**
299
     * Matches documents where any of the bit positions given by the query are
300
     * set.
301
     *
302
     * @see Expr::bitsAnySet()
303
     * @see https://docs.mongodb.org/manual/reference/operator/query/bitsAnySet/
304
     *
305
     * @param int|array|Binary $value
306
     */
307 1
    public function bitsAnySet($value) : self
308
    {
309 1
        $this->expr->bitsAnySet($value);
310
311 1
        return $this;
312
    }
313
314
    /**
315
     * Apply a bitwise xor operation on the current field.
316
     *
317
     * @see Expr::bitXor()
318
     * @see http://docs.mongodb.org/manual/reference/operator/update/bit/
319
     */
320 1
    public function bitXor(int $value) : self
321
    {
322 1
        $this->expr->bitXor($value);
323
324 1
        return $this;
325
    }
326
327
    /**
328
     * A boolean flag to enable or disable case sensitive search for $text
329
     * criteria.
330
     *
331
     * This method must be called after text().
332
     *
333
     * @see Expr::caseSensitive()
334
     * @see http://docs.mongodb.org/manual/reference/operator/text/
335
     *
336
     * @throws BadMethodCallException If the query does not already have $text criteria.
337
     */
338 1
    public function caseSensitive(bool $caseSensitive) : self
339
    {
340 1
        $this->expr->caseSensitive($caseSensitive);
341
342 1
        return $this;
343
    }
344
345
    /**
346
     * Associates a comment to any expression taking a query predicate.
347
     *
348
     * @see Expr::comment()
349
     * @see http://docs.mongodb.org/manual/reference/operator/query/comment/
350
     */
351 1
    public function comment(string $comment) : self
352
    {
353 1
        $this->expr->comment($comment);
354
355 1
        return $this;
356
    }
357
358
    /**
359
     * Change the query type to count.
360
     */
361
    public function count() : self
362
    {
363
        $this->query['type'] = Query::TYPE_COUNT;
364
365
        return $this;
366
    }
367
368
    /**
369
     * Sets the value of the current field to the current date, either as a date or a timestamp.
370
     *
371
     * @see Expr::currentDate()
372
     * @see http://docs.mongodb.org/manual/reference/operator/currentDate/
373
     */
374 3
    public function currentDate(string $type = 'date') : self
375
    {
376 3
        $this->expr->currentDate($type);
377
378 2
        return $this;
379
    }
380
381
    /**
382
     * Return an array of information about the Builder state for debugging.
383
     *
384
     * The $name parameter may be used to return a specific key from the
385
     * internal $query array property. If omitted, the entire array will be
386
     * returned.
387
     *
388
     * @return mixed
389
     */
390 26
    public function debug(?string $name = null)
391
    {
392 26
        return $name !== null ? $this->query[$name] : $this->query;
393
    }
394
395
    /**
396
     * A boolean flag to enable or disable diacritic sensitive search for $text
397
     * criteria.
398
     *
399
     * This method must be called after text().
400
     *
401
     * @see Builder::diacriticSensitive()
402
     * @see http://docs.mongodb.org/manual/reference/operator/text/
403
     *
404
     * @throws BadMethodCallException If the query does not already have $text criteria.
405
     */
406 1
    public function diacriticSensitive(bool $diacriticSensitive) : self
407
    {
408 1
        $this->expr->diacriticSensitive($diacriticSensitive);
409
410 1
        return $this;
411
    }
412
413
    /**
414
     * Change the query type to a distinct command.
415
     *
416
     * @see http://docs.mongodb.org/manual/reference/command/distinct/
417
     */
418 2
    public function distinct(string $field) : self
419
    {
420 2
        $this->query['type']     = Query::TYPE_DISTINCT;
421 2
        $this->query['distinct'] = $field;
422
423 2
        return $this;
424
    }
425
426
    /**
427
     * Specify $elemMatch criteria for the current field.
428
     *
429
     * You can create a new expression using the {@link Builder::expr()} method.
430
     *
431
     * @see Expr::elemMatch()
432
     * @see http://docs.mongodb.org/manual/reference/operator/elemMatch/
433
     *
434
     * @param array|Expr $expression
435
     */
436 6
    public function elemMatch($expression) : self
437
    {
438 6
        $this->expr->elemMatch($expression);
439
440 6
        return $this;
441
    }
442
443
    /**
444
     * Specify an equality match for the current field.
445
     *
446
     * @see Expr::equals()
447
     *
448
     * @param mixed $value
449
     */
450 79
    public function equals($value) : self
451
    {
452 79
        $this->expr->equals($value);
453
454 79
        return $this;
455
    }
456
457
    /**
458
     * Set one or more fields to be excluded from the query projection.
459
     *
460
     * If fields have been selected for inclusion, only the "_id" field may be
461
     * excluded.
462
     *
463
     * @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...
464
     */
465 6
    public function exclude($fieldName = null) : self
466
    {
467 6
        if (! isset($this->query['select'])) {
468 6
            $this->query['select'] = [];
469
        }
470
471 6
        $fieldNames = is_array($fieldName) ? $fieldName : func_get_args();
472
473 6
        foreach ($fieldNames as $fieldName) {
474 4
            $this->query['select'][$fieldName] = 0;
475
        }
476
477 6
        return $this;
478
    }
479
480
    /**
481
     * Specify $exists criteria for the current field.
482
     *
483
     * @see Expr::exists()
484
     * @see http://docs.mongodb.org/manual/reference/operator/exists/
485
     */
486 5
    public function exists(bool $bool) : self
487
    {
488 5
        $this->expr->exists($bool);
489
490 5
        return $this;
491
    }
492
493
    /**
494
     * Create a new Expr instance that can be used as an expression with the Builder
495
     */
496 26
    public function expr() : Expr
497
    {
498 26
        $expr = new Expr($this->dm);
499 26
        $expr->setClassMetadata($this->class);
500
501 26
        return $expr;
502
    }
503
504
    /**
505
     * Set the current field to operate on.
506
     */
507 145
    public function field(string $field) : self
508
    {
509 145
        $this->currentField = $field;
510 145
        $this->expr->field($field);
511
512 145
        return $this;
513
    }
514
515
    /**
516
     * Change the query type to find and optionally set and change the class being queried.
517
     */
518 13
    public function find(?string $documentName = null) : self
519
    {
520 13
        $this->setDocumentName($documentName);
521 13
        $this->query['type'] = Query::TYPE_FIND;
522
523 13
        return $this;
524
    }
525
526 1
    public function findAndRemove(?string $documentName = null) : self
527
    {
528 1
        $this->setDocumentName($documentName);
529 1
        $this->query['type'] = Query::TYPE_FIND_AND_REMOVE;
530
531 1
        return $this;
532
    }
533
534 13
    public function findAndUpdate(?string $documentName = null) : self
535
    {
536 13
        $this->setDocumentName($documentName);
537 13
        $this->query['type'] = Query::TYPE_FIND_AND_UPDATE;
538
539 13
        return $this;
540
    }
541
542
    /**
543
     * Add $geoIntersects criteria with a GeoJSON geometry to the query.
544
     *
545
     * The geometry parameter GeoJSON object or an array corresponding to the
546
     * geometry's JSON representation.
547
     *
548
     * @see Expr::geoIntersects()
549
     * @see http://docs.mongodb.org/manual/reference/operator/geoIntersects/
550
     *
551
     * @param array|Geometry $geometry
552
     */
553 1
    public function geoIntersects($geometry) : self
554
    {
555 1
        $this->expr->geoIntersects($geometry);
556
557 1
        return $this;
558
    }
559
560
    /**
561
     * Add $geoWithin criteria with a GeoJSON geometry to the query.
562
     *
563
     * The geometry parameter GeoJSON object or an array corresponding to the
564
     * geometry's JSON representation.
565
     *
566
     * @see Expr::geoWithin()
567
     * @see http://docs.mongodb.org/manual/reference/operator/geoWithin/
568
     *
569
     * @param array|Geometry $geometry
570
     */
571 1
    public function geoWithin($geometry) : self
572
    {
573 1
        $this->expr->geoWithin($geometry);
574
575 1
        return $this;
576
    }
577
578
    /**
579
     * Add $geoWithin criteria with a $box shape to the query.
580
     *
581
     * A rectangular polygon will be constructed from a pair of coordinates
582
     * corresponding to the bottom left and top right corners.
583
     *
584
     * Note: the $box operator only supports legacy coordinate pairs and 2d
585
     * indexes. This cannot be used with 2dsphere indexes and GeoJSON shapes.
586
     *
587
     * @see Expr::geoWithinBox()
588
     * @see http://docs.mongodb.org/manual/reference/operator/box/
589
     */
590 1
    public function geoWithinBox(float $x1, float $y1, float $x2, float $y2) : self
591
    {
592 1
        $this->expr->geoWithinBox($x1, $y1, $x2, $y2);
593
594 1
        return $this;
595
    }
596
597
    /**
598
     * Add $geoWithin criteria with a $center shape to the query.
599
     *
600
     * Note: the $center operator only supports legacy coordinate pairs and 2d
601
     * indexes. This cannot be used with 2dsphere indexes and GeoJSON shapes.
602
     *
603
     * @see Expr::geoWithinCenter()
604
     * @see http://docs.mongodb.org/manual/reference/operator/center/
605
     */
606 1
    public function geoWithinCenter(float $x, float $y, float $radius) : self
607
    {
608 1
        $this->expr->geoWithinCenter($x, $y, $radius);
609
610 1
        return $this;
611
    }
612
613
    /**
614
     * Add $geoWithin criteria with a $centerSphere shape to the query.
615
     *
616
     * Note: the $centerSphere operator supports both 2d and 2dsphere indexes.
617
     *
618
     * @see Expr::geoWithinCenterSphere()
619
     * @see http://docs.mongodb.org/manual/reference/operator/centerSphere/
620
     */
621 1
    public function geoWithinCenterSphere(float $x, float $y, float $radius) : self
622
    {
623 1
        $this->expr->geoWithinCenterSphere($x, $y, $radius);
624
625 1
        return $this;
626
    }
627
628
    /**
629
     * Add $geoWithin criteria with a $polygon shape to the query.
630
     *
631
     * Point coordinates are in x, y order (easting, northing for projected
632
     * coordinates, longitude, latitude for geographic coordinates).
633
     *
634
     * The last point coordinate is implicitly connected with the first.
635
     *
636
     * Note: the $polygon operator only supports legacy coordinate pairs and 2d
637
     * indexes. This cannot be used with 2dsphere indexes and GeoJSON shapes.
638
     *
639
     * @see Expr::geoWithinPolygon()
640
     * @see http://docs.mongodb.org/manual/reference/operator/polygon/
641
     *
642
     * @param array $point1    First point of the polygon
643
     * @param array $point2    Second point of the polygon
644
     * @param array $point3    Third point of the polygon
645
     * @param array ...$points Additional points of the polygon
646
     */
647 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...
648
    {
649 1
        $this->expr->geoWithinPolygon(...func_get_args());
650
651 1
        return $this;
652
    }
653
654
    /**
655
     * Return the expression's "new object".
656
     *
657
     * @see Expr::getNewObj()
658
     */
659 13
    public function getNewObj() : array
660
    {
661 13
        return $this->expr->getNewObj();
662
    }
663
664
    /**
665
     * Gets the Query executable.
666
     */
667 158
    public function getQuery(array $options = []) : Query
668
    {
669 158
        $documentPersister = $this->dm->getUnitOfWork()->getDocumentPersister($this->class->name);
670
671 158
        $query = $this->query;
672
673 158
        $query['query'] = $this->expr->getQuery();
674 158
        $query['query'] = $documentPersister->addDiscriminatorToPreparedQuery($query['query']);
675 158
        $query['query'] = $documentPersister->addFilterToPreparedQuery($query['query']);
676
677 158
        $query['newObj'] = $this->expr->getNewObj();
678
679 158
        if (isset($query['distinct'])) {
680 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...
681
        }
682
683 158
        if ($this->class->inheritanceType === ClassMetadata::INHERITANCE_TYPE_SINGLE_COLLECTION && ! empty($query['upsert']) &&
684 158
            (empty($query['query'][$this->class->discriminatorField]) || is_array($query['query'][$this->class->discriminatorField]))) {
685 1
            throw new InvalidArgumentException('Upsert query that is to be performed on discriminated document does not have single ' .
686 1
                'discriminator. Either not use base class or set \'' . $this->class->discriminatorField . '\' field manually.');
687
        }
688
689 157
        if (! empty($query['select'])) {
690 15
            $query['select'] = $documentPersister->prepareProjection($query['select']);
691 15
            if ($this->hydrate && $this->class->inheritanceType === ClassMetadata::INHERITANCE_TYPE_SINGLE_COLLECTION
692 15
                && ! isset($query['select'][$this->class->discriminatorField])) {
693
                $includeMode = 0 < count(array_filter($query['select'], static function ($mode) {
694 2
                    return $mode === 1;
695 2
                }));
696 2
                if ($includeMode && ! isset($query['select'][$this->class->discriminatorField])) {
697 1
                    $query['select'][$this->class->discriminatorField] = 1;
698
                }
699
            }
700
        }
701
702 157
        if (isset($query['sort'])) {
703 22
            $query['sort'] = $documentPersister->prepareSort($query['sort']);
704
        }
705
706 157
        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...
707 1
            $query['readPreference'] = new ReadPreference($this->class->readPreference, $this->class->readPreferenceTags);
708
        }
709
710 157
        return new Query(
711 157
            $this->dm,
712 157
            $this->class,
713 157
            $this->collection,
714 157
            $query,
715 157
            $options,
716 157
            $this->hydrate,
717 157
            $this->refresh,
718 157
            $this->primers,
719 157
            $this->readOnly,
720 157
            $this->rewindable
721
        );
722
    }
723
724
    /**
725
     * Return the expression's query criteria.
726
     *
727
     * @see Expr::getQuery()
728
     */
729 32
    public function getQueryArray() : array
730
    {
731 32
        return $this->expr->getQuery();
732
    }
733
734
    /**
735
     * Get the type of this query.
736
     */
737
    public function getType() : int
738
    {
739
        return $this->query['type'];
740
    }
741
742
    /**
743
     * Specify $gt criteria for the current field.
744
     *
745
     * @see Expr::gt()
746
     * @see http://docs.mongodb.org/manual/reference/operator/gt/
747
     *
748
     * @param mixed $value
749
     */
750 2
    public function gt($value) : self
751
    {
752 2
        $this->expr->gt($value);
753
754 2
        return $this;
755
    }
756
757
    /**
758
     * Specify $gte criteria for the current field.
759
     *
760
     * @see Expr::gte()
761
     * @see http://docs.mongodb.org/manual/reference/operator/gte/
762
     *
763
     * @param mixed $value
764
     */
765 2
    public function gte($value) : self
766
    {
767 2
        $this->expr->gte($value);
768
769 2
        return $this;
770
    }
771
772
    /**
773
     * Set the index hint for the query.
774
     *
775
     * @param array|string $index
776
     */
777
    public function hint($index) : self
778
    {
779
        $this->query['hint'] = $index;
780
781
        return $this;
782
    }
783
784 17
    public function hydrate(bool $bool = true) : self
785
    {
786 17
        $this->hydrate = $bool;
787
788 17
        return $this;
789
    }
790
791
    /**
792
     * Set the immortal cursor flag.
793
     */
794
    public function immortal(bool $bool = true) : self
795
    {
796
        $this->query['immortal'] = $bool;
797
798
        return $this;
799
    }
800
801
    /**
802
     * Specify $in criteria for the current field.
803
     *
804
     * @see Expr::in()
805
     * @see http://docs.mongodb.org/manual/reference/operator/in/
806
     */
807 24
    public function in(array $values) : self
808
    {
809 24
        $this->expr->in($values);
810
811 24
        return $this;
812
    }
813
814
    /**
815
     * Increment the current field.
816
     *
817
     * If the field does not exist, it will be set to this value.
818
     *
819
     * @see Expr::inc()
820
     * @see http://docs.mongodb.org/manual/reference/operator/inc/
821
     *
822
     * @param float|int $value
823
     */
824 6
    public function inc($value) : self
825
    {
826 6
        $this->expr->inc($value);
827
828 6
        return $this;
829
    }
830
831 6
    public function includesReferenceTo(object $document) : self
832
    {
833 6
        $this->expr->includesReferenceTo($document);
834
835 4
        return $this;
836
    }
837
838 1
    public function insert(?string $documentName = null) : self
839
    {
840 1
        $this->setDocumentName($documentName);
841 1
        $this->query['type'] = Query::TYPE_INSERT;
842
843 1
        return $this;
844
    }
845
846
    /**
847
     * Set the $language option for $text criteria.
848
     *
849
     * This method must be called after text().
850
     *
851
     * @see Expr::language()
852
     * @see http://docs.mongodb.org/manual/reference/operator/text/
853
     */
854 1
    public function language(string $language) : self
855
    {
856 1
        $this->expr->language($language);
857
858 1
        return $this;
859
    }
860
861
    /**
862
     * Set the limit for the query.
863
     *
864
     * This is only relevant for find queries and count commands.
865
     *
866
     * @see Query::prepareCursor()
867
     */
868 3
    public function limit(int $limit) : self
869
    {
870 3
        $this->query['limit'] = $limit;
871
872 3
        return $this;
873
    }
874
875
    /**
876
     * Specify $lt criteria for the current field.
877
     *
878
     * @see Expr::lte()
879
     * @see http://docs.mongodb.org/manual/reference/operator/lte/
880
     *
881
     * @param mixed $value
882
     */
883
    public function lt($value) : self
884
    {
885
        $this->expr->lt($value);
886
887
        return $this;
888
    }
889
890
    /**
891
     * Specify $lte criteria for the current field.
892
     *
893
     * @see Expr::lte()
894
     * @see http://docs.mongodb.org/manual/reference/operator/lte/
895
     *
896
     * @param mixed $value
897
     */
898
    public function lte($value) : self
899
    {
900
        $this->expr->lte($value);
901
902
        return $this;
903
    }
904
905
    /**
906
     * Updates the value of the field to a specified value if the specified value is greater than the current value of the field.
907
     *
908
     * @see Expr::max()
909
     * @see http://docs.mongodb.org/manual/reference/operator/update/max/
910
     *
911
     * @param mixed $value
912
     */
913 1
    public function max($value) : self
914
    {
915 1
        $this->expr->max($value);
916
917 1
        return $this;
918
    }
919
920
    /**
921
     * Specifies a cumulative time limit in milliseconds for processing operations on a cursor.
922
     */
923
    public function maxTimeMS(int $ms) : self
924
    {
925
        $this->query['maxTimeMS'] = $ms;
926
927
        return $this;
928
    }
929
930
    /**
931
     * Updates the value of the field to a specified value if the specified value is less than the current value of the field.
932
     *
933
     * @see Expr::min()
934
     * @see http://docs.mongodb.org/manual/reference/operator/update/min/
935
     *
936
     * @param mixed $value
937
     */
938 1
    public function min($value) : self
939
    {
940 1
        $this->expr->min($value);
941
942 1
        return $this;
943
    }
944
945
    /**
946
     * Specify $mod criteria for the current field.
947
     *
948
     * @see Expr::mod()
949
     * @see http://docs.mongodb.org/manual/reference/operator/mod/
950
     *
951
     * @param float|int $divisor
952
     * @param float|int $remainder
953
     */
954 1
    public function mod($divisor, $remainder = 0) : self
955
    {
956 1
        $this->expr->mod($divisor, $remainder);
957
958 1
        return $this;
959
    }
960
961
    /**
962
     * Multiply the current field.
963
     *
964
     * If the field does not exist, it will be set to 0.
965
     *
966
     * @see Expr::mul()
967
     * @see http://docs.mongodb.org/manual/reference/operator/mul/
968
     *
969
     * @param float|int $value
970
     */
971 1
    public function mul($value) : self
972
    {
973 1
        $this->expr->mul($value);
974
975 1
        return $this;
976
    }
977
978
    /**
979
     * Add $near criteria to the query.
980
     *
981
     * A GeoJSON point may be provided as the first and only argument for
982
     * 2dsphere queries. This single parameter may be a GeoJSON point object or
983
     * an array corresponding to the point's JSON representation.
984
     *
985
     * @see Expr::near()
986
     * @see http://docs.mongodb.org/manual/reference/operator/near/
987
     *
988
     * @param float|array|Point $x
989
     * @param float             $y
990
     */
991 1
    public function near($x, $y = null) : self
992
    {
993 1
        $this->expr->near($x, $y);
994
995 1
        return $this;
996
    }
997
998
    /**
999
     * Add $nearSphere criteria to the query.
1000
     *
1001
     * A GeoJSON point may be provided as the first and only argument for
1002
     * 2dsphere queries. This single parameter may be a GeoJSON point object or
1003
     * an array corresponding to the point's JSON representation.
1004
     *
1005
     * @see Expr::nearSphere()
1006
     * @see http://docs.mongodb.org/manual/reference/operator/nearSphere/
1007
     *
1008
     * @param float|array|Point $x
1009
     * @param float             $y
1010
     */
1011 1
    public function nearSphere($x, $y = null) : self
1012
    {
1013 1
        $this->expr->nearSphere($x, $y);
1014
1015 1
        return $this;
1016
    }
1017
1018
    /**
1019
     * Negates an expression for the current field.
1020
     *
1021
     * You can create a new expression using the {@link Builder::expr()} method.
1022
     *
1023
     * @see Expr::not()
1024
     * @see http://docs.mongodb.org/manual/reference/operator/not/
1025
     *
1026
     * @param array|Expr $expression
1027
     */
1028 3
    public function not($expression) : self
1029
    {
1030 3
        $this->expr->not($expression);
1031
1032 3
        return $this;
1033
    }
1034
1035
    /**
1036
     * Specify $ne criteria for the current field.
1037
     *
1038
     * @see Expr::notEqual()
1039
     * @see http://docs.mongodb.org/manual/reference/operator/ne/
1040
     *
1041
     * @param mixed $value
1042
     */
1043 4
    public function notEqual($value) : self
1044
    {
1045 4
        $this->expr->notEqual($value);
1046
1047 4
        return $this;
1048
    }
1049
1050
    /**
1051
     * Specify $nin criteria for the current field.
1052
     *
1053
     * @see Expr::notIn()
1054
     * @see http://docs.mongodb.org/manual/reference/operator/nin/
1055
     *
1056
     * @param array $values
1057
     */
1058 4
    public function notIn(array $values) : self
1059
    {
1060 4
        $this->expr->notIn($values);
1061
1062 4
        return $this;
1063
    }
1064
1065
    /**
1066
     * Remove the first element from the current array field.
1067
     *
1068
     * @see Expr::popFirst()
1069
     * @see http://docs.mongodb.org/manual/reference/operator/pop/
1070
     */
1071 3
    public function popFirst() : self
1072
    {
1073 3
        $this->expr->popFirst();
1074
1075 3
        return $this;
1076
    }
1077
1078
    /**
1079
     * Remove the last element from the current array field.
1080
     *
1081
     * @see Expr::popLast()
1082
     * @see http://docs.mongodb.org/manual/reference/operator/pop/
1083
     */
1084 2
    public function popLast() : self
1085
    {
1086 2
        $this->expr->popLast();
1087
1088 2
        return $this;
1089
    }
1090
1091
    /**
1092
     * Use a primer to eagerly load all references in the current field.
1093
     *
1094
     * If $primer is true or a callable is provided, referenced documents for
1095
     * this field will loaded into UnitOfWork immediately after the query is
1096
     * executed. This will avoid multiple queries due to lazy initialization of
1097
     * Proxy objects.
1098
     *
1099
     * If $primer is false, no priming will take place. That is also the default
1100
     * behavior.
1101
     *
1102
     * If a custom callable is used, its signature should conform to the default
1103
     * Closure defined in {@link ReferencePrimer::__construct()}.
1104
     *
1105
     * @param bool|callable $primer
1106
     *
1107
     * @throws InvalidArgumentException If $primer is not boolean or callable.
1108
     */
1109 22
    public function prime($primer = true) : self
1110
    {
1111 22
        if (! is_bool($primer) && ! is_callable($primer)) {
1112 1
            throw new InvalidArgumentException('$primer is not a boolean or callable');
1113
        }
1114
1115 21
        if ($primer === false) {
1116
            unset($this->primers[$this->currentField]);
1117
1118
            return $this;
1119
        }
1120
1121 21
        $this->primers[$this->currentField] = $primer;
1122
1123 21
        return $this;
1124
    }
1125
1126
    /**
1127
     * Remove all elements matching the given value or expression from the
1128
     * current array field.
1129
     *
1130
     * @see Expr::pull()
1131
     * @see http://docs.mongodb.org/manual/reference/operator/pull/
1132
     *
1133
     * @param mixed|Expr $valueOrExpression
1134
     */
1135 1
    public function pull($valueOrExpression) : self
1136
    {
1137 1
        $this->expr->pull($valueOrExpression);
1138
1139 1
        return $this;
1140
    }
1141
1142
    /**
1143
     * Remove all elements matching any of the given values from the current
1144
     * array field.
1145
     *
1146
     * @see Expr::pullAll()
1147
     * @see http://docs.mongodb.org/manual/reference/operator/pullAll/
1148
     */
1149 1
    public function pullAll(array $values) : self
1150
    {
1151 1
        $this->expr->pullAll($values);
1152
1153 1
        return $this;
1154
    }
1155
1156
    /**
1157
     * Append one or more values to the current array field.
1158
     *
1159
     * If the field does not exist, it will be set to an array containing the
1160
     * value(s) in the argument. If the field is not an array, the query
1161
     * will yield an error.
1162
     *
1163
     * Multiple values may be specified by providing an Expr object and using
1164
     * {@link Expr::each()}. {@link Expr::slice()} and {@link Expr::sort()} may
1165
     * also be used to limit and order array elements, respectively.
1166
     *
1167
     * @see Expr::push()
1168
     * @see http://docs.mongodb.org/manual/reference/operator/push/
1169
     * @see http://docs.mongodb.org/manual/reference/operator/each/
1170
     * @see http://docs.mongodb.org/manual/reference/operator/slice/
1171
     * @see http://docs.mongodb.org/manual/reference/operator/sort/
1172
     *
1173
     * @param mixed|Expr $valueOrExpression
1174
     */
1175 6
    public function push($valueOrExpression) : self
1176
    {
1177 6
        $this->expr->push($valueOrExpression);
1178
1179 6
        return $this;
1180
    }
1181
1182
    /**
1183
     * Specify $gte and $lt criteria for the current field.
1184
     *
1185
     * This method is shorthand for specifying $gte criteria on the lower bound
1186
     * and $lt criteria on the upper bound. The upper bound is not inclusive.
1187
     *
1188
     * @see Expr::range()
1189
     *
1190
     * @param mixed $start
1191
     * @param mixed $end
1192
     */
1193 3
    public function range($start, $end) : self
1194
    {
1195 3
        $this->expr->range($start, $end);
1196
1197 3
        return $this;
1198
    }
1199
1200 2
    public function readOnly(bool $bool = true) : self
1201
    {
1202 2
        $this->readOnly = $bool;
1203
1204 2
        return $this;
1205
    }
1206
1207 10
    public function references(object $document) : self
1208
    {
1209 10
        $this->expr->references($document);
1210
1211 8
        return $this;
1212
    }
1213
1214 5
    public function refresh(bool $bool = true) : self
1215
    {
1216 5
        $this->refresh = $bool;
1217
1218 5
        return $this;
1219
    }
1220
1221 1
    public function remove(?string $documentName = null) : self
1222
    {
1223 1
        $this->setDocumentName($documentName);
1224 1
        $this->query['type'] = Query::TYPE_REMOVE;
1225
1226 1
        return $this;
1227
    }
1228
1229
    /**
1230
     * Rename the current field.
1231
     *
1232
     * @see Expr::rename()
1233
     * @see http://docs.mongodb.org/manual/reference/operator/rename/
1234
     */
1235
    public function rename(string $name) : self
1236
    {
1237
        $this->expr->rename($name);
1238
1239
        return $this;
1240
    }
1241
1242 4
    public function returnNew(bool $bool = true) : self
1243
    {
1244 4
        $this->refresh(true);
1245 4
        $this->query['new'] = $bool;
1246
1247 4
        return $this;
1248
    }
1249
1250
    /**
1251
     * Set one or more fields to be included in the query projection.
1252
     *
1253
     * @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...
1254
     */
1255 19
    public function select($fieldName = null) : self
1256
    {
1257 19
        if (! isset($this->query['select'])) {
1258 18
            $this->query['select'] = [];
1259
        }
1260
1261 19
        $fieldNames = is_array($fieldName) ? $fieldName : func_get_args();
1262
1263 19
        foreach ($fieldNames as $fieldName) {
1264 16
            $this->query['select'][$fieldName] = 1;
1265
        }
1266
1267 19
        return $this;
1268
    }
1269
1270
    /**
1271
     * Select only matching embedded documents in an array field for the query
1272
     * projection.
1273
     *
1274
     * @see http://docs.mongodb.org/manual/reference/projection/elemMatch/
1275
     *
1276
     * @param array|Expr $expression
1277
     */
1278 2
    public function selectElemMatch(string $fieldName, $expression) : self
1279
    {
1280 2
        if ($expression instanceof Expr) {
1281 1
            $expression = $expression->getQuery();
1282
        }
1283 2
        $this->query['select'][$fieldName] = ['$elemMatch' => $expression];
1284
1285 2
        return $this;
1286
    }
1287
1288
    /**
1289
     * Select a metadata field for the query projection.
1290
     *
1291
     * @see http://docs.mongodb.org/master/reference/operator/projection/meta/
1292
     */
1293 3
    public function selectMeta(string $fieldName, string $metaDataKeyword) : self
1294
    {
1295 3
        $this->query['select'][$fieldName] = ['$meta' => $metaDataKeyword];
1296
1297 3
        return $this;
1298
    }
1299
1300
    /**
1301
     * Select a slice of an array field for the query projection.
1302
     *
1303
     * The $countOrSkip parameter has two very different meanings, depending on
1304
     * whether or not $limit is provided. See the MongoDB documentation for more
1305
     * information.
1306
     *
1307
     * @see http://docs.mongodb.org/manual/reference/projection/slice/
1308
     */
1309 3
    public function selectSlice(string $fieldName, int $countOrSkip, ?int $limit = null) : self
1310
    {
1311 3
        $slice = $countOrSkip;
1312 3
        if ($limit !== null) {
1313 2
            $slice = [$slice, $limit];
1314
        }
1315 3
        $this->query['select'][$fieldName] = ['$slice' => $slice];
1316
1317 3
        return $this;
1318
    }
1319
1320
    /**
1321
     * Set the current field to a value.
1322
     *
1323
     * This is only relevant for insert, update, or findAndUpdate queries. For
1324
     * update and findAndUpdate queries, the $atomic parameter will determine
1325
     * whether or not a $set operator is used.
1326
     *
1327
     * @see Expr::set()
1328
     * @see http://docs.mongodb.org/manual/reference/operator/set/
1329
     *
1330
     * @param mixed $value
1331
     */
1332 16
    public function set($value, bool $atomic = true) : self
1333
    {
1334 16
        $this->expr->set($value, $atomic && $this->query['type'] !== Query::TYPE_INSERT);
1335
1336 16
        return $this;
1337
    }
1338
1339
    /**
1340
     * Set the expression's "new object".
1341
     *
1342
     * @see Expr::setNewObj()
1343
     */
1344 1
    public function setNewObj(array $newObj) : self
1345
    {
1346 1
        $this->expr->setNewObj($newObj);
1347
1348 1
        return $this;
1349
    }
1350
1351
    /**
1352
     * Set the current field to the value if the document is inserted in an
1353
     * upsert operation.
1354
     *
1355
     * If an update operation with upsert: true results in an insert of a
1356
     * document, then $setOnInsert assigns the specified values to the fields in
1357
     * the document. If the update operation does not result in an insert,
1358
     * $setOnInsert does nothing.
1359
     *
1360
     * @see Expr::setOnInsert()
1361
     * @see https://docs.mongodb.org/manual/reference/operator/update/setOnInsert/
1362
     *
1363
     * @param mixed $value
1364
     */
1365 2
    public function setOnInsert($value) : self
1366
    {
1367 2
        $this->expr->setOnInsert($value);
1368
1369 2
        return $this;
1370
    }
1371
1372
    /**
1373
     * Set the read preference for the query.
1374
     *
1375
     * This is only relevant for read-only queries and commands.
1376
     *
1377
     * @see http://docs.mongodb.org/manual/core/read-preference/
1378
     */
1379 6
    public function setReadPreference(ReadPreference $readPreference) : self
1380
    {
1381 6
        $this->query['readPreference'] = $readPreference;
1382
1383 6
        return $this;
1384
    }
1385
1386 1
    public function setRewindable(bool $rewindable = true) : self
1387
    {
1388 1
        $this->rewindable = $rewindable;
1389
1390 1
        return $this;
1391
    }
1392
1393
    /**
1394
     * Set the expression's query criteria.
1395
     *
1396
     * @see Expr::setQuery()
1397
     */
1398 19
    public function setQueryArray(array $query) : self
1399
    {
1400 19
        $this->expr->setQuery($query);
1401
1402 19
        return $this;
1403
    }
1404
1405
    /**
1406
     * Specify $size criteria for the current field.
1407
     *
1408
     * @see Expr::size()
1409
     * @see http://docs.mongodb.org/manual/reference/operator/size/
1410
     */
1411 1
    public function size(int $size) : self
1412
    {
1413 1
        $this->expr->size($size);
1414
1415 1
        return $this;
1416
    }
1417
1418
    /**
1419
     * Set the skip for the query cursor.
1420
     *
1421
     * This is only relevant for find queries, or mapReduce queries that store
1422
     * results in an output collection and return a cursor.
1423
     *
1424
     * @see Query::prepareCursor()
1425
     */
1426
    public function skip(int $skip) : self
1427
    {
1428
        $this->query['skip'] = $skip;
1429
1430
        return $this;
1431
    }
1432
1433
    /**
1434
     * Set the snapshot cursor flag.
1435
     */
1436
    public function snapshot(bool $bool = true) : self
1437
    {
1438
        $this->query['snapshot'] = $bool;
1439
1440
        return $this;
1441
    }
1442
1443
    /**
1444
     * Set one or more field/order pairs on which to sort the query.
1445
     *
1446
     * If sorting by multiple fields, the first argument should be an array of
1447
     * field name (key) and order (value) pairs.
1448
     *
1449
     * @param array|string $fieldName Field name or array of field/order pairs
1450
     * @param int|string   $order     Field order (if one field is specified)
1451
     */
1452 30
    public function sort($fieldName, $order = 1) : self
1453
    {
1454 30
        if (! isset($this->query['sort'])) {
1455 30
            $this->query['sort'] = [];
1456
        }
1457
1458 30
        $fields = is_array($fieldName) ? $fieldName : [$fieldName => $order];
1459
1460 30
        foreach ($fields as $fieldName => $order) {
1461 16
            if (is_string($order)) {
1462 9
                $order = strtolower($order) === 'asc' ? 1 : -1;
1463
            }
1464 16
            $this->query['sort'][$fieldName] = (int) $order;
1465
        }
1466
1467 30
        return $this;
1468
    }
1469
1470
    /**
1471
     * Specify a projected metadata field on which to sort the query.
1472
     *
1473
     * Sort order is not configurable for metadata fields. Sorting by a metadata
1474
     * field requires the same field and $meta expression to exist in the
1475
     * projection document. This method will call {@link Builder::selectMeta()}
1476
     * if the field is not already set in the projection.
1477
     *
1478
     * @see http://docs.mongodb.org/master/reference/operator/projection/meta/#sort
1479
     */
1480 3
    public function sortMeta(string $fieldName, string $metaDataKeyword) : self
1481
    {
1482
        /* It's possible that the field is already projected without the $meta
1483
         * operator. We'll assume that the user knows what they're doing in that
1484
         * case and will not attempt to override the projection.
1485
         */
1486 3
        if (! isset($this->query['select'][$fieldName])) {
1487 2
            $this->selectMeta($fieldName, $metaDataKeyword);
1488
        }
1489
1490 3
        $this->query['sort'][$fieldName] = ['$meta' => $metaDataKeyword];
1491
1492 3
        return $this;
1493
    }
1494
1495
    /**
1496
     * Specify $text criteria for the current field.
1497
     *
1498
     * The $language option may be set with {@link Builder::language()}.
1499
     *
1500
     * @see Expr::text()
1501
     * @see http://docs.mongodb.org/master/reference/operator/query/text/
1502
     */
1503 1
    public function text(string $search) : self
1504
    {
1505 1
        $this->expr->text($search);
1506
1507 1
        return $this;
1508
    }
1509
1510
    /**
1511
     * Specify $type criteria for the current field.
1512
     *
1513
     * @see Expr::type()
1514
     * @see http://docs.mongodb.org/manual/reference/operator/type/
1515
     *
1516
     * @param int|string $type
1517
     */
1518 2
    public function type($type) : self
1519
    {
1520 2
        $this->expr->type($type);
1521
1522 2
        return $this;
1523
    }
1524
1525
    /**
1526
     * Unset the current field.
1527
     *
1528
     * The field will be removed from the document (not set to null).
1529
     *
1530
     * @see Expr::unsetField()
1531
     * @see http://docs.mongodb.org/manual/reference/operator/unset/
1532
     */
1533 4
    public function unsetField() : self
1534
    {
1535 4
        $this->expr->unsetField();
1536
1537 4
        return $this;
1538
    }
1539
1540 23
    public function updateOne(?string $documentName = null) : self
1541
    {
1542 23
        $this->setDocumentName($documentName);
1543 23
        $this->query['type']     = Query::TYPE_UPDATE;
1544 23
        $this->query['multiple'] = false;
1545
1546 23
        return $this;
1547
    }
1548
1549 4
    public function updateMany(?string $documentName = null) : self
1550
    {
1551 4
        $this->setDocumentName($documentName);
1552 4
        $this->query['type']     = Query::TYPE_UPDATE;
1553 4
        $this->query['multiple'] = true;
1554
1555 4
        return $this;
1556
    }
1557
1558
    /**
1559
     * Set the "upsert" option for an update or findAndUpdate query.
1560
     */
1561 7
    public function upsert(bool $bool = true) : self
1562
    {
1563 7
        $this->query['upsert'] = $bool;
1564
1565 7
        return $this;
1566
    }
1567
1568
    /**
1569
     * Specify a JavaScript expression to use for matching documents.
1570
     *
1571
     * @see Expr::where()
1572
     * @see http://docs.mongodb.org/manual/reference/operator/where/
1573
     *
1574
     * @param string|Javascript $javascript
1575
     */
1576 3
    public function where($javascript) : self
1577
    {
1578 3
        $this->expr->where($javascript);
1579
1580 3
        return $this;
1581
    }
1582
1583
    /**
1584
     * Get Discriminator Values
1585
     *
1586
     * @param string[] $classNames
1587
     *
1588
     * @throws InvalidArgumentException If the number of found collections > 1.
1589
     */
1590 2
    private function getDiscriminatorValues($classNames) : array
1591
    {
1592 2
        $discriminatorValues = [];
1593 2
        $collections         = [];
1594 2
        foreach ($classNames as $className) {
1595 2
            $class                 = $this->dm->getClassMetadata($className);
1596 2
            $discriminatorValues[] = $class->discriminatorValue;
1597 2
            $key                   = $this->dm->getDocumentDatabase($className)->getDatabaseName() . '.' . $class->getCollection();
1598 2
            $collections[$key]     = $key;
1599
        }
1600 2
        if (count($collections) > 1) {
1601 1
            throw new InvalidArgumentException('Documents involved are not all mapped to the same database collection.');
1602
        }
1603
1604 1
        return $discriminatorValues;
1605
    }
1606
1607
    /**
1608
     * @param string[]|string|null $documentName an array of document names or just one.
1609
     */
1610 284
    private function setDocumentName($documentName)
1611
    {
1612 284
        if (is_array($documentName)) {
1613 2
            $documentNames = $documentName;
1614 2
            $documentName  = $documentNames[0];
1615
1616 2
            $metadata            = $this->dm->getClassMetadata($documentName);
1617 2
            $discriminatorField  = $metadata->discriminatorField ?? ClassMetadata::DEFAULT_DISCRIMINATOR_FIELD;
1618 2
            $discriminatorValues = $this->getDiscriminatorValues($documentNames);
1619
1620
            // If a defaultDiscriminatorValue is set and it is among the discriminators being queries, add NULL to the list
1621 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...
1622 1
                $discriminatorValues[] = null;
1623
            }
1624
1625 1
            $this->field($discriminatorField)->in($discriminatorValues);
1626
        }
1627
1628 283
        if ($documentName === null) {
1629 42
            return;
1630
        }
1631
1632 283
        $this->collection = $this->dm->getDocumentCollection($documentName);
1633 283
        $this->class      = $this->dm->getClassMetadata($documentName);
1634
1635
        // Expr also needs to know
1636 283
        $this->expr->setClassMetadata($this->class);
1637 283
    }
1638
}
1639