Failed Conditions
Push — master ( 4bfa22...148895 )
by Guilherme
17:21 queued 09:56
created

Collection/AbstractCollectionPersister.php (6 issues)

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\ORM\Cache\Persister\Collection;
6
7
use Doctrine\Common\Collections\Collection;
8
use Doctrine\Common\Collections\Criteria;
9
use Doctrine\ORM\Cache\CollectionCacheKey;
10
use Doctrine\ORM\Cache\CollectionHydrator;
11
use Doctrine\ORM\Cache\EntityCacheKey;
12
use Doctrine\ORM\Cache\Logging\CacheLogger;
13
use Doctrine\ORM\Cache\Persister\Entity\CachedEntityPersister;
14
use Doctrine\ORM\Cache\Region;
15
use Doctrine\ORM\EntityManagerInterface;
16
use Doctrine\ORM\Mapping\AssociationMetadata;
17
use Doctrine\ORM\Mapping\ClassMetadata;
18
use Doctrine\ORM\Mapping\ClassMetadataFactory;
19
use Doctrine\ORM\Mapping\ToManyAssociationMetadata;
20
use Doctrine\ORM\PersistentCollection;
21
use Doctrine\ORM\Persisters\Collection\CollectionPersister;
22
use Doctrine\ORM\UnitOfWork;
23
use Doctrine\ORM\Utility\StaticClassNameConverter;
24
use function array_values;
25
use function count;
26
27
abstract class AbstractCollectionPersister implements CachedCollectionPersister
28
{
29
    /** @var UnitOfWork */
30
    protected $uow;
31
32
    /** @var ClassMetadataFactory */
33
    protected $metadataFactory;
34
35
    /** @var CollectionPersister */
36
    protected $persister;
37
38
    /** @var ClassMetadata */
39
    protected $sourceEntity;
40
41
    /** @var ClassMetadata */
42
    protected $targetEntity;
43
44
    /** @var AssociationMetadata */
45
    protected $association;
46
47
    /** @var mixed[][] */
48
    protected $queuedCache = [];
49
50
    /** @var Region */
51
    protected $region;
52
53
    /** @var string */
54
    protected $regionName;
55
56
    /** @var CollectionHydrator */
57
    protected $hydrator;
58
59
    /** @var CacheLogger */
60
    protected $cacheLogger;
61
62
    /**
63
     * @param CollectionPersister    $persister   The collection persister that will be cached.
64
     * @param Region                 $region      The collection region.
65
     * @param EntityManagerInterface $em          The entity manager.
66
     * @param AssociationMetadata    $association The association mapping.
67
     */
68 117
    public function __construct(
69
        CollectionPersister $persister,
70
        Region $region,
71
        EntityManagerInterface $em,
72
        AssociationMetadata $association
73
    ) {
74 117
        $configuration = $em->getConfiguration();
75 117
        $cacheConfig   = $configuration->getSecondLevelCacheConfiguration();
76 117
        $cacheFactory  = $cacheConfig->getCacheFactory();
77
78 117
        $this->region          = $region;
79 117
        $this->persister       = $persister;
80 117
        $this->association     = $association;
81 117
        $this->regionName      = $region->getName();
82 117
        $this->uow             = $em->getUnitOfWork();
83 117
        $this->metadataFactory = $em->getMetadataFactory();
84 117
        $this->cacheLogger     = $cacheConfig->getCacheLogger();
85 117
        $this->hydrator        = $cacheFactory->buildCollectionHydrator($em, $association);
86 117
        $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...
87 117
        $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...
88 117
    }
89
90
    /**
91
     * {@inheritdoc}
92
     */
93 63
    public function getCacheRegion()
94
    {
95 63
        return $this->region;
96
    }
97
98
    /**
99
     * {@inheritdoc}
100
     */
101
    public function getSourceEntityMetadata()
102
    {
103
        return $this->sourceEntity;
104
    }
105
106
    /**
107
     * {@inheritdoc}
108
     */
109
    public function getTargetEntityMetadata()
110
    {
111
        return $this->targetEntity;
112
    }
113
114
    /**
115
     * @return PersistentCollection|null
116
     */
117 18
    public function loadCollectionCache(PersistentCollection $collection, CollectionCacheKey $key)
118
    {
119 18
        $cache = $this->region->get($key);
120
121 18
        if ($cache === null) {
122 14
            return null;
123
        }
124
125 15
        return $this->hydrator->loadCacheEntry($this->sourceEntity, $key, $cache, $collection);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->hydrator->...y, $cache, $collection) returns the type array<mixed,object> which is incompatible with the documented return type Doctrine\ORM\PersistentCollection|null.
Loading history...
126
    }
127
128
    /**
129
     * {@inheritdoc}
130
     */
131 45
    public function storeCollectionCache(CollectionCacheKey $key, $elements)
132
    {
133
        /** @var CachedEntityPersister $targetPersister */
134 45
        $association     = $this->sourceEntity->getProperty($key->association);
135 45
        $targetPersister = $this->uow->getEntityPersister($this->targetEntity->getRootClassName());
136 45
        $targetRegion    = $targetPersister->getCacheRegion();
0 ignored issues
show
The method getCacheRegion() does not exist on Doctrine\ORM\Persisters\Entity\EntityPersister. It seems like you code against a sub-type of Doctrine\ORM\Persisters\Entity\EntityPersister such as Doctrine\ORM\Cache\Persi...y\CachedEntityPersister. ( Ignorable by Annotation )

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

136
        /** @scrutinizer ignore-call */ 
137
        $targetRegion    = $targetPersister->getCacheRegion();
Loading history...
137 45
        $targetHydrator  = $targetPersister->getEntityHydrator();
0 ignored issues
show
The method getEntityHydrator() does not exist on Doctrine\ORM\Persisters\Entity\EntityPersister. It seems like you code against a sub-type of Doctrine\ORM\Persisters\Entity\EntityPersister such as Doctrine\ORM\Cache\Persi...y\CachedEntityPersister. ( Ignorable by Annotation )

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

137
        /** @scrutinizer ignore-call */ 
138
        $targetHydrator  = $targetPersister->getEntityHydrator();
Loading history...
138
139
        // Only preserve ordering if association configured it
140 45
        if (! ($association instanceof ToManyAssociationMetadata && $association->getIndexedBy())) {
141
            // Elements may be an array or a Collection
142 45
            $elements = array_values($elements instanceof Collection ? $elements->getValues() : $elements);
143
        }
144
145 45
        $entry = $this->hydrator->buildCacheEntry($this->targetEntity, $key, $elements);
146
147 45
        foreach ($entry->identifiers as $index => $entityKey) {
148 45
            if ($targetRegion->contains($entityKey)) {
149 45
                continue;
150
            }
151
152 11
            $class     = $this->targetEntity;
153 11
            $className = StaticClassNameConverter::getClass($elements[$index]);
154
155 11
            if ($className !== $this->targetEntity->getClassName()) {
156 2
                $class = $this->metadataFactory->getMetadataFor($className);
157
            }
158
159 11
            $entity      = $elements[$index];
160 11
            $entityEntry = $targetHydrator->buildCacheEntry($class, $entityKey, $entity);
161
162 11
            $targetRegion->put($entityKey, $entityEntry);
163
        }
164
165 45
        $cached = $this->region->put($key, $entry);
166
167 45
        if ($this->cacheLogger && $cached) {
168 45
            $this->cacheLogger->collectionCachePut($this->regionName, $key);
169
        }
170 45
    }
171
172
    /**
173
     * {@inheritdoc}
174
     */
175 3
    public function contains(PersistentCollection $collection, $element)
176
    {
177 3
        return $this->persister->contains($collection, $element);
178
    }
179
180
    /**
181
     * {@inheritdoc}
182
     */
183 3
    public function containsKey(PersistentCollection $collection, $key)
184
    {
185 3
        return $this->persister->containsKey($collection, $key);
186
    }
187
188
    /**
189
     * {@inheritdoc}
190
     */
191 4
    public function count(PersistentCollection $collection)
192
    {
193 4
        $fieldName = $this->association->getName();
194 4
        $ownerId   = $this->uow->getEntityIdentifier($collection->getOwner());
195 4
        $key       = new CollectionCacheKey($this->sourceEntity->getRootClassName(), $fieldName, $ownerId);
196 4
        $entry     = $this->region->get($key);
197
198 4
        if ($entry !== null) {
199 1
            return count($entry->identifiers);
0 ignored issues
show
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...
200
        }
201
202 4
        return $this->persister->count($collection);
203
    }
204
205
    /**
206
     * {@inheritdoc}
207
     */
208 3
    public function get(PersistentCollection $collection, $index)
209
    {
210 3
        return $this->persister->get($collection, $index);
211
    }
212
213
    /**
214
     * {@inheritdoc}
215
     */
216 3
    public function removeElement(PersistentCollection $collection, $element)
217
    {
218 3
        $persisterResult = $this->persister->removeElement($collection, $element);
219
220 3
        if ($persisterResult) {
221
            $this->evictCollectionCache($collection);
222
            $this->evictElementCache($this->sourceEntity->getRootClassName(), $collection->getOwner());
223
            $this->evictElementCache($this->targetEntity->getRootClassName(), $element);
224
        }
225
226 3
        return $persisterResult;
227
    }
228
229
    /**
230
     * {@inheritdoc}
231
     */
232 3
    public function slice(PersistentCollection $collection, $offset, $length = null)
233
    {
234 3
        return $this->persister->slice($collection, $offset, $length);
235
    }
236
237
    /**
238
     * {@inheritDoc}
239
     */
240
    public function loadCriteria(PersistentCollection $collection, Criteria $criteria)
241
    {
242
        return $this->persister->loadCriteria($collection, $criteria);
243
    }
244
245
    /**
246
     * Clears cache entries related to the current collection
247
     */
248
    protected function evictCollectionCache(PersistentCollection $collection)
249
    {
250
        $key = new CollectionCacheKey(
251
            $this->sourceEntity->getRootClassName(),
252
            $this->association->getName(),
253
            $this->uow->getEntityIdentifier($collection->getOwner())
254
        );
255
256
        $this->region->evict($key);
257
258
        if ($this->cacheLogger) {
259
            $this->cacheLogger->collectionCachePut($this->regionName, $key);
260
        }
261
    }
262
263
    /**
264
     * @param string $targetEntity
265
     * @param object $element
266
     */
267
    protected function evictElementCache($targetEntity, $element)
268
    {
269
        /** @var CachedEntityPersister $targetPersister */
270
        $targetPersister = $this->uow->getEntityPersister($targetEntity);
271
        $targetRegion    = $targetPersister->getCacheRegion();
272
        $key             = new EntityCacheKey($targetEntity, $this->uow->getEntityIdentifier($element));
273
274
        $targetRegion->evict($key);
275
276
        if ($this->cacheLogger) {
277
            $this->cacheLogger->entityCachePut($targetRegion->getName(), $key);
278
        }
279
    }
280
}
281