Completed
Pull Request — master (#1720)
by Maciej
15:21 queued 01:21
created

Builder::multiple()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 5
ccs 0
cts 3
cp 0
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 1
crap 2
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->slaveOkay) {
0 ignored issues
show
Deprecated Code introduced by
The property Doctrine\ODM\MongoDB\Map...etadataInfo::$slaveOkay has been deprecated with message: in version 1.2 and will be removed in 2.0.

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
756
            $query['slaveOkay'] = $this->class->slaveOkay;
0 ignored issues
show
Deprecated Code introduced by
The property Doctrine\ODM\MongoDB\Map...etadataInfo::$slaveOkay has been deprecated with message: in version 1.2 and will be removed in 2.0.

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
757
        }
758
759 149
        if ($this->class->readPreference && ! array_key_exists('readPreference', $query)) {
760 1
            $query['readPreference'] = new ReadPreference($this->class->readPreference, $this->class->readPreferenceTags);
761
        }
762
763 149
        return new Query(
764 149
            $this->dm,
765 149
            $this->class,
766 149
            $this->collection,
767 149
            $query,
768 149
            $options,
769 149
            $this->hydrate,
770 149
            $this->refresh,
771 149
            $this->primers,
772 149
            $this->readOnly
773
        );
774
    }
775
776
    /**
777
     * Return the expression's query criteria.
778
     *
779
     * @see Expr::getQuery()
780
     * @return array
781
     */
782 33
    public function getQueryArray()
783
    {
784 33
        return $this->expr->getQuery();
785
    }
786
787
    /**
788
     * Get the type of this query.
789
     *
790
     * @return integer $type
791
     */
792 2
    public function getType()
793
    {
794 2
        return $this->query['type'];
795
    }
796
797
    /**
798
     * Specify $gt criteria for the current field.
799
     *
800
     * @see Expr::gt()
801
     * @see http://docs.mongodb.org/manual/reference/operator/gt/
802
     * @param mixed $value
803
     * @return $this
804
     */
805 2
    public function gt($value)
806
    {
807 2
        $this->expr->gt($value);
808 2
        return $this;
809
    }
810
811
    /**
812
     * Specify $gte criteria for the current field.
813
     *
814
     * @see Expr::gte()
815
     * @see http://docs.mongodb.org/manual/reference/operator/gte/
816
     * @param mixed $value
817
     * @return $this
818
     */
819 2
    public function gte($value)
820
    {
821 2
        $this->expr->gte($value);
822 2
        return $this;
823
    }
824
825
    /**
826
     * Set the index hint for the query.
827
     *
828
     * @param array|string $index
829
     * @return $this
830
     */
831
    public function hint($index)
832
    {
833
        $this->query['hint'] = $index;
834
        return $this;
835
    }
836
837
    /**
838
     * @param bool $bool
839
     * @return $this
840
     */
841 17
    public function hydrate($bool = true)
842
    {
843 17
        $this->hydrate = $bool;
844 17
        return $this;
845
    }
846
847
    /**
848
     * Set the immortal cursor flag.
849
     *
850
     * @param boolean $bool
851
     * @return $this
852
     */
853
    public function immortal($bool = true)
854
    {
855
        $this->query['immortal'] = (boolean) $bool;
856
        return $this;
857
    }
858
859
    /**
860
     * Specify $in criteria for the current field.
861
     *
862
     * @see Expr::in()
863
     * @see http://docs.mongodb.org/manual/reference/operator/in/
864
     * @param array $values
865
     * @return $this
866
     */
867 24
    public function in(array $values)
868
    {
869 24
        $this->expr->in($values);
870 24
        return $this;
871
    }
872
873
    /**
874
     * Increment the current field.
875
     *
876
     * If the field does not exist, it will be set to this value.
877
     *
878
     * @see Expr::inc()
879
     * @see http://docs.mongodb.org/manual/reference/operator/inc/
880
     * @param float|integer $value
881
     * @return $this
882
     */
883 6
    public function inc($value)
884
    {
885 6
        $this->expr->inc($value);
886 6
        return $this;
887
    }
888
889
    /**
890
     * @param object $document
891
     * @return $this
892
     */
893 6
    public function includesReferenceTo($document)
894
    {
895 6
        $this->expr->includesReferenceTo($document);
896 4
        return $this;
897
    }
898
899
    /**
900
     * @param string $documentName
901
     * @return $this
902
     */
903 1
    public function insert($documentName = null)
