Completed
Push — master ( 95608f...d78a07 )
by Andreas
14s queued 10s
created

Builder::references()   A

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