Completed
Pull Request — master (#1803)
by Maciej
20:22
created

DocumentRepository::matching()   A

Complexity

Conditions 5
Paths 16

Size

Total Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 5.3073

Importance

Changes 0
Metric Value
dl 0
loc 25
ccs 10
cts 13
cp 0.7692
rs 9.2088
c 0
b 0
f 0
cc 5
nc 16
nop 1
crap 5.3073
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\ODM\MongoDB\Repository;
6
7
use Doctrine\Common\Collections\ArrayCollection;
8
use Doctrine\Common\Collections\Criteria;
9
use Doctrine\Common\Collections\Selectable;
10
use Doctrine\Common\Persistence\ObjectRepository;
11
use Doctrine\ODM\MongoDB\Aggregation\Builder as AggregationBuilder;
12
use Doctrine\ODM\MongoDB\DocumentManager;
13
use Doctrine\ODM\MongoDB\LockException;
14
use Doctrine\ODM\MongoDB\LockMode;
15
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
16
use Doctrine\ODM\MongoDB\Mapping\MappingException;
17
use Doctrine\ODM\MongoDB\Persisters\DocumentPersister;
18
use Doctrine\ODM\MongoDB\Query\Builder as QueryBuilder;
19
use Doctrine\ODM\MongoDB\Query\QueryExpressionVisitor;
20
use Doctrine\ODM\MongoDB\UnitOfWork;
21
use function count;
22
use function is_array;
23
24
/**
25
 * A DocumentRepository serves as a repository for documents with generic as well as
26
 * business specific methods for retrieving documents.
27
 *
28
 * This class is designed for inheritance and users can subclass this class to
29
 * write their own repositories with business-specific methods to locate documents.
30
 */
31
class DocumentRepository implements ObjectRepository, Selectable
32
{
33
    /** @var string */
34
    protected $documentName;
35
36
    /** @var DocumentManager */
37
    protected $dm;
38
39
    /** @var UnitOfWork */
40
    protected $uow;
41
42
    /** @var ClassMetadata */
43
    protected $class;
44
45
    /**
46
     * Initializes this instance with the specified document manager, unit of work and
47
     * class metadata.
48
     *
49
     * @param DocumentManager $dm            The DocumentManager to use.
50
     * @param UnitOfWork      $uow           The UnitOfWork to use.
51
     * @param ClassMetadata   $classMetadata The class metadata.
52
     */
53 356
    public function __construct(DocumentManager $dm, UnitOfWork $uow, ClassMetadata $classMetadata)
54
    {
55 356
        $this->documentName = $classMetadata->name;
56 356
        $this->dm           = $dm;
57 356
        $this->uow          = $uow;
58 356
        $this->class        = $classMetadata;
59 356
    }
60
61
    /**
62
     * Creates a new Query\Builder instance that is preconfigured for this document name.
63
     */
64 15
    public function createQueryBuilder() : QueryBuilder
65
    {
66 15
        return $this->dm->createQueryBuilder($this->documentName);
67
    }
68
69
    /**
70
     * Creates a new Aggregation\Builder instance that is prepopulated for this document name.
71
     */
72
    public function createAggregationBuilder() : AggregationBuilder
73
    {
74
        return $this->dm->createAggregationBuilder($this->documentName);
75
    }
76
77
    /**
78
     * Clears the repository, causing all managed documents to become detached.
79
     */
80
    public function clear() : void
81
    {
82
        $this->dm->clear($this->class->rootDocumentName);
83
    }
84
85
    /**
86
     * Finds a document matching the specified identifier. Optionally a lock mode and
87
     * expected version may be specified.
88
     *
89
     * @param mixed $id Identifier.
90
     *
91
     * @throws MappingException
92
     * @throws LockException
93
     */
94 249
    public function find($id, int $lockMode = LockMode::NONE, ?int $lockVersion = null) : ?object
95
    {
96 249
        if ($id === null) {
97
            return null;
98
        }
99
100
        /* TODO: What if the ID object has a field with the same name as the
101
         * class' mapped identifier field name?
102
         */
103 249
        if (is_array($id)) {
104 2
            [$identifierFieldName] = $this->class->getIdentifierFieldNames();
0 ignored issues
show
Bug introduced by
The variable $identifierFieldName does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
105
106 2
            if (isset($id[$identifierFieldName])) {
107
                $id = $id[$identifierFieldName];
108
            }
109
        }
110
111
        // Check identity map first
112 249
        $document = $this->uow->tryGetById($id, $this->class);
113 249
        if ($document) {
114 21
            if ($lockMode !== LockMode::NONE) {
115
                $this->dm->lock($document, $lockMode, $lockVersion);
116
            }
117
118 21
            return $document; // Hit!
119
        }
120
121 242
        $criteria = ['_id' => $id];
122
123 242
        if ($lockMode === LockMode::NONE) {
124 242
            return $this->getDocumentPersister()->load($criteria);
125
        }
126
127
        if ($lockMode === LockMode::OPTIMISTIC) {
128
            if (! $this->class->isVersioned) {
129
                throw LockException::notVersioned($this->documentName);
130
            }
131
132
            $document = $this->getDocumentPersister()->load($criteria);
133
            if ($document) {
134
                $this->uow->lock($document, $lockMode, $lockVersion);
135
            }
136
137
            return $document;
138
        }
139
140
        return $this->getDocumentPersister()->load($criteria, null, [], $lockMode);
141
    }
142
143
    /**
144
     * Finds all documents in the repository.
145
     */
146 10
    public function findAll() : array
147
    {
148 10
        return $this->findBy([]);
149
    }
150
151
    /**
152
     * Finds documents by a set of criteria.
153
     *
154
     * @param int|null $limit
155
     * @param int|null $skip
156
     */
157 11
    public function findBy(array $criteria, ?array $sort = null, $limit = null, $skip = null) : array
158
    {
159 11
        return $this->getDocumentPersister()->loadAll($criteria, $sort, $limit, $skip)->toArray();
160
    }
161
162
    /**
163
     * Finds a single document by a set of criteria.
164
     */
165 84
    public function findOneBy(array $criteria) : ?object
166
    {
167 84
        return $this->getDocumentPersister()->load($criteria);
168
    }
169
170 8
    public function getDocumentName() : string
171
    {
172 8
        return $this->documentName;
173
    }
174
175
    public function getDocumentManager() : DocumentManager
176
    {
177
        return $this->dm;
178
    }
179
180
    public function getClassMetadata() : ClassMetadata
181
    {
182
        return $this->class;
183
    }
184
185 8
    public function getClassName() : string
186
    {
187 8
        return $this->getDocumentName();
188
    }
189
190
    /**
191
     * Selects all elements from a selectable that match the expression and
192
     * returns a new collection containing these elements.
193
     *
194
     * @see Selectable::matching()
195
     */
196 4
    public function matching(Criteria $criteria) : ArrayCollection
197
    {
198 4
        $visitor      = new QueryExpressionVisitor($this->createQueryBuilder());
199 4
        $queryBuilder = $this->createQueryBuilder();
200
201 4
        if ($criteria->getWhereExpression() !== null) {
202 1
            $expr = $visitor->dispatch($criteria->getWhereExpression());
203 1
            $queryBuilder->setQueryArray($expr->getQuery());
204
        }
205
206 4
        if ($criteria->getMaxResults() !== null) {
207
            $queryBuilder->limit($criteria->getMaxResults());
208
        }
209
210 4
        if ($criteria->getFirstResult() !== null) {
211
            $queryBuilder->skip($criteria->getFirstResult());
212
        }
213
214 4
        if (count($criteria->getOrderings())) {
215
            $queryBuilder->sort($criteria->getOrderings());
216
        }
217
218
        // @TODO: wrap around a specialized Collection for efficient count on large collections
219 4
        return new ArrayCollection($queryBuilder->getQuery()->execute()->toArray());
220
    }
221
222 334
    protected function getDocumentPersister() : DocumentPersister
223
    {
224 334
        return $this->uow->getDocumentPersister($this->documentName);
225
    }
226
}
227