904
    {
905 1
        $this->setDocumentName($documentName);
906 1
        $this->query['type'] = Query::TYPE_INSERT;
907
908 1
        return $this;
909
    }
910
911
    /**
912
     * Set the $language option for $text criteria.
913
     *
914
     * This method must be called after text().
915
     *
916
     * @see Expr::language()
917
     * @see http://docs.mongodb.org/manual/reference/operator/text/
918
     * @param string $language
919
     * @return $this
920
     */
921 1
    public function language($language)
922
    {
923 1
        $this->expr->language($language);
924 1
        return $this;
925
    }
926
927
    /**
928
     * Set the limit for the query.
929
     *
930
     * This is only relevant for find queries and geoNear and mapReduce
931
     * commands.
932
     *
933
     * @see Query::prepareCursor()
934
     * @param integer $limit
935
     * @return $this
936
     */
937 2
    public function limit($limit)
938
    {
939 2
        $this->query['limit'] = (integer) $limit;
940 2
        return $this;
941
    }
942
943
    /**
944
     * Specify $lt criteria for the current field.
945
     *
946
     * @see Expr::lte()
947
     * @see http://docs.mongodb.org/manual/reference/operator/lte/
948
     * @param mixed $value
949
     * @return $this
950
     */
951
    public function lt($value)
952
    {
953
        $this->expr->lt($value);
954
        return $this;
955
    }
956
957
    /**
958
     * Specify $lte criteria for the current field.
959
     *
960
     * @see Expr::lte()
961
     * @see http://docs.mongodb.org/manual/reference/operator/lte/
962
     * @param mixed $value
963
     * @return $this
964
     */
965
    public function lte($value)
966
    {
967
        $this->expr->lte($value);
968
        return $this;
969
    }
970
971
    /**
972
     * Change the query type to a mapReduce command.
973
     *
974
     * The "reduce" option is not specified when calling this method; it must
975
     * be set with the {@link Builder::reduce()} method.
976
     *
977
     * The "out" option defaults to inline, like {@link Builder::mapReduce()}.
978
     *
979
     * @see http://docs.mongodb.org/manual/reference/command/mapReduce/
980
     * @param string|\MongoCode $map
981
     * @return $this
982
     */
983 1
    public function map($map)
984
    {
985 1
        $this->query['type'] = Query::TYPE_MAP_REDUCE;
986 1
        $this->query['mapReduce'] = [
987 1
            'map' => $map,
988
            'reduce' => null,
989
            'out' => ['inline' => true],
990
            'options' => [],
991
        ];
992 1
        return $this;
993
    }
994
995
    /**
996
     * Change the query type to a mapReduce command.
997
     *
998
     * @see http://docs.mongodb.org/manual/reference/command/mapReduce/
999
     * @param string|\MongoCode $map
1000
     * @param string|\MongoCode $reduce
1001
     * @param array|string $out
1002
     * @param array $options
1003
     * @return $this
1004
     */
1005 1
    public function mapReduce($map, $reduce, $out = ['inline' => true], array $options = [])
1006
    {
1007 1
        $this->query['type'] = Query::TYPE_MAP_REDUCE;
1008 1
        $this->query['mapReduce'] = [
1009 1
            'map' => $map,
1010 1
            'reduce' => $reduce,
1011 1
            'out' => $out,
1012 1
            'options' => $options
1013
        ];
1014 1
        return $this;
1015
    }
1016
1017
    /**
1018
     * Set additional options for a mapReduce command.
1019
     *
1020
     * @param array $options
1021
     * @return $this
1022
     * @throws \BadMethodCallException if the query is not a mapReduce command
1023
     */
1024 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...
1025
    {
1026 1
        if ($this->query['type'] !== Query::TYPE_MAP_REDUCE) {
1027 1
            throw new \BadMethodCallException('This method requires a mapReduce command (call map() or mapReduce() first)');
1028
        }
1029
1030
        $this->query['mapReduce']['options'] = $options;
1031
        return $this;
1032
    }
1033
1034
    /**
1035
     * Updates the value of the field to a specified value if the specified value is greater than the current value of the field.
1036
     *
1037
     * @see Expr::max()
1038
     * @see http://docs.mongodb.org/manual/reference/operator/update/max/
1039
     * @param mixed $value
1040
     * @return $this
1041
     */
1042 1
    public function max($value)
1043
    {
1044 1
        $this->expr->max($value);
1045 1
        return $this;
1046
    }
