Completed
Pull Request — master (#1610)
by Maciej
09:22
created

Query   B

Complexity

Total Complexity 38

Size/Duplication

Total Lines 320
Duplicated Lines 1.25 %

Coupling/Cohesion

Components 1
Dependencies 8

Test Coverage

Coverage 92.71%

Importance

Changes 0
Metric Value
wmc 38
lcom 1
cbo 8
dl 4
loc 320
ccs 89
cts 96
cp 0.9271
rs 8.3999
c 0
b 0
f 0

12 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 31 5
A getDocumentManager() 0 4 1
A getClass() 0 4 1
A setHydrate() 0 4 1
A setReadOnly() 0 4 1
A setRefresh() 0 4 1
A getFieldsInQuery() 0 12 3
A isIndexed() 0 14 3
A getUnindexedFields() 0 15 3
C execute() 4 53 14
A prepareCursor() 0 18 3
A isIndexRequired() 0 4 2

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

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
    /** @deprecated */
41
    const HINT_SLAVE_OKAY = 2;
42
    const HINT_READ_PREFERENCE = 3;
43
    const HINT_READ_PREFERENCE_TAGS = 4;
44
    const HINT_READ_ONLY = 5;
45
46
    /**
47
     * The DocumentManager instance.
48
     *
49
     * @var DocumentManager
50
     */
51
    private $dm;
52
53
    /**
54
     * The ClassMetadata instance.
55
     *
56
     * @var ClassMetadata
57
     */
58
    private $class;
59
60
    /**
61
     * Whether to hydrate results as document class instances.
62
     *
63
     * @var boolean
64
     */
65
    private $hydrate = true;
66
67
    /**
68
     * Array of primer Closure instances.
69
     *
70
     * @var array
71
     */
72
    private $primers = array();
73
74
    /**
75
     * Whether or not to require indexes.
76
     *
77
     * @var boolean
78
     */
79
    private $requireIndexes;
80
81
    /**
82
     * Hints for UnitOfWork behavior.
83
     *
84
     * @var array
85
     */
86
    private $unitOfWorkHints = array();
87
88
    /**
89
     * Constructor.
90
     *
91
     * Please note that $requireIndexes was deprecated in 1.2 and will be removed in 2.0
92
     *
93
     * @param DocumentManager $dm
94
     * @param ClassMetadata $class
95
     * @param Collection $collection
96
     * @param array $query
97
     * @param array $options
98
     * @param boolean $hydrate
99
     * @param boolean $refresh
100
     * @param array $primers
101
     * @param null $requireIndexes deprecated
102
     * @param boolean $readOnly
103
     */
104 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)
105
    {
106 200
        $primers = array_filter($primers);
107
108 200
        if ( ! empty($primers)) {
109 23
            $query['eagerCursor'] = true;
110
        }
111
112 200
        if ( ! empty($query['eagerCursor'])) {
113 24
            $query['useIdentifierKeys'] = false;
114
        }
115
116 200
        parent::__construct($collection, $query, $options);
117 200
        $this->dm = $dm;
118 200
        $this->class = $class;
119 200
        $this->hydrate = $hydrate;
120 200
        $this->primers = $primers;
121 200
        $this->requireIndexes = $requireIndexes;
122
123 200
        $this->setReadOnly($readOnly);
124 200
        $this->setRefresh($refresh);
125
126 200
        if (isset($query['slaveOkay'])) {
127 4
            $this->unitOfWorkHints[self::HINT_SLAVE_OKAY] = $query['slaveOkay'];
0 ignored issues
show
Deprecated Code introduced by
The constant Doctrine\ODM\MongoDB\Query\Query::HINT_SLAVE_OKAY has been deprecated.

This class constant has been deprecated.

Loading history...
128
        }
129
130 200
        if (isset($query['readPreference'])) {
131 3
            $this->unitOfWorkHints[self::HINT_READ_PREFERENCE] = $query['readPreference'];
132 3
            $this->unitOfWorkHints[self::HINT_READ_PREFERENCE_TAGS] = $query['readPreferenceTags'];
133
        }
134 200
    }
135
136
    /**
137
     * Gets the DocumentManager instance.
138
     *
139
     * @return DocumentManager $dm
140
     */
141
    public function getDocumentManager()
142
    {
143
        return $this->dm;
144
    }
145
146
    /**
147
     * Gets the ClassMetadata instance.
148
     *
149
     * @return ClassMetadata $class
150
     */
151
    public function getClass()
152
    {
153
        return $this->class;
154
    }
155
156
    /**
157
     * Sets whether or not to hydrate the documents to objects.
158
     *
159
     * @param boolean $hydrate
160
     */
161
    public function setHydrate($hydrate)
162
    {
163
        $this->hydrate = (boolean) $hydrate;
164
    }
165
166
    /**
167
     * Set whether documents should be registered in UnitOfWork. If document would
168
     * already be managed it will be left intact and new instance returned.
169
     * 
170
     * This option has no effect if hydration is disabled.
171
     * 
172
     * @param boolean $readOnly
173
     */
174 200
    public function setReadOnly($readOnly)
175
    {
176 200
        $this->unitOfWorkHints[Query::HINT_READ_ONLY] = (boolean) $readOnly;
177 200
    }
178
179
    /**
180
     * Set whether to refresh hydrated documents that are already in the
181
     * identity map.
182
     *
183
     * This option has no effect if hydration is disabled.
184
     *
185
     * @param boolean $refresh
186
     */
187 200
    public function setRefresh($refresh)
188
    {
189 200
        $this->unitOfWorkHints[Query::HINT_REFRESH] = (boolean) $refresh;
190 200
    }
191
192
    /**
193
     * Gets the fields involved in this query.
194
     *
195
     * @return array $fields An array of fields names used in this query.
196
     *
197
     * @deprecated method was deprecated in 1.2 and will be removed in 2.0
198
     */
199 22
    public function getFieldsInQuery()
200
    {
201 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...
202 22
            sprintf('%s was deprecated in version 1.2 and will be removed altogether in 2.0.', __METHOD__),
203 22
            E_USER_DEPRECATED
204
        );
205 22
        $query = isset($this->query['query']) ? $this->query['query'] : array();
206 22
        $sort = isset($this->query['sort']) ? $this->query['sort'] : array();
207
208 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...
209 22
        return $extractor->getFields();
210
    }
211
212
    /**
213
     * Check if this query is indexed.
214
     *
215
     * @return bool
216
     *
217
     * @deprecated method was deprecated in 1.2 and will be removed in 2.0
218
     */
219 8
    public function isIndexed()
220
    {
221 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...
222 8
            sprintf('%s was deprecated in version 1.2 and will be removed altogether in 2.0.', __METHOD__),
223 8
            E_USER_DEPRECATED
224
        );
225 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...
226 8
        foreach ($fields as $field) {
227 8
            if ( ! $this->collection->isFieldIndexed($field)) {
228 8
                return false;
229
            }
230
        }
231 2
        return true;
232
    }
233
234
    /**
235
     * Gets an array of the unindexed fields in this query.
236
     *
237
     * @return array
238
     *
239
     * @deprecated method was deprecated in 1.2 and will be removed in 2.0
240
     */
241 6
    public function getUnindexedFields()
242
    {
243 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...
244 6
            sprintf('%s was deprecated in version 1.2 and will be removed altogether in 2.0.', __METHOD__),
245 6
            E_USER_DEPRECATED
246
        );
247 6
        $unindexedFields = array();
248 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...
249 6
        foreach ($fields as $field) {
250 6
            if ( ! $this->collection->isFieldIndexed($field)) {
251 6
                $unindexedFields[] = $field;
252
            }
253
        }
254 6
        return $unindexedFields;
255
    }
256
257
    /**
258
     * Execute the query and returns the results.
259
     *
260
     * @throws \Doctrine\ODM\MongoDB\MongoDBException
261
     * @return mixed
262
     */
263 154
    public function execute()
264
    {
265 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...
266 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...
267
        }
268
269 149
        $results = parent::execute();
270
271 148
        if ( ! $this->hydrate) {
272 10
            return $results;
273
        }
274
275 141
        $uow = $this->dm->getUnitOfWork();
276
277
        /* A geoNear command returns an ArrayIterator, where each result is an
278
         * object with "dis" (computed distance) and "obj" (original document)
279
         * properties. If hydration is enabled, eagerly hydrate these results.
280
         *
281
         * Other commands results are not handled, since their results may not
282
         * resemble documents in the collection.
283
         */
284 141
        if ($this->query['type'] === self::TYPE_GEO_NEAR) {
285 2
            foreach ($results as $key => $result) {
286 2
                $document = $result['obj'];
287 2
                if ($this->class->distance !== null) {
288 2
                    $document[$this->class->distance] = $result['dis'];
289
                }
290 2
                $results[$key] = $uow->getOrCreateDocument($this->class->name, $document, $this->unitOfWorkHints);
291
            }
292 2
            $results->reset();
293
        }
294
295
        /* If a single document is returned from a findAndModify command and it
296
         * includes the identifier field, attempt hydration.
297
         */
298 141
        if (($this->query['type'] === self::TYPE_FIND_AND_UPDATE ||
299 141
             $this->query['type'] === self::TYPE_FIND_AND_REMOVE) &&
300 141
            is_array($results) && isset($results['_id'])) {
301
302 5
            $results = $uow->getOrCreateDocument($this->class->name, $results, $this->unitOfWorkHints);
303
304 5
            if ( ! empty($this->primers)) {
305 1
                $referencePrimer = new ReferencePrimer($this->dm, $uow);
306
307 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...
308 1
                    $primer = is_callable($primer) ? $primer : null;
309 1
                    $referencePrimer->primeReferences($this->class, array($results), $fieldName, $this->unitOfWorkHints, $primer);
310
                }
311
            }
312
        }
313
314 141
        return $results;
315
    }
316
317
    /**
318
     * Prepare the Cursor returned by {@link Query::execute()}.
319
     *
320
     * This method will wrap the base Cursor with an ODM Cursor or EagerCursor,
321
     * and set the hydrate option and UnitOfWork hints. This occurs in addition
322
     * to any preparation done by the base Query class.
323
     *
324
     * @see \Doctrine\MongoDB\Cursor::prepareCursor()
325
     * @param BaseCursor $cursor
326
     * @return CursorInterface
327
     */
328 133
    protected function prepareCursor(BaseCursor $cursor)
329
    {
330 133
        $cursor = parent::prepareCursor($cursor);
331
332
        // Convert the base Cursor into an ODM Cursor
333 133
        $cursorClass = ( ! empty($this->query['eagerCursor'])) ? EagerCursor::class : Cursor::class;
334 133
        $cursor = new $cursorClass($cursor, $this->dm->getUnitOfWork(), $this->class);
335
336 133
        $cursor->hydrate($this->hydrate);
337 133
        $cursor->setHints($this->unitOfWorkHints);
338
339 133
        if ( ! empty($this->primers)) {
340 21
            $referencePrimer = new ReferencePrimer($this->dm, $this->dm->getUnitOfWork());
341 21
            $cursor->enableReferencePriming($this->primers, $referencePrimer);
342
        }
343
344 133
        return $cursor;
345
    }
346
347
    /**
348
     * Return whether queries on this document should require indexes.
349
     *
350
     * @return boolean
351
     */
352 154
    private function isIndexRequired()
353
    {
354 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...
355
    }
356
}
357