Completed
Pull Request — master (#1956)
by Jeremy
22:51
created

Builder::mapReduce()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 1

Importance

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