1047
1048
    /**
1049
     * Specifies a cumulative time limit in milliseconds for processing operations on a cursor.
1050
     *
1051
     * @param int $ms
1052
     * @return $this
1053
     */
1054
    public function maxTimeMS($ms)
1055
    {
1056
        $this->query['maxTimeMS'] = $ms;
1057
        return $this;
1058
    }
1059
1060
    /**
1061
     * Updates the value of the field to a specified value if the specified value is less than the current value of the field.
1062
     *
1063
     * @see Expr::min()
1064
     * @see http://docs.mongodb.org/manual/reference/operator/update/min/
1065
     * @param mixed $value
1066
     * @return $this
1067
     */
1068 1
    public function min($value)
1069
    {
1070 1
        $this->expr->min($value);
1071 1
        return $this;
1072
    }
1073
1074
    /**
1075
     * Specify $mod criteria for the current field.
1076
     *
1077
     * @see Expr::mod()
1078
     * @see http://docs.mongodb.org/manual/reference/operator/mod/
1079
     * @param float|integer $divisor
1080
     * @param float|integer $remainder
1081
     * @return $this
1082
     */
1083 1
    public function mod($divisor, $remainder = 0)
1084
    {
1085 1
        $this->expr->mod($divisor, $remainder);
1086 1
        return $this;
1087
    }
1088
1089
    /**
1090
     * Multiply the current field.
1091
     *
1092
     * If the field does not exist, it will be set to 0.
1093
     *
1094
     * @see Expr::mul()
1095
     * @see http://docs.mongodb.org/manual/reference/operator/mul/
1096
     * @param float|integer $value
1097
     * @return $this
1098
     */
1099 1
    public function mul($value)
1100
    {
1101 1
        $this->expr->mul($value);
1102 1
        return $this;
1103
    }
1104
1105
    /**
1106
     * Add $near criteria to the query.
1107
     *
1108
     * A GeoJSON point may be provided as the first and only argument for
1109
     * 2dsphere queries. This single parameter may be a GeoJSON point object or
1110
     * an array corresponding to the point's JSON representation.
1111
     *
1112
     * @see Expr::near()
1113
     * @see http://docs.mongodb.org/manual/reference/operator/near/
1114
     * @param float|array|Point $x
1115
     * @param float $y
1116
     * @return $this
1117
     */
1118 1
    public function near($x, $y = null)
1119
    {
1120 1
        $this->expr->near($x, $y);
1121 1
        return $this;
1122
    }
1123
1124
    /**
1125
     * Add $nearSphere criteria to the query.
1126
     *
1127
     * A GeoJSON point may be provided as the first and only argument for
1128
     * 2dsphere queries. This single parameter may be a GeoJSON point object or
1129
     * an array corresponding to the point's JSON representation.
1130
     *
1131
     * @see Expr::nearSphere()
1132
     * @see http://docs.mongodb.org/manual/reference/operator/nearSphere/
1133
     * @param float|array|Point $x
1134
     * @param float $y
1135
     * @return $this
1136
     */
1137 1
    public function nearSphere($x, $y = null)
1138
    {
1139 1
        $this->expr->nearSphere($x, $y);
1140 1
        return $this;
1141
    }
1142
1143
    /**
1144
     * Negates an expression for the current field.
1145
     *
1146
     * You can create a new expression using the {@link Builder::expr()} method.
1147
     *
1148
     * @see Expr::not()
1149
     * @see http://docs.mongodb.org/manual/reference/operator/not/
1150
     * @param array|Expr $expression
1151
     * @return $this
1152
     */
1153 3
    public function not($expression)
1154
    {
1155 3
        $this->expr->not($expression);
1156 3
        return $this;
1157
    }
1158
1159
    /**
1160
     * Specify $ne criteria for the current field.
1161
     *
1162
     * @see Expr::notEqual()
1163
     * @see http://docs.mongodb.org/manual/reference/operator/ne/
1164
     * @param mixed $value
1165
     * @return $this
1166
     */
1167 4
    public function notEqual($value)
1168
    {
1169 4
        $this->expr->notEqual($value);
1170 4
        return $this;
1171
    }
1172
1173
    /**
1174
     * Specify $nin criteria for the current field.
1175
     *
1176
     * @see Expr::notIn()
1177
     * @see http://docs.mongodb.org/manual/reference/operator/nin/
1178
     * @param array $values
1179
     * @return $this
1180
     */
1181 4
    public function notIn(array $values)
