Completed
Pull Request — 1.0.x (#1362)
by Maciej
10:16 queued 04:44
created

Query::setRefresh()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1
Metric Value
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 1
crap 1
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 181
    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 181
        $primers = array_filter($primers);
105
106 181
        if ( ! empty($primers)) {
107 17
            $query['eagerCursor'] = true;
108 17
        }
109
110 181
        if ( ! empty($query['eagerCursor'])) {
111 17
            $query['useIdentifierKeys'] = false;
112 17
        }
113
114 181
        parent::__construct($collection, $query, $options);
115 181
        $this->dm = $dm;
116 181
        $this->class = $class;
117 181
        $this->hydrate = $hydrate;
118 181
        $this->primers = $primers;
119 181
        $this->requireIndexes = $requireIndexes;
120
121 181
        $this->setRefresh($refresh);
122
123 181
        if (isset($query['slaveOkay'])) {
124 4
            $this->unitOfWorkHints[self::HINT_SLAVE_OKAY] = $query['slaveOkay'];
125 4
        }
126
127 181
        if (isset($query['readPreference'])) {
128 3
            $this->unitOfWorkHints[self::HINT_READ_PREFERENCE] = $query['readPreference'];
129 3
            $this->unitOfWorkHints[self::HINT_READ_PREFERENCE_TAGS] = $query['readPreferenceTags'];
130 3
        }
131 181
    }
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 181
    public function setRefresh($refresh)
172
    {
173 181
        $this->unitOfWorkHints[Query::HINT_REFRESH] = (boolean) $refresh;
174 181
    }
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 143
    public function execute()
230
    {
231 143
        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 138
        $results = parent::execute();
236
237 137
        if ( ! $this->hydrate) {
238 10
            return $results;
239
        }
240
241 130
        $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 130
        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 130
        if (($this->query['type'] === self::TYPE_FIND_AND_UPDATE ||
265 130
             $this->query['type'] === self::TYPE_FIND_AND_REMOVE) &&
266 130
            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 130
        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 122
    protected function prepareCursor(BaseCursor $cursor)
295
    {
296 122
        $cursor = parent::prepareCursor($cursor);
297
298
        // Convert the base Cursor into an ODM Cursor
299 122
        $cursorClass = ( ! empty($this->query['eagerCursor'])) ? 'Doctrine\ODM\MongoDB\EagerCursor' : 'Doctrine\ODM\MongoDB\Cursor';
300 122
        $cursor = new $cursorClass($cursor, $this->dm->getUnitOfWork(), $this->class);
301
302 122
        $cursor->hydrate($this->hydrate);
303 122
        $cursor->setHints($this->unitOfWorkHints);
304
305 122
        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 122
        return $cursor;
311
    }
312
313
    /**
314
     * Return whether queries on this document should require indexes.
315
     *
316
     * @return boolean
317
     */
318 143
    private function isIndexRequired()
319
    {
320 143
        return $this->requireIndexes !== null ? $this->requireIndexes : $this->class->requireIndexes;
321
    }
322
}
323