Completed
Pull Request — master (#1385)
by Andreas
18:26
created

Query   B

Complexity

Total Complexity 37

Size/Duplication

Total Lines 283
Duplicated Lines 1.41 %

Coupling/Cohesion

Components 1
Dependencies 8

Test Coverage

Coverage 92.78%
Metric Value
wmc 37
lcom 1
cbo 8
dl 4
loc 283
ccs 90
cts 97
cp 0.9278
rs 8.6

11 Methods

Rating   Name   Duplication   Size   Complexity  
A getDocumentManager() 0 4 1
A getClass() 0 4 1
A setHydrate() 0 4 1
A setRefresh() 0 4 1
A isIndexRequired() 0 4 2
A getFieldsInQuery() 0 8 3
A isIndexed() 0 10 3
A getUnindexedFields() 0 11 3
C execute() 4 53 14
B __construct() 0 30 5
A prepareCursor() 0 18 3

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
    const HINT_SLAVE_OKAY = 2;
41
    const HINT_READ_PREFERENCE = 3;
42
    const HINT_READ_PREFERENCE_TAGS = 4;
43
44
    /**
45
     * The DocumentManager instance.
46
     *
47
     * @var DocumentManager
48
     */
49
    private $dm;
50
51
    /**
52
     * The ClassMetadata instance.
53
     *
54
     * @var ClassMetadata
55
     */
56
    private $class;
57
58
    /**
59
     * Whether to hydrate results as document class instances.
60
     *
61
     * @var boolean
62
     */
63
    private $hydrate = true;
64
65
    /**
66
     * Array of primer Closure instances.
67
     *
68
     * @var array
69
     */
70
    private $primers = array();
71
72
    /**
73
     * Whether or not to require indexes.
74
     *
75
     * @var boolean
76
     */
77
    private $requireIndexes;
78
79
    /**
80
     * Hints for UnitOfWork behavior.
81
     *
82
     * @var array
83
     */
84
    private $unitOfWorkHints = array();
85
86
    /**
87
     * Constructor.
88
     *
89
     * @param DocumentManager $dm
90
     * @param ClassMetadata $class
91
     * @param Collection $collection
92
     * @param array $query
93
     * @param array $options
94
     * @param boolean $hydrate
95
     * @param boolean $refresh
96
     * @param array $primers
97
     * @param null $requireIndexes
98
     */
99 183
    public function __construct(DocumentManager $dm, ClassMetadata $class, Collection $collection, array $query = array(), array $options = array(), $hydrate = true, $refresh = false, array $primers = array(), $requireIndexes = null)
100
    {
101 183
        $primers = array_filter($primers);
102
103 183
        if ( ! empty($primers)) {
104 17
            $query['eagerCursor'] = true;
105 17
        }
106
107 183
        if ( ! empty($query['eagerCursor'])) {
108 17
            $query['useIdentifierKeys'] = false;
109 17
        }
110
111 183
        parent::__construct($collection, $query, $options);
112 183
        $this->dm = $dm;
113 183
        $this->class = $class;
114 183
        $this->hydrate = $hydrate;
115 183
        $this->primers = $primers;
116 183
        $this->requireIndexes = $requireIndexes;
117
118 183
        $this->setRefresh($refresh);
119
120 183
        if (isset($query['slaveOkay'])) {
121 4
            $this->unitOfWorkHints[self::HINT_SLAVE_OKAY] = $query['slaveOkay'];
122 4
        }
123
124 183
        if (isset($query['readPreference'])) {
125 3
            $this->unitOfWorkHints[self::HINT_READ_PREFERENCE] = $query['readPreference'];
126 3
            $this->unitOfWorkHints[self::HINT_READ_PREFERENCE_TAGS] = $query['readPreferenceTags'];
127 3
        }
128 183
    }
129
130
    /**
131
     * Gets the DocumentManager instance.
132
     *
133
     * @return DocumentManager $dm
134
     */
135
    public function getDocumentManager()
136
    {
137
        return $this->dm;
138
    }
139
140
    /**
141
     * Gets the ClassMetadata instance.
142
     *
143
     * @return ClassMetadata $class
144
     */
145
    public function getClass()
146
    {
147
        return $this->class;
148
    }
149
150
    /**
151
     * Sets whether or not to hydrate the documents to objects.
152
     *
153
     * @param boolean $hydrate
154
     */
155
    public function setHydrate($hydrate)
156
    {
157
        $this->hydrate = (boolean) $hydrate;
158
    }
159
160
    /**
161
     * Set whether to refresh hydrated documents that are already in the
162
     * identity map.
163
     *
164
     * This option has no effect if hydration is disabled.
165
     *
166
     * @param boolean $refresh
167
     */
168 183
    public function setRefresh($refresh)
169
    {
170 183
        $this->unitOfWorkHints[Query::HINT_REFRESH] = (boolean) $refresh;
171 183
    }
172
173
    /**
174
     * Gets the fields involved in this query.
175
     *
176
     * @return array $fields An array of fields names used in this query.
177
     */
178 22
    public function getFieldsInQuery()
179
    {
180 22
        $query = isset($this->query['query']) ? $this->query['query'] : array();
181 22
        $sort = isset($this->query['sort']) ? $this->query['sort'] : array();
182
183 22
        $extractor = new FieldExtractor($query, $sort);
184 22
        return $extractor->getFields();
185
    }
186
187
    /**
188
     * Check if this query is indexed.
189
     *
190
     * @return bool
191
     */
192 8
    public function isIndexed()
193
    {
194 8
        $fields = $this->getFieldsInQuery();
195 8
        foreach ($fields as $field) {
196 8
            if ( ! $this->collection->isFieldIndexed($field)) {
197 6
                return false;
198
            }
199 3
        }
200 2
        return true;
201
    }
202
203
    /**
204
     * Gets an array of the unindexed fields in this query.
205
     *
206
     * @return array
207
     */
208 6
    public function getUnindexedFields()
209
    {
210 6
        $unindexedFields = array();
211 6
        $fields = $this->getFieldsInQuery();
212 6
        foreach ($fields as $field) {
213 6
            if ( ! $this->collection->isFieldIndexed($field)) {
214 6
                $unindexedFields[] = $field;
215 6
            }
216 6
        }
217 6
        return $unindexedFields;
218
    }
219
220
    /**
221
     * Execute the query and returns the results.
222
     *
223
     * @throws \Doctrine\ODM\MongoDB\MongoDBException
224
     * @return mixed
225
     */
226 143
    public function execute()
227
    {
228 143
        if ($this->isIndexRequired() && ! $this->isIndexed()) {
229 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...
230
        }
231
232 138
        $results = parent::execute();
233
234 137
        if ( ! $this->hydrate) {
235 10
            return $results;
236
        }
237
238 130
        $uow = $this->dm->getUnitOfWork();
239
240
        /* A geoNear command returns an ArrayIterator, where each result is an
241
         * object with "dis" (computed distance) and "obj" (original document)
242
         * properties. If hydration is enabled, eagerly hydrate these results.
243
         *
244
         * Other commands results are not handled, since their results may not
245
         * resemble documents in the collection.
246
         */
247 130
        if ($this->query['type'] === self::TYPE_GEO_NEAR) {
248 2
            foreach ($results as $key => $result) {
249 2
                $document = $result['obj'];
250 2
                if ($this->class->distance !== null) {
251 2
                    $document[$this->class->distance] = $result['dis'];
252 2
                }
253 2
                $results[$key] = $uow->getOrCreateDocument($this->class->name, $document, $this->unitOfWorkHints);
254 2
            }
255 2
            $results->reset();
256 2
        }
257
258
        /* If a single document is returned from a findAndModify command and it
259
         * includes the identifier field, attempt hydration.
260
         */
261 130
        if (($this->query['type'] === self::TYPE_FIND_AND_UPDATE ||
262 130
             $this->query['type'] === self::TYPE_FIND_AND_REMOVE) &&
263 130
            is_array($results) && isset($results['_id'])) {
264
265 5
            $results = $uow->getOrCreateDocument($this->class->name, $results, $this->unitOfWorkHints);
266
267 5
            if ( ! empty($this->primers)) {
268 1
                $referencePrimer = new ReferencePrimer($this->dm, $uow);
269
270 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...
271 1
                    $primer = is_callable($primer) ? $primer : null;
272 1
                    $referencePrimer->primeReferences($this->class, array($results), $fieldName, $this->unitOfWorkHints, $primer);
273 1
                }
274 1
            }
275 5
        }
276
277 130
        return $results;
278
    }
279
280
    /**
281
     * Prepare the Cursor returned by {@link Query::execute()}.
282
     *
283
     * This method will wrap the base Cursor with an ODM Cursor or EagerCursor,
284
     * and set the hydrate option and UnitOfWork hints. This occurs in addition
285
     * to any preparation done by the base Query class.
286
     *
287
     * @see \Doctrine\MongoDB\Cursor::prepareCursor()
288
     * @param BaseCursor $cursor
289
     * @return CursorInterface
290
     */
291 122
    protected function prepareCursor(BaseCursor $cursor)
292
    {
293 122
        $cursor = parent::prepareCursor($cursor);
294
295
        // Convert the base Cursor into an ODM Cursor
296 122
        $cursorClass = ( ! empty($this->query['eagerCursor'])) ? EagerCursor::class : Cursor::class;
297 122
        $cursor = new $cursorClass($cursor, $this->dm->getUnitOfWork(), $this->class);
298
299 122
        $cursor->hydrate($this->hydrate);
300 122
        $cursor->setHints($this->unitOfWorkHints);
301
302 122
        if ( ! empty($this->primers)) {
303 15
            $referencePrimer = new ReferencePrimer($this->dm, $this->dm->getUnitOfWork());
304 15
            $cursor->enableReferencePriming($this->primers, $referencePrimer);
305 15
        }
306
307 122
        return $cursor;
308
    }
309
310
    /**
311
     * Return whether queries on this document should require indexes.
312
     *
313
     * @return boolean
314
     */
315 143
    private function isIndexRequired()
316
    {
317 143
        return $this->requireIndexes !== null ? $this->requireIndexes : $this->class->requireIndexes;
318
    }
319
}
320