1182
    {
1183 4
        $this->expr->notIn($values);
1184 4
        return $this;
1185
    }
1186
1187
    /**
1188
     * Set the "out" option for a mapReduce command.
1189
     *
1190
     * @param array|string $out
1191
     * @return $this
1192
     * @throws \BadMethodCallException if the query is not a mapReduce command
1193
     */
1194 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...
1195
    {
1196 1
        if ($this->query['type'] !== Query::TYPE_MAP_REDUCE) {
1197 1
            throw new \BadMethodCallException('This method requires a mapReduce command (call map() or mapReduce() first)');
1198
        }
1199
1200
        $this->query['mapReduce']['out'] = $out;
1201
        return $this;
1202
    }
1203
1204
    /**
1205
     * Remove the first element from the current array field.
1206
     *
1207
     * @see Expr::popFirst()
1208
     * @see http://docs.mongodb.org/manual/reference/operator/pop/
1209
     * @return $this
1210
     */
1211 2
    public function popFirst()
1212
    {
1213 2
        $this->expr->popFirst();
1214 2
        return $this;
1215
    }
1216
1217
    /**
1218
     * Remove the last element from the current array field.
1219
     *
1220
     * @see Expr::popLast()
1221
     * @see http://docs.mongodb.org/manual/reference/operator/pop/
1222
     * @return $this
1223
     */
1224 1
    public function popLast()
1225
    {
1226 1
        $this->expr->popLast();
1227 1
        return $this;
1228
    }
1229
1230
    /**
1231
     * Use a primer to eagerly load all references in the current field.
1232
     *
1233
     * If $primer is true or a callable is provided, referenced documents for
1234
     * this field will loaded into UnitOfWork immediately after the query is
1235
     * executed. This will avoid multiple queries due to lazy initialization of
1236
     * Proxy objects.
1237
     *
1238
     * If $primer is false, no priming will take place. That is also the default
1239
     * behavior.
1240
     *
1241
     * If a custom callable is used, its signature should conform to the default
1242
     * Closure defined in {@link ReferencePrimer::__construct()}.
1243
     *
1244
     * @param boolean|callable $primer
1245
     * @return $this
1246
     * @throws \InvalidArgumentException If $primer is not boolean or callable
1247
     */
1248 25
    public function prime($primer = true)
1249
    {
1250 25
        if ( ! is_bool($primer) && ! is_callable($primer)) {
1251 1
            throw new \InvalidArgumentException('$primer is not a boolean or callable');
1252
        }
1253
1254 24
        if ($primer === false) {
1255
            unset($this->primers[$this->currentField]);
1256
1257
            return $this;
1258
        }
1259
1260 24
        if (array_key_exists('eagerCursor', $this->query) && !$this->query['eagerCursor']) {
1261 1
            throw new \BadMethodCallException("Can't call prime() when setting eagerCursor to false");
1262
        }
1263
1264 23
        $this->primers[$this->currentField] = $primer;
1265 23
        return $this;
1266
    }
1267
1268
    /**
1269
     * Remove all elements matching the given value or expression from the
1270
     * current array field.
1271
     *
1272
     * @see Expr::pull()
1273
     * @see http://docs.mongodb.org/manual/reference/operator/pull/
1274
     * @param mixed|Expr $valueOrExpression
1275
     * @return $this
1276
     */
1277 1
    public function pull($valueOrExpression)
1278
    {
1279 1
        $this->expr->pull($valueOrExpression);
1280 1
        return $this;
1281
    }
1282
1283
    /**
1284
     * Remove all elements matching any of the given values from the current
1285
     * array field.
1286
     *
1287
     * @see Expr::pullAll()
1288
     * @see http://docs.mongodb.org/manual/reference/operator/pullAll/
1289
     * @param array $values
1290
     * @return $this
1291
     */
1292 1
    public function pullAll(array $values)
1293
    {
1294 1
        $this->expr->pullAll($values);
1295 1
        return $this;
1296
    }
