Completed
Push — master ( 1ec641...92e389 )
by Maciej
63:17 queued 60:26
created

Builder::prime()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 19
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 6.288

Importance

Changes 0
Metric Value
dl 0
loc 19
ccs 8
cts 10
cp 0.8
rs 8.8571
c 0
b 0
f 0
cc 6
eloc 10
nc 4
nop 1
crap 6.288
1
<?php
2
3
namespace Doctrine\ODM\MongoDB\Query;
4
5
use Doctrine\ODM\MongoDB\DocumentManager;
6
use Doctrine\ODM\MongoDB\Mapping\ClassMetadataInfo;
7
use GeoJson\Geometry\Geometry;
8
use GeoJson\Geometry\Point;
9
use MongoDB\Collection;
10
use MongoDB\Driver\ReadPreference;
11
12
/**
13
 * Query builder for ODM.
14
 *
15
 * @since       1.0
16
 */
17
class Builder
18
{
19
    /**
20
     * The DocumentManager instance for this query
21
     *
22
     * @var DocumentManager
23
     */
24
    private $dm;
25
26
    /**
27
     * The ClassMetadata instance.
28
     *
29
     * @var \Doctrine\ODM\MongoDB\Mapping\ClassMetadata
30
     */
31
    private $class;
32
33
    /**
34
     * The current field we are operating on.
35
     *
36
     * @todo Change this to private once ODM requires doctrine/mongodb 1.1+
37
     * @var string
38
     */
39
    protected $currentField;
40
41
    /**
42
     * Whether or not to hydrate the data to documents.
43
     *
44
     * @var boolean
45
     */
46
    private $hydrate = true;
47
48
    /**
49
     * Whether or not to refresh the data for documents that are already in the identity map.
50
     *
51
     * @var boolean
52
     */
53
    private $refresh = false;
54
55
    /**
56
     * Array of primer Closure instances.
57
     *
58
     * @var array
59
     */
60
    private $primers = array();
61
62
    /**
63
     * Whether or not to register documents in UnitOfWork.
64
     *
65
     * @var bool
66
     */
67
    private $readOnly;
68
69
    /**
70
     * The Collection instance.
71
     *
72
     * @var Collection
73
     */
74
    private $collection;
75
76
    /**
77
     * Array containing the query data.
78
     *
79
     * @var array
80
     */
81
    private $query = ['type' => Query::TYPE_FIND];
82
83
    /**
84
     * The Expr instance used for building this query.
85
     *
86
     * This object includes the query criteria and the "new object" used for
87
     * insert and update queries.
88
     *
89
     * @var Expr $expr
90
     */
91
    private $expr;
92
93
    /**
94
     * Construct a Builder
95
     *
96
     * @param DocumentManager $dm
97
     * @param string[]|string|null $documentName (optional) an array of document names, the document name, or none
98
     */
99 285
    public function __construct(DocumentManager $dm, $documentName = null)
100
    {
101 285
        $this->dm = $dm;
102 285
        $this->expr = new Expr($dm);
103 285
        if ($documentName !== null) {
104 277
            $this->setDocumentName($documentName);
105
        }
106 284
    }
107
108 1
    public function __clone()
109
    {
110 1
        $this->expr = clone $this->expr;
111 1
    }
112
113
    /**
114
     * Add one or more $and clauses to the current query.
115
     *
116
     * You can create a new expression using the {@link Builder::expr()} method.
117
     *
118
     * @see Expr::addAnd()
119
     * @see http://docs.mongodb.org/manual/reference/operator/and/
120
     * @param array|Expr $expression
121
     * @return $this
122
     */
123 4
    public function addAnd($expression /* , $expression2, ... */)
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...
124
    {
125 4
        $this->expr->addAnd(...func_get_args());
126 4
        return $this;
127
    }
128
129
    /**
130
     * Add one or more $nor clauses to the current query.
131
     *
132
     * You can create a new expression using the {@link Builder::expr()} method.
133
     *
134
     * @see Expr::addNor()
135
     * @see http://docs.mongodb.org/manual/reference/operator/nor/
136
     * @param array|Expr $expression
137
     * @return $this
138
     */
139 3
    public function addNor($expression /* , $expression2, ... */)
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...
140
    {
141 3
        $this->expr->addNor(...func_get_args());
142 3
        return $this;
143
    }
144
145
    /**
146
     * Add one or more $or clauses to the current query.
147
     *
148
     * You can create a new expression using the {@link Builder::expr()} method.
149
     *
150
     * @see Expr::addOr()
151
     * @see http://docs.mongodb.org/manual/reference/operator/or/
152
     * @param array|Expr $expression
153
     * @return $this
154
     */
155 6
    public function addOr($expression /* , $expression2, ... */)
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...
156
    {
157 6
        $this->expr->addOr(...func_get_args());
158 6
        return $this;
159
    }
160
161
    /**
162
     * Append one or more values to the current array field only if they do not
163
     * already exist in the array.
164
     *
165
     * If the field does not exist, it will be set to an array containing the
166
     * unique value(s) in the argument. If the field is not an array, the query
167
     * will yield an error.
168
     *
169
     * Multiple values may be specified by provided an Expr object and using
170
     * {@link Expr::each()}.
171
     *
172
     * @see Expr::addToSet()
173
     * @see http://docs.mongodb.org/manual/reference/operator/addToSet/
174
     * @see http://docs.mongodb.org/manual/reference/operator/each/
175
     * @param mixed|Expr $valueOrExpression
176
     * @return $this
177
     */
178 5
    public function addToSet($valueOrExpression)
179
    {
180 5
        $this->expr->addToSet($valueOrExpression);
181 5
        return $this;
182
    }
183
184
    /**
185
     * Specify $all criteria for the current field.
186
     *
187
     * @see Expr::all()
188
     * @see http://docs.mongodb.org/manual/reference/operator/all/
189
     * @param array $values
190
     * @return $this
191
     */
192 3
    public function all(array $values)
193
    {
194 3
        $this->expr->all($values);
195 3
        return $this;
196
    }
197
198
    /**
199
     * Apply a bitwise and operation on the current field.
200
     *
201
     * @see Expr::bitAnd()
202
     * @see http://docs.mongodb.org/manual/reference/operator/update/bit/
203
     * @param int $value
204
     * @return $this
205
     */
206 1
    public function bitAnd($value)
207
    {
208 1
        $this->expr->bitAnd($value);
209 1
        return $this;
210
    }
211
212
    /**
213
     * Apply a bitwise or operation on the current field.
214
     *
215
     * @see Expr::bitOr()
216
     * @see http://docs.mongodb.org/manual/reference/operator/update/bit/
217
     * @param int $value
218
     * @return $this
219
     */
220 1
    public function bitOr($value)
221
    {
222 1
        $this->expr->bitOr($value);
223 1
        return $this;
224
    }
225
226
    /**
227
     * Matches documents where all of the bit positions given by the query are
228
     * clear.
229
     *
230
     * @see Expr::bitsAllClear()
231
     * @see https://docs.mongodb.org/manual/reference/operator/query/bitsAllClear/
232
     * @param int|array|\MongoBinData $value
233
     * @return $this
234
     */
235 1
    public function bitsAllClear($value)
236
    {
237 1
        $this->expr->bitsAllClear($value);
238 1
        return $this;
239
    }
240
241
    /**
242
     * Matches documents where all of the bit positions given by the query are
243
     * set.
244
     *
245
     * @see Expr::bitsAllSet()
246
     * @see https://docs.mongodb.org/manual/reference/operator/query/bitsAllSet/
247
     * @param int|array|\MongoBinData $value
248
     * @return $this
249
     */
250 1
    public function bitsAllSet($value)
251
    {
252 1
        $this->expr->bitsAllSet($value);
253 1
        return $this;
254
    }
255
256
    /**
257
     * Matches documents where any of the bit positions given by the query are
258
     * clear.
259
     *
260
     * @see Expr::bitsAnyClear()
261
     * @see https://docs.mongodb.org/manual/reference/operator/query/bitsAnyClear/
262
     * @param int|array|\MongoBinData $value
263
     * @return $this
264
     */
265 1
    public function bitsAnyClear($value)
266
    {
267 1
        $this->expr->bitsAnyClear($value);
268 1
        return $this;
269
    }
270
271
    /**
272
     * Matches documents where any of the bit positions given by the query are
273
     * set.
274
     *
275
     * @see Expr::bitsAnySet()
276
     * @see https://docs.mongodb.org/manual/reference/operator/query/bitsAnySet/
277
     * @param int|array|\MongoBinData $value
278
     * @return $this
279
     */
280 1
    public function bitsAnySet($value)
281
    {
282 1
        $this->expr->bitsAnySet($value);
283 1
        return $this;
284
    }
285
286
    /**
287
     * Apply a bitwise xor operation on the current field.
288
     *
289
     * @see Expr::bitXor()
290
     * @see http://docs.mongodb.org/manual/reference/operator/update/bit/
291
     * @param int $value
292
     * @return $this
293
     */
294 1
    public function bitXor($value)
295
    {
296 1
        $this->expr->bitXor($value);
297 1
        return $this;
298
    }
299
300
    /**
301
     * A boolean flag to enable or disable case sensitive search for $text
302
     * criteria.
303
     *
304
     * This method must be called after text().
305
     *
306
     * @see Expr::caseSensitive()
307
     * @see http://docs.mongodb.org/manual/reference/operator/text/
308
     * @param bool $caseSensitive
309
     * @return $this
310
     * @throws \BadMethodCallException if the query does not already have $text criteria
311
     *
312
     * @since 1.3
313
     */
314 1
    public function caseSensitive($caseSensitive)
315
    {
316 1
        $this->expr->caseSensitive($caseSensitive);
317 1
        return $this;
318
    }
319
320
    /**
321
     * Associates a comment to any expression taking a query predicate.
322
     *
323
     * @see Expr::comment()
324
     * @see http://docs.mongodb.org/manual/reference/operator/query/comment/
325
     * @param string $comment
326
     * @return $this
327
     */
328 1
    public function comment($comment)
329
    {
330 1
        $this->expr->comment($comment);
331 1
        return $this;
332
    }
333
334
    /**
335
     * Change the query type to count.
336
     *
337
     * @return $this
338
     */
339
    public function count()
340
    {
341
        $this->query['type'] = Query::TYPE_COUNT;
342
        return $this;
343
    }
344
345
    /**
346
     * Sets the value of the current field to the current date, either as a date or a timestamp.
347
     *
348
     * @see Expr::currentDate()
349
     * @see http://docs.mongodb.org/manual/reference/operator/currentDate/
350
     * @param string $type
351
     * @return $this
352
     */
353 3
    public function currentDate($type = 'date')
354
    {
355 3
        $this->expr->currentDate($type);
356 2
        return $this;
357
    }
358
359
    /**
360
     * Return an array of information about the Builder state for debugging.
361
     *
362
     * The $name parameter may be used to return a specific key from the
363
     * internal $query array property. If omitted, the entire array will be
364
     * returned.
365
     *
366
     * @param string $name
367
     * @return mixed
368
     */
369 28
    public function debug($name = null)
370
    {
371 28
        return $name !== null ? $this->query[$name] : $this->query;
372
    }
373
374
    /**
375
     * A boolean flag to enable or disable diacritic sensitive search for $text
376
     * criteria.
377
     *
378
     * This method must be called after text().
379
     *
380
     * @see Builder::diacriticSensitive()
381
     * @see http://docs.mongodb.org/manual/reference/operator/text/
382
     * @param bool $diacriticSensitive
383
     * @return $this
384
     * @throws \BadMethodCallException if the query does not already have $text criteria
385
     *
386
     * @since 1.3
387
     */
388 1
    public function diacriticSensitive($diacriticSensitive)
389
    {
390 1
        $this->expr->diacriticSensitive($diacriticSensitive);
391 1
        return $this;
392
    }
393
394
    /**
395
     * Change the query type to a distinct command.
396
     *
397
     * @see http://docs.mongodb.org/manual/reference/command/distinct/
398
     * @param string $field
399
     * @return $this
400
     */
401 2
    public function distinct($field)
402
    {
403 2
        $this->query['type'] = Query::TYPE_DISTINCT;
404 2
        $this->query['distinct'] = $field;
405 2
        return $this;
406
    }
407
408
    /**
409
     * Set whether the query should return its result as an EagerCursor.
410
     *
411
     * @param boolean $bool
412
     * @return $this
413
     */
414 5
    public function eagerCursor($bool = true)
415
    {
416 5
        if ( ! $bool && ! empty($this->primers)) {
417 1
            throw new \BadMethodCallException("Can't set eagerCursor to false when using reference primers");
418
        }
419
420 4
        $this->query['eagerCursor'] = (boolean) $bool;
421 4
        return $this;
422
    }
423
424
    /**
425
     * Specify $elemMatch criteria for the current field.
426
     *
427
     * You can create a new expression using the {@link Builder::expr()} method.
428
     *
429
     * @see Expr::elemMatch()
430
     * @see http://docs.mongodb.org/manual/reference/operator/elemMatch/
431
     * @param array|Expr $expression
432
     * @return $this
433
     */
434 6
    public function elemMatch($expression)
435
    {
436 6
        $this->expr->elemMatch($expression);
437 6
        return $this;
438
    }
439
440
    /**
441
     * Specify an equality match for the current field.
442
     *
443
     * @see Expr::equals()
444
     * @param mixed $value
445
     * @return $this
446
     */
447 75
    public function equals($value)
448
    {
449 75
        $this->expr->equals($value);
450 75
        return $this;
451
    }
452
453
    /**
454
     * Set one or more fields to be excluded from the query projection.
455
     *
456
     * If fields have been selected for inclusion, only the "_id" field may be
457
     * excluded.
458
     *
459
     * @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...
460
     * @return $this
461
     */
462 6 View Code Duplication
    public function exclude($fieldName = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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
     * @param boolean $bool
483
     * @return $this
484
     */
485 5
    public function exists($bool)
486
    {
487 5
        $this->expr->exists((boolean) $bool);
488 5
        return $this;
489
    }
490
491
    /**
492
     * Create a new Expr instance that can be used as an expression with the Builder
493
     *
494
     * @return Expr $expr
495
     */
496 26
    public function expr()
497
    {
498 26
        $expr = new Expr($this->dm);
499 26
        $expr->setClassMetadata($this->class);
500
501 26
        return $expr;
502
    }
503
504
    /**
505
     * Set the current field to operate on.
506
     *
507
     * @param string $field
508
     * @return $this
509
     */
510 144
    public function field($field)
511
    {
512 144
        $this->currentField = $field;
513 144
        $this->expr->field((string) $field);
514
515 144
        return $this;
516
    }
517
518
    /**
519
     * Set the "finalize" option for a mapReduce or group command.
520
     *
521
     * @param string|\MongoCode $finalize
522
     * @return $this
523
     * @throws \BadMethodCallException if the query is not a mapReduce or group command
524
     */
525 2 View Code Duplication
    public function finalize($finalize)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
526
    {
527 2
        switch ($this->query['type']) {
528
            case Query::TYPE_MAP_REDUCE:
529 1
                $this->query['mapReduce']['options']['finalize'] = $finalize;
530 1
                break;
531
532
            case Query::TYPE_GROUP:
533
                $this->query['group']['options']['finalize'] = $finalize;
534
                break;
535
536
            default:
537 1
                throw new \BadMethodCallException('mapReduce(), map() or group() must be called before finalize()');
538
        }
539
540 1
        return $this;
541
    }
542
543
    /**
544
     * Change the query type to find and optionally set and change the class being queried.
545
     *
546
     * @param string $documentName
547
     * @return $this
548
     */
549 12
    public function find($documentName = null)
550
    {
551 12
        $this->setDocumentName($documentName);
552 12
        $this->query['type'] = Query::TYPE_FIND;
553
554 12
        return $this;
555
    }
556
557
    /**
558
     * @param string $documentName
559
     * @return $this
560
     */
561 1
    public function findAndRemove($documentName = null)
562
    {
563 1
        $this->setDocumentName($documentName);
564 1
        $this->query['type'] = Query::TYPE_FIND_AND_REMOVE;
565
566 1
        return $this;
567
    }
568
569
    /**
570
     * @param string $documentName
571
     * @return $this
572
     */
573 13
    public function findAndUpdate($documentName = null)
574
    {
575 13
        $this->setDocumentName($documentName);
576 13
        $this->query['type'] = Query::TYPE_FIND_AND_UPDATE;
577
578 13
        return $this;
579
    }
580
581
    /**
582
     * Add $geoIntersects criteria with a GeoJSON geometry to the query.
583
     *
584
     * The geometry parameter GeoJSON object or an array corresponding to the
585
     * geometry's JSON representation.
586
     *
587
     * @see Expr::geoIntersects()
588
     * @see http://docs.mongodb.org/manual/reference/operator/geoIntersects/
589
     * @param array|Geometry $geometry
590
     * @return $this
591
     */
592 1
    public function geoIntersects($geometry)
593
    {
594 1
        $this->expr->geoIntersects($geometry);
595 1
        return $this;
596
    }
597
598
    /**
599
     * Add $geoWithin criteria with a GeoJSON geometry to the query.
600
     *
601
     * The geometry parameter GeoJSON object or an array corresponding to the
602
     * geometry's JSON representation.
603
     *
604
     * @see Expr::geoWithin()
605
     * @see http://docs.mongodb.org/manual/reference/operator/geoWithin/
606
     * @param array|Geometry $geometry
607
     * @return $this
608
     */
609 1
    public function geoWithin($geometry)
610
    {
611 1
        $this->expr->geoWithin($geometry);
612 1
        return $this;
613
    }
614
615
    /**
616
     * Add $geoWithin criteria with a $box shape to the query.
617
     *
618
     * A rectangular polygon will be constructed from a pair of coordinates
619
     * corresponding to the bottom left and top right corners.
620
     *
621
     * Note: the $box operator only supports legacy coordinate pairs and 2d
622
     * indexes. This cannot be used with 2dsphere indexes and GeoJSON shapes.
623
     *
624
     * @see Expr::geoWithinBox()
625
     * @see http://docs.mongodb.org/manual/reference/operator/box/
626
     * @param float $x1
627
     * @param float $y1
628
     * @param float $x2
629
     * @param float $y2
630
     * @return $this
631
     */
632 1
    public function geoWithinBox($x1, $y1, $x2, $y2)
633
    {
634 1
        $this->expr->geoWithinBox($x1, $y1, $x2, $y2);
635 1
        return $this;
636
    }
637
638
    /**
639
     * Add $geoWithin criteria with a $center shape to the query.
640
     *
641
     * Note: the $center operator only supports legacy coordinate pairs and 2d
642
     * indexes. This cannot be used with 2dsphere indexes and GeoJSON shapes.
643
     *
644
     * @see Expr::geoWithinCenter()
645
     * @see http://docs.mongodb.org/manual/reference/operator/center/
646
     * @param float $x
647
     * @param float $y
648
     * @param float $radius
649
     * @return $this
650
     */
651 1
    public function geoWithinCenter($x, $y, $radius)
652
    {
653 1
        $this->expr->geoWithinCenter($x, $y, $radius);
654 1
        return $this;
655
    }
656
657
    /**
658
     * Add $geoWithin criteria with a $centerSphere shape to the query.
659
     *
660
     * Note: the $centerSphere operator supports both 2d and 2dsphere indexes.
661
     *
662
     * @see Expr::geoWithinCenterSphere()
663
     * @see http://docs.mongodb.org/manual/reference/operator/centerSphere/
664
     * @param float $x
665
     * @param float $y
666
     * @param float $radius
667
     * @return $this
668
     */
669 1
    public function geoWithinCenterSphere($x, $y, $radius)
670
    {
671 1
        $this->expr->geoWithinCenterSphere($x, $y, $radius);
672 1
        return $this;
673
    }
674
675
    /**
676
     * Add $geoWithin criteria with a $polygon shape to the query.
677
     *
678
     * Point coordinates are in x, y order (easting, northing for projected
679
     * coordinates, longitude, latitude for geographic coordinates).
680
     *
681
     * The last point coordinate is implicitly connected with the first.
682
     *
683
     * Note: the $polygon operator only supports legacy coordinate pairs and 2d
684
     * indexes. This cannot be used with 2dsphere indexes and GeoJSON shapes.
685
     *
686
     * @see Expr::geoWithinPolygon()
687
     * @see http://docs.mongodb.org/manual/reference/operator/polygon/
688
     * @param array $point,... Three or more point coordinate tuples
0 ignored issues
show
Bug introduced by
There is no parameter named $point,.... Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

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

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

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

Loading history...
689
     * @return $this
690
     */
691 1
    public function geoWithinPolygon(/* array($x1, $y1), ... */)
692
    {
693 1
        $this->expr->geoWithinPolygon(...func_get_args());
694 1
        return $this;
695
    }
696
697
    /**
698
     * Return the expression's "new object".
699
     *
700
     * @see Expr::getNewObj()
701
     * @return array
702
     */
703 13
    public function getNewObj()
704
    {
705 13
        return $this->expr->getNewObj();
706
    }
707
708
    /**
709
     * Gets the Query executable.
710
     *
711
     * @param array $options
712
     * @return Query $query
713
     */
714 150
    public function getQuery(array $options = array())
715
    {
716 150
        if ($this->query['type'] === Query::TYPE_MAP_REDUCE) {
717
            $this->hydrate = false;
718
        }
719
720 150
        $documentPersister = $this->dm->getUnitOfWork()->getDocumentPersister($this->class->name);
721
722 150
        $query = $this->query;
723
724 150
        $query['query'] = $this->expr->getQuery();
725 150
        $query['query'] = $documentPersister->addDiscriminatorToPreparedQuery($query['query']);
726 150
        $query['query'] = $documentPersister->addFilterToPreparedQuery($query['query']);
727
728 150
        $query['newObj'] = $this->expr->getNewObj();
729
730 150
        if (isset($query['distinct'])) {
731 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...
732
        }
733
734 150
        if ($this->class->inheritanceType === ClassMetadataInfo::INHERITANCE_TYPE_SINGLE_COLLECTION && ! empty($query['upsert']) &&
735 150
            (empty($query['query'][$this->class->discriminatorField]) || is_array($query['query'][$this->class->discriminatorField]))) {
736 1
            throw new \InvalidArgumentException('Upsert query that is to be performed on discriminated document does not have single ' .
737 1
                'discriminator. Either not use base class or set \'' . $this->class->discriminatorField . '\' field manually.');
738
        }
739
740 149
        if ( ! empty($query['select'])) {
741 14
            $query['select'] = $documentPersister->prepareProjection($query['select']);
742 14
            if ($this->hydrate && $this->class->inheritanceType === ClassMetadataInfo::INHERITANCE_TYPE_SINGLE_COLLECTION
743 14
                && ! isset($query['select'][$this->class->discriminatorField])) {
744
                $includeMode = 0 < count(array_filter($query['select'], function($mode) { return $mode == 1; }));
745 2
                if ($includeMode && ! isset($query['select'][$this->class->discriminatorField])) {
746 1
                    $query['select'][$this->class->discriminatorField] = 1;
747
                }
748
            }
749
        }
750
751 149
        if (isset($query['sort'])) {
752 23
            $query['sort'] = $documentPersister->prepareSort($query['sort']);
753
        }
754
755 149
        if ($this->class->readPreference && ! array_key_exists('readPreference', $query)) {
756 1
            $query['readPreference'] = new ReadPreference($this->class->readPreference, $this->class->readPreferenceTags);
757
        }
758
759 149
        return new Query(
760 149
            $this->dm,
761 149
            $this->class,
762 149
            $this->collection,
763 149
            $query,
764 149
            $options,
765 149
            $this->hydrate,
766 149
            $this->refresh,
767 149
            $this->primers,
768 149
            $this->readOnly
769
        );
770
    }
771
772
    /**
773
     * Return the expression's query criteria.
774
     *
775
     * @see Expr::getQuery()
776
     * @return array
777
     */
778 33
    public function getQueryArray()
779
    {
780 33
        return $this->expr->getQuery();
781
    }
782
783
    /**
784
     * Get the type of this query.
785
     *
786
     * @return integer $type
787
     */
788 2
    public function getType()
789
    {
790 2
        return $this->query['type'];
791
    }
792
793
    /**
794
     * Specify $gt criteria for the current field.
795
     *
796
     * @see Expr::gt()
797
     * @see http://docs.mongodb.org/manual/reference/operator/gt/
798
     * @param mixed $value
799
     * @return $this
800
     */
801 2
    public function gt($value)
802
    {
803 2
        $this->expr->gt($value);
804 2
        return $this;
805
    }
806
807
    /**
808
     * Specify $gte criteria for the current field.
809
     *
810
     * @see Expr::gte()
811
     * @see http://docs.mongodb.org/manual/reference/operator/gte/
812
     * @param mixed $value
813
     * @return $this
814
     */
815 2
    public function gte($value)
816
    {
817 2
        $this->expr->gte($value);
818 2
        return $this;
819
    }
820
821
    /**
822
     * Set the index hint for the query.
823
     *
824
     * @param array|string $index
825
     * @return $this
826
     */
827
    public function hint($index)
828
    {
829
        $this->query['hint'] = $index;
830
        return $this;
831
    }
832
833
    /**
834
     * @param bool $bool
835
     * @return $this
836
     */
837 17
    public function hydrate($bool = true)
838
    {
839 17
        $this->hydrate = $bool;
840 17
        return $this;
841
    }
842
843
    /**
844
     * Set the immortal cursor flag.
845
     *
846
     * @param boolean $bool
847
     * @return $this
848
     */
849
    public function immortal($bool = true)
850
    {
851
        $this->query['immortal'] = (boolean) $bool;
852
        return $this;
853
    }
854
855
    /**
856
     * Specify $in criteria for the current field.
857
     *
858
     * @see Expr::in()
859
     * @see http://docs.mongodb.org/manual/reference/operator/in/
860
     * @param array $values
861
     * @return $this
862
     */
863 24
    public function in(array $values)
864
    {
865 24
        $this->expr->in($values);
866 24
        return $this;
867
    }
868
869
    /**
870
     * Increment the current field.
871
     *
872
     * If the field does not exist, it will be set to this value.
873
     *
874
     * @see Expr::inc()
875
     * @see http://docs.mongodb.org/manual/reference/operator/inc/
876
     * @param float|integer $value
877
     * @return $this
878
     */
879 6
    public function inc($value)
880
    {
881 6
        $this->expr->inc($value);
882 6
        return $this;
883
    }
884
885
    /**
886
     * @param object $document
887
     * @return $this
888
     */
889 6
    public function includesReferenceTo($document)
890
    {
891 6
        $this->expr->includesReferenceTo($document);
892 4
        return $this;
893
    }
894
895
    /**
896
     * @param string $documentName
897
     * @return $this
898
     */
899 1
    public function insert($documentName = null)
900
    {
901 1
        $this->setDocumentName($documentName);
902 1
        $this->query['type'] = Query::TYPE_INSERT;
903
904 1
        return $this;
905
    }
906
907
    /**
908
     * Set the $language option for $text criteria.
909
     *
910
     * This method must be called after text().
911
     *
912
     * @see Expr::language()
913
     * @see http://docs.mongodb.org/manual/reference/operator/text/
914
     * @param string $language
915
     * @return $this
916
     */
917 1
    public function language($language)
918
    {
919 1
        $this->expr->language($language);
920 1
        return $this;
921
    }
922
923
    /**
924
     * Set the limit for the query.
925
     *
926
     * This is only relevant for find queries and geoNear and mapReduce
927
     * commands.
928
     *
929
     * @see Query::prepareCursor()
930
     * @param integer $limit
931
     * @return $this
932
     */
933 2
    public function limit($limit)
934
    {
935 2
        $this->query['limit'] = (integer) $limit;
936 2
        return $this;
937
    }
938
939
    /**
940
     * Specify $lt criteria for the current field.
941
     *
942
     * @see Expr::lte()
943
     * @see http://docs.mongodb.org/manual/reference/operator/lte/
944
     * @param mixed $value
945
     * @return $this
946
     */
947
    public function lt($value)
948
    {
949
        $this->expr->lt($value);
950
        return $this;
951
    }
952
953
    /**
954
     * Specify $lte criteria for the current field.
955
     *
956
     * @see Expr::lte()
957
     * @see http://docs.mongodb.org/manual/reference/operator/lte/
958
     * @param mixed $value
959
     * @return $this
960
     */
961
    public function lte($value)
962
    {
963
        $this->expr->lte($value);
964
        return $this;
965
    }
966
967
    /**
968
     * Change the query type to a mapReduce command.
969
     *
970
     * The "reduce" option is not specified when calling this method; it must
971
     * be set with the {@link Builder::reduce()} method.
972
     *
973
     * The "out" option defaults to inline, like {@link Builder::mapReduce()}.
974
     *
975
     * @see http://docs.mongodb.org/manual/reference/command/mapReduce/
976
     * @param string|\MongoCode $map
977
     * @return $this
978
     */
979 1
    public function map($map)
980
    {
981 1
        $this->query['type'] = Query::TYPE_MAP_REDUCE;
982 1
        $this->query['mapReduce'] = [
983 1
            'map' => $map,
984
            'reduce' => null,
985
            'out' => ['inline' => true],
986
            'options' => [],
987
        ];
988 1
        return $this;
989
    }
990
991
    /**
992
     * Change the query type to a mapReduce command.
993
     *
994
     * @see http://docs.mongodb.org/manual/reference/command/mapReduce/
995
     * @param string|\MongoCode $map
996
     * @param string|\MongoCode $reduce
997
     * @param array|string $out
998
     * @param array $options
999
     * @return $this
1000
     */
1001 1
    public function mapReduce($map, $reduce, $out = ['inline' => true], array $options = [])
1002
    {
1003 1
        $this->query['type'] = Query::TYPE_MAP_REDUCE;
1004 1
        $this->query['mapReduce'] = [
1005 1
            'map' => $map,
1006 1
            'reduce' => $reduce,
1007 1
            'out' => $out,
1008 1
            'options' => $options
1009
        ];
1010 1
        return $this;
1011
    }
1012
1013
    /**
1014
     * Set additional options for a mapReduce command.
1015
     *
1016
     * @param array $options
1017
     * @return $this
1018
     * @throws \BadMethodCallException if the query is not a mapReduce command
1019
     */
1020 1 View Code Duplication
    public function mapReduceOptions(array $options)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1021
    {
1022 1
        if ($this->query['type'] !== Query::TYPE_MAP_REDUCE) {
1023 1
            throw new \BadMethodCallException('This method requires a mapReduce command (call map() or mapReduce() first)');
1024
        }
1025
1026
        $this->query['mapReduce']['options'] = $options;
1027
        return $this;
1028
    }
1029
1030
    /**
1031
     * Updates the value of the field to a specified value if the specified value is greater than the current value of the field.
1032
     *
1033
     * @see Expr::max()
1034
     * @see http://docs.mongodb.org/manual/reference/operator/update/max/
1035
     * @param mixed $value
1036
     * @return $this
1037
     */
1038 1
    public function max($value)
1039
    {
1040 1
        $this->expr->max($value);
1041 1
        return $this;
1042
    }
1043
1044
    /**
1045
     * Specifies a cumulative time limit in milliseconds for processing operations on a cursor.
1046
     *
1047
     * @param int $ms
1048
     * @return $this
1049
     */
1050
    public function maxTimeMS($ms)
1051
    {
1052
        $this->query['maxTimeMS'] = $ms;
1053
        return $this;
1054
    }
1055
1056
    /**
1057
     * Updates the value of the field to a specified value if the specified value is less than the current value of the field.
1058
     *
1059
     * @see Expr::min()
1060
     * @see http://docs.mongodb.org/manual/reference/operator/update/min/
1061
     * @param mixed $value
1062
     * @return $this
1063
     */
1064 1
    public function min($value)
1065
    {
1066 1
        $this->expr->min($value);
1067 1
        return $this;
1068
    }
1069
1070
    /**
1071
     * Specify $mod criteria for the current field.
1072
     *
1073
     * @see Expr::mod()
1074
     * @see http://docs.mongodb.org/manual/reference/operator/mod/
1075
     * @param float|integer $divisor
1076
     * @param float|integer $remainder
1077
     * @return $this
1078
     */
1079 1
    public function mod($divisor, $remainder = 0)
1080
    {
1081 1
        $this->expr->mod($divisor, $remainder);
1082 1
        return $this;
1083
    }
1084
1085
    /**
1086
     * Multiply the current field.
1087
     *
1088
     * If the field does not exist, it will be set to 0.
1089
     *
1090
     * @see Expr::mul()
1091
     * @see http://docs.mongodb.org/manual/reference/operator/mul/
1092
     * @param float|integer $value
1093
     * @return $this
1094
     */
1095 1
    public function mul($value)
1096
    {
1097 1
        $this->expr->mul($value);
1098 1
        return $this;
1099
    }
1100
1101
    /**
1102
     * Add $near criteria to the query.
1103
     *
1104
     * A GeoJSON point may be provided as the first and only argument for
1105
     * 2dsphere queries. This single parameter may be a GeoJSON point object or
1106
     * an array corresponding to the point's JSON representation.
1107
     *
1108
     * @see Expr::near()
1109
     * @see http://docs.mongodb.org/manual/reference/operator/near/
1110
     * @param float|array|Point $x
1111
     * @param float $y
1112
     * @return $this
1113
     */
1114 1
    public function near($x, $y = null)
1115
    {
1116 1
        $this->expr->near($x, $y);
1117 1
        return $this;
1118
    }
1119
1120
    /**
1121
     * Add $nearSphere criteria to the query.
1122
     *
1123
     * A GeoJSON point may be provided as the first and only argument for
1124
     * 2dsphere queries. This single parameter may be a GeoJSON point object or
1125
     * an array corresponding to the point's JSON representation.
1126
     *
1127
     * @see Expr::nearSphere()
1128
     * @see http://docs.mongodb.org/manual/reference/operator/nearSphere/
1129
     * @param float|array|Point $x
1130
     * @param float $y
1131
     * @return $this
1132
     */
1133 1
    public function nearSphere($x, $y = null)
1134
    {
1135 1
        $this->expr->nearSphere($x, $y);
1136 1
        return $this;
1137
    }
1138
1139
    /**
1140
     * Negates an expression for the current field.
1141
     *
1142
     * You can create a new expression using the {@link Builder::expr()} method.
1143
     *
1144
     * @see Expr::not()
1145
     * @see http://docs.mongodb.org/manual/reference/operator/not/
1146
     * @param array|Expr $expression
1147
     * @return $this
1148
     */
1149 3
    public function not($expression)
1150
    {
1151 3
        $this->expr->not($expression);
1152 3
        return $this;
1153
    }
1154
1155
    /**
1156
     * Specify $ne criteria for the current field.
1157
     *
1158
     * @see Expr::notEqual()
1159
     * @see http://docs.mongodb.org/manual/reference/operator/ne/
1160
     * @param mixed $value
1161
     * @return $this
1162
     */
1163 4
    public function notEqual($value)
1164
    {
1165 4
        $this->expr->notEqual($value);
1166 4
        return $this;
1167
    }
1168
1169
    /**
1170
     * Specify $nin criteria for the current field.
1171
     *
1172
     * @see Expr::notIn()
1173
     * @see http://docs.mongodb.org/manual/reference/operator/nin/
1174
     * @param array $values
1175
     * @return $this
1176
     */
1177 4
    public function notIn(array $values)
1178
    {
1179 4
        $this->expr->notIn($values);
1180 4
        return $this;
1181
    }
1182
1183
    /**
1184
     * Set the "out" option for a mapReduce command.
1185
     *
1186
     * @param array|string $out
1187
     * @return $this
1188
     * @throws \BadMethodCallException if the query is not a mapReduce command
1189
     */
1190 1 View Code Duplication
    public function out($out)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1191
    {
1192 1
        if ($this->query['type'] !== Query::TYPE_MAP_REDUCE) {
1193 1
            throw new \BadMethodCallException('This method requires a mapReduce command (call map() or mapReduce() first)');
1194
        }
1195
1196
        $this->query['mapReduce']['out'] = $out;
1197
        return $this;
1198
    }
1199
1200
    /**
1201
     * Remove the first element from the current array field.
1202
     *
1203
     * @see Expr::popFirst()
1204
     * @see http://docs.mongodb.org/manual/reference/operator/pop/
1205
     * @return $this
1206
     */
1207 2
    public function popFirst()
1208
    {
1209 2
        $this->expr->popFirst();
1210 2
        return $this;
1211
    }
1212
1213
    /**
1214
     * Remove the last element from the current array field.
1215
     *
1216
     * @see Expr::popLast()
1217
     * @see http://docs.mongodb.org/manual/reference/operator/pop/
1218
     * @return $this
1219
     */
1220 1
    public function popLast()
1221
    {
1222 1
        $this->expr->popLast();
1223 1
        return $this;
1224
    }
1225
1226
    /**
1227
     * Use a primer to eagerly load all references in the current field.
1228
     *
1229
     * If $primer is true or a callable is provided, referenced documents for
1230
     * this field will loaded into UnitOfWork immediately after the query is
1231
     * executed. This will avoid multiple queries due to lazy initialization of
1232
     * Proxy objects.
1233
     *
1234
     * If $primer is false, no priming will take place. That is also the default
1235
     * behavior.
1236
     *
1237
     * If a custom callable is used, its signature should conform to the default
1238
     * Closure defined in {@link ReferencePrimer::__construct()}.
1239
     *
1240
     * @param boolean|callable $primer
1241
     * @return $this
1242
     * @throws \InvalidArgumentException If $primer is not boolean or callable
1243
     */
1244 25
    public function prime($primer = true)
1245
    {
1246 25
        if ( ! is_bool($primer) && ! is_callable($primer)) {
1247 1
            throw new \InvalidArgumentException('$primer is not a boolean or callable');
1248
        }
1249
1250 24
        if ($primer === false) {
1251
            unset($this->primers[$this->currentField]);
1252
1253
            return $this;
1254
        }
1255
1256 24
        if (array_key_exists('eagerCursor', $this->query) && !$this->query['eagerCursor']) {
1257 1
            throw new \BadMethodCallException("Can't call prime() when setting eagerCursor to false");
1258
        }
1259
1260 23
        $this->primers[$this->currentField] = $primer;
1261 23
        return $this;
1262
    }
1263
1264
    /**
1265
     * Remove all elements matching the given value or expression from the
1266
     * current array field.
1267
     *
1268
     * @see Expr::pull()
1269
     * @see http://docs.mongodb.org/manual/reference/operator/pull/
1270
     * @param mixed|Expr $valueOrExpression
1271
     * @return $this
1272
     */
1273 1
    public function pull($valueOrExpression)
1274
    {
1275 1
        $this->expr->pull($valueOrExpression);
1276 1
        return $this;
1277
    }
1278
1279
    /**
1280
     * Remove all elements matching any of the given values from the current
1281
     * array field.
1282
     *
1283
     * @see Expr::pullAll()
1284
     * @see http://docs.mongodb.org/manual/reference/operator/pullAll/
1285
     * @param array $values
1286
     * @return $this
1287
     */
1288 1
    public function pullAll(array $values)
1289
    {
1290 1
        $this->expr->pullAll($values);
1291 1
        return $this;
1292
    }
1293
1294
    /**
1295
     * Append one or more values to the current array field.
1296
     *
1297
     * If the field does not exist, it will be set to an array containing the
1298
     * value(s) in the argument. If the field is not an array, the query
1299
     * will yield an error.
1300
     *
1301
     * Multiple values may be specified by providing an Expr object and using
1302
     * {@link Expr::each()}. {@link Expr::slice()} and {@link Expr::sort()} may
1303
     * also be used to limit and order array elements, respectively.
1304
     *
1305
     * @see Expr::push()
1306
     * @see http://docs.mongodb.org/manual/reference/operator/push/
1307
     * @see http://docs.mongodb.org/manual/reference/operator/each/
1308
     * @see http://docs.mongodb.org/manual/reference/operator/slice/
1309
     * @see http://docs.mongodb.org/manual/reference/operator/sort/
1310
     * @param mixed|Expr $valueOrExpression
1311
     * @return $this
1312
     */
1313 6
    public function push($valueOrExpression)
1314
    {
1315 6
        $this->expr->push($valueOrExpression);
1316 6
        return $this;
1317
    }
1318
1319
    /**
1320
     * Specify $gte and $lt criteria for the current field.
1321
     *
1322
     * This method is shorthand for specifying $gte criteria on the lower bound
1323
     * and $lt criteria on the upper bound. The upper bound is not inclusive.
1324
     *
1325
     * @see Expr::range()
1326
     * @param mixed $start
1327
     * @param mixed $end
1328
     * @return $this
1329
     */
1330 3
    public function range($start, $end)
1331
    {
1332 3
        $this->expr->range($start, $end);
1333 3
        return $this;
1334
    }
1335
1336
    /**
1337
     * @param bool $bool
1338
     * @return $this
1339
     */
1340 2
    public function readOnly($bool = true)
1341
    {
1342 2
        $this->readOnly = $bool;
1343 2
        return $this;
1344
    }
1345
1346
    /**
1347
     * Set the "reduce" option for a mapReduce or group command.
1348
     *
1349
     * @param string|\MongoCode $reduce
1350
     * @return $this
1351
     * @throws \BadMethodCallException if the query is not a mapReduce or group command
1352
     */
1353 2 View Code Duplication
    public function reduce($reduce)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1354
    {
1355 2
        switch ($this->query['type']) {
1356
            case Query::TYPE_MAP_REDUCE:
1357 1
                $this->query['mapReduce']['reduce'] = $reduce;
1358 1
                break;
1359
1360
            case Query::TYPE_GROUP:
1361
                $this->query['group']['reduce'] = $reduce;
1362
                break;
1363
1364
            default:
1365 1
                throw new \BadMethodCallException('mapReduce(), map() or group() must be called before reduce()');
1366
        }
1367
1368 1
        return $this;
1369
    }
1370
1371
    /**
1372
     * @param object $document
1373
     * @return $this
1374
     */
1375 10
    public function references($document)
1376
    {
1377 10
        $this->expr->references($document);
1378 8
        return $this;
1379
    }
1380
1381
    /**
1382
     * @param bool $bool
1383
     * @return $this
1384
     */
1385 5
    public function refresh($bool = true)
1386
    {
1387 5
        $this->refresh = $bool;
1388 5
        return $this;
1389
    }
1390
1391
    /**
1392
     * @param string $documentName
1393
     * @return $this
1394
     */
1395 1
    public function remove($documentName = null)
1396
    {
1397 1
        $this->setDocumentName($documentName);
1398 1
        $this->query['type'] = Query::TYPE_REMOVE;
1399
1400 1
        return $this;
1401
    }
1402
1403
    /**
1404
     * Rename the current field.
1405
     *
1406
     * @see Expr::rename()
1407
     * @see http://docs.mongodb.org/manual/reference/operator/rename/
1408
     * @param string $name
1409
     * @return $this
1410
     */
1411
    public function rename($name)
1412
    {
1413
        $this->expr->rename($name);
1414
        return $this;
1415
    }
1416
1417
    /**
1418
     * @param bool $bool
1419
     * @return $this
1420
     */
1421 4
    public function returnNew($bool = true)
1422
    {
1423 4
        $this->refresh(true);
1424 4
        $this->query['new'] = (boolean) $bool;
1425
1426 4
        return $this;
1427
    }
1428
1429
    /**
1430
     * Set one or more fields to be included in the query projection.
1431
     *
1432
     * @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...
1433
     * @return $this
1434
     */
1435 19 View Code Duplication
    public function select($fieldName = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1436
    {
1437 19
        if ( ! isset($this->query['select'])) {
1438 18
            $this->query['select'] = [];
1439
        }
1440
1441 19
        $fieldNames = is_array($fieldName) ? $fieldName : func_get_args();
1442
1443 19
        foreach ($fieldNames as $fieldName) {
1444 16
            $this->query['select'][$fieldName] = 1;
1445
        }
1446
1447 19
        return $this;
1448
    }
1449
1450
    /**
1451
     * Select only matching embedded documents in an array field for the query
1452
     * projection.
1453
     *
1454
     * @see http://docs.mongodb.org/manual/reference/projection/elemMatch/
1455
     * @param string $fieldName
1456
     * @param array|Expr $expression
1457
     * @return $this
1458
     */
1459 2
    public function selectElemMatch($fieldName, $expression)
1460
    {
1461 2
        if ($expression instanceof Expr) {
1462 1
            $expression = $expression->getQuery();
1463
        }
1464 2
        $this->query['select'][$fieldName] = ['$elemMatch' => $expression];
1465 2
        return $this;
1466
    }
1467
1468
    /**
1469
     * Select a metadata field for the query projection.
1470
     *
1471
     * @see http://docs.mongodb.org/master/reference/operator/projection/meta/
1472
     * @param string $fieldName
1473
     * @param string $metaDataKeyword
1474
     * @return $this
1475
     */
1476 2
    public function selectMeta($fieldName, $metaDataKeyword)
1477
    {
1478 2
        $this->query['select'][$fieldName] = ['$meta' => $metaDataKeyword];
1479 2
        return $this;
1480
    }
1481
1482
    /**
1483
     * Select a slice of an array field for the query projection.
1484
     *
1485
     * The $countOrSkip parameter has two very different meanings, depending on
1486
     * whether or not $limit is provided. See the MongoDB documentation for more
1487
     * information.
1488
     *
1489
     * @see http://docs.mongodb.org/manual/reference/projection/slice/
1490
     * @param string $fieldName
1491
     * @param integer $countOrSkip Count parameter, or skip if limit is specified
1492
     * @param integer $limit       Limit parameter used in conjunction with skip
1493
     * @return $this
1494
     */
1495 3
    public function selectSlice($fieldName, $countOrSkip, $limit = null)
1496
    {
1497 3
        $slice = $countOrSkip;
1498 3
        if ($limit !== null) {
1499 2
            $slice = [$slice, $limit];
1500
        }
1501 3
        $this->query['select'][$fieldName] = ['$slice' => $slice];
1502 3
        return $this;
1503
    }
1504
1505
    /**
1506
     * Set the current field to a value.
1507
     *
1508
     * This is only relevant for insert, update, or findAndUpdate queries. For
1509
     * update and findAndUpdate queries, the $atomic parameter will determine
1510
     * whether or not a $set operator is used.
1511
     *
1512
     * @see Expr::set()
1513
     * @see http://docs.mongodb.org/manual/reference/operator/set/
1514
     * @param mixed $value
1515
     * @param boolean $atomic
1516
     * @return $this
1517
     */
1518 16
    public function set($value, $atomic = true)
1519
    {
1520 16
        $this->expr->set($value, $atomic && $this->query['type'] !== Query::TYPE_INSERT);
1521 16
        return $this;
1522
    }
1523
1524
    /**
1525
     * Set the expression's "new object".
1526
     *
1527
     * @see Expr::setNewObj()
1528
     * @param array $newObj
1529
     * @return $this
1530
     */
1531
    public function setNewObj(array $newObj)
1532
    {
1533
        $this->expr->setNewObj($newObj);
1534
        return $this;
1535
    }
1536
1537
    /**
1538
     * Set the current field to the value if the document is inserted in an
1539
     * upsert operation.
1540
     *
1541
     * If an update operation with upsert: true results in an insert of a
1542
     * document, then $setOnInsert assigns the specified values to the fields in
1543
     * the document. If the update operation does not result in an insert,
1544
     * $setOnInsert does nothing.
1545
     *
1546
     * @see Expr::setOnInsert()
1547
     * @see https://docs.mongodb.org/manual/reference/operator/update/setOnInsert/
1548
     * @param mixed $value
1549
     * @return $this
1550
     */
1551 2
    public function setOnInsert($value)
1552
    {
1553 2
        $this->expr->setOnInsert($value);
1554 2
        return $this;
1555
    }
1556
1557
    /**
1558
     * Set the read preference for the query.
1559
     *
1560
     * This is only relevant for read-only queries and commands.
1561
     *
1562
     * @see http://docs.mongodb.org/manual/core/read-preference/
1563
     * @param ReadPreference $readPreference
1564
     * @return $this
1565
     */
1566 6
    public function setReadPreference(ReadPreference $readPreference)
1567
    {
1568 6
        $this->query['readPreference'] = $readPreference;
1569 6
        return $this;
1570
    }
1571
1572
    /**
1573
     * Set the expression's query criteria.
1574
     *
1575
     * @see Expr::setQuery()
1576
     * @param array $query
1577
     * @return $this
1578
     */
1579 18
    public function setQueryArray(array $query)
1580
    {
1581 18
        $this->expr->setQuery($query);
1582 18
        return $this;
1583
    }
1584
1585
    /**
1586
     * Specify $size criteria for the current field.
1587
     *
1588
     * @see Expr::size()
1589
     * @see http://docs.mongodb.org/manual/reference/operator/size/
1590
     * @param integer $size
1591
     * @return $this
1592
     */
1593 1
    public function size($size)
1594
    {
1595 1
        $this->expr->size((integer) $size);
1596 1
        return $this;
1597
    }
1598
1599
    /**
1600
     * Set the skip for the query cursor.
1601
     *
1602
     * This is only relevant for find queries, or mapReduce queries that store
1603
     * results in an output collecton and return a cursor.
1604
     *
1605
     * @see Query::prepareCursor()
1606
     * @param integer $skip
1607
     * @return $this
1608
     */
1609
    public function skip($skip)
1610
    {
1611
        $this->query['skip'] = (integer) $skip;
1612
        return $this;
1613
    }
1614
1615
    /**
1616
     * Set the snapshot cursor flag.
1617
     *
1618
     * @param boolean $bool
1619
     * @return $this
1620
     */
1621
    public function snapshot($bool = true)
1622
    {
1623
        $this->query['snapshot'] = (boolean) $bool;
1624
        return $this;
1625
    }
1626
1627
    /**
1628
     * Set one or more field/order pairs on which to sort the query.
1629
     *
1630
     * If sorting by multiple fields, the first argument should be an array of
1631
     * field name (key) and order (value) pairs.
1632
     *
1633
     * @param array|string $fieldName Field name or array of field/order pairs
1634
     * @param int|string $order       Field order (if one field is specified)
1635
     * @return $this
1636
     */
1637 31
    public function sort($fieldName, $order = 1)
1638
    {
1639 31
        if ( ! isset($this->query['sort'])) {
1640 31
            $this->query['sort'] = [];
1641
        }
1642
1643 31
        $fields = is_array($fieldName) ? $fieldName : [$fieldName => $order];
1644
1645 31
        foreach ($fields as $fieldName => $order) {
1646 14
            if (is_string($order)) {
1647 9
                $order = strtolower($order) === 'asc' ? 1 : -1;
1648
            }
1649 14
            $this->query['sort'][$fieldName] = (integer) $order;
1650
        }
1651
1652 31
        return $this;
1653
    }
1654
1655
    /**
1656
     * Specify a projected metadata field on which to sort the query.
1657
     *
1658
     * Sort order is not configurable for metadata fields. Sorting by a metadata
1659
     * field requires the same field and $meta expression to exist in the
1660
     * projection document. This method will call {@link Builder::selectMeta()}
1661
     * if the field is not already set in the projection.
1662
     *
1663
     * @see http://docs.mongodb.org/master/reference/operator/projection/meta/#sort
1664
     * @param string $fieldName       Field name of the projected metadata
1665
     * @param string $metaDataKeyword
1666
     * @return $this
1667
     */
1668 2
    public function sortMeta($fieldName, $metaDataKeyword)
1669
    {
1670
        /* It's possible that the field is already projected without the $meta
1671
         * operator. We'll assume that the user knows what they're doing in that
1672
         * case and will not attempt to override the projection.
1673
         */
1674 2
        if ( ! isset($this->query['select'][$fieldName])) {
1675 1
            $this->selectMeta($fieldName, $metaDataKeyword);
1676
        }
1677
1678 2
        $this->query['sort'][$fieldName] = ['$meta' => $metaDataKeyword];
1679
1680 2
        return $this;
1681
    }
1682
1683
    /**
1684
     * Specify $text criteria for the current field.
1685
     *
1686
     * The $language option may be set with {@link Builder::language()}.
1687
     *
1688
     * @see Expr::text()
1689
     * @see http://docs.mongodb.org/master/reference/operator/query/text/
1690
     * @param string $search
1691
     * @return $this
1692
     */
1693 1
    public function text($search)
1694
    {
1695 1
        $this->expr->text($search);
1696 1
        return $this;
1697
    }
1698
1699
    /**
1700
     * Specify $type criteria for the current field.
1701
     *
1702
     * @see Expr::type()
1703
     * @see http://docs.mongodb.org/manual/reference/operator/type/
1704
     * @param integer $type
1705
     * @return $this
1706
     */
1707 2
    public function type($type)
1708
    {
1709 2
        $this->expr->type($type);
1710 2
        return $this;
1711
    }
1712
1713
    /**
1714
     * Unset the current field.
1715
     *
1716
     * The field will be removed from the document (not set to null).
1717
     *
1718
     * @see Expr::unsetField()
1719
     * @see http://docs.mongodb.org/manual/reference/operator/unset/
1720
     * @return $this
1721
     */
1722 4
    public function unsetField()
1723
    {
1724 4
        $this->expr->unsetField();
1725 4
        return $this;
1726
    }
1727
1728
    /**
1729
     * @param string $documentName
1730
     * @return $this
1731
     */
1732 21 View Code Duplication
    public function updateOne($documentName = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1733
    {
1734 21
        $this->setDocumentName($documentName);
1735 21
        $this->query['type'] = Query::TYPE_UPDATE;
1736 21
        $this->query['multiple'] = false;
1737
1738 21
        return $this;
1739
    }
1740
1741
    /**
1742
     * @param string $documentName
1743
     * @return $this
1744
     */
1745 3 View Code Duplication
    public function updateMany($documentName = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1746
    {
1747 3
        $this->setDocumentName($documentName);
1748 3
        $this->query['type'] = Query::TYPE_UPDATE;
1749 3
        $this->query['multiple'] = true;
1750
1751 3
        return $this;
1752
    }
1753
1754
    /**
1755
     * Set the "upsert" option for an update or findAndUpdate query.
1756
     *
1757
     * @param boolean $bool
1758
     * @return $this
1759
     */
1760 7
    public function upsert($bool = true)
1761
    {
1762 7
        $this->query['upsert'] = (boolean) $bool;
1763 7
        return $this;
1764
    }
1765
1766
    /**
1767
     * Specify a JavaScript expression to use for matching documents.
1768
     *
1769
     * @see Expr::where()
1770
     * @see http://docs.mongodb.org/manual/reference/operator/where/
1771
     * @param string|\MongoCode $javascript
1772
     * @return $this
1773
     */
1774 3
    public function where($javascript)
1775
    {
1776 3
        $this->expr->where($javascript);
1777 3
        return $this;
1778
    }
1779
1780
    /**
1781
     * Get Discriminator Values
1782
     *
1783
     * @param \Traversable $classNames
1784
     * @return array an array of discriminatorValues (mixed type)
1785
     * @throws \InvalidArgumentException if the number of found collections > 1
1786
     */
1787 2
    private function getDiscriminatorValues($classNames)
1788
    {
1789 2
        $discriminatorValues = array();
1790 2
        $collections = array();
1791 2
        foreach ($classNames as $className) {
1792 2
            $class = $this->dm->getClassMetadata($className);
1793 2
            $discriminatorValues[] = $class->discriminatorValue;
1794 2
            $key = $this->dm->getDocumentDatabase($className)->getDatabaseName() . '.' . $class->getCollection();
1795 2
            $collections[$key] = $key;
1796
        }
1797 2
        if (count($collections) > 1) {
1798 1
            throw new \InvalidArgumentException('Documents involved are not all mapped to the same database collection.');
1799
        }
1800 1
        return $discriminatorValues;
1801
    }
1802
1803
    /**
1804
     * @param string[]|string $documentName an array of document names or just one.
1805
     */
1806 284
    private function setDocumentName($documentName)
1807
    {
1808 284
        if (is_array($documentName)) {
1809 2
            $documentNames = $documentName;
1810 2
            $documentName = $documentNames[0];
1811
1812 2
            $metadata = $this->dm->getClassMetadata($documentName);
1813 2
            $discriminatorField = $metadata->discriminatorField;
1814 2
            $discriminatorValues = $this->getDiscriminatorValues($documentNames);
0 ignored issues
show
Documentation introduced by
$documentNames is of type array, but the function expects a object<Traversable>.

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...
1815
1816
            // If a defaultDiscriminatorValue is set and it is among the discriminators being queries, add NULL to the list
1817 1 View Code Duplication
            if ($metadata->defaultDiscriminatorValue && array_search($metadata->defaultDiscriminatorValue, $discriminatorValues) !== false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1818 1
                $discriminatorValues[] = null;
1819
            }
1820
1821 1
            $this->field($discriminatorField)->in($discriminatorValues);
1822
        }
1823
1824 283
        if ($documentName !== null) {
1825 283
            $this->collection = $this->dm->getDocumentCollection($documentName);
1826 283
            $this->class = $this->dm->getClassMetadata($documentName);
1827
1828
            // Expr also needs to know
1829 283
            $this->expr->setClassMetadata($this->class);
1830
        }
1831 283
    }
1832
}
1833