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

DocumentRepository::getClassMetadata()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2
Metric Value
dl 0
loc 4
ccs 0
cts 2
cp 0
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 2
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