1297
1298
    /**
1299
     * Append one or more values to the current array field.
1300
     *
1301
     * If the field does not exist, it will be set to an array containing the
1302
     * value(s) in the argument. If the field is not an array, the query
1303
     * will yield an error.
1304
     *
1305
     * Multiple values may be specified by providing an Expr object and using
1306
     * {@link Expr::each()}. {@link Expr::slice()} and {@link Expr::sort()} may
1307
     * also be used to limit and order array elements, respectively.
1308
     *
1309
     * @see Expr::push()
1310
     * @see http://docs.mongodb.org/manual/reference/operator/push/
1311
     * @see http://docs.mongodb.org/manual/reference/operator/each/
1312
     * @see http://docs.mongodb.org/manual/reference/operator/slice/
1313
     * @see http://docs.mongodb.org/manual/reference/operator/sort/
1314
     * @param mixed|Expr $valueOrExpression
1315
     * @return $this
1316
     */
1317 6
    public function push($valueOrExpression)
1318
    {
1319 6
        $this->expr->push($valueOrExpression);
1320 6
        return $this;
1321
    }
1322
1323
    /**
1324
     * Specify $gte and $lt criteria for the current field.
1325
     *
1326
     * This method is shorthand for specifying $gte criteria on the lower bound
1327
     * and $lt criteria on the upper bound. The upper bound is not inclusive.
1328
     *
1329
     * @see Expr::range()
1330
     * @param mixed $start
1331
     * @param mixed $end
1332
     * @return $this
1333
     */
1334 3
    public function range($start, $end)
1335
    {
1336 3
        $this->expr->range($start, $end);
1337 3
        return $this;
1338
    }
1339
1340
    /**
1341
     * @param bool $bool
1342
     * @return $this
1343
     */
1344 2
    public function readOnly($bool = true)
1345
    {
1346 2
        $this->readOnly = $bool;
1347 2
        return $this;
1348
    }
1349
1350
    /**
1351
     * Set the "reduce" option for a mapReduce or group command.
1352
     *
1353
     * @param string|\MongoCode $reduce
1354
     * @return $this
1355
     * @throws \BadMethodCallException if the query is not a mapReduce or group command
1356
     */
1357 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...
1358
    {
1359 2
        switch ($this->query['type']) {
1360
            case Query::TYPE_MAP_REDUCE:
1361 1
                $this->query['mapReduce']['reduce'] = $reduce;
1362 1
                break;
1363
1364
            case Query::TYPE_GROUP:
1365
                $this->query['group']['reduce'] = $reduce;
1366
                break;
1367
1368
            default:
1369 1
                throw new \BadMethodCallException('mapReduce(), map() or group() must be called before reduce()');
1370
        }
1371
1372 1
        return $this;
1373
    }
1374
1375
    /**
1376
     * @param object $document
1377
     * @return $this
1378
     */
1379 10
    public function references($document)
1380
    {
1381 10
        $this->expr->references($document);
1382 8
        return $this;
1383
    }
1384
1385
    /**
1386
     * @param bool $bool
1387
     * @return $this
1388
     */
1389 5
    public function refresh($bool = true)
1390
    {
1391 5
        $this->refresh = $bool;
1392 5
        return $this;
1393
    }
1394
1395
    /**
1396
     * @param string $documentName
1397
     * @return $this
1398
     */
1399 1
    public function remove($documentName = null)
1400
    {
1401 1
        $this->setDocumentName($documentName);
1402 1
        $this->query['type'] = Query::TYPE_REMOVE;
1403
1404 1
        return $this;
1405
    }
1406
1407
    /**
1408
     * Rename the current field.
1409
     *
1410
     * @see Expr::rename()
1411
     * @see http://docs.mongodb.org/manual/reference/operator/rename/
1412
     * @param string $name
1413
     * @return $this
1414
     */
1415
    public function rename($name)
1416
    {
1417
        $this->expr->rename($name);
1418
        return $this;
1419
    }
1420
1421
    /**
1422
     * @param bool $bool
1423
     * @return $this
1424
     */
1425 4
    public function returnNew($bool = true)
1426
    {
1427 4
        $this->refresh(true);
1428 4
        $this->query['new'] = (boolean) $bool;
1429
1430 4
        return $this;
1431
    }
1432
1433
    /**
1434
     * Set one or more fields to be included in the query projection.
1435
     *
1436
     * @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...
1437
     * @return $this
1438
     */
1439 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...
1440
    {
1441 19
        if ( ! isset($this->query['select'])) {
1442 18
            $this->query['select'] = [];
1443
        }
1444
1445 19
        $fieldNames = is_array($fieldName) ? $fieldName : func_get_args();
1446
1447 19
        foreach ($fieldNames as $fieldName) {
1448 16
            $this->query['select'][$fieldName] = 1;
1449
        }
1450
1451 19
        return $this;
1452
    }
