Completed
Pull Request — master (#1714)
by
unknown
11:33
created

Expr::references()   D

Complexity

Conditions 9
Paths 40

Size

Total Lines 41
Code Lines 27

Duplication

Lines 41
Ratio 100 %

Code Coverage

Tests 21
CRAP Score 9.1582

Importance

Changes 0
Metric Value
dl 41
loc 41
ccs 21
cts 24
cp 0.875
rs 4.909
c 0
b 0
f 0
cc 9
eloc 27
nc 40
nop 1
crap 9.1582
1
<?php
2
3
namespace Doctrine\ODM\MongoDB\Query;
4
5
use Doctrine\ODM\MongoDB\DocumentManager;
6
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
7
use Doctrine\ODM\MongoDB\Mapping\ClassMetadataInfo;
8
use Doctrine\ODM\MongoDB\Mapping\MappingException;
9
use GeoJson\Geometry\Geometry;
10
use GeoJson\Geometry\Point;
11
12
/**
13
 * Query expression builder for ODM.
14
 *
15
 * @since       1.0
16
 */
17
class Expr
18
{
19
    /**
20
     * The query criteria array.
21
     *
22
     * @var array
23
     */
24
    private $query = [];
25
26
    /**
27
     * The "new object" array containing either a full document or a number of
28
     * atomic update operators.
29
     *
30
     * @see docs.mongodb.org/manual/reference/method/db.collection.update/#update-parameter
31
     * @var array
32
     */
33
    private $newObj = [];
34
35
    /**
36
     * The current field we are operating on.
37
     *
38
     * @var string
39
     */
40
    private $currentField;
41
42
    /**
43
     * The DocumentManager instance for this query
44
     *
45
     * @var DocumentManager
46
     */
47
    private $dm;
48
49
    /**
50
     * The ClassMetadata instance for the document being queried
51
     *
52
     * @var ClassMetadata
53
     */
54
    private $class;
55
56
    /**
57
     * @param DocumentManager $dm
58
     */
59 392
    public function __construct(DocumentManager $dm)
60
    {
61 392
        $this->dm = $dm;
62 392
    }
63
64
    /**
65
     * Add one or more $and clauses to the current query.
66
     *
67
     * @see Builder::addAnd()
68
     * @see http://docs.mongodb.org/manual/reference/operator/and/
69
     * @param array|Expr $expression
70
     * @return $this
71
     */
72 3 View Code Duplication
    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...
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...
73
    {
74 3
        if (! isset($this->query['$and'])) {
75 3
            $this->query['$and'] = [];
76
        }
77
78 3
        $this->query['$and'] = array_merge(
79 3
            $this->query['$and'],
80 3
            array_map(
81 3
                function ($expression) {
82 3
                    return $expression instanceof Expr ? $expression->getQuery() : $expression;
83 3
                },
84 3
                func_get_args()
85
            )
86
        );
87
88 3
        return $this;
89
    }
90
91
    /**
92
     * Add one or more $nor clauses to the current query.
93
     *
94
     * @see Builder::addNor()
95
     * @see http://docs.mongodb.org/manual/reference/operator/nor/
96
     * @param array|Expr $expression
97
     * @return $this
98
     */
99 1 View Code Duplication
    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...
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...
100
    {
101 1
        if (! isset($this->query['$nor'])) {
102 1
            $this->query['$nor'] = [];
103
        }
104
105 1
        $this->query['$nor'] = array_merge(
106 1
            $this->query['$nor'],
107
            array_map(function ($expression) { return $expression instanceof Expr ? $expression->getQuery() : $expression; }, func_get_args())
108
        );
109
110 1
        return $this;
111
    }
112
113
    /**
114
     * Add one or more $or clauses to the current query.
115
     *
116
     * @see Builder::addOr()
117
     * @see http://docs.mongodb.org/manual/reference/operator/or/
118
     * @param array|Expr $expression
119
     * @return $this
120
     */
121 5 View Code Duplication
    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...
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...
122
    {
123 5
        if (! isset($this->query['$or'])) {
124 5
            $this->query['$or'] = [];
125
        }
126
127 5
        $this->query['$or'] = array_merge(
128 5
            $this->query['$or'],
129
            array_map(function ($expression) { return $expression instanceof Expr ? $expression->getQuery() : $expression; }, func_get_args())
130
        );
131
132 5
        return $this;
133
    }
134
135
    /**
136
     * Append one or more values to the current array field only if they do not
137
     * already exist in the array.
138
     *
139
     * If the field does not exist, it will be set to an array containing the
140
     * unique value(s) in the argument. If the field is not an array, the query
141
     * will yield an error.
142
     *
143
     * Multiple values may be specified by provided an Expr object and using
144
     * {@link Expr::each()}.
145
     *
146
     * @see Builder::addToSet()
147
     * @see http://docs.mongodb.org/manual/reference/operator/addToSet/
148
     * @see http://docs.mongodb.org/manual/reference/operator/each/
149
     * @param mixed|Expr $valueOrExpression
150
     * @return $this
151
     */
152 5 View Code Duplication
    public function addToSet($valueOrExpression)
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...
153
    {
154 5
        if ($valueOrExpression instanceof Expr) {
155 1
            $valueOrExpression = $valueOrExpression->getQuery();
156
        }
157
158 5
        $this->requiresCurrentField();
159 5
        $this->newObj['$addToSet'][$this->currentField] = $valueOrExpression;
160 5
        return $this;
161
    }
162
163
    /**
164
     * Specify $all criteria for the current field.
165
     *
166
     * @see Builder::all()
167
     * @see http://docs.mongodb.org/manual/reference/operator/all/
168
     * @param array $values
169
     * @return $this
170
     */
171 2
    public function all(array $values)
172
    {
173 2
        return $this->operator('$all', (array) $values);
174
    }
175
176
    /**
177
     * Apply a bitwise operation on the current field
178
     *
179
     * @see http://docs.mongodb.org/manual/reference/operator/update/bit/
180
     * @param string $operator
181
     * @param int $value
182
     * @return $this
183
     */
184 3
    protected function bit($operator, $value)
185
    {
186 3
        $this->requiresCurrentField();
187 3
        $this->newObj['$bit'][$this->currentField][$operator] = $value;
188 3
        return $this;
189
    }
190
191
    /**
192
     * Apply a bitwise and operation on the current field.
193
     *
194
     * @see Builder::bitAnd()
195
     * @see http://docs.mongodb.org/manual/reference/operator/update/bit/
196
     * @param int $value
197
     * @return $this
198
     */
199 1
    public function bitAnd($value)
200
    {
201 1
        return $this->bit('and', $value);
202
    }
203
204
    /**
205
     * Apply a bitwise or operation on the current field.
206
     *
207
     * @see Builder::bitOr()
208
     * @see http://docs.mongodb.org/manual/reference/operator/update/bit/
209
     * @param int $value
210
     * @return $this
211
     */
212 1
    public function bitOr($value)
213
    {
214 1
        return $this->bit('or', $value);
215
    }
216
217
    /**
218
     * Matches documents where all of the bit positions given by the query are
219
     * clear.
220
     *
221
     * @see Builder::bitsAllClear()
222
     * @see https://docs.mongodb.org/manual/reference/operator/query/bitsAllClear/
223
     * @param int|array|\MongoDB\BSON\Binary $value
224
     * @return $this
225
     */
226
    public function bitsAllClear($value)
227
    {
228
        $this->requiresCurrentField();
229
        return $this->operator('$bitsAllClear', $value);
230
    }
231
232
    /**
233
     * Matches documents where all of the bit positions given by the query are
234
     * set.
235
     *
236
     * @see Builder::bitsAllSet()
237
     * @see https://docs.mongodb.org/manual/reference/operator/query/bitsAllSet/
238
     * @param int|array|\MongoDB\BSON\Binary $value
239
     * @return $this
240
     */
241
    public function bitsAllSet($value)
242
    {
243
        $this->requiresCurrentField();
244
        return $this->operator('$bitsAllSet', $value);
245
    }
246
247
    /**
248
     * Matches documents where any of the bit positions given by the query are
249
     * clear.
250
     *
251
     * @see Builder::bitsAnyClear()
252
     * @see https://docs.mongodb.org/manual/reference/operator/query/bitsAnyClear/
253
     * @param int|array|\MongoDB\BSON\Binary $value
254
     * @return $this
255
     */
256
    public function bitsAnyClear($value)
257
    {
258
        $this->requiresCurrentField();
259
        return $this->operator('$bitsAnyClear', $value);
260
    }
261
262
    /**
263
     * Matches documents where any of the bit positions given by the query are
264
     * set.
265
     *
266
     * @see Builder::bitsAnySet()
267
     * @see https://docs.mongodb.org/manual/reference/operator/query/bitsAnySet/
268
     * @param int|array|\MongoDB\BSON\Binary $value
269
     * @return $this
270
     */
271
    public function bitsAnySet($value)
272
    {
273
        $this->requiresCurrentField();
274
        return $this->operator('$bitsAnySet', $value);
275
    }
276
277
    /**
278
     * Apply a bitwise xor operation on the current field.
279
     *
280
     * @see Builder::bitXor()
281
     * @see http://docs.mongodb.org/manual/reference/operator/update/bit/
282
     * @param int $value
283
     * @return $this
284
     */
285 1
    public function bitXor($value)
286
    {
287 1
        return $this->bit('xor', $value);
288
    }
289
290
    /**
291
     * A boolean flag to enable or disable case sensitive search for $text
292
     * criteria.
293
     *
294
     * This method must be called after text().
295
     *
296
     * @see Builder::caseSensitive()
297
     * @see http://docs.mongodb.org/manual/reference/operator/text/
298
     * @param bool $caseSensitive
299
     * @return $this
300
     * @throws \BadMethodCallException if the query does not already have $text criteria
301
     *
302
     * @since 1.3
303
     */
304 3 View Code Duplication
    public function caseSensitive($caseSensitive)
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...
305
    {
306 3
        if ( ! isset($this->query['$text'])) {
307 1
            throw new \BadMethodCallException('This method requires a $text operator (call text() first)');
308
        }
309
310
        // Remove caseSensitive option to keep support for older database versions
311 2
        if ($caseSensitive) {
312 2
            $this->query['$text']['$caseSensitive'] = true;
313 1
        } elseif (isset($this->query['$text']['$caseSensitive'])) {
314 1
            unset($this->query['$text']['$caseSensitive']);
315
        }
316
317 2
        return $this;
318
    }
319
320
    /**
321
     * Associates a comment to any expression taking a query predicate.
322
     *
323
     * @see Builder::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->query['$comment'] = $comment;
331
        return $this;
332
    }
333
334
    /**
335
     * Sets the value of the current field to the current date, either as a date or a timestamp.
336
     *
337
     * @see Builder::currentDate()
338
     * @see http://docs.mongodb.org/manual/reference/operator/update/currentDate/
339
     * @param string $type
340
     * @return $this
341
     * @throws \InvalidArgumentException if an invalid type is given
342
     */
343 3
    public function currentDate($type = 'date')
344
    {
345 3
        if (! in_array($type, ['date', 'timestamp'])) {
346 1
            throw new \InvalidArgumentException('Type for currentDate operator must be date or timestamp.');
347
        }
348
349 2
        $this->requiresCurrentField();
350 2
        $this->newObj['$currentDate'][$this->currentField]['$type'] = $type;
351 2
        return $this;
352
    }
353
354
    /**
355
     * A boolean flag to enable or disable diacritic sensitive search for $text
356
     * criteria.
357
     *
358
     * This method must be called after text().
359
     *
360
     * @see Builder::diacriticSensitive()
361
     * @see http://docs.mongodb.org/manual/reference/operator/text/
362
     * @param bool $diacriticSensitive
363
     * @return $this
364
     * @throws \BadMethodCallException if the query does not already have $text criteria
365
     *
366
     * @since 1.3
367
     */
368 3 View Code Duplication
    public function diacriticSensitive($diacriticSensitive)
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...
369
    {
370 3
        if ( ! isset($this->query['$text'])) {
371 1
            throw new \BadMethodCallException('This method requires a $text operator (call text() first)');
372
        }
373
374
        // Remove diacriticSensitive option to keep support for older database versions
375 2
        if ($diacriticSensitive) {
376 2
            $this->query['$text']['$diacriticSensitive'] = true;
377 1
        } elseif (isset($this->query['$text']['$diacriticSensitive'])) {
378 1
            unset($this->query['$text']['$diacriticSensitive']);
379
        }
380
381 2
        return $this;
382
    }
383
384
    /**
385
     * Add $each criteria to the expression for a $push operation.
386
     *
387
     * @see Expr::push()
388
     * @see http://docs.mongodb.org/manual/reference/operator/each/
389
     * @param array $values
390
     * @return $this
391
     */
392 4
    public function each(array $values)
393
    {
394 4
        return $this->operator('$each', $values);
395
    }
396
397
    /**
398
     * Specify $elemMatch criteria for the current field.
399
     *
400
     * @see Builder::elemMatch()
401
     * @see http://docs.mongodb.org/manual/reference/operator/elemMatch/
402
     * @param array|Expr $expression
403
     * @return $this
404
     */
405 4
    public function elemMatch($expression)
406
    {
407 4
        return $this->operator('$elemMatch', $expression instanceof Expr ? $expression->getQuery() : $expression);
408
    }
409
410
    /**
411
     * Specify an equality match for the current field.
412
     *
413
     * @see Builder::equals()
414
     * @param mixed $value
415
     * @return $this
416
     */
417 100
    public function equals($value)
418
    {
419 100
        if ($this->currentField) {
420 99
            $this->query[$this->currentField] = $value;
421
        } else {
422 1
            $this->query = $value;
0 ignored issues
show
Documentation Bug introduced by
It seems like $value of type * is incompatible with the declared type array of property $query.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
423
        }
424 100
        return $this;
425
    }
426
427
    /**
428
     * Specify $exists criteria for the current field.
429
     *
430
     * @see Builder::exists()
431
     * @see http://docs.mongodb.org/manual/reference/operator/exists/
432
     * @param boolean $bool
433
     * @return $this
434
     */
435 5
    public function exists($bool)
436
    {
437 5
        return $this->operator('$exists', (boolean) $bool);
438
    }
439
440
    /**
441
     * Set the current field for building the expression.
442
     *
443
     * @see Builder::field()
444
     * @param string $field
445
     * @return $this
446
     */
447 191
    public function field($field)
448
    {
449 191
        $this->currentField = (string) $field;
450 191
        return $this;
451
    }
452
453
    /**
454
     * Add $geoIntersects criteria with a GeoJSON geometry to the expression.
455
     *
456
     * The geometry parameter GeoJSON object or an array corresponding to the
457
     * geometry's JSON representation.
458
     *
459
     * @see Builder::geoIntersects()
460
     * @see http://docs.mongodb.org/manual/reference/operator/geoIntersects/
461
     * @param array|Geometry $geometry
462
     * @return $this
463
     */
464 2 View Code Duplication
    public function geoIntersects($geometry)
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...
465
    {
466 2
        if ($geometry instanceof Geometry) {
0 ignored issues
show
Bug introduced by
The class GeoJson\Geometry\Geometry does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
467 1
            $geometry = $geometry->jsonSerialize();
468
        }
469
470 2
        return $this->operator('$geoIntersects', ['$geometry' => $geometry]);
471
    }
472
473
    /**
474
     * Add $geoWithin criteria with a GeoJSON geometry to the expression.
475
     *
476
     * The geometry parameter GeoJSON object or an array corresponding to the
477
     * geometry's JSON representation.
478
     *
479
     * @see Builder::geoWithin()
480
     * @see http://docs.mongodb.org/manual/reference/operator/geoIntersects/
481
     * @param array|Geometry $geometry
482
     * @return $this
483
     */
484 2 View Code Duplication
    public function geoWithin($geometry)
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...
485
    {
486 2
        if ($geometry instanceof Geometry) {
0 ignored issues
show
Bug introduced by
The class GeoJson\Geometry\Geometry does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
487 1
            $geometry = $geometry->jsonSerialize();
488
        }
489
490 2
        return $this->operator('$geoWithin', ['$geometry' => $geometry]);
491
    }
492
493
    /**
494
     * Add $geoWithin criteria with a $box shape to the expression.
495
     *
496
     * A rectangular polygon will be constructed from a pair of coordinates
497
     * corresponding to the bottom left and top right corners.
498
     *
499
     * Note: the $box operator only supports legacy coordinate pairs and 2d
500
     * indexes. This cannot be used with 2dsphere indexes and GeoJSON shapes.
501
     *
502
     * @see Builder::geoWithinBox()
503
     * @see http://docs.mongodb.org/manual/reference/operator/box/
504
     * @param float $x1
505
     * @param float $y1
506
     * @param float $x2
507
     * @param float $y2
508
     * @return $this
509
     */
510 1
    public function geoWithinBox($x1, $y1, $x2, $y2)
511
    {
512 1
        $shape = ['$box' => [[$x1, $y1], [$x2, $y2]]];
513
514 1
        return $this->operator('$geoWithin', $shape);
515
    }
516
517
    /**
518
     * Add $geoWithin criteria with a $center shape to the expression.
519
     *
520
     * Note: the $center operator only supports legacy coordinate pairs and 2d
521
     * indexes. This cannot be used with 2dsphere indexes and GeoJSON shapes.
522
     *
523
     * @see Builider::geoWithinCenter()
524
     * @see http://docs.mongodb.org/manual/reference/operator/center/
525
     * @param float $x
526
     * @param float $y
527
     * @param float $radius
528
     * @return $this
529
     */
530 1
    public function geoWithinCenter($x, $y, $radius)
531
    {
532 1
        $shape = ['$center' => [[$x, $y], $radius]];
533
534 1
        return $this->operator('$geoWithin', $shape);
535
    }
536
537
    /**
538
     * Add $geoWithin criteria with a $centerSphere shape to the expression.
539
     *
540
     * Note: the $centerSphere operator supports both 2d and 2dsphere indexes.
541
     *
542
     * @see Builder::geoWithinCenterSphere()
543
     * @see http://docs.mongodb.org/manual/reference/operator/centerSphere/
544
     * @param float $x
545
     * @param float $y
546
     * @param float $radius
547
     * @return $this
548
     */
549 1
    public function geoWithinCenterSphere($x, $y, $radius)
550
    {
551 1
        $shape = ['$centerSphere' => [[$x, $y], $radius]];
552
553 1
        return $this->operator('$geoWithin', $shape);
554
    }
555
556
    /**
557
     * Add $geoWithin criteria with a $polygon shape to the expression.
558
     *
559
     * Point coordinates are in x, y order (easting, northing for projected
560
     * coordinates, longitude, latitude for geographic coordinates).
561
     *
562
     * The last point coordinate is implicitly connected with the first.
563
     *
564
     * Note: the $polygon operator only supports legacy coordinate pairs and 2d
565
     * indexes. This cannot be used with 2dsphere indexes and GeoJSON shapes.
566
     *
567
     * @see Builder::geoWithinPolygon()
568
     * @see http://docs.mongodb.org/manual/reference/operator/polygon/
569
     * @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...
570
     * @return $this
571
     * @throws \InvalidArgumentException if less than three points are given
572
     */
573 2
    public function geoWithinPolygon(/* array($x1, $y1), ... */)
574
    {
575 2
        if (func_num_args() < 3) {
576 1
            throw new \InvalidArgumentException('Polygon must be defined by three or more points.');
577
        }
578
579 1
        $shape = ['$polygon' => func_get_args()];
580
581 1
        return $this->operator('$geoWithin', $shape);
582
    }
583
584
    /**
585
     * Return the current field.
586
     *
587
     * @return string
588
     */
589 2
    public function getCurrentField()
590
    {
591 2
        return $this->currentField;
592
    }
593
594
    /**
595
     * Gets prepared newObj part of expression.
596
     *
597
     * @return array
598
     */
599 174
    public function getNewObj()
600
    {
601 174
        return $this->dm->getUnitOfWork()
602 174
            ->getDocumentPersister($this->class->name)
603 174
            ->prepareQueryOrNewObj($this->newObj, true);
604
    }
605
606
    /**
607
     * Gets prepared query part of expression.
608
     *
609
     * @return array
610
     */
611 250
    public function getQuery()
612
    {
613 250
        return $this->dm->getUnitOfWork()
614 250
            ->getDocumentPersister($this->class->name)
615 250
            ->prepareQueryOrNewObj($this->query);
616
    }
617
618
    /**
619
     * Specify $gt criteria for the current field.
620
     *
621
     * @see Builder::gt()
622
     * @see http://docs.mongodb.org/manual/reference/operator/gt/
623
     * @param mixed $value
624
     * @return $this
625
     */
626 2
    public function gt($value)
627
    {
628 2
        return $this->operator('$gt', $value);
629
    }
630
631
    /**
632
     * Specify $gte criteria for the current field.
633
     *
634
     * @see Builder::gte()
635
     * @see http://docs.mongodb.org/manual/reference/operator/gte/
636
     * @param mixed $value
637
     * @return $this
638
     */
639 2
    public function gte($value)
640
    {
641 2
        return $this->operator('$gte', $value);
642
    }
643
644
    /**
645
     * Specify $in criteria for the current field.
646
     *
647
     * @see Builder::in()
648
     * @see http://docs.mongodb.org/manual/reference/operator/in/
649
     * @param array $values
650
     * @return $this
651
     */
652 31
    public function in(array $values)
653
    {
654 31
        return $this->operator('$in', array_values($values));
655
    }
656
657
    /**
658
     * Increment the current field.
659
     *
660
     * If the field does not exist, it will be set to this value.
661
     *
662
     * @see Builder::inc()
663
     * @see http://docs.mongodb.org/manual/reference/operator/inc/
664
     * @param float|integer $value
665
     * @return $this
666
     */
667 5
    public function inc($value)
668
    {
669 5
        $this->requiresCurrentField();
670 5
        $this->newObj['$inc'][$this->currentField] = $value;
671 5
        return $this;
672
    }
673
674
    /**
675
     * Checks that the current field includes a reference to the supplied document.
676
     *
677
     * @param object $document
678
     * @return Expr
679
     */
680 6 View Code Duplication
    public function includesReferenceTo($document)
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...
681
    {
682 6
        $this->requiresCurrentField();
683 6
        $mapping = $this->getReferenceMapping();
684 4
        $reference = $this->dm->createReference($document, $mapping);
685 4
        $storeAs = array_key_exists('storeAs', $mapping) ? $mapping['storeAs'] : null;
686 4
        $keys = [];
0 ignored issues
show
Unused Code introduced by
$keys is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
687
688
        switch ($storeAs) {
689 4
            case ClassMetadataInfo::REFERENCE_STORE_AS_ID:
690 2
                $this->query[$mapping['name']] = $reference;
691 2
                return $this;
692
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
693
694 3
            case ClassMetadataInfo::REFERENCE_STORE_AS_REF:
695
                $keys = ['id' => true];
696
                break;
697
698 3
            case ClassMetadataInfo::REFERENCE_STORE_AS_DB_REF:
699 1
            case ClassMetadataInfo::REFERENCE_STORE_AS_DB_REF_WITH_DB:
700 3
                $keys = ['$ref' => true, '$id' => true, '$db' => true];
701
702 3
                if ($storeAs === ClassMetadataInfo::REFERENCE_STORE_AS_DB_REF) {
703 2
                    unset($keys['$db']);
704
                }
705
706 3
                if (isset($mapping['targetDocument'])) {
707 1
                    unset($keys['$ref'], $keys['$db']);
708
                }
709 3
                break;
710
711
            default:
712
                throw new \InvalidArgumentException("Reference type {$storeAs} is invalid.");
713
        }
714
715 3
        foreach ($keys as $key => $value) {
716 3
            $this->query[$mapping['name']]['$elemMatch'][$key] = $reference[$key];
717
        }
718
719 3
        return $this;
720
    }
721
722
    /**
723
     * Set the $language option for $text criteria.
724
     *
725
     * This method must be called after text().
726
     *
727
     * @see Builder::language()
728
     * @see http://docs.mongodb.org/manual/reference/operator/text/
729
     * @param string $language
730
     * @return $this
731
     * @throws \BadMethodCallException if the query does not already have $text criteria
732
     */
733 2
    public function language($language)
734
    {
735 2
        if ( ! isset($this->query['$text'])) {
736 1
            throw new \BadMethodCallException('This method requires a $text operator (call text() first)');
737
        }
738
739 1
        $this->query['$text']['$language'] = (string) $language;
740
741 1
        return $this;
742
    }
743
744
    /**
745
     * Specify $lt criteria for the current field.
746
     *
747
     * @see Builder::lte()
748
     * @see http://docs.mongodb.org/manual/reference/operator/lte/
749
     * @param mixed $value
750
     * @return $this
751
     */
752 4
    public function lt($value)
753
    {
754 4
        return $this->operator('$lt', $value);
755
    }
756
757
    /**
758
     * Specify $lte criteria for the current field.
759
     *
760
     * @see Builder::lte()
761
     * @see http://docs.mongodb.org/manual/reference/operator/lte/
762
     * @param mixed $value
763
     * @return $this
764
     */
765 2
    public function lte($value)
766
    {
767 2
        return $this->operator('$lte', $value);
768
    }
769
770
    /**
771
     * Updates the value of the field to a specified value if the specified value is greater than the current value of the field.
772
     *
773
     * @see Builder::max()
774
     * @see http://docs.mongodb.org/manual/reference/operator/update/max/
775
     * @param mixed $value
776
     * @return $this
777
     */
778
    public function max($value)
779
    {
780
        $this->requiresCurrentField();
781
        $this->newObj['$max'][$this->currentField] = $value;
782
        return $this;
783
    }
784
785
    /**
786
     * Updates the value of the field to a specified value if the specified value is less than the current value of the field.
787
     *
788
     * @see Builder::min()
789
     * @see http://docs.mongodb.org/manual/reference/operator/update/min/
790
     * @param mixed $value
791
     * @return $this
792
     */
793
    public function min($value)
794
    {
795
        $this->requiresCurrentField();
796
        $this->newObj['$min'][$this->currentField] = $value;
797
        return $this;
798
    }
799
800
    /**
801
     * Specify $mod criteria for the current field.
802
     *
803
     * @see Builder::mod()
804
     * @see http://docs.mongodb.org/manual/reference/operator/mod/
805
     * @param float|integer $divisor
806
     * @param float|integer $remainder
807
     * @return $this
808
     */
809
    public function mod($divisor, $remainder = 0)
810
    {
811
        return $this->operator('$mod', [$divisor, $remainder]);
812
    }
813
814
    /**
815
     * Multiply the current field.
816
     *
817
     * If the field does not exist, it will be set to 0.
818
     *
819
     * @see Builder::mul()
820
     * @see http://docs.mongodb.org/manual/reference/operator/mul/
821
     * @param float|integer $value
822
     * @return $this
823
     */
824
    public function mul($value)
825
    {
826
        $this->requiresCurrentField();
827
        $this->newObj['$mul'][$this->currentField] = $value;
828
        return $this;
829
    }
830
831
    /**
832
     * Add $near criteria to the expression.
833
     *
834
     * A GeoJSON point may be provided as the first and only argument for
835
     * 2dsphere queries. This single parameter may be a GeoJSON point object or
836
     * an array corresponding to the point's JSON representation.
837
     *
838
     * @see Builder::near()
839
     * @see http://docs.mongodb.org/manual/reference/operator/near/
840
     * @param float|array|Point $x
841
     * @param float $y
842
     * @return $this
843
     */
844 3 View Code Duplication
    public function near($x, $y = 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...
845
    {
846 3
        if ($x instanceof Point) {
0 ignored issues
show
Bug introduced by
The class GeoJson\Geometry\Point does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
847 1
            $x = $x->jsonSerialize();
848
        }
849
850 3
        if (is_array($x)) {
851 2
            return $this->operator('$near', ['$geometry' => $x]);
852
        }
853
854 1
        return $this->operator('$near', [$x, $y]);
855
    }
856
857
    /**
858
     * Add $nearSphere criteria to the expression.
859
     *
860
     * A GeoJSON point may be provided as the first and only argument for
861
     * 2dsphere queries. This single parameter may be a GeoJSON point object or
862
     * an array corresponding to the point's JSON representation.
863
     *
864
     * @see Builder::nearSphere()
865
     * @see http://docs.mongodb.org/manual/reference/operator/nearSphere/
866
     * @param float|array|Point $x
867
     * @param float $y
868
     * @return $this
869
     */
870 3 View Code Duplication
    public function nearSphere($x, $y = 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...
871
    {
872 3
        if ($x instanceof Point) {
0 ignored issues
show
Bug introduced by
The class GeoJson\Geometry\Point does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
873 1
            $x = $x->jsonSerialize();
874
        }
875
876 3
        if (is_array($x)) {
877 2
            return $this->operator('$nearSphere', ['$geometry' => $x]);
878
        }
879
880 1
        return $this->operator('$nearSphere', [$x, $y]);
881
    }
882
883
    /**
884
     * Negates an expression for the current field.
885
     *
886
     * @see Builder::not()
887
     * @see http://docs.mongodb.org/manual/reference/operator/not/
888
     * @param array|Expr $expression
889
     * @return $this
890
     */
891 2
    public function not($expression)
892
    {
893 2
        return $this->operator('$not', $expression instanceof Expr ? $expression->getQuery() : $expression);
894
    }
895
896
    /**
897
     * Specify $ne criteria for the current field.
898
     *
899
     * @see Builder::notEqual()
900
     * @see http://docs.mongodb.org/manual/reference/operator/ne/
901
     * @param mixed $value
902
     * @return $this
903
     */
904 6
    public function notEqual($value)
905
    {
906 6
        return $this->operator('$ne', $value);
907
    }
908
909
    /**
910
     * Specify $nin criteria for the current field.
911
     *
912
     * @see Builder::notIn()
913
     * @see http://docs.mongodb.org/manual/reference/operator/nin/
914
     * @param array $values
915
     * @return $this
916
     */
917 6
    public function notIn(array $values)
918
    {
919 6
        return $this->operator('$nin', array_values($values));
920
    }
921
922
    /**
923
     * Defines an operator and value on the expression.
924
     *
925
     * If there is a current field, the operator will be set on it; otherwise,
926
     * the operator is set at the top level of the query.
927
     *
928
     * @param string $operator
929
     * @param mixed $value
930
     * @return $this
931
     */
932 81
    public function operator($operator, $value)
933
    {
934 81
        $this->wrapEqualityCriteria();
935
936 81
        if ($this->currentField) {
937 56
            $this->query[$this->currentField][$operator] = $value;
938
        } else {
939 27
            $this->query[$operator] = $value;
940
        }
941 81
        return $this;
942
    }
943
944
    /**
945
     * Remove the first element from the current array field.
946
     *
947
     * @see Builder::popFirst()
948
     * @see http://docs.mongodb.org/manual/reference/operator/pop/
949
     * @return $this
950
     */
951 1
    public function popFirst()
952
    {
953 1
        $this->requiresCurrentField();
954 1
        $this->newObj['$pop'][$this->currentField] = 1;
955 1
        return $this;
956
    }
957
958
    /**
959
     * Remove the last element from the current array field.
960
     *
961
     * @see Builder::popLast()
962
     * @see http://docs.mongodb.org/manual/reference/operator/pop/
963
     * @return $this
964
     */
965
    public function popLast()
966
    {
967
        $this->requiresCurrentField();
968
        $this->newObj['$pop'][$this->currentField] = -1;
969
        return $this;
970
    }
971
972
    /**
973
     * Add $position criteria to the expression for a $push operation.
974
     *
975
     * This is useful in conjunction with {@link Expr::each()} for a
976
     * {@link Expr::push()} operation.
977
     *
978
     * @see http://docs.mongodb.org/manual/reference/operator/update/position/
979
     * @param integer $position
980
     * @return $this
981
     */
982 1
    public function position($position)
983
    {
984 1
        return $this->operator('$position', $position);
985
    }
986
987
    /**
988
     * Remove all elements matching the given value or expression from the
989
     * current array field.
990
     *
991
     * @see Builder::pull()
992
     * @see http://docs.mongodb.org/manual/reference/operator/pull/
993
     * @param mixed|Expr $valueOrExpression
994
     * @return $this
995
     */
996 2 View Code Duplication
    public function pull($valueOrExpression)
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...
997
    {
998 2
        if ($valueOrExpression instanceof Expr) {
999 1
            $valueOrExpression = $valueOrExpression->getQuery();
1000
        }
1001
1002 2
        $this->requiresCurrentField();
1003 2
        $this->newObj['$pull'][$this->currentField] = $valueOrExpression;
1004 2
        return $this;
1005
    }
1006
1007
    /**
1008
     * Remove all elements matching any of the given values from the current
1009
     * array field.
1010
     *
1011
     * @see Builder::pullAll()
1012
     * @see http://docs.mongodb.org/manual/reference/operator/pullAll/
1013
     * @param array $values
1014
     * @return $this
1015
     */
1016
    public function pullAll(array $values)
1017
    {
1018
        $this->requiresCurrentField();
1019
        $this->newObj['$pullAll'][$this->currentField] = $values;
1020
        return $this;
1021
    }
1022
1023
    /**
1024
     * Append one or more values to the current array field.
1025
     *
1026
     * If the field does not exist, it will be set to an array containing the
1027
     * value(s) in the argument. If the field is not an array, the query
1028
     * will yield an error.
1029
     *
1030
     * Multiple values may be specified by providing an Expr object and using
1031
     * {@link Expr::each()}. {@link Expr::slice()} and {@link Expr::sort()} may
1032
     * also be used to limit and order array elements, respectively.
1033
     *
1034
     * @see Builder::push()
1035
     * @see http://docs.mongodb.org/manual/reference/operator/push/
1036
     * @see http://docs.mongodb.org/manual/reference/operator/each/
1037
     * @see http://docs.mongodb.org/manual/reference/operator/slice/
1038
     * @see http://docs.mongodb.org/manual/reference/operator/sort/
1039
     * @param mixed|Expr $valueOrExpression
1040
     * @return $this
1041
     */
1042 8 View Code Duplication
    public function push($valueOrExpression)
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...
1043
    {
1044 8
        if ($valueOrExpression instanceof Expr) {
1045 3
            $valueOrExpression = array_merge(
1046 3
                ['$each' => []],
1047 3
                $valueOrExpression->getQuery()
1048
            );
1049
        }
1050
1051 8
        $this->requiresCurrentField();
1052 8
        $this->newObj['$push'][$this->currentField] = $valueOrExpression;
1053 8
        return $this;
1054
    }
1055
1056
    /**
1057
     * Specify $gte and $lt criteria for the current field.
1058
     *
1059
     * This method is shorthand for specifying $gte criteria on the lower bound
1060
     * and $lt criteria on the upper bound. The upper bound is not inclusive.
1061
     *
1062
     * @see Builder::range()
1063
     * @param mixed $start
1064
     * @param mixed $end
1065
     * @return $this
1066
     */
1067 2
    public function range($start, $end)
1068
    {
1069 2
        return $this->operator('$gte', $start)->operator('$lt', $end);
1070
    }
1071
1072
    /**
1073
     * Checks that the value of the current field is a reference to the supplied document.
1074
     *
1075
     * @param object $document
1076
     * @return Expr
1077
     */
1078 13 View Code Duplication
    public function references($document)
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...
1079
    {
1080 13
        $this->requiresCurrentField();
1081 13
        $mapping = $this->getReferenceMapping();
1082 11
        $reference = $this->dm->createReference($document, $mapping);
1083 11
        $storeAs = array_key_exists('storeAs', $mapping) ? $mapping['storeAs'] : null;
1084 11
        $keys = [];
0 ignored issues
show
Unused Code introduced by
$keys is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1085
1086
        switch ($storeAs) {
1087 11
            case ClassMetadataInfo::REFERENCE_STORE_AS_ID:
1088 4
                $this->query[$mapping['name']] = $reference;
1089 4
                return $this;
1090
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
1091
1092 8
            case ClassMetadataInfo::REFERENCE_STORE_AS_REF:
1093
                $keys = ['id' => true];
1094
                break;
1095
1096 8
            case ClassMetadataInfo::REFERENCE_STORE_AS_DB_REF:
1097 3
            case ClassMetadataInfo::REFERENCE_STORE_AS_DB_REF_WITH_DB:
1098 8
                $keys = ['$ref' => true, '$id' => true, '$db' => true];
1099
1100 8
                if ($storeAs === ClassMetadataInfo::REFERENCE_STORE_AS_DB_REF) {
1101 5
                    unset($keys['$db']);
1102
                }
1103
1104 8
                if (isset($mapping['targetDocument'])) {
1105 4
                    unset($keys['$ref'], $keys['$db']);
1106
                }
1107 8
                break;
1108
1109
            default:
1110
                throw new \InvalidArgumentException("Reference type {$storeAs} is invalid.");
1111
        }
1112
1113 8
        foreach ($keys as $key => $value) {
1114 8
            $this->query[$mapping['name'] . '.' . $key] = $reference[$key];
1115
        }
1116
1117 8
        return $this;
1118
    }
1119
1120
    /**
1121
     * Rename the current field.
1122
     *
1123
     * @see Builder::rename()
1124
     * @see http://docs.mongodb.org/manual/reference/operator/rename/
1125
     * @param string $name
1126
     * @return $this
1127
     */
1128
    public function rename($name)
1129
    {
1130
        $this->requiresCurrentField();
1131
        $this->newObj['$rename'][$this->currentField] = $name;
1132
        return $this;
1133
    }
1134
1135
    /**
1136
     * Set the current field to a value.
1137
     *
1138
     * This is only relevant for insert, update, or findAndUpdate queries. For
1139
     * update and findAndUpdate queries, the $atomic parameter will determine
1140
     * whether or not a $set operator is used.
1141
     *
1142
     * @see Builder::set()
1143
     * @see http://docs.mongodb.org/manual/reference/operator/set/
1144
     * @param mixed $value
1145
     * @param boolean $atomic
1146
     * @return $this
1147
     */
1148 19
    public function set($value, $atomic = true)
1149
    {
1150 19
        $this->requiresCurrentField();
1151
1152 19
        if ($atomic) {
1153 16
            $this->newObj['$set'][$this->currentField] = $value;
1154 16
            return $this;
1155
        }
1156
1157 3
        if (strpos($this->currentField, '.') === false) {
1158 2
            $this->newObj[$this->currentField] = $value;
1159 2
            return $this;
1160
        }
1161
1162 2
        $keys = explode('.', $this->currentField);
1163 2
        $current = &$this->newObj;
1164 2
        foreach ($keys as $key) {
1165 2
            $current = &$current[$key];
1166
        }
1167 2
        $current = $value;
1168
1169 2
        return $this;
1170
    }
1171
1172
    /**
1173
     * Sets ClassMetadata for document being queried.
1174
     *
1175
     * @param ClassMetadata $class
1176
     */
1177 390
    public function setClassMetadata(ClassMetadata $class)
1178
    {
1179 390
        $this->class = $class;
1180 390
    }
1181
1182
    /**
1183
     * Set the "new object".
1184
     *
1185
     * @see Builder::setNewObj()
1186
     * @param array $newObj
1187
     * @return $this
1188
     */
1189
    public function setNewObj(array $newObj)
1190
    {
1191
        $this->newObj = $newObj;
1192
        return $this;
1193
    }
1194
1195
    /**
1196
     * Set the current field to the value if the document is inserted in an
1197
     * upsert operation.
1198
     *
1199
     * If an update operation with upsert: true results in an insert of a
1200
     * document, then $setOnInsert assigns the specified values to the fields in
1201
     * the document. If the update operation does not result in an insert,
1202
     * $setOnInsert does nothing.
1203
     *
1204
     * @see Builder::setOnInsert()
1205
     * @see https://docs.mongodb.org/manual/reference/operator/update/setOnInsert/
1206
     * @param mixed $value
1207
     * @return $this
1208
     */
1209 1
    public function setOnInsert($value)
1210
    {
1211 1
        $this->requiresCurrentField();
1212 1
        $this->newObj['$setOnInsert'][$this->currentField] = $value;
1213
1214 1
        return $this;
1215
    }
1216
1217
    /**
1218
     * Set the query criteria.
1219
     *
1220
     * @see Builder::setQueryArray()
1221
     * @param array $query
1222
     * @return $this
1223
     */
1224 18
    public function setQuery(array $query)
1225
    {
1226 18
        $this->query = $query;
1227 18
        return $this;
1228
    }
1229
1230
    /**
1231
     * Specify $size criteria for the current field.
1232
     *
1233
     * @see Builder::size()
1234
     * @see http://docs.mongodb.org/manual/reference/operator/size/
1235
     * @param integer $size
1236
     * @return $this
1237
     */
1238
    public function size($size)
1239
    {
1240
        return $this->operator('$size', (integer) $size);
1241
    }
1242
1243
    /**
1244
     * Add $slice criteria to the expression for a $push operation.
1245
     *
1246
     * This is useful in conjunction with {@link Expr::each()} for a
1247
     * {@link Expr::push()} operation. {@link Builder::selectSlice()} should be
1248
     * used for specifying $slice for a query projection.
1249
     *
1250
     * @see http://docs.mongodb.org/manual/reference/operator/slice/
1251
     * @param integer $slice
1252
     * @return $this
1253
     */
1254 2
    public function slice($slice)
1255
    {
1256 2
        return $this->operator('$slice', $slice);
1257
    }
1258
1259
    /**
1260
     * Add $sort criteria to the expression for a $push operation.
1261
     *
1262
     * If sorting by multiple fields, the first argument should be an array of
1263
     * field name (key) and order (value) pairs.
1264
     *
1265
     * This is useful in conjunction with {@link Expr::each()} for a
1266
     * {@link Expr::push()} operation. {@link Builder::sort()} should be used to
1267
     * sort the results of a query.
1268
     *
1269
     * @see http://docs.mongodb.org/manual/reference/operator/sort/
1270
     * @param array|string $fieldName Field name or array of field/order pairs
1271
     * @param int|string $order       Field order (if one field is specified)
1272
     * @return $this
1273
     */
1274 2
    public function sort($fieldName, $order = null)
1275
    {
1276 2
        $fields = is_array($fieldName) ? $fieldName : [$fieldName => $order];
1277
1278 2
        return $this->operator('$sort', array_map([$this, 'normalizeSortOrder'], $fields));
1279
    }
1280
1281
    /**
1282
     * Specify $text criteria for the current query.
1283
     *
1284
     * The $language option may be set with {@link Expr::language()}.
1285
     *
1286
     * @see Builder::text()
1287
     * @see http://docs.mongodb.org/master/reference/operator/query/text/
1288
     * @param string $search
1289
     * @return $this
1290
     */
1291 6
    public function text($search)
1292
    {
1293 6
        $this->query['$text'] = ['$search' => (string) $search];
1294 6
        return $this;
1295
    }
1296
1297
    /**
1298
     * Specify $type criteria for the current field.
1299
     *
1300
     * @todo Remove support for string $type argument in 2.0
1301
     * @see Builder::type()
1302
     * @see http://docs.mongodb.org/manual/reference/operator/type/
1303
     * @param integer $type
1304
     * @return $this
1305
     */
1306 1
    public function type($type)
1307
    {
1308 1
        if (is_string($type)) {
1309
            $map = [
1310 1
                'double' => 1,
1311
                'string' => 2,
1312
                'object' => 3,
1313
                'array' => 4,
1314
                'binary' => 5,
1315
                'undefined' => 6,
1316
                'objectid' => 7,
1317
                'boolean' => 8,
1318
                'date' => 9,
1319
                'null' => 10,
1320
                'regex' => 11,
1321
                'jscode' => 13,
1322
                'symbol' => 14,
1323
                'jscodewithscope' => 15,
1324
                'integer32' => 16,
1325
                'timestamp' => 17,
1326
                'integer64' => 18,
1327
                'maxkey' => 127,
1328
                'minkey' => 255,
1329
            ];
1330
1331 1
            $type = $map[$type] ?? $type;
1332
        }
1333
1334 1
        return $this->operator('$type', $type);
1335
    }
1336
1337
    /**
1338
     * Unset the current field.
1339
     *
1340
     * The field will be removed from the document (not set to null).
1341
     *
1342
     * @see Builder::unsetField()
1343
     * @see http://docs.mongodb.org/manual/reference/operator/unset/
1344
     * @return $this
1345
     */
1346 3
    public function unsetField()
1347
    {
1348 3
        $this->requiresCurrentField();
1349 3
        $this->newObj['$unset'][$this->currentField] = 1;
1350 3
        return $this;
1351
    }
1352
1353
    /**
1354
     * Specify a JavaScript expression to use for matching documents.
1355
     *
1356
     * @see Builder::where()
1357
     * @see http://docs.mongodb.org/manual/reference/operator/where/
1358
     * @param string|\MongoDB\BSON\Javascript $javascript
1359
     * @return $this
1360
     */
1361 3
    public function where($javascript)
1362
    {
1363 3
        $this->query['$where'] = $javascript;
1364 3
        return $this;
1365
    }
1366
1367
    /**
1368
     * Gets reference mapping for current field from current class or its descendants.
1369
     *
1370
     * @return array
1371
     * @throws MappingException
1372
     */
1373 19
    private function getReferenceMapping()
1374
    {
1375 19
        $mapping = null;
1376
        try {
1377 19
            $mapping = $this->class->getFieldMapping($this->currentField);
1378 6
        } catch (MappingException $e) {
1379 6
            if (empty($this->class->discriminatorMap)) {
1380
                throw $e;
1381
            }
1382 6
            $foundIn = null;
1383 6
            foreach ($this->class->discriminatorMap as $child) {
1384 6
                $childClass = $this->dm->getClassMetadata($child);
1385 6
                if ($childClass->hasAssociation($this->currentField)) {
1386 4
                    if ($mapping !== null && $mapping !== $childClass->getFieldMapping($this->currentField)) {
1387 2
                        throw MappingException::referenceFieldConflict($this->currentField, $foundIn->name, $childClass->name);
1388
                    }
1389 4
                    $mapping = $childClass->getFieldMapping($this->currentField);
1390 6
                    $foundIn = $childClass;
1391
                }
1392
            }
1393 4
            if ($mapping === null) {
1394 2
                throw MappingException::mappingNotFoundInClassNorDescendants($this->class->name, $this->currentField);
1395
            }
1396
        }
1397 15
        return $mapping;
1398
    }
1399
1400
    /**
1401
     * @param int|string $order
1402
     *
1403
     * @return int
1404
     */
1405 2
    private function normalizeSortOrder($order): int
1406
    {
1407 2
        if (is_string($order)) {
1408
            $order = strtolower($order) === 'asc' ? 1 : -1;
1409
        }
1410
1411 2
        return (int) $order;
1412
    }
1413
1414
    /**
1415
     * Ensure that a current field has been set.
1416
     *
1417
     * @throws \LogicException if a current field has not been set
1418
     */
1419 67
    private function requiresCurrentField()
1420
    {
1421 67
        if ( ! $this->currentField) {
1422
            throw new \LogicException('This method requires you set a current field using field().');
1423
        }
1424 67
    }
1425
1426
    /**
1427
     * Wraps equality criteria with an operator.
1428
     *
1429
     * If equality criteria was previously specified for a field, it cannot be
1430
     * merged with other operators without first being wrapped in an operator of
1431
     * its own. Ideally, we would wrap it with $eq, but that is only available
1432
     * in MongoDB 2.8. Using a single-element $in is backwards compatible.
1433
     *
1434
     * @see Expr::operator()
1435
     */
1436 81
    private function wrapEqualityCriteria()
1437
    {
1438
        /* If the current field has no criteria yet, do nothing. This ensures
1439
         * that we do not inadvertently inject {"$in": null} into the query.
1440
         */
1441 81
        if ($this->currentField && ! isset($this->query[$this->currentField]) && ! array_key_exists($this->currentField, $this->query)) {
1442 55
            return;
1443
        }
1444
1445 31
        if ($this->currentField) {
1446 5
            $query = &$this->query[$this->currentField];
1447
        } else {
1448 27
            $query = &$this->query;
1449
        }
1450
1451
        /* If the query is an empty array, we'll assume that the user has not
1452
         * specified criteria. Otherwise, check if the array includes a query
1453
         * operator (checking the first key is sufficient). If neither of these
1454
         * conditions are met, we'll wrap the query value with $in.
1455
         */
1456 31
        if (is_array($query) && (empty($query) || strpos(key($query), '$') === 0)) {
1457 31
            return;
1458
        }
1459
1460 2
        $query = ['$in' => [$query]];
1461 2
    }
1462
}
1463