Completed
Push — master ( e8a141...1ec641 )
by Maciej
61:02 queued 35:57
created

Builder::withinBox()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 5
ccs 3
cts 3
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 4
crap 1
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 291
    public function __construct(DocumentManager $dm, $documentName = null)
100
    {
101 291
        $this->dm = $dm;
102 291
        $this->expr = new Expr($dm);
103 291
        if ($documentName !== null) {
104 283
            $this->setDocumentName($documentName);
105
        }
106 290
    }
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
    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
        $this->expr->addNor(...func_get_args());
142
        return $this;
143
    }
144 1
145
    /**
146 1
     * Add one or more $or clauses to the current query.
147 1
     *
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
    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
        $this->expr->addOr(...func_get_args());
158
        return $this;
159
    }
160 3
161
    /**
162 3
     * Append one or more values to the current array field only if they do not
163 3
     * 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 6
     * @return $this
177
     */
178 6
    public function addToSet($valueOrExpression)
179 6
    {
180
        $this->expr->addToSet($valueOrExpression);
181
        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
    public function all(array $values)
193
    {
194
        $this->expr->all($values);
195
        return $this;
196
    }
197
198
    /**
199 5
     * Apply a bitwise and operation on the current field.
200
     *
201 5
     * @see Expr::bitAnd()
202 5
     * @see http://docs.mongodb.org/manual/reference/operator/update/bit/
203
     * @param int $value
204
     * @return $this
205
     */
206
    public function bitAnd($value)
207
    {
208
        $this->expr->bitAnd($value);
209
        return $this;
210
    }
211
212
    /**
213 3
     * Apply a bitwise or operation on the current field.
214
     *
215 3
     * @see Expr::bitOr()
216 3
     * @see http://docs.mongodb.org/manual/reference/operator/update/bit/
217
     * @param int $value
218
     * @return $this
219
     */
220
    public function bitOr($value)
221
    {
222
        $this->expr->bitOr($value);
223
        return $this;
224
    }
225
226
    /**
227 1
     * Matches documents where all of the bit positions given by the query are
228
     * clear.
229 1
     *
230 1
     * @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
    public function bitsAllClear($value)
236
    {
237
        $this->expr->bitsAllClear($value);
238
        return $this;
239
    }
240
241 1
    /**
242
     * Matches documents where all of the bit positions given by the query are
243 1
     * set.
244 1
     *
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
    public function bitsAllSet($value)
251
    {
252
        $this->expr->bitsAllSet($value);
253
        return $this;
254
    }
255
256 1
    /**
257
     * Matches documents where any of the bit positions given by the query are
258 1
     * clear.
259 1
     *
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
    public function bitsAnyClear($value)
266
    {
267
        $this->expr->bitsAnyClear($value);
268
        return $this;
269
    }
270
271 1
    /**
272
     * Matches documents where any of the bit positions given by the query are
273 1
     * set.
274 1
     *
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
    public function bitsAnySet($value)
281
    {
282
        $this->expr->bitsAnySet($value);
283
        return $this;
284
    }
285
286 1
    /**
287
     * Apply a bitwise xor operation on the current field.
288 1
     *
289 1
     * @see Expr::bitXor()
290
     * @see http://docs.mongodb.org/manual/reference/operator/update/bit/
291
     * @param int $value
292
     * @return $this
293
     */
294
    public function bitXor($value)
295
    {
296
        $this->expr->bitXor($value);
297
        return $this;
298
    }
299
300
    /**
301 1
     * A boolean flag to enable or disable case sensitive search for $text
302
     * criteria.
303 1
     *
304 1
     * 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
    public function caseSensitive($caseSensitive)
315 1
    {
316
        $this->expr->caseSensitive($caseSensitive);
317 1
        return $this;
318 1
    }
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
    public function comment($comment)
329
    {
330
        $this->expr->comment($comment);
331
        return $this;
332
    }
333
334
    /**
335 1
     * Change the query type to count.
336
     *
337 1
     * @return $this
338 1
     */
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 1
     * @see http://docs.mongodb.org/manual/reference/operator/currentDate/
350
     * @param string $type
351 1
     * @return $this
352 1
     */
353
    public function currentDate($type = 'date')
354
    {
355
        $this->expr->currentDate($type);
356
        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
    public function debug($name = null)
370
    {
371
        return $name !== null ? $this->query[$name] : $this->query;
372
    }
373
374 3
    /**
375
     * A boolean flag to enable or disable diacritic sensitive search for $text
376 3
     * criteria.
377 2
     *
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
    public function diacriticSensitive($diacriticSensitive)
389
    {
390 28
        $this->expr->diacriticSensitive($diacriticSensitive);
391
        return $this;
392 28
    }
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
    public function distinct($field)
402
    {
403
        $this->query['type'] = Query::TYPE_DISTINCT;
404
        $this->query['distinct'] = $field;
405
        return $this;
406
    }
407
408
    /**
409 1
     * Set whether the query should return its result as an EagerCursor.
410
     *
411 1
     * @param boolean $bool
412 1
     * @return $this
413
     */
414
    public function eagerCursor($bool = true)
415
    {
416
        if ( ! $bool && ! empty($this->primers)) {
417
            throw new \BadMethodCallException("Can't set eagerCursor to false when using reference primers");
418
        }
419
420
        $this->query['eagerCursor'] = (boolean) $bool;
421
        return $this;
422 2
    }
423
424 2
    /**
425 2
     * Specify $elemMatch criteria for the current field.
426 2
     *
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
    public function elemMatch($expression)
435 5
    {
436
        $this->expr->elemMatch($expression);
437 5
        return $this;
438 1
    }
439
440
    /**
441 4
     * Specify an equality match for the current field.
442 4
     *
443
     * @see Expr::equals()
444
     * @param mixed $value
445
     * @return $this
446
     */
447
    public function equals($value)
448
    {
449
        $this->expr->equals($value);
450
        return $this;
451
    }
452
453
    /**
454
     * Set one or more fields to be excluded from the query projection.
455 6
     *
456
     * If fields have been selected for inclusion, only the "_id" field may be
457 6
     * excluded.
458 6
     *
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 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
        if ( ! isset($this->query['select'])) {
465
            $this->query['select'] = [];
466
        }
467
468 75
        $fieldNames = is_array($fieldName) ? $fieldName : func_get_args();
469
470 75
        foreach ($fieldNames as $fieldName) {
471 75
            $this->query['select'][$fieldName] = 0;
472
        }
473
474
        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 6
     * @return $this
484
     */
485 6
    public function exists($bool)
486 6
    {
487
        $this->expr->exists((boolean) $bool);
488
        return $this;
489 6
    }
490
491 6
    /**
492 4
     * Create a new Expr instance that can be used as an expression with the Builder
493
     *
494
     * @return Expr $expr
495 6
     */
496
    public function expr()
497
    {
498
        $expr = new Expr($this->dm);
499
        $expr->setClassMetadata($this->class);
500
501
        return $expr;
502
    }
503
504
    /**
505
     * Set the current field to operate on.
506 5
     *
507
     * @param string $field
508 5
     * @return $this
509 5
     */
510
    public function field($field)
511
    {
512
        $this->currentField = $field;
513
        $this->expr->field((string) $field);
514
515
        return $this;
516
    }
517 26
518
    /**
519 26
     * Set the "finalize" option for a mapReduce or group command.
520 26
     *
521
     * @param string|\MongoCode $finalize
522 26
     * @return $this
523
     * @throws \BadMethodCallException if the query is not a mapReduce or group command
524
     */
525 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
        switch ($this->query['type']) {
528
            case Query::TYPE_MAP_REDUCE:
529
                $this->query['mapReduce']['options']['finalize'] = $finalize;
530
                break;
531 144
532
            case Query::TYPE_GROUP:
533 144
                $this->query['group']['options']['finalize'] = $finalize;
534 144
                break;
535
536 144
            default:
537
                throw new \BadMethodCallException('mapReduce(), map() or group() must be called before finalize()');
538
        }
539
540
        return $this;
541
    }
542
543
    /**
544
     * Change the query type to find and optionally set and change the class being queried.
545
     *
546 2
     * @param string $documentName
547
     * @return $this
548 2
     */
549
    public function find($documentName = null)
550 1
    {
551 1
        $this->setDocumentName($documentName);
552
        $this->query['type'] = Query::TYPE_FIND;
553
554
        return $this;
555
    }
556
557
    /**
558 1
     * @param string $documentName
559
     * @return $this
560
     */
561 1
    public function findAndRemove($documentName = null)
562
    {
563
        $this->setDocumentName($documentName);
564
        $this->query['type'] = Query::TYPE_FIND_AND_REMOVE;
565
566
        return $this;
567
    }
568
569
    /**
570 12
     * @param string $documentName
571
     * @return $this
572 12
     */
573 12
    public function findAndUpdate($documentName = null)
574
    {
575 12
        $this->setDocumentName($documentName);
576
        $this->query['type'] = Query::TYPE_FIND_AND_UPDATE;
577
578
        return $this;
579
    }
580
581
    /**
582 1
     * Add $geoIntersects criteria with a GeoJSON geometry to the query.
583
     *
584 1
     * The geometry parameter GeoJSON object or an array corresponding to the
585 1
     * geometry's JSON representation.
586
     *
587 1
     * @see Expr::geoIntersects()
588
     * @see http://docs.mongodb.org/manual/reference/operator/geoIntersects/
589
     * @param array|Geometry $geometry
590
     * @return $this
591
     */
592
    public function geoIntersects($geometry)
593
    {
594 13
        $this->expr->geoIntersects($geometry);
595
        return $this;
596 13
    }
597 13
598
    /**
599 13
     * 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
    public function geoWithin($geometry)
610
    {
611
        $this->expr->geoWithin($geometry);
612
        return $this;
613 1
    }
614
615 1
    /**
616 1
     * 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 1
     * @return $this
631
     */
632 1
    public function geoWithinBox($x1, $y1, $x2, $y2)
633 1
    {
634
        $this->expr->geoWithinBox($x1, $y1, $x2, $y2);
635
        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
    public function geoWithinCenter($x, $y, $radius)
652
    {
653 1
        $this->expr->geoWithinCenter($x, $y, $radius);
654
        return $this;
655 1
    }
656 1
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
    public function geoWithinCenterSphere($x, $y, $radius)
670
    {
671
        $this->expr->geoWithinCenterSphere($x, $y, $radius);
672 1
        return $this;
673
    }
674 1
675 1
    /**
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 1
     */
691
    public function geoWithinPolygon(/* array($x1, $y1), ... */)
692 1
    {
693 1
        $this->expr->geoWithinPolygon(...func_get_args());
694
        return $this;
695
    }
696
697
    /**
698
     * Return the expression's "new object".
699
     *
700
     * @see Expr::getNewObj()
701
     * @return array
702
     */
703
    public function getNewObj()
704
    {
705
        return $this->expr->getNewObj();
706
    }
707
708
    /**
709
     * Gets the Query executable.
710
     *
711
     * @param array $options
712 1
     * @return Query $query
713
     */
714 1
    public function getQuery(array $options = array())
715 1
    {
716
        if ($this->query['type'] === Query::TYPE_MAP_REDUCE) {
717
            $this->hydrate = false;
718
        }
719
720
        $documentPersister = $this->dm->getUnitOfWork()->getDocumentPersister($this->class->name);
721
722
        $query = $this->query;
723
724 13
        $query['query'] = $this->expr->getQuery();
725
        $query['query'] = $documentPersister->addDiscriminatorToPreparedQuery($query['query']);
726 13
        $query['query'] = $documentPersister->addFilterToPreparedQuery($query['query']);
727
728
        $query['newObj'] = $this->expr->getNewObj();
729
730
        if (isset($query['distinct'])) {
731
            $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
        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
            throw new \InvalidArgumentException('Upsert query that is to be performed on discriminated document does not have single ' .
737 150
                'discriminator. Either not use base class or set \'' . $this->class->discriminatorField . '\' field manually.');
738
        }
739
740
        if ( ! empty($query['select'])) {
741 150
            $query['select'] = $documentPersister->prepareProjection($query['select']);
742
            if ($this->hydrate && $this->class->inheritanceType === ClassMetadataInfo::INHERITANCE_TYPE_SINGLE_COLLECTION
743 150
                && ! isset($query['select'][$this->class->discriminatorField])) {
744
                $includeMode = 0 < count(array_filter($query['select'], function($mode) { return $mode == 1; }));
745 150
                if ($includeMode && ! isset($query['select'][$this->class->discriminatorField])) {
746 150
                    $query['select'][$this->class->discriminatorField] = 1;
747 150
                }
748
            }
749 150
        }
750
751 150
        if (isset($query['sort'])) {
752 2
            $query['sort'] = $documentPersister->prepareSort($query['sort']);
753
        }
754
755 150
        if ($this->class->readPreference && ! array_key_exists('readPreference', $query)) {
756 150
            $query['readPreference'] = new ReadPreference($this->class->readPreference, $this->class->readPreferenceTags);
757 1
        }
758 1
759
        return new Query(
760
            $this->dm,
761 149
            $this->class,
762 14
            $this->collection,
763 14
            $query,
764 14
            $options,
765
            $this->hydrate,
766 2
            $this->refresh,
767 1
            $this->primers,
768
            $this->readOnly
769
        );
770
    }
771
772 149
    /**
773 23
     * Return the expression's query criteria.
774
     *
775
     * @see Expr::getQuery()
776 149
     * @return array
777 1
     */
778
    public function getQueryArray()
779
    {
780 149
        return $this->expr->getQuery();
781 149
    }
782 149
783 149
    /**
784 149
     * Get the type of this query.
785 149
     *
786 149
     * @return integer $type
787 149
     */
788 149
    public function getType()
789 149
    {
790
        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 33
     * @return $this
800
     */
801 33
    public function gt($value)
802
    {
803
        $this->expr->gt($value);
804
        return $this;
805
    }
806
807
    /**
808
     * Specify $gte criteria for the current field.
809 2
     *
810
     * @see Expr::gte()
811 2
     * @see http://docs.mongodb.org/manual/reference/operator/gte/
812
     * @param mixed $value
813
     * @return $this
814
     */
815
    public function gte($value)
816
    {
817
        $this->expr->gte($value);
818
        return $this;
819
    }
820
821
    /**
822 2
     * Set the index hint for the query.
823
     *
824 2
     * @param array|string $index
825 2
     * @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 2
     */
837
    public function hydrate($bool = true)
838 2
    {
839 2
        $this->hydrate = $bool;
840
        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 17
     * @see Expr::in()
859
     * @see http://docs.mongodb.org/manual/reference/operator/in/
860 17
     * @param array $values
861 17
     * @return $this
862
     */
863
    public function in(array $values)
864
    {
865
        $this->expr->in($values);
866
        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
    public function inc($value)
880
    {
881
        $this->expr->inc($value);
882
        return $this;
883
    }
884 24
885
    /**
886 24
     * @param object $document
887 24
     * @return $this
888
     */
889
    public function includesReferenceTo($document)
890
    {
891
        $this->expr->includesReferenceTo($document);
892
        return $this;
893
    }
894
895
    /**
896
     * @param string $documentName
897
     * @return $this
898
     */
899
    public function insert($documentName = null)
900 6
    {
901
        $this->setDocumentName($documentName);
902 6
        $this->query['type'] = Query::TYPE_INSERT;
903 6
904
        return $this;
905
    }
906
907
    /**
908
     * Set the $language option for $text criteria.
909
     *
910 6
     * This method must be called after text().
911
     *
912 6
     * @see Expr::language()
913 4
     * @see http://docs.mongodb.org/manual/reference/operator/text/
914
     * @param string $language
915
     * @return $this
916
     */
917
    public function language($language)
918
    {
919
        $this->expr->language($language);
920 1
        return $this;
921
    }
922 1
923 1
    /**
924
     * Set the limit for the query.
925 1
     *
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
    public function limit($limit)
934
    {
935
        $this->query['limit'] = (integer) $limit;
936
        return $this;
937
    }
938 1
939
    /**
940 1
     * Specify $lt criteria for the current field.
941 1
     *
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 2
     * Specify $lte criteria for the current field.
955
     *
956 2
     * @see Expr::lte()
957 2
     * @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
    public function map($map)
980
    {
981
        $this->query['type'] = Query::TYPE_MAP_REDUCE;
982
        $this->query['mapReduce'] = [
983
            'map' => $map,
984
            'reduce' => null,
985
            'out' => ['inline' => true],
986
            'options' => [],
987
        ];
988
        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 1
     */
1001
    public function mapReduce($map, $reduce, $out = ['inline' => true], array $options = [])
1002 1
    {
1003 1
        $this->query['type'] = Query::TYPE_MAP_REDUCE;
1004 1
        $this->query['mapReduce'] = [
1005
            'map' => $map,
1006
            'reduce' => $reduce,
1007
            'out' => $out,
1008
            'options' => $options
1009 1
        ];
1010
        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 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
            throw new \BadMethodCallException('This method requires a mapReduce command (call map() or mapReduce() first)');
1024 1
        }
1025 1
1026 1
        $this->query['mapReduce']['options'] = $options;
1027 1
        return $this;
1028 1
    }
1029 1
1030
    /**
1031 1
     * 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
    public function max($value)
1039
    {
1040
        $this->expr->max($value);
1041 1
        return $this;
1042
    }
1043 1
1044 1
    /**
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 1
     * @see Expr::min()
1060
     * @see http://docs.mongodb.org/manual/reference/operator/update/min/
1061 1
     * @param mixed $value
1062 1
     * @return $this
1063
     */
1064
    public function min($value)
1065
    {
1066
        $this->expr->min($value);
1067
        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
    public function mod($divisor, $remainder = 0)
1080
    {
1081
        $this->expr->mod($divisor, $remainder);
1082
        return $this;
1083
    }
1084
1085 1
    /**
1086
     * Multiply the current field.
1087 1
     *
1088 1
     * 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
    public function mul($value)
1096
    {
1097
        $this->expr->mul($value);
1098
        return $this;
1099
    }
1100 1
1101
    /**
1102 1
     * Add $near criteria to the query.
1103 1
     *
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
    public function near($x, $y = null)
1115
    {
1116 1
        $this->expr->near($x, $y);
1117
        return $this;
1118 1
    }
1119 1
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
    public function nearSphere($x, $y = null)
1134
    {
1135
        $this->expr->nearSphere($x, $y);
1136
        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 1
    public function not($expression)
1150
    {
1151 1
        $this->expr->not($expression);
1152 1
        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
    public function notEqual($value)
1164
    {
1165
        $this->expr->notEqual($value);
1166
        return $this;
1167
    }
1168 1
1169
    /**
1170 1
     * Specify $nin criteria for the current field.
1171 1
     *
1172
     * @see Expr::notIn()
1173
     * @see http://docs.mongodb.org/manual/reference/operator/nin/
1174
     * @param array $values
1175
     * @return $this
1176
     */
1177
    public function notIn(array $values)
1178
    {
1179
        $this->expr->notIn($values);
1180
        return $this;
1181
    }
1182
1183
    /**
1184 3
     * Set the "out" option for a mapReduce command.
1185
     *
1186 3
     * @param array|string $out
1187 3
     * @return $this
1188
     * @throws \BadMethodCallException if the query is not a mapReduce command
1189
     */
1190 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
        if ($this->query['type'] !== Query::TYPE_MAP_REDUCE) {
1193
            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 4
    }
1199
1200 4
    /**
1201 4
     * 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
    public function popFirst()
1208
    {
1209
        $this->expr->popFirst();
1210
        return $this;
1211
    }
1212 4
1213
    /**
1214 4
     * Remove the last element from the current array field.
1215 4
     *
1216
     * @see Expr::popLast()
1217
     * @see http://docs.mongodb.org/manual/reference/operator/pop/
1218
     * @return $this
1219
     */
1220
    public function popLast()
1221
    {
1222
        $this->expr->popLast();
1223
        return $this;
1224
    }
1225 1
1226
    /**
1227 1
     * Use a primer to eagerly load all references in the current field.
1228 1
     *
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 2
     * @throws \InvalidArgumentException If $primer is not boolean or callable
1243
     */
1244 2
    public function prime($primer = true)
1245 2
    {
1246
        if ( ! is_bool($primer) && ! is_callable($primer)) {
1247
            throw new \InvalidArgumentException('$primer is not a boolean or callable');
1248
        }
1249
1250
        if ($primer === false) {
1251
            unset($this->primers[$this->currentField]);
1252
1253
            return $this;
1254
        }
1255 1
1256
        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 1
        }
1259
1260
        $this->primers[$this->currentField] = $primer;
1261
        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
    public function pull($valueOrExpression)
1274
    {
1275
        $this->expr->pull($valueOrExpression);
1276
        return $this;
1277
    }
1278
1279 25
    /**
1280
     * Remove all elements matching any of the given values from the current
1281 25
     * array field.
1282 1
     *
1283
     * @see Expr::pullAll()
1284
     * @see http://docs.mongodb.org/manual/reference/operator/pullAll/
1285 24
     * @param array $values
1286
     * @return $this
1287
     */
1288
    public function pullAll(array $values)
1289
    {
1290
        $this->expr->pullAll($values);
1291 24
        return $this;
1292 1
    }
1293
1294
    /**
1295 23
     * Append one or more values to the current array field.
1296 23
     *
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 1
     * @see http://docs.mongodb.org/manual/reference/operator/slice/
1309
     * @see http://docs.mongodb.org/manual/reference/operator/sort/
1310 1
     * @param mixed|Expr $valueOrExpression
1311 1
     * @return $this
1312
     */
1313
    public function push($valueOrExpression)
1314
    {
1315
        $this->expr->push($valueOrExpression);
1316
        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 1
     * and $lt criteria on the upper bound. The upper bound is not inclusive.
1324
     *
1325 1
     * @see Expr::range()
1326 1
     * @param mixed $start
1327
     * @param mixed $end
1328
     * @return $this
1329
     */
1330
    public function range($start, $end)
1331
    {
1332
        $this->expr->range($start, $end);
1333
        return $this;
1334
    }
1335
1336
    /**
1337
     * @param bool $bool
1338
     * @return $this
1339
     */
1340
    public function readOnly($bool = true)
1341
    {
1342
        $this->readOnly = $bool;
1343
        return $this;
1344
    }
1345
1346
    /**
1347
     * Set the "reduce" option for a mapReduce or group command.
1348 6
     *
1349
     * @param string|\MongoCode $reduce
1350 6
     * @return $this
1351 6
     * @throws \BadMethodCallException if the query is not a mapReduce or group command
1352
     */
1353 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
        switch ($this->query['type']) {
1356
            case Query::TYPE_MAP_REDUCE:
1357
                $this->query['mapReduce']['reduce'] = $reduce;
1358
                break;
1359
1360
            case Query::TYPE_GROUP:
1361
                $this->query['group']['reduce'] = $reduce;
1362
                break;
1363
1364
            default:
1365
                throw new \BadMethodCallException('mapReduce(), map() or group() must be called before reduce()');
1366
        }
1367
1368
        return $this;
1369 1
    }
1370
1371 1
    /**
1372 1
     * @param object $document
1373
     * @return $this
1374
     */
1375
    public function references($document)
1376
    {
1377
        $this->expr->references($document);
1378
        return $this;
1379
    }
1380
1381
    /**
1382
     * @param bool $bool
1383
     * @return $this
1384
     */
1385
    public function refresh($bool = true)
1386 3
    {
1387
        $this->refresh = $bool;
1388 3
        return $this;
1389 3
    }
1390
1391
    /**
1392
     * @param string $documentName
1393
     * @return $this
1394
     */
1395
    public function remove($documentName = null)
1396 2
    {
1397
        $this->setDocumentName($documentName);
1398 2
        $this->query['type'] = Query::TYPE_REMOVE;
1399 2
1400
        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 2
     * @return $this
1410
     */
1411 2
    public function rename($name)
1412
    {
1413 1
        $this->expr->rename($name);
1414 1
        return $this;
1415
    }
1416
1417
    /**
1418
     * @param bool $bool
1419
     * @return $this
1420
     */
1421 1
    public function returnNew($bool = true)
1422
    {
1423
        $this->refresh(true);
1424 1
        $this->query['new'] = (boolean) $bool;
1425
1426
        return $this;
1427
    }
1428
1429
    /**
1430
     * Set one or more fields to be included in the query projection.
1431 10
     *
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 10
     * @return $this
1434 8
     */
1435 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
        if ( ! isset($this->query['select'])) {
1438
            $this->query['select'] = [];
1439
        }
1440
1441 5
        $fieldNames = is_array($fieldName) ? $fieldName : func_get_args();
1442
1443 5
        foreach ($fieldNames as $fieldName) {
1444 5
            $this->query['select'][$fieldName] = 1;
1445
        }
1446
1447
        return $this;
1448
    }
1449
1450
    /**
1451 1
     * Select only matching embedded documents in an array field for the query
1452
     * projection.
1453 1
     *
1454 1
     * @see http://docs.mongodb.org/manual/reference/projection/elemMatch/
1455
     * @param string $fieldName
1456 1
     * @param array|Expr $expression
1457
     * @return $this
1458
     */
1459
    public function selectElemMatch($fieldName, $expression)
1460
    {
1461
        if ($expression instanceof Expr) {
1462
            $expression = $expression->getQuery();
1463
        }
1464
        $this->query['select'][$fieldName] = ['$elemMatch' => $expression];
1465
        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
    public function selectMeta($fieldName, $metaDataKeyword)
1477 4
    {
1478
        $this->query['select'][$fieldName] = ['$meta' => $metaDataKeyword];
1479 4
        return $this;
1480 4
    }
1481
1482 4
    /**
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 19
     * @param integer $countOrSkip Count parameter, or skip if limit is specified
1492
     * @param integer $limit       Limit parameter used in conjunction with skip
1493 19
     * @return $this
1494 18
     */
1495
    public function selectSlice($fieldName, $countOrSkip, $limit = null)
1496
    {
1497 19
        $slice = $countOrSkip;
1498
        if ($limit !== null) {
1499 19
            $slice = [$slice, $limit];
1500 16
        }
1501
        $this->query['select'][$fieldName] = ['$slice' => $slice];
1502
        return $this;
1503 19
    }
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 2
     * @param boolean $atomic
1516
     * @return $this
1517 2
     */
1518 1
    public function set($value, $atomic = true)
1519
    {
1520 2
        $this->expr->set($value, $atomic && $this->query['type'] !== Query::TYPE_INSERT);
1521 2
        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 2
    {
1533
        $this->expr->setNewObj($newObj);
1534 2
        return $this;
1535 2
    }
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 3
    public function setOnInsert($value)
1552
    {
1553 3
        $this->expr->setOnInsert($value);
1554 3
        return $this;
1555 2
    }
1556
1557 3
    /**
1558 3
     * 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
    public function setReadPreference(ReadPreference $readPreference)
1567
    {
1568
        $this->query['readPreference'] = $readPreference;
1569
        return $this;
1570
    }
1571
1572
    /**
1573
     * Set the expression's query criteria.
1574 16
     *
1575
     * @see Expr::setQuery()
1576 16
     * @param array $query
1577 16
     * @return $this
1578
     */
1579
    public function setQueryArray(array $query)
1580
    {
1581
        $this->expr->setQuery($query);
1582
        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
    public function size($size)
1594
    {
1595
        $this->expr->size((integer) $size);
1596
        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 2
     * @return $this
1608
     */
1609 2
    public function skip($skip)
1610 2
    {
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 6
    {
1623
        $this->query['snapshot'] = (boolean) $bool;
1624 6
        return $this;
1625 6
    }
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 18
     * @return $this
1636
     */
1637 18
    public function sort($fieldName, $order = 1)
1638 18
    {
1639
        if ( ! isset($this->query['sort'])) {
1640
            $this->query['sort'] = [];
1641
        }
1642
1643
        $fields = is_array($fieldName) ? $fieldName : [$fieldName => $order];
1644
1645
        foreach ($fields as $fieldName => $order) {
1646
            if (is_string($order)) {
1647
                $order = strtolower($order) === 'asc' ? 1 : -1;
1648
            }
1649 1
            $this->query['sort'][$fieldName] = (integer) $order;
1650
        }
1651 1
1652 1
        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
    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
        if ( ! isset($this->query['select'][$fieldName])) {
1675
            $this->selectMeta($fieldName, $metaDataKeyword);
1676
        }
1677
1678
        $this->query['sort'][$fieldName] = ['$meta' => $metaDataKeyword];
1679
1680
        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 31
    public function text($search)
1694
    {
1695 31
        $this->expr->text($search);
1696 31
        return $this;
1697
    }
1698
1699 31
    /**
1700
     * Specify $type criteria for the current field.
1701 31
     *
1702 14
     * @see Expr::type()
1703 9
     * @see http://docs.mongodb.org/manual/reference/operator/type/
1704
     * @param integer $type
1705 14
     * @return $this
1706
     */
1707
    public function type($type)
1708 31
    {
1709
        $this->expr->type($type);
1710
        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
    public function unsetField()
1723
    {
1724 2
        $this->expr->unsetField();
1725
        return $this;
1726
    }
1727
1728
    /**
1729
     * @param string $documentName
1730 2
     * @return $this
1731 1
     */
1732 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 2
        $this->setDocumentName($documentName);
1735
        $this->query['type'] = Query::TYPE_UPDATE;
1736 2
        $this->query['multiple'] = false;
1737
1738
        return $this;
1739
    }
1740
1741
    /**
1742
     * @param string $documentName
1743
     * @return $this
1744
     */
1745 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
        $this->setDocumentName($documentName);
1748
        $this->query['type'] = Query::TYPE_UPDATE;
1749 1
        $this->query['multiple'] = true;
1750
1751 1
        return $this;
1752 1
    }
1753
1754
    /**
1755
     * Set the "upsert" option for an update or findAndUpdate query.
1756
     *
1757
     * @param boolean $bool
1758
     * @return $this
1759
     */
1760
    public function upsert($bool = true)
1761
    {
1762
        $this->query['upsert'] = (boolean) $bool;
1763 2
        return $this;
1764
    }
1765 2
1766 2
    /**
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
    public function where($javascript)
1775
    {
1776
        $this->expr->where($javascript);
1777
        return $this;
1778 4
    }
1779
1780 4
    /**
1781 4
     * 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
    private function getDiscriminatorValues($classNames)
1788 21
    {
1789
        $discriminatorValues = array();
1790 21
        $collections = array();
1791 21
        foreach ($classNames as $className) {
1792 21
            $class = $this->dm->getClassMetadata($className);
1793
            $discriminatorValues[] = $class->discriminatorValue;
1794 21
            $key = $this->dm->getDocumentDatabase($className)->getDatabaseName() . '.' . $class->getCollection();
1795
            $collections[$key] = $key;
1796
        }
1797
        if (count($collections) > 1) {
1798
            throw new \InvalidArgumentException('Documents involved are not all mapped to the same database collection.');
1799
        }
1800
        return $discriminatorValues;
1801 3
    }
1802
1803 3
    /**
1804 3
     * @param string[]|string $documentName an array of document names or just one.
1805 3
     */
1806
    private function setDocumentName($documentName)
1807 3
    {
1808
        if (is_array($documentName)) {
1809
            $documentNames = $documentName;
1810
            $documentName = $documentNames[0];
1811
1812
            $metadata = $this->dm->getClassMetadata($documentName);
1813
            $discriminatorField = $metadata->discriminatorField;
1814
            $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 7
            // If a defaultDiscriminatorValue is set and it is among the discriminators being queries, add NULL to the list
1817 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 7
                $discriminatorValues[] = null;
1819 7
            }
1820
1821
            $this->field($discriminatorField)->in($discriminatorValues);
1822
        }
1823
1824
        if ($documentName !== null) {
1825
            $this->collection = $this->dm->getDocumentCollection($documentName);
1826
            $this->class = $this->dm->getClassMetadata($documentName);
1827
1828
            // Expr also needs to know
1829
            $this->expr->setClassMetadata($this->class);
1830 3
        }
1831
    }
1832
}
1833