1453
1454
    /**
1455
     * Select only matching embedded documents in an array field for the query
1456
     * projection.
1457
     *
1458
     * @see http://docs.mongodb.org/manual/reference/projection/elemMatch/
1459
     * @param string $fieldName
1460
     * @param array|Expr $expression
1461
     * @return $this
1462
     */
1463 2
    public function selectElemMatch($fieldName, $expression)
1464
    {
1465 2
        if ($expression instanceof Expr) {
1466 1
            $expression = $expression->getQuery();
1467
        }
1468 2
        $this->query['select'][$fieldName] = ['$elemMatch' => $expression];
1469 2
        return $this;
1470
    }
1471
1472
    /**
1473
     * Select a metadata field for the query projection.
1474
     *
1475
     * @see http://docs.mongodb.org/master/reference/operator/projection/meta/
1476
     * @param string $fieldName
1477
     * @param string $metaDataKeyword
1478
     * @return $this
1479
     */
1480 2
    public function selectMeta($fieldName, $metaDataKeyword)
1481
    {
1482 2
        $this->query['select'][$fieldName] = ['$meta' => $metaDataKeyword];
1483 2
        return $this;
1484
    }
1485
1486
    /**
1487
     * Select a slice of an array field for the query projection.
1488
     *
1489
     * The $countOrSkip parameter has two very different meanings, depending on
1490
     * whether or not $limit is provided. See the MongoDB documentation for more
1491
     * information.
1492
     *
1493
     * @see http://docs.mongodb.org/manual/reference/projection/slice/
1494
     * @param string $fieldName
1495
     * @param integer $countOrSkip Count parameter, or skip if limit is specified
1496
     * @param integer $limit       Limit parameter used in conjunction with skip
1497
     * @return $this
1498
     */
1499 3
    public function selectSlice($fieldName, $countOrSkip, $limit = null)
1500
    {
1501 3
        $slice = $countOrSkip;
1502 3
        if ($limit !== null) {
1503 2
            $slice = [$slice, $limit];
1504
        }
1505 3
        $this->query['select'][$fieldName] = ['$slice' => $slice];
1506 3
        return $this;
1507
    }
1508
1509
    /**
1510
     * Set the current field to a value.
1511
     *
1512
     * This is only relevant for insert, update, or findAndUpdate queries. For
1513
     * update and findAndUpdate queries, the $atomic parameter will determine
1514
     * whether or not a $set operator is used.
1515
     *
1516
     * @see Expr::set()
1517
     * @see http://docs.mongodb.org/manual/reference/operator/set/
1518
     * @param mixed $value
1519
     * @param boolean $atomic
1520
     * @return $this
1521
     */
1522 16
    public function set($value, $atomic = true)
1523
    {
1524 16
        $this->expr->set($value, $atomic && $this->query['type'] !== Query::TYPE_INSERT);
1525 16
        return $this;
1526
    }
1527
1528
    /**
1529
     * Set the expression's "new object".
1530
     *
1531
     * @see Expr::setNewObj()
1532
     * @param array $newObj
1533
     * @return $this
1534
     */
1535
    public function setNewObj(array $newObj)
1536
    {
1537
        $this->expr->setNewObj($newObj);
1538
        return $this;
1539
    }
1540
1541
    /**
1542
     * Set the current field to the value if the document is inserted in an
1543
     * upsert operation.
1544
     *
1545
     * If an update operation with upsert: true results in an insert of a
1546
     * document, then $setOnInsert assigns the specified values to the fields in
1547
     * the document. If the update operation does not result in an insert,
1548
     * $setOnInsert does nothing.
1549
     *
1550
     * @see Expr::setOnInsert()
1551
     * @see https://docs.mongodb.org/manual/reference/operator/update/setOnInsert/
1552
     * @param mixed $value
1553
     * @return $this
1554
     */
1555 2
    public function setOnInsert($value)
1556
    {
1557 2
        $this->expr->setOnInsert($value);
1558 2
        return $this;
1559
    }
1560
1561
    /**
1562
     * Set the read preference for the query.
1563
     *
1564
     * This is only relevant for read-only queries and commands.
1565
     *
1566
     * @see http://docs.mongodb.org/manual/core/read-preference/
1567
     * @param ReadPreference $readPreference
1568
     * @return $this
1569
     */
1570 6
    public function setReadPreference(ReadPreference $readPreference)
1571
    {
1572 6
        $this->query['readPreference'] = $readPreference;
1573 6
        return $this;
1574
    }
