Completed
Push — master ( 1b798f...92ca1d )
by Maciej
24:10
created

DocumentRepository::find()   D

Complexity

Conditions 10
Paths 22

Size

Total Lines 45
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 14.6656

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 10
eloc 21
c 1
b 0
f 0
nc 22
nop 3
dl 0
loc 45
ccs 16
cts 25
cp 0.64
crap 14.6656
rs 4.8196

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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;
21
22
use Doctrine\Common\Collections\ArrayCollection;
23
use Doctrine\Common\Collections\Collection;
24
use Doctrine\Common\Collections\Criteria;
25
use Doctrine\Common\Collections\Selectable;
26
use Doctrine\Common\Inflector\Inflector;
27
use Doctrine\Common\Persistence\ObjectRepository;
28
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
29
use Doctrine\ODM\MongoDB\Query\QueryExpressionVisitor;
30
31
/**
32
 * A DocumentRepository serves as a repository for documents with generic as well as
33
 * business specific methods for retrieving documents.
34
 *
35
 * This class is designed for inheritance and users can subclass this class to
36
 * write their own repositories with business-specific methods to locate documents.
37
 *
38
 * @since       1.0
39
 */
40
class DocumentRepository implements ObjectRepository, Selectable
41
{
42
    /**
43
     * @var string
44
     */
45
    protected $documentName;
46
47
    /**
48
     * @var DocumentManager
49
     */
50
    protected $dm;
51
52
    /**
53
     * @var UnitOfWork
54
     */
55
    protected $uow;
56
57
    /**
58
     * @var ClassMetadata
59
     */
60
    protected $class;
61
62
    /**
63
     * Initializes this instance with the specified document manager, unit of work and
64
     * class metadata.
65
     *
66
     * @param DocumentManager $dm The DocumentManager to use.
67
     * @param UnitOfWork $uow The UnitOfWork to use.
68
     * @param ClassMetadata $classMetadata The class metadata.
69
     */
70 342
    public function __construct(DocumentManager $dm, UnitOfWork $uow, ClassMetadata $classMetadata)
71
    {
72 342
        $this->documentName = $classMetadata->name;
73 342
        $this->dm = $dm;
74 342
        $this->uow = $uow;
75 342
        $this->class = $classMetadata;
76 342
    }
77
78
    /**
79
     * Creates a new Query\Builder instance that is preconfigured for this document name.
80
     *
81
     * @return Query\Builder $qb
82
     */
83 27
    public function createQueryBuilder()
84
    {
85 27
        return $this->dm->createQueryBuilder($this->documentName);
86
    }
87
88
    /**
89
     * Clears the repository, causing all managed documents to become detached.
90
     */
91
    public function clear()
92
    {
93
        $this->dm->clear($this->class->rootDocumentName);
94
    }
95
96
    /**
97
     * Finds a document matching the specified identifier. Optionally a lock mode and
98
     * expected version may be specified.
99
     *
100
     * @param mixed $id Identifier.
101
     * @param int $lockMode Optional. Lock mode; one of the LockMode constants.
102
     * @param int $lockVersion Optional. Expected version.
103
     * @throws Mapping\MappingException
104
     * @throws LockException
105
     * @return object|null The document, if found, otherwise null.
106
     */
107 235
    public function find($id, $lockMode = LockMode::NONE, $lockVersion = null)
108
    {
109 235
        if ($id === null) {
110
            return null;
111
        }
112
113
        /* TODO: What if the ID object has a field with the same name as the
114
         * class' mapped identifier field name?
115
         */
116 235
        if (is_array($id)) {
117 2
            list($identifierFieldName) = $this->class->getIdentifierFieldNames();
118
119 2
            if (isset($id[$identifierFieldName])) {
120
                $id = $id[$identifierFieldName];
121
            }
122 2
        }
123
124
        // Check identity map first
125 235
        if ($document = $this->uow->tryGetById($id, $this->class)) {
126 14
            if ($lockMode !== LockMode::NONE) {
127
                $this->dm->lock($document, $lockMode, $lockVersion);
128
            }
129
130 14
            return $document; // Hit!
131
        }
132
133 231
        $criteria = array('_id' => $id);
134
135 231
        if ($lockMode === LockMode::NONE) {
136 230
            return $this->getDocumentPersister()->load($criteria);
137
        }
138
139 1
        if ($lockMode === LockMode::OPTIMISTIC) {
140 1
            if (!$this->class->isVersioned) {
141
                throw LockException::notVersioned($this->documentName);
142
            }
143 1
            if ($document = $this->getDocumentPersister()->load($criteria)) {
144
                $this->uow->lock($document, $lockMode, $lockVersion);
145
            }
146
147 1
            return $document;
148
        }
149
150
        return $this->getDocumentPersister()->load($criteria, null, array(), $lockMode);
151
    }
152
153
    /**
154
     * Finds all documents in the repository.
155
     *
156
     * @return array
157
     */
158 10
    public function findAll()
159
    {
160 10
        return $this->findBy(array());
161
    }
162
163
    /**
164
     * Finds documents by a set of criteria.
165
     *
166
     * @param array        $criteria Query criteria
167
     * @param array        $sort     Sort array for Cursor::sort()
168
     * @param integer|null $limit    Limit for Cursor::limit()
169
     * @param integer|null $skip     Skip for Cursor::skip()
170
     *
171
     * @return array
172
     */
173 11
    public function findBy(array $criteria, array $sort = null, $limit = null, $skip = null)
174
    {
175 11
        return $this->getDocumentPersister()->loadAll($criteria, $sort, $limit, $skip)->toArray(false);
176
    }
177
178
    /**
179
     * Finds a single document by a set of criteria.
180
     *
181
     * @param array $criteria
182
     * @return object
183
     */
184 73
    public function findOneBy(array $criteria)
185
    {
186 73
        return $this->getDocumentPersister()->load($criteria);
187
    }
188
189
    /**
190
     * Adds support for magic finders.
191
     *
192
     * @param string $method
193
     * @param array $arguments
194
     * @throws MongoDBException
195
     * @throws \BadMethodCallException If the method called is an invalid find* method
196
     *                                 or no find* method at all and therefore an invalid
197
     *                                 method call.
198
     * @return array|object The found document/documents.
199
     */
200 10
    public function __call($method, $arguments)
201
    {
202 10
        if (strpos($method, 'findBy') === 0) {
203
            $by = substr($method, 6, strlen($method));
204
            $method = 'findBy';
205 10
        } elseif (strpos($method, 'findOneBy') === 0) {
206 10
            $by = substr($method, 9, strlen($method));
207 10
            $method = 'findOneBy';
208 10
        } else {
209
            throw new \BadMethodCallException(
210
                "Undefined method: '$method'. The method name must start with 'findBy' or 'findOneBy'!"
211
            );
212
        }
213
214 10
        if (!isset($arguments[0])) {
215
            throw MongoDBException::findByRequiresParameter($method . $by);
216
        }
217
218 10
        $fieldName = Inflector::camelize($by);
219
220 10
        if ($this->class->hasField($fieldName)) {
221 10
            return $this->$method(array($fieldName => $arguments[0]));
222
        } else {
223
            throw MongoDBException::invalidFindByCall($this->documentName, $fieldName, $method . $by);
224
        }
225
    }
226
227
    /**
228
     * @return string
229
     */
230
    public function getDocumentName()
231
    {
232
        return $this->documentName;
233
    }
234
235
    /**
236
     * @return DocumentManager
237
     */
238
    public function getDocumentManager()
239
    {
240
        return $this->dm;
241
    }
242
243
    /**
244
     * @return Mapping\ClassMetadata
245
     */
246
    public function getClassMetadata()
247
    {
248
        return $this->class;
249
    }
250
251
    /**
252
     * @return string
253
     */
254
    public function getClassName()
255
    {
256
        return $this->getDocumentName();
257
    }
258
259
    /**
260
     * Selects all elements from a selectable that match the expression and
261
     * returns a new collection containing these elements.
262
     *
263
     * @see Selectable::matching()
264
     * @param Criteria $criteria
265
     * @return Collection
266
     */
267 4
    public function matching(Criteria $criteria)
268
    {
269 4
        $visitor = new QueryExpressionVisitor($this->createQueryBuilder());
270 4
        $queryBuilder = $this->createQueryBuilder();
271
272 4
        if ($criteria->getWhereExpression() !== null) {
273 1
            $expr = $visitor->dispatch($criteria->getWhereExpression());
274 1
            $queryBuilder->setQueryArray($expr->getQuery());
275 1
        }
276
277 4
        if ($criteria->getMaxResults() !== null) {
278
            $queryBuilder->limit($criteria->getMaxResults());
279
        }
280
281 4
        if ($criteria->getFirstResult() !== null) {
282
            $queryBuilder->skip($criteria->getFirstResult());
283
        }
284
285 4
        if ($criteria->getOrderings() !== null) {
286 4
            $queryBuilder->sort($criteria->getOrderings());
287 4
        }
288
289
        // @TODO: wrap around a specialized Collection for efficient count on large collections
290 4
        return new ArrayCollection($queryBuilder->getQuery()->execute()->toArray(false));
291
    }
292
293 312
    protected function getDocumentPersister()
294
    {
295 312
        return $this->uow->getDocumentPersister($this->documentName);
296
    }
297
}
298