Failed Conditions
Pull Request — develop (#6873)
by
unknown
112:44 queued 47:41
created

AbstractCollectionPersister::count()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 7
nc 2
nop 1
dl 0
loc 12
ccs 5
cts 5
cp 1
crap 2
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
4
declare(strict_types=1);
5
6
namespace Doctrine\ORM\Cache\Persister\Collection;
7
8
use Doctrine\Common\Collections\Collection;
9
use Doctrine\Common\Collections\Criteria;
10
use Doctrine\ORM\Cache\CollectionCacheKey;
11
use Doctrine\ORM\Cache\EntityCacheKey;
12
use Doctrine\ORM\Cache\Persister\Entity\CachedEntityPersister;
13
use Doctrine\ORM\Cache\Region;
14
use Doctrine\ORM\EntityManagerInterface;
15
use Doctrine\ORM\Mapping\AssociationMetadata;
16
use Doctrine\ORM\Mapping\ToManyAssociationMetadata;
17
use Doctrine\ORM\PersistentCollection;
18
use Doctrine\ORM\Persisters\Collection\CollectionPersister;
19
use Doctrine\ORM\Utility\StaticClassNameConverter;
20
21
/**
22
 * @author Fabio B. Silva <[email protected]>
23
 * @author Guilherme Blanco <[email protected]>
24
 * @since 2.5
25
 */
26
abstract class AbstractCollectionPersister implements CachedCollectionPersister
27
{
28
     /**
29
     * @var \Doctrine\ORM\UnitOfWork
30
     */
31
    protected $uow;
32
33
    /**
34
     * @var \Doctrine\ORM\Mapping\ClassMetadataFactory
35
     */
36
    protected $metadataFactory;
37
38
    /**
39
     * @var \Doctrine\ORM\Persisters\Collection\CollectionPersister
40
     */
41
    protected $persister;
42
43
    /**
44
     * @var \Doctrine\ORM\Mapping\ClassMetadata
45
     */
46
    protected $sourceEntity;
47
48
    /**
49
     * @var \Doctrine\ORM\Mapping\ClassMetadata
50
     */
51
    protected $targetEntity;
52
53
    /**
54
     * @var \Doctrine\ORM\Mapping\AssociationMetadata
55
     */
56
    protected $association;
57
58
     /**
59
     * @var array
60
     */
61
    protected $queuedCache = [];
62
63
    /**
64
     * @var \Doctrine\ORM\Cache\Region
65
     */
66
    protected $region;
67
68
    /**
69
     * @var string
70
     */
71
    protected $regionName;
72
73
    /**
74
     * @var \Doctrine\ORM\Cache\CollectionHydrator
75
     */
76
    protected $hydrator;
77
78
    /**
79
     * @var \Doctrine\ORM\Cache\Logging\CacheLogger
80
     */
81
    protected $cacheLogger;
82
83
    /**
84
     * @param CollectionPersister    $persister   The collection persister that will be cached.
85
     * @param Region                 $region      The collection region.
86
     * @param EntityManagerInterface $em          The entity manager.
87
     * @param AssociationMetadata    $association The association mapping.
88
     */
89
    public function __construct(
90
        CollectionPersister $persister,
91
        Region $region,
92
        EntityManagerInterface $em,
93
        AssociationMetadata $association
94
    )
95
    {
96
        $configuration  = $em->getConfiguration();
97
        $cacheConfig    = $configuration->getSecondLevelCacheConfiguration();
98
        $cacheFactory   = $cacheConfig->getCacheFactory();
99
100
        $this->region           = $region;
101 115
        $this->persister        = $persister;
102
        $this->association      = $association;
103 115
        $this->regionName       = $region->getName();
104 115
        $this->uow              = $em->getUnitOfWork();
105 115
        $this->metadataFactory  = $em->getMetadataFactory();
106
        $this->cacheLogger      = $cacheConfig->getCacheLogger();
107 115
        $this->hydrator         = $cacheFactory->buildCollectionHydrator($em, $association);
108 115
        $this->sourceEntity     = $em->getClassMetadata($association->getSourceEntity());
0 ignored issues
show
Documentation Bug introduced by
It seems like $em->getClassMetadata($a...ion->getSourceEntity()) of type Doctrine\Common\Persistence\Mapping\ClassMetadata is incompatible with the declared type Doctrine\ORM\Mapping\ClassMetadata of property $sourceEntity.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
109 115
        $this->targetEntity     = $em->getClassMetadata($association->getTargetEntity());
0 ignored issues
show
Documentation Bug introduced by
It seems like $em->getClassMetadata($a...ion->getTargetEntity()) of type Doctrine\Common\Persistence\Mapping\ClassMetadata is incompatible with the declared type Doctrine\ORM\Mapping\ClassMetadata of property $targetEntity.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
110 115
    }
111 115
112 115
    /**
113 115
     * {@inheritdoc}
114 115
     */
115 115
    public function getCacheRegion()
116 115
    {
117 115
        return $this->region;
118
    }
119
120
    /**
121
     * {@inheritdoc}
122 62
     */
123
    public function getSourceEntityMetadata()
124 62
    {
125
        return $this->sourceEntity;
126
    }
127
128
    /**
129
     * {@inheritdoc}
130
     */
131
    public function getTargetEntityMetadata()
132
    {
133
        return $this->targetEntity;
134
    }
135
136
    /**
137
     * @param \Doctrine\ORM\PersistentCollection     $collection
138
     * @param \Doctrine\ORM\Cache\CollectionCacheKey $key
139
     *
140
     * @return \Doctrine\ORM\PersistentCollection|null
141
     */
142
    public function loadCollectionCache(PersistentCollection $collection, CollectionCacheKey $key)
143
    {
144
        if (($cache = $this->region->get($key)) === null) {
145
            return null;
146
        }
147
148
        if (($cache = $this->hydrator->loadCacheEntry($this->sourceEntity, $key, $cache, $collection)) === null) {
149 17
            return null;
150
        }
151 17
152 13
        return $cache;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $cache returns the type array which is incompatible with the documented return type null|Doctrine\ORM\PersistentCollection.
Loading history...
153
    }
154
155 15
    /**
156 1
     * {@inheritdoc}
157
     */
158
    public function storeCollectionCache(CollectionCacheKey $key, $elements)
159 15
    {
160
        /* @var $targetPersister CachedEntityPersister */
161
        $association     = $this->sourceEntity->getProperty($key->association);
162
        $targetPersister = $this->uow->getEntityPersister($this->targetEntity->getRootClassName());
163
        $targetRegion    = $targetPersister->getCacheRegion();
0 ignored issues
show
Bug introduced by
The method getCacheRegion() does not exist on Doctrine\ORM\Persisters\Entity\EntityPersister. Since it exists in all sub-types, consider adding an abstract or default implementation to Doctrine\ORM\Persisters\Entity\EntityPersister. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

163
        /** @scrutinizer ignore-call */ 
164
        $targetRegion    = $targetPersister->getCacheRegion();
Loading history...
164
        $targetHydrator  = $targetPersister->getEntityHydrator();
0 ignored issues
show
Bug introduced by
The method getEntityHydrator() does not exist on Doctrine\ORM\Persisters\Entity\EntityPersister. Since it exists in all sub-types, consider adding an abstract or default implementation to Doctrine\ORM\Persisters\Entity\EntityPersister. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

164
        /** @scrutinizer ignore-call */ 
165
        $targetHydrator  = $targetPersister->getEntityHydrator();
Loading history...
165 43
166
        // Only preserve ordering if association configured it
167
        if (! ($association instanceof ToManyAssociationMetadata && $association->getIndexedBy())) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $association->getIndexedBy() of type null|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
168 43
            // Elements may be an array or a Collection
169 43
            $elements = \array_values($elements instanceof Collection ? $elements->getValues() : $elements);
170 43
        }
171 43
172
        $entry = $this->hydrator->buildCacheEntry($this->targetEntity, $key, $elements);
173
174 43
        foreach ($entry->identifiers as $index => $entityKey) {
175
            if ($targetRegion->contains($entityKey)) {
176 43
                continue;
177
            }
178
179 43
            $class      = $this->targetEntity;
180
            $className  = StaticClassNameConverter::getClass($elements[$index]);
181 43
182 43
            if ($className !== $this->targetEntity->getClassName()) {
183 43
                $class = $this->metadataFactory->getMetadataFor($className);
184
            }
185
186 11
            $entity       = $elements[$index];
187 11
            $entityEntry  = $targetHydrator->buildCacheEntry($class, $entityKey, $entity);
188
189 11
            $targetRegion->put($entityKey, $entityEntry);
190 2
        }
191
192
        $cached = $this->region->put($key, $entry);
193 11
194 11
        if ($this->cacheLogger && $cached) {
195
            $this->cacheLogger->collectionCachePut($this->regionName, $key);
196 11
        }
197
    }
198
199 43
    /**
200
     * {@inheritdoc}
201 43
     */
202 43
    public function contains(PersistentCollection $collection, $element)
203
    {
204 43
        return $this->persister->contains($collection, $element);
205
    }
206
207
    /**
208
     * {@inheritdoc}
209 3
     */
210
    public function containsKey(PersistentCollection $collection, $key)
211 3
    {
212
        return $this->persister->containsKey($collection, $key);
213
    }
214
215
    /**
216
     * {@inheritdoc}
217 3
     */
218
    public function count(PersistentCollection $collection)
219 3
    {
220
        $fieldName = $this->association->getName();
221
        $ownerId   = $this->uow->getEntityIdentifier($collection->getOwner());
222
        $key       = new CollectionCacheKey($this->sourceEntity->getRootClassName(), $fieldName, $ownerId);
223
        $entry     = $this->region->get($key);
224
225 4
        if ($entry !== null) {
226
            return count($entry->identifiers);
0 ignored issues
show
Bug introduced by
Accessing identifiers on the interface Doctrine\ORM\Cache\CacheEntry suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
227 4
        }
228 4
229 4
        return $this->persister->count($collection);
230
    }
231 4
232 1
    /**
233
     * {@inheritdoc}
234
     */
235 4
    public function get(PersistentCollection $collection, $index)
236
    {
237
        return $this->persister->get($collection, $index);
238
    }
239
240
    /**
241 3
     * {@inheritdoc}
242
     */
243 3
    public function removeElement(PersistentCollection $collection, $element)
244
    {
245
        if ($persisterResult = $this->persister->removeElement($collection, $element)) {
246
            $this->evictCollectionCache($collection);
247
            $this->evictElementCache($this->sourceEntity->getRootClassName(), $collection->getOwner());
248
            $this->evictElementCache($this->targetEntity->getRootClassName(), $element);
249 3
        }
250
251 3
        return $persisterResult;
252
    }
253
254
    /**
255
     * {@inheritdoc}
256
     */
257 3
    public function slice(PersistentCollection $collection, $offset, $length = null)
258
    {
259
        return $this->persister->slice($collection, $offset, $length);
260
    }
261
262
    /**
263 3
     * {@inheritDoc}
264
     */
265 3
    public function loadCriteria(PersistentCollection $collection, Criteria $criteria)
266
    {
267
        return $this->persister->loadCriteria($collection, $criteria);
268
    }
269
270
    /**
271
     * Clears cache entries related to the current collection
272
     *
273
     * @param PersistentCollection $collection
274
     */
275
    protected function evictCollectionCache(PersistentCollection $collection)
276
    {
277
        $key = new CollectionCacheKey(
278
            $this->sourceEntity->getRootClassName(),
279
            $this->association->getName(),
280
            $this->uow->getEntityIdentifier($collection->getOwner())
281
        );
282
283
        $this->region->evict($key);
284
285
        if ($this->cacheLogger) {
286
            $this->cacheLogger->collectionCachePut($this->regionName, $key);
287
        }
288
    }
289
290
    /**
291
     * @param string $targetEntity
292
     * @param object $element
293
     */
294
    protected function evictElementCache($targetEntity, $element)
295
    {
296
        /* @var $targetPersister CachedEntityPersister */
297
        $targetPersister = $this->uow->getEntityPersister($targetEntity);
298
        $targetRegion    = $targetPersister->getCacheRegion();
299
        $key             = new EntityCacheKey($targetEntity, $this->uow->getEntityIdentifier($element));
300
301
        $targetRegion->evict($key);
302
303
        if ($this->cacheLogger) {
304
            $this->cacheLogger->entityCachePut($targetRegion->getName(), $key);
305
        }
306
    }
307
}
308