1575
1576
    /**
1577
     * Set the expression's query criteria.
1578
     *
1579
     * @see Expr::setQuery()
1580
     * @param array $query
1581
     * @return $this
1582
     */
1583 18
    public function setQueryArray(array $query)
1584
    {
1585 18
        $this->expr->setQuery($query);
1586 18
        return $this;
1587
    }
1588
1589
    /**
1590
     * Specify $size criteria for the current field.
1591
     *
1592
     * @see Expr::size()
1593
     * @see http://docs.mongodb.org/manual/reference/operator/size/
1594
     * @param integer $size
1595
     * @return $this
1596
     */
1597 1
    public function size($size)
1598
    {
1599 1
        $this->expr->size((integer) $size);
1600 1
        return $this;
1601
    }
1602
1603
    /**
1604
     * Set the skip for the query cursor.
1605
     *
1606
     * This is only relevant for find queries, or mapReduce queries that store
1607
     * results in an output collecton and return a cursor.
1608
     *
1609
     * @see Query::prepareCursor()
1610
     * @param integer $skip
1611
     * @return $this
1612
     */
1613
    public function skip($skip)
1614
    {
1615
        $this->query['skip'] = (integer) $skip;
1616
        return $this;
1617
    }
1618
1619
    /**
1620
     * Set the snapshot cursor flag.
1621
     *
1622
     * @param boolean $bool
1623
     * @return $this
1624
     */
1625
    public function snapshot($bool = true)
1626
    {
1627
        $this->query['snapshot'] = (boolean) $bool;
1628
        return $this;
1629
    }
1630
1631
    /**
1632
     * Set one or more field/order pairs on which to sort the query.
1633
     *
1634
     * If sorting by multiple fields, the first argument should be an array of
1635
     * field name (key) and order (value) pairs.
1636
     *
1637
     * @param array|string $fieldName Field name or array of field/order pairs
1638
     * @param int|string $order       Field order (if one field is specified)
1639
     * @return $this
1640
     */
1641 31
    public function sort($fieldName, $order = 1)
1642
    {
1643 31
        if ( ! isset($this->query['sort'])) {
1644 31
            $this->query['sort'] = [];
1645
        }
1646
1647 31
        $fields = is_array($fieldName) ? $fieldName : [$fieldName => $order];
1648
1649 31
        foreach ($fields as $fieldName => $order) {
1650 14
            if (is_string($order)) {
1651 9
                $order = strtolower($order) === 'asc' ? 1 : -1;
1652
            }
1653 14
            $this->query['sort'][$fieldName] = (integer) $order;
1654
        }
1655
1656 31
        return $this;
1657
    }
1658
1659
    /**
1660
     * Specify a projected metadata field on which to sort the query.
1661
     *
1662
     * Sort order is not configurable for metadata fields. Sorting by a metadata
1663
     * field requires the same field and $meta expression to exist in the
1664
     * projection document. This method will call {@link Builder::selectMeta()}
1665
     * if the field is not already set in the projection.
1666
     *
1667
     * @see http://docs.mongodb.org/master/reference/operator/projection/meta/#sort
1668
     * @param string $fieldName       Field name of the projected metadata
1669
     * @param string $metaDataKeyword
1670
     * @return $this
1671
     */
1672 2
    public function sortMeta($fieldName, $metaDataKeyword)
1673
    {
1674
        /* It's possible that the field is already projected without the $meta
1675
         * operator. We'll assume that the user knows what they're doing in that
1676
         * case and will not attempt to override the projection.
1677
         */
1678 2
        if ( ! isset($this->query['select'][$fieldName])) {
1679 1
            $this->selectMeta($fieldName, $metaDataKeyword);
1680
        }
1681
1682 2
        $this->query['sort'][$fieldName] = ['$meta' => $metaDataKeyword];
1683
1684 2
        return $this;
1685
    }
1686
1687
    /**
1688
     * Specify $text criteria for the current field.
1689
     *
1690
     * The $language option may be set with {@link Builder::language()}.
1691
     *
1692
     * @see Expr::text()
1693
     * @see http://docs.mongodb.org/master/reference/operator/query/text/
1694
     * @param string $search
1695
     * @return $this
1696
     */
1697 1
    public function text($search)
1698
    {
1699 1
        $this->expr->text($search);
1700 1
        return $this;
1701
    }
1702
1703
    /**
1704
     * Specify $type criteria for the current field.
1705
     *
1706
     * @see Expr::type()
1707
     * @see http://docs.mongodb.org/manual/reference/operator/type/
1708
     * @param integer $type
1709
     * @return $this
1710
     */
1711 2
    public function type($type)
1712
    {
1713 2
        $this->expr->type($type);
1714 2
        return $this;
1715
    }
1716
1717
    /**
1718
     * Unset the current field.
1719
     *
1720
     * The field will be removed from the document (not set to null).
1721
     *
1722
     * @see Expr::unsetField()
1723
     * @see http://docs.mongodb.org/manual/reference/operator/unset/
1724
     * @return $this
1725
     */
1726 4
    public function unsetField()
1727
    {
1728 4
        $this->expr->unsetField();
1729 4
        return $this;
1730
    }
1731
1732
    /**
1733
     * @param string $documentName
1734
     * @return $this
1735
     */
1736 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...
1737
    {
1738 21
        $this->setDocumentName($documentName);
1739 21
        $this->query['type'] = Query::TYPE_UPDATE;
1740 21
        $this->query['multiple'] = false;
1741
1742 21
        return $this;
1743
    }
1744
1745
    /**
1746
     * @param string $documentName
1747
     * @return $this
1748
     */
1749 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...
1750
    {
1751 3
        $this->setDocumentName($documentName);
1752 3
        $this->query['type'] = Query::TYPE_UPDATE;
1753 3
        $this->query['multiple'] = true;
1754
1755 3
        return $this;
1756
    }
1757
1758
    /**
1759
     * Set the "upsert" option for an update or findAndUpdate query.
1760
     *
1761
     * @param boolean $bool
1762
     * @return $this
1763
     */
1764 7
    public function upsert($bool = true)
1765
    {
1766 7
        $this->query['upsert'] = (boolean) $bool;
1767 7
        return $this;
1768
    }
1769
1770
    /**
1771
     * Specify a JavaScript expression to use for matching documents.
1772
     *
1773
     * @see Expr::where()
1774
     * @see http://docs.mongodb.org/manual/reference/operator/where/
1775
     * @param string|\MongoCode $javascript
1776
     * @return $this
1777
     */
1778 3
    public function where($javascript)
1779
    {
1780 3
        $this->expr->where($javascript);
1781 3
        return $this;
1782
    }
1783
1784
    /**
1785
     * Get Discriminator Values
1786
     *
1787
     * @param \Traversable $classNames
1788
     * @return array an array of discriminatorValues (mixed type)
1789
     * @throws \InvalidArgumentException if the number of found collections > 1
1790
     */
1791 2
    private function getDiscriminatorValues($classNames)
1792
    {
1793 2
        $discriminatorValues = array();
1794 2
        $collections = array();
1795 2
        foreach ($classNames as $className) {
1796 2
            $class = $this->dm->getClassMetadata($className);
1797 2
            $discriminatorValues[] = $class->discriminatorValue;
1798 2
            $key = $this->dm->getDocumentDatabase($className)->getDatabaseName() . '.' . $class->getCollection();
1799 2
            $collections[$key] = $key;
1800
        }
1801 2
        if (count($collections) > 1) {
1802 1
            throw new \InvalidArgumentException('Documents involved are not all mapped to the same database collection.');
1803
        }
1804 1
        return $discriminatorValues;
1805
    }
1806
1807
    /**
1808
     * @param string[]|string $documentName an array of document names or just one.
1809
     */
1810 284
    private function setDocumentName($documentName)
1811
    {
1812 284
        if (is_array($documentName)) {
1813 2
            $documentNames = $documentName;
1814 2
            $documentName = $documentNames[0];
1815
1816 2
            $metadata = $this->dm->getClassMetadata($documentName);
1817 2
            $discriminatorField = $metadata->discriminatorField;
1818 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...
1819
1820
            // If a defaultDiscriminatorValue is set and it is among the discriminators being queries, add NULL to the list
1821 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...
1822 1
                $discriminatorValues[] = null;
1823
            }
1824
1825 1
            $this->field($discriminatorField)->in($discriminatorValues);
1826
        }
1827
1828 283
        if ($documentName !== null) {
1829 283
            $this->collection = $this->dm->getDocumentCollection($documentName);
1830 283
            $this->class = $this->dm->getClassMetadata($documentName);
1831
1832
            // Expr also needs to know
1833 283
            $this->expr->setClassMetadata($this->class);
1834
        }
1835 283
    }
1836
}
1837