Completed
Pull Request — master (#1640)
by Olivier
23:26 queued 21:41
created

DocumentRepository::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 7
ccs 6
cts 6
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 5
nc 1
nop 3
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\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 367
    public function __construct(DocumentManager $dm, UnitOfWork $uow, ClassMetadata $classMetadata)
71
    {
72 367
        $this->documentName = $classMetadata->name;
73 367
        $this->dm = $dm;
74 367
        $this->uow = $uow;
75 367
        $this->class = $classMetadata;
76 367
    }
77
78
    /**
79
     * Creates a new Query\Builder instance that is preconfigured for this document name.
80
     *
81
     * @return Query\Builder $qb
82
     */
83 29
    public function createQueryBuilder()
84
    {
85 29
        return $this->dm->createQueryBuilder($this->documentName);
86
    }
87
88
    /**
89
     * Creates a new Aggregation\Builder instance that is prepopulated for this document name.
90
     *
91
     * @return Aggregation\Builder
92
     */
93
    public function createAggregationBuilder()
94
    {
95
        return $this->dm->createAggregationBuilder($this->documentName);
96
    }
97
98
    /**
99
     * Clears the repository, causing all managed documents to become detached.
100
     */
101
    public function clear()
102
    {
103
        $this->dm->clear($this->class->rootDocumentName);
104
    }
105
106
    /**
107
     * Finds a document matching the specified identifier. Optionally a lock mode and
108
     * expected version may be specified.
109
     *
110
     * @param mixed $id Identifier.
111
     * @param int $lockMode Optional. Lock mode; one of the LockMode constants.
112
     * @param int $lockVersion Optional. Expected version.
113
     * @throws Mapping\MappingException
114
     * @throws LockException
115
     * @return object|null The document, if found, otherwise null.
116
     */
117 252
    public function find($id, $lockMode = LockMode::NONE, $lockVersion = null)
118
    {
119 252
        if ($id === null) {
120
            return null;
121
        }
122
123
        /* TODO: What if the ID object has a field with the same name as the
124
         * class' mapped identifier field name?
125
         */
126 252
        if (is_array($id)) {
127 2
            list($identifierFieldName) = $this->class->getIdentifierFieldNames();
128
129 2
            if (isset($id[$identifierFieldName])) {
130
                $id = $id[$identifierFieldName];
131
            }
132 2
        }
133
134
        // Check identity map first
135 252
        if ($document = $this->uow->tryGetById($id, $this->class)) {
136 22
            if ($lockMode !== LockMode::NONE) {
137
                $this->dm->lock($document, $lockMode, $lockVersion);
138
            }
139
140 22
            return $document; // Hit!
141
        }
142
143 245
        $criteria = array('_id' => $id);
144
145 245
        if ($lockMode === LockMode::NONE) {
146 244
            return $this->getDocumentPersister()->load($criteria);
147
        }
148
149 1
        if ($lockMode === LockMode::OPTIMISTIC) {
150 1
            if (!$this->class->isVersioned) {
151
                throw LockException::notVersioned($this->documentName);
152
            }
153 1
            if ($document = $this->getDocumentPersister()->load($criteria)) {
154
                $this->uow->lock($document, $lockMode, $lockVersion);
155
            }
156
157 1
            return $document;
158
        }
159
160
        return $this->getDocumentPersister()->load($criteria, null, array(), $lockMode);
161
    }
162
163
    /**
164
     * Finds all documents in the repository.
165
     *
166
     * @return array
167
     */
168 11
    public function findAll()
169
    {
170 11
        return $this->findBy(array());
171
    }
172
173
    /**
174
     * Finds documents by a set of criteria.
175
     *
176
     * @param array        $criteria Query criteria
177
     * @param array        $sort     Sort array for Cursor::sort()
178
     * @param integer|null $limit    Limit for Cursor::limit()
179
     * @param integer|null $skip     Skip for Cursor::skip()
180
     *
181
     * @return array
182
     */
183 12
    public function findBy(array $criteria, array $sort = null, $limit = null, $skip = null)
184
    {
185 12
        return $this->getDocumentPersister()->loadAll($criteria, $sort, $limit, $skip)->toArray(false);
186
    }
187
188
    /**
189
     * Finds a single document by a set of criteria.
190
     *
191
     * @param array $criteria
192
     * @return object
193
     */
194 79
    public function findOneBy(array $criteria)
195
    {
196 79
        return $this->getDocumentPersister()->load($criteria);
197
    }
198
199
    /**
200
     * Adds support for magic finders.
201
     *
202
     * @param string $method
203
     * @param array $arguments
204
     * @throws MongoDBException
205
     * @throws \BadMethodCallException If the method called is an invalid find* method
206
     *                                 or no find* method at all and therefore an invalid
207
     *                                 method call.
208
     * @return array|object The found document/documents.
209
     *
210
     * @deprecated method was deprecated in 1.2 and will be removed in 2.0
211
     */
212 10
    public function __call($method, $arguments)
213
    {
214 10
        @trigger_error(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
215 10
            'Using magic findBy and findOneBy calls was deprecated in version 1.2 and will be removed altogether in 2.0.',
216
            E_USER_DEPRECATED
217 10
        );
218 10
        if (strpos($method, 'findBy') === 0) {
219
            $by = substr($method, 6, strlen($method));
220
            $method = 'findBy';
221 10
        } elseif (strpos($method, 'findOneBy') === 0) {
222 10
            $by = substr($method, 9, strlen($method));
223 10
            $method = 'findOneBy';
224 10
        } else {
225
            throw new \BadMethodCallException(
226
                "Undefined method: '$method'. The method name must start with 'findBy' or 'findOneBy'!"
227
            );
228
        }
229
230 10
        if (!isset($arguments[0])) {
231
            throw MongoDBException::findByRequiresParameter($method . $by);
232
        }
233
234 10
        $fieldName = Inflector::camelize($by);
235
236 10
        if ($this->class->hasField($fieldName)) {
237 10
            return $this->$method(array($fieldName => $arguments[0]));
238
        } else {
239
            throw MongoDBException::invalidFindByCall($this->documentName, $fieldName, $method . $by);
240
        }
241
    }
242
243
    /**
244
     * @return string
245
     */
246
    public function getDocumentName()
247
    {
248
        return $this->documentName;
249
    }
250
251
    /**
252
     * @return DocumentManager
253
     */
254
    public function getDocumentManager()
255
    {
256
        return $this->dm;
257
    }
258
259
    /**
260
     * @return Mapping\ClassMetadata
261
     */
262
    public function getClassMetadata()
263
    {
264
        return $this->class;
265
    }
266
267
    /**
268
     * @return string
269
     */
270
    public function getClassName()
271
    {
272
        return $this->getDocumentName();
273
    }
274
275
    /**
276
     * Selects all elements from a selectable that match the expression and
277
     * returns a new collection containing these elements.
278
     *
279
     * @see Selectable::matching()
280
     * @param Criteria $criteria
281
     * @return Collection
282
     */
283 4
    public function matching(Criteria $criteria)
284
    {
285 4
        $visitor = new QueryExpressionVisitor($this->createQueryBuilder());
286 4
        $queryBuilder = $this->createQueryBuilder();
287
288 4
        if ($criteria->getWhereExpression() !== null) {
289 1
            $expr = $visitor->dispatch($criteria->getWhereExpression());
290 1
            $queryBuilder->setQueryArray($expr->getQuery());
291 1
        }
292
293 4
        if ($criteria->getMaxResults() !== null) {
294
            $queryBuilder->limit($criteria->getMaxResults());
295
        }
296
297 4
        if ($criteria->getFirstResult() !== null) {
298
            $queryBuilder->skip($criteria->getFirstResult());
299
        }
300
301 4
        if ($criteria->getOrderings() !== null) {
302
            $queryBuilder->sort($criteria->getOrderings());
303
        }
304
305
        // @TODO: wrap around a specialized Collection for efficient count on large collections
306 4
        return new ArrayCollection($queryBuilder->getQuery()->execute()->toArray(false));
307
    }
308
309 333
    protected function getDocumentPersister()
310
    {
311 333
        return $this->uow->getDocumentPersister($this->documentName);
312
    }
313
}
314