Completed
Push — 1.0.x ( 21c8a9...01b334 )
by Maciej
10:27
created

Query::__construct()   B

Complexity

Conditions 5
Paths 16

Size

Total Lines 30
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 5.0554

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 30
ccs 20
cts 23
cp 0.8696
rs 8.439
cc 5
eloc 18
nc 16
nop 9
crap 5.0554

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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