Completed
Pull Request — master (#1606)
by Maciej
08:09
created

Query::isIndexRequired()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 2
eloc 2
nc 2
nop 0
crap 2
1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\ODM\MongoDB\Query;
21
22
use Doctrine\MongoDB\Collection;
23
use Doctrine\MongoDB\Cursor as BaseCursor;
24
use Doctrine\MongoDB\CursorInterface;
25
use Doctrine\ODM\MongoDB\Cursor;
26
use Doctrine\ODM\MongoDB\DocumentManager;
27
use Doctrine\ODM\MongoDB\EagerCursor;
28
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
29
use Doctrine\ODM\MongoDB\MongoDBException;
30
31
/**
32
 * ODM Query wraps the raw Doctrine MongoDB queries to add additional functionality
33
 * and to hydrate the raw arrays of data to Doctrine document objects.
34
 *
35
 * @since       1.0
36
 */
37
class Query extends \Doctrine\MongoDB\Query\Query
38
{
39
    const HINT_REFRESH = 1;
40
    const HINT_SLAVE_OKAY = 2;
41
    const HINT_READ_PREFERENCE = 3;
42
    const HINT_READ_PREFERENCE_TAGS = 4;
43
    const HINT_READ_ONLY = 5;
44
45
    /**
46
     * The DocumentManager instance.
47
     *
48
     * @var DocumentManager
49
     */
50
    private $dm;
51
52
    /**
53
     * The ClassMetadata instance.
54
     *
55
     * @var ClassMetadata
56
     */
57
    private $class;
58
59
    /**
60
     * Whether to hydrate results as document class instances.
61
     *
62
     * @var boolean
63
     */
64
    private $hydrate = true;
65
66
    /**
67
     * Array of primer Closure instances.
68
     *
69
     * @var array
70
     */
71
    private $primers = array();
72
73
    /**
74
     * Whether or not to require indexes.
75
     *
76
     * @var boolean
77
     */
78
    private $requireIndexes;
79
80
    /**
81
     * Hints for UnitOfWork behavior.
82
     *
83
     * @var array
84
     */
85
    private $unitOfWorkHints = array();
86
87
    /**
88
     * Constructor.
89
     *
90
     * Please note that $requireIndexes was deprecated in 1.2 and will be removed in 2.0
91
     *
92
     * @param DocumentManager $dm
93
     * @param ClassMetadata $class
94
     * @param Collection $collection
95
     * @param array $query
96
     * @param array $options
97
     * @param boolean $hydrate
98
     * @param boolean $refresh
99
     * @param array $primers
100
     * @param null $requireIndexes deprecated
101
     * @param boolean $readOnly
102
     */
103 200
    public function __construct(DocumentManager $dm, ClassMetadata $class, Collection $collection, array $query = array(), array $options = array(), $hydrate = true, $refresh = false, array $primers = array(), $requireIndexes = null, $readOnly = false)
104
    {
105 200
        $primers = array_filter($primers);
106
107 200
        if ( ! empty($primers)) {
108 23
            $query['eagerCursor'] = true;
109
        }
110
111 200
        if ( ! empty($query['eagerCursor'])) {
112 24
            $query['useIdentifierKeys'] = false;
113
        }
114
115 200
        parent::__construct($collection, $query, $options);
116 200
        $this->dm = $dm;
117 200
        $this->class = $class;
118 200
        $this->hydrate = $hydrate;
119 200
        $this->primers = $primers;
120 200
        $this->requireIndexes = $requireIndexes;
121
122 200
        $this->setReadOnly($readOnly);
123 200
        $this->setRefresh($refresh);
124
125 200
        if (isset($query['slaveOkay'])) {
126 4
            $this->unitOfWorkHints[self::HINT_SLAVE_OKAY] = $query['slaveOkay'];
127
        }
128
129 200
        if (isset($query['readPreference'])) {
130 3
            $this->unitOfWorkHints[self::HINT_READ_PREFERENCE] = $query['readPreference'];
131 3
            $this->unitOfWorkHints[self::HINT_READ_PREFERENCE_TAGS] = $query['readPreferenceTags'];
132
        }
133 200
    }
134
135
    /**
136
     * Gets the DocumentManager instance.
137
     *
138
     * @return DocumentManager $dm
139
     */
140
    public function getDocumentManager()
141
    {
142
        return $this->dm;
143
    }
144
145
    /**
146
     * Gets the ClassMetadata instance.
147
     *
148
     * @return ClassMetadata $class
149
     */
150
    public function getClass()
151
    {
152
        return $this->class;
153
    }
154
155
    /**
156
     * Sets whether or not to hydrate the documents to objects.
157
     *
158
     * @param boolean $hydrate
159
     */
160
    public function setHydrate($hydrate)
161
    {
162
        $this->hydrate = (boolean) $hydrate;
163
    }
164
165
    /**
166
     * Set whether documents should be registered in UnitOfWork. If document would
167
     * already be managed it will be left intact and new instance returned.
168
     * 
169
     * This option has no effect if hydration is disabled.
170
     * 
171
     * @param boolean $readOnly
172
     */
173 200
    public function setReadOnly($readOnly)
174
    {
175 200
        $this->unitOfWorkHints[Query::HINT_READ_ONLY] = (boolean) $readOnly;
176 200
    }
177
178
    /**
179
     * Set whether to refresh hydrated documents that are already in the
180
     * identity map.
181
     *
182
     * This option has no effect if hydration is disabled.
183
     *
184
     * @param boolean $refresh
185
     */
186 200
    public function setRefresh($refresh)
187
    {
188 200
        $this->unitOfWorkHints[Query::HINT_REFRESH] = (boolean) $refresh;
189 200
    }
190
191
    /**
192
     * Gets the fields involved in this query.
193
     *
194
     * @return array $fields An array of fields names used in this query.
195
     *
196
     * @deprecated method was deprecated in 1.2 and will be removed in 2.0
197
     */
198 22
    public function getFieldsInQuery()
199
    {
200 22
        @trigger_error(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
201 22
            sprintf('%s was deprecated in version 1.2 and will be removed altogether in 2.0.', __METHOD__),
202 22
            E_USER_DEPRECATED
203
        );
204 22
        $query = isset($this->query['query']) ? $this->query['query'] : array();
205 22
        $sort = isset($this->query['sort']) ? $this->query['sort'] : array();
206
207 22
        $extractor = new FieldExtractor($query, $sort);
0 ignored issues
show
Deprecated Code introduced by
The class Doctrine\ODM\MongoDB\Query\FieldExtractor has been deprecated with message: class was deprecated in 1.2 and will be removed in 2.0

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

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

Loading history...
208 22
        return $extractor->getFields();
209
    }
210
211
    /**
212
     * Check if this query is indexed.
213
     *
214
     * @return bool
215
     *
216
     * @deprecated method was deprecated in 1.2 and will be removed in 2.0
217
     */
218 8
    public function isIndexed()
219
    {
220 8
        @trigger_error(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
221 8
            sprintf('%s was deprecated in version 1.2 and will be removed altogether in 2.0.', __METHOD__),
222 8
            E_USER_DEPRECATED
223
        );
224 8
        $fields = $this->getFieldsInQuery();
0 ignored issues
show
Deprecated Code introduced by
The method Doctrine\ODM\MongoDB\Que...ery::getFieldsInQuery() has been deprecated with message: method was deprecated in 1.2 and will be removed in 2.0

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

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

Loading history...
225 8
        foreach ($fields as $field) {
226 8
            if ( ! $this->collection->isFieldIndexed($field)) {
227 6
                return false;
228
            }
229
        }
230 2
        return true;
231
    }
232
233
    /**
234
     * Gets an array of the unindexed fields in this query.
235
     *
236
     * @return array
237
     *
238
     * @deprecated method was deprecated in 1.2 and will be removed in 2.0
239
     */
240 6
    public function getUnindexedFields()
241
    {
242 6
        @trigger_error(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
243 6
            sprintf('%s was deprecated in version 1.2 and will be removed altogether in 2.0.', __METHOD__),
244 6
            E_USER_DEPRECATED
245
        );
246 6
        $unindexedFields = array();
247 6
        $fields = $this->getFieldsInQuery();
0 ignored issues
show
Deprecated Code introduced by
The method Doctrine\ODM\MongoDB\Que...ery::getFieldsInQuery() has been deprecated with message: method was deprecated in 1.2 and will be removed in 2.0

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

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

Loading history...
248 6
        foreach ($fields as $field) {
249 6
            if ( ! $this->collection->isFieldIndexed($field)) {
250 6
                $unindexedFields[] = $field;
251
            }
252
        }
253 6
        return $unindexedFields;
254
    }
255
256
    /**
257
     * Execute the query and returns the results.
258
     *
259
     * @throws \Doctrine\ODM\MongoDB\MongoDBException
260
     * @return mixed
261
     */
262 154
    public function execute()
263
    {
264 154
        if ($this->isIndexRequired() && ! $this->isIndexed()) {
0 ignored issues
show
Deprecated Code introduced by
The method Doctrine\ODM\MongoDB\Query\Query::isIndexed() has been deprecated with message: method was deprecated in 1.2 and will be removed in 2.0

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

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

Loading history...
265 5
            throw MongoDBException::queryNotIndexed($this->class->name, $this->getUnindexedFields());
0 ignored issues
show
Documentation introduced by
$this->getUnindexedFields() 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...
Deprecated Code introduced by
The method Doctrine\ODM\MongoDB\Que...y::getUnindexedFields() has been deprecated with message: method was deprecated in 1.2 and will be removed in 2.0

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

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

Loading history...
Deprecated Code introduced by
The method Doctrine\ODM\MongoDB\Mon...tion::queryNotIndexed() has been deprecated with message: method was deprecated in 1.2 and will be removed in 2.0

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

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

Loading history...
266
        }
267
268 149
        $results = parent::execute();
269
270 148
        if ( ! $this->hydrate) {
271 10
            return $results;
272
        }
273
274 141
        $uow = $this->dm->getUnitOfWork();
275
276
        /* A geoNear command returns an ArrayIterator, where each result is an
277
         * object with "dis" (computed distance) and "obj" (original document)
278
         * properties. If hydration is enabled, eagerly hydrate these results.
279
         *
280
         * Other commands results are not handled, since their results may not
281
         * resemble documents in the collection.
282
         */
283 141
        if ($this->query['type'] === self::TYPE_GEO_NEAR) {
284 2
            foreach ($results as $key => $result) {
285 2
                $document = $result['obj'];
286 2
                if ($this->class->distance !== null) {
287 2
                    $document[$this->class->distance] = $result['dis'];
288
                }
289 2
                $results[$key] = $uow->getOrCreateDocument($this->class->name, $document, $this->unitOfWorkHints);
290
            }
291 2
            $results->reset();
292
        }
293
294
        /* If a single document is returned from a findAndModify command and it
295
         * includes the identifier field, attempt hydration.
296
         */
297 141
        if (($this->query['type'] === self::TYPE_FIND_AND_UPDATE ||
298 138
             $this->query['type'] === self::TYPE_FIND_AND_REMOVE) &&
299 6
            is_array($results) && isset($results['_id'])) {
300
301 5
            $results = $uow->getOrCreateDocument($this->class->name, $results, $this->unitOfWorkHints);
302
303 5
            if ( ! empty($this->primers)) {
304 1
                $referencePrimer = new ReferencePrimer($this->dm, $uow);
305
306 1 View Code Duplication
                foreach ($this->primers as $fieldName => $primer) {
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...
307 1
                    $primer = is_callable($primer) ? $primer : null;
308 1
                    $referencePrimer->primeReferences($this->class, array($results), $fieldName, $this->unitOfWorkHints, $primer);
309
                }
310
            }
311
        }
312
313 141
        return $results;
314
    }
315
316
    /**
317
     * Prepare the Cursor returned by {@link Query::execute()}.
318
     *
319
     * This method will wrap the base Cursor with an ODM Cursor or EagerCursor,
320
     * and set the hydrate option and UnitOfWork hints. This occurs in addition
321
     * to any preparation done by the base Query class.
322
     *
323
     * @see \Doctrine\MongoDB\Cursor::prepareCursor()
324
     * @param BaseCursor $cursor
325
     * @return CursorInterface
326
     */
327 133
    protected function prepareCursor(BaseCursor $cursor)
328
    {
329 133
        $cursor = parent::prepareCursor($cursor);
330
331
        // Convert the base Cursor into an ODM Cursor
332 133
        $cursorClass = ( ! empty($this->query['eagerCursor'])) ? EagerCursor::class : Cursor::class;
333 133
        $cursor = new $cursorClass($cursor, $this->dm->getUnitOfWork(), $this->class);
334
335 133
        $cursor->hydrate($this->hydrate);
336 133
        $cursor->setHints($this->unitOfWorkHints);
337
338 133
        if ( ! empty($this->primers)) {
339 21
            $referencePrimer = new ReferencePrimer($this->dm, $this->dm->getUnitOfWork());
340 21
            $cursor->enableReferencePriming($this->primers, $referencePrimer);
341
        }
342
343 133
        return $cursor;
344
    }
345
346
    /**
347
     * Return whether queries on this document should require indexes.
348
     *
349
     * @return boolean
350
     */
351 154
    private function isIndexRequired()
352
    {
353 154
        return $this->requireIndexes !== null ? $this->requireIndexes : $this->class->requireIndexes;
0 ignored issues
show
Deprecated Code introduced by
The property Doctrine\ODM\MongoDB\Map...taInfo::$requireIndexes has been deprecated with message: property was deprecated in 1.2 and will be removed in 2.0

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

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

Loading history...
354
    }
355
}
356