Completed
Push — master ( d6bb56...1beca3 )
by Maciej
9s
created

Query::setReadOnly()   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

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
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\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
     * @param DocumentManager $dm
91
     * @param ClassMetadata $class
92
     * @param Collection $collection
93
     * @param array $query
94
     * @param array $options
95
     * @param boolean $hydrate
96
     * @param boolean $refresh
97
     * @param array $primers
98
     * @param null $requireIndexes
99
     * @param boolean $readOnly
100
     */
101 189
    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)
102
    {
103 189
        $primers = array_filter($primers);
104
105 189
        if ( ! empty($primers)) {
106 18
            $query['eagerCursor'] = true;
107
        }
108
109 189
        if ( ! empty($query['eagerCursor'])) {
110 18
            $query['useIdentifierKeys'] = false;
111
        }
112
113 189
        parent::__construct($collection, $query, $options);
114 189
        $this->dm = $dm;
115 189
        $this->class = $class;
116 189
        $this->hydrate = $hydrate;
117 189
        $this->primers = $primers;
118 189
        $this->requireIndexes = $requireIndexes;
119
120 189
        $this->setReadOnly($readOnly);
121 189
        $this->setRefresh($refresh);
122
123 189
        if (isset($query['slaveOkay'])) {
124 4
            $this->unitOfWorkHints[self::HINT_SLAVE_OKAY] = $query['slaveOkay'];
125
        }
126
127 189
        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
        }
131 189
    }
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 documents should be registered in UnitOfWork. If document would
165
     * already be managed it will be left intact and new instance returned.
166
     * 
167
     * This option has no effect if hydration is disabled.
168
     * 
169
     * @param boolean $readOnly
170
     */
171 189
    public function setReadOnly($readOnly)
172
    {
173 189
        $this->unitOfWorkHints[Query::HINT_READ_ONLY] = (boolean) $readOnly;
174 189
    }
175
176
    /**
177
     * Set whether to refresh hydrated documents that are already in the
178
     * identity map.
179
     *
180
     * This option has no effect if hydration is disabled.
181
     *
182
     * @param boolean $refresh
183
     */
184 189
    public function setRefresh($refresh)
185
    {
186 189
        $this->unitOfWorkHints[Query::HINT_REFRESH] = (boolean) $refresh;
187 189
    }
188
189
    /**
190
     * Gets the fields involved in this query.
191
     *
192
     * @return array $fields An array of fields names used in this query.
193
     */
194 22
    public function getFieldsInQuery()
195
    {
196 22
        $query = isset($this->query['query']) ? $this->query['query'] : array();
197 22
        $sort = isset($this->query['sort']) ? $this->query['sort'] : array();
198
199 22
        $extractor = new FieldExtractor($query, $sort);
200 22
        return $extractor->getFields();
201
    }
202
203
    /**
204
     * Check if this query is indexed.
205
     *
206
     * @return bool
207
     */
208 8
    public function isIndexed()
209
    {
210 8
        $fields = $this->getFieldsInQuery();
211 8
        foreach ($fields as $field) {
212 8
            if ( ! $this->collection->isFieldIndexed($field)) {
213 8
                return false;
214
            }
215
        }
216 2
        return true;
217
    }
218
219
    /**
220
     * Gets an array of the unindexed fields in this query.
221
     *
222
     * @return array
223
     */
224 6
    public function getUnindexedFields()
225
    {
226 6
        $unindexedFields = array();
227 6
        $fields = $this->getFieldsInQuery();
228 6
        foreach ($fields as $field) {
229 6
            if ( ! $this->collection->isFieldIndexed($field)) {
230 6
                $unindexedFields[] = $field;
231
            }
232
        }
233 6
        return $unindexedFields;
234
    }
235
236
    /**
237
     * Execute the query and returns the results.
238
     *
239
     * @throws \Doctrine\ODM\MongoDB\MongoDBException
240
     * @return mixed
241
     */
242 149
    public function execute()
243
    {
244 149
        if ($this->isIndexRequired() && ! $this->isIndexed()) {
245 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...
246
        }
247
248 144
        $results = parent::execute();
249
250 143
        if ( ! $this->hydrate) {
251 10
            return $results;
252
        }
253
254 136
        $uow = $this->dm->getUnitOfWork();
255
256
        /* A geoNear command returns an ArrayIterator, where each result is an
257
         * object with "dis" (computed distance) and "obj" (original document)
258
         * properties. If hydration is enabled, eagerly hydrate these results.
259
         *
260
         * Other commands results are not handled, since their results may not
261
         * resemble documents in the collection.
262
         */
263 136
        if ($this->query['type'] === self::TYPE_GEO_NEAR) {
264 2
            foreach ($results as $key => $result) {
265 2
                $document = $result['obj'];
266 2
                if ($this->class->distance !== null) {
267 2
                    $document[$this->class->distance] = $result['dis'];
268
                }
269 2
                $results[$key] = $uow->getOrCreateDocument($this->class->name, $document, $this->unitOfWorkHints);
270
            }
271 2
            $results->reset();
272
        }
273
274
        /* If a single document is returned from a findAndModify command and it
275
         * includes the identifier field, attempt hydration.
276
         */
277 136
        if (($this->query['type'] === self::TYPE_FIND_AND_UPDATE ||
278 136
             $this->query['type'] === self::TYPE_FIND_AND_REMOVE) &&
279 136
            is_array($results) && isset($results['_id'])) {
280
281 5
            $results = $uow->getOrCreateDocument($this->class->name, $results, $this->unitOfWorkHints);
282
283 5
            if ( ! empty($this->primers)) {
284 1
                $referencePrimer = new ReferencePrimer($this->dm, $uow);
285
286 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...
287 1
                    $primer = is_callable($primer) ? $primer : null;
288 1
                    $referencePrimer->primeReferences($this->class, array($results), $fieldName, $this->unitOfWorkHints, $primer);
289
                }
290
            }
291
        }
292
293 136
        return $results;
294
    }
295
296
    /**
297
     * Prepare the Cursor returned by {@link Query::execute()}.
298
     *
299
     * This method will wrap the base Cursor with an ODM Cursor or EagerCursor,
300
     * and set the hydrate option and UnitOfWork hints. This occurs in addition
301
     * to any preparation done by the base Query class.
302
     *
303
     * @see \Doctrine\MongoDB\Cursor::prepareCursor()
304
     * @param BaseCursor $cursor
305
     * @return CursorInterface
306
     */
307 128
    protected function prepareCursor(BaseCursor $cursor)
308
    {
309 128
        $cursor = parent::prepareCursor($cursor);
310
311
        // Convert the base Cursor into an ODM Cursor
312 128
        $cursorClass = ( ! empty($this->query['eagerCursor'])) ? EagerCursor::class : Cursor::class;
313 128
        $cursor = new $cursorClass($cursor, $this->dm->getUnitOfWork(), $this->class);
314
315 128
        $cursor->hydrate($this->hydrate);
316 128
        $cursor->setHints($this->unitOfWorkHints);
317
318 128
        if ( ! empty($this->primers)) {
319 16
            $referencePrimer = new ReferencePrimer($this->dm, $this->dm->getUnitOfWork());
320 16
            $cursor->enableReferencePriming($this->primers, $referencePrimer);
321
        }
322
323 128
        return $cursor;
324
    }
325
326
    /**
327
     * Return whether queries on this document should require indexes.
328
     *
329
     * @return boolean
330
     */
331 149
    private function isIndexRequired()
332
    {
333 149
        return $this->requireIndexes !== null ? $this->requireIndexes : $this->class->requireIndexes;
334
    }
335
}
336