Completed
Pull Request — master (#1263)
by Andreas
10:33
created

DocumentRepository::findBy()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 4
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;
21
22
use Doctrine\Common\Collections\ArrayCollection;
23
use Doctrine\Common\Collections\Criteria;
24
use Doctrine\Common\Collections\Selectable;
25
use Doctrine\Common\Persistence\ObjectRepository;
26
use Doctrine\ODM\MongoDB\Mapping\MappingException;
27
use Doctrine\ODM\MongoDB\Query\QueryExpressionVisitor;
28
29
/**
30
 * A DocumentRepository serves as a repository for documents with generic as well as
31
 * business specific methods for retrieving documents.
32
 *
33
 * This class is designed for inheritance and users can subclass this class to
34
 * write their own repositories with business-specific methods to locate documents.
35
 *
36
 * @since       1.0
37
 * @author      Jonathan H. Wage <[email protected]>
38
 * @author      Roman Borschel <[email protected]>
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 \Doctrine\ODM\MongoDB\Mapping\ClassMetadata
59
     */
60
    protected $class;
61
62
    /**
63
     * Initializes a new <tt>DocumentRepository</tt>.
64
     *
65
     * @param DocumentManager $dm The DocumentManager to use.
66
     * @param UnitOfWork $uow The UnitOfWork to use.
67
     * @param Mapping\ClassMetadata $classMetadata The class descriptor.
0 ignored issues
show
Bug introduced by
There is no parameter named $classMetadata. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
68
     */
69 333
    public function __construct(DocumentManager $dm, UnitOfWork $uow, Mapping\ClassMetadata $class)
70
    {
71 333
        $this->documentName = $class->name;
72 333
        $this->dm = $dm;
73 333
        $this->uow = $uow;
74 333
        $this->class = $class;
75 333
    }
76
77
    /**
78
     * Create a new Query\Builder instance that is prepopulated for this document name
79
     *
80
     * @return Query\Builder $qb
81
     */
82 27
    public function createQueryBuilder()
83
    {
84 27
        return $this->dm->createQueryBuilder($this->documentName);
85
    }
86
87
    /**
88
     * Create a new Aggregation\Builder instance that is prepopulated for this document name
89
     *
90
     * @return Aggregation\Builder
91
     */
92
    public function createAggregationBuilder()
93
    {
94
        return $this->dm->createAggregationBuilder($this->documentName);
95
    }
96
97
    /**
98
     * Clears the repository, causing all managed documents to become detached.
99
     */
100
    public function clear()
101
    {
102
        $this->dm->clear($this->class->rootDocumentName);
103
    }
104
105
    /**
106
     * Finds a document by its identifier
107
     *
108
     * @param string|object $id The identifier
109
     * @param int $lockMode
110
     * @param int $lockVersion
111
     * @throws Mapping\MappingException
112
     * @throws LockException
113
     * @return object The document.
114
     */
115 232
    public function find($id, $lockMode = LockMode::NONE, $lockVersion = null)
116
    {
117 232
        if ($id === null) {
118
            return;
119
        }
120
121
        /* TODO: What if the ID object has a field with the same name as the
122
         * class' mapped identifier field name?
123
         */
124 232
        if (is_array($id)) {
125 2
            list($identifierFieldName) = $this->class->getIdentifierFieldNames();
126
127 2
            if (isset($id[$identifierFieldName])) {
128
                $id = $id[$identifierFieldName];
129
            }
130 2
        }
131
132
        // Check identity map first
133 232
        if ($document = $this->uow->tryGetById($id, $this->class)) {
134 14
            if ($lockMode != LockMode::NONE) {
135
                $this->dm->lock($document, $lockMode, $lockVersion);
136
            }
137
138 14
            return $document; // Hit!
139
        }
140
141 228
        $criteria = array('_id' => $id);
142
143 228
        if ($lockMode == LockMode::NONE) {
144 227
            return $this->getDocumentPersister()->load($criteria);
145
        }
146
147 1
        if ($lockMode == LockMode::OPTIMISTIC) {
148 1
            if (!$this->class->isVersioned) {
149
                throw LockException::notVersioned($this->documentName);
150
            }
151 1
            if ($document = $this->getDocumentPersister()->load($criteria)) {
152
                $this->uow->lock($document, $lockMode, $lockVersion);
153
            }
154
155 1
            return $document;
156
        }
157
158
        return $this->getDocumentPersister()->load($criteria, null, array(), $lockMode);
159
    }
160
161
    /**
162
     * Finds all documents in the repository.
163
     *
164
     * @return array
165
     */
166 10
    public function findAll()
167
    {
168 10
        return $this->findBy(array());
169
    }
170
171
    /**
172
     * Finds documents by a set of criteria.
173
     *
174
     * @param array        $criteria Query criteria
175
     * @param array        $sort     Sort array for Cursor::sort()
176
     * @param integer|null $limit    Limit for Cursor::limit()
177
     * @param integer|null $skip     Skip for Cursor::skip()
178
     *
179
     * @return array
180
     */
181 11
    public function findBy(array $criteria, array $sort = null, $limit = null, $skip = null)
182
    {
183 11
        return $this->getDocumentPersister()->loadAll($criteria, $sort, $limit, $skip)->toArray(false);
184
    }
185
186
    /**
187
     * Finds a single document by a set of criteria.
188
     *
189
     * @param array $criteria
190
     * @return object
191
     */
192 67
    public function findOneBy(array $criteria)
193
    {
194 67
        return $this->getDocumentPersister()->load($criteria);
195
    }
196
197
    /**
198
     * Adds support for magic finders.
199
     *
200
     * @param string $method
201
     * @param array $arguments
202
     * @throws MongoDBException
203
     * @throws \BadMethodCallException If the method called is an invalid find* method
204
     *                                 or no find* method at all and therefore an invalid
205
     *                                 method call.
206
     * @return array|object The found document/documents.
207
     */
208 10
    public function __call($method, $arguments)
209
    {
210 10
        if (substr($method, 0, 6) == 'findBy') {
211
            $by = substr($method, 6, strlen($method));
212
            $method = 'findBy';
213 10
        } elseif (substr($method, 0, 9) == 'findOneBy') {
214 10
            $by = substr($method, 9, strlen($method));
215 10
            $method = 'findOneBy';
216 10
        } else {
217
            throw new \BadMethodCallException(
218
                "Undefined method '$method'. The method name must start with " .
219
                "either findBy or findOneBy!"
220
            );
221
        }
222
223 10
        if ( ! isset($arguments[0])) {
224
            throw MongoDBException::findByRequiresParameter($method . $by);
225
        }
226
227 10
        $fieldName = lcfirst(\Doctrine\Common\Util\Inflector::classify($by));
228
229 10
        if ($this->class->hasField($fieldName)) {
230 10
            return $this->$method(array($fieldName => $arguments[0]));
231
        } else {
232
            throw MongoDBException::invalidFindByCall($this->documentName, $fieldName, $method . $by);
233
        }
234
    }
235
236
    /**
237
     * @return string
238
     */
239
    public function getDocumentName()
240
    {
241
        return $this->documentName;
242
    }
243
244
    /**
245
     * @return DocumentManager
246
     */
247
    public function getDocumentManager()
248
    {
249
        return $this->dm;
250
    }
251
252
    /**
253
     * @return Mapping\ClassMetadata
254
     */
255
    public function getClassMetadata()
256
    {
257
        return $this->class;
258
    }
259
260
    /**
261
     * @return string
262
     */
263
    public function getClassName()
264
    {
265
        return $this->getDocumentName();
266
    }
267
268
    /**
269
     * Selects all elements from a selectable that match the expression and
270
     * returns a new collection containing these elements.
271
     *
272
     * @see Selectable::matching()
273
     * @param Criteria $criteria
274
     * @return Collection
275
     */
276 4
    public function matching(Criteria $criteria)
277
    {
278 4
        $visitor = new QueryExpressionVisitor($this->createQueryBuilder());
279 4
        $queryBuilder = $this->createQueryBuilder();
280
281 4
        if ($criteria->getWhereExpression() !== null) {
282 1
            $expr = $visitor->dispatch($criteria->getWhereExpression());
283 1
            $queryBuilder->setQueryArray($expr->getQuery());
284 1
        }
285
286 4
        if ($criteria->getMaxResults() !== null) {
287
            $queryBuilder->limit($criteria->getMaxResults());
288
        }
289
290 4
        if ($criteria->getFirstResult() !== null) {
291
            $queryBuilder->skip($criteria->getFirstResult());
292
        }
293
294 4
        if ($criteria->getOrderings() !== null) {
295 4
            $queryBuilder->sort($criteria->getOrderings());
296 4
        }
297
298
        // @TODO: wrap around a specialized Collection for efficient count on large collections
299 4
        return new ArrayCollection($queryBuilder->getQuery()->execute()->toArray(false));
300
    }
301
302 303
    protected function getDocumentPersister()
303
    {
304 303
        return $this->uow->getDocumentPersister($this->documentName);
305
    }
306
}
307