Completed
Pull Request — master (#6245)
by Luís
10:04
created

DefaultQueryCache::storeAssociationCache()   C

Complexity

Conditions 10
Paths 7

Size

Total Lines 48
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 10

Importance

Changes 0
Metric Value
dl 0
loc 48
ccs 24
cts 24
cp 1
rs 5.3454
c 0
b 0
f 0
cc 10
eloc 26
nc 7
nop 3
crap 10

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/*
4
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
5
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
6
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
7
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
8
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
9
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
10
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
11
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
12
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
13
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
14
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
15
 *
16
 * This software consists of voluntary contributions made by many individuals
17
 * and is licensed under the MIT license. For more information, see
18
 * <http://www.doctrine-project.org>.
19
 */
20
21
namespace Doctrine\ORM\Cache;
22
23
use Doctrine\Common\Collections\ArrayCollection;
24
use Doctrine\ORM\Cache\Persister\CachedPersister;
25
use Doctrine\ORM\EntityManagerInterface;
26
use Doctrine\ORM\Query\ResultSetMapping;
27
use Doctrine\ORM\Mapping\ClassMetadata;
28
use Doctrine\ORM\PersistentCollection;
29
use Doctrine\Common\Proxy\Proxy;
30
use Doctrine\ORM\Cache;
31
use Doctrine\ORM\Query;
32
33
/**
34
 * Default query cache implementation.
35
 *
36
 * @since   2.5
37
 * @author  Fabio B. Silva <[email protected]>
38
 */
39
class DefaultQueryCache implements QueryCache
40
{
41
     /**
42
     * @var \Doctrine\ORM\EntityManagerInterface
43
     */
44
    private $em;
45
46
    /**
47
     * @var \Doctrine\ORM\UnitOfWork
48
     */
49
    private $uow;
50
51
    /**
52
     * @var \Doctrine\ORM\Cache\Region
53
     */
54
    private $region;
55
56
    /**
57
     * @var \Doctrine\ORM\Cache\QueryCacheValidator
58
     */
59
    private $validator;
60
61
    /**
62
     * @var \Doctrine\ORM\Cache\Logging\CacheLogger
63
     */
64
    protected $cacheLogger;
65
66
    /**
67
     * @var array
68
     */
69
    private static $hints = [Query::HINT_CACHE_ENABLED => true];
70
71
    /**
72
     * @param \Doctrine\ORM\EntityManagerInterface $em     The entity manager.
73
     * @param \Doctrine\ORM\Cache\Region           $region The query region.
74
     */
75 84
    public function __construct(EntityManagerInterface $em, Region $region)
76
    {
77 84
        $cacheConfig = $em->getConfiguration()->getSecondLevelCacheConfiguration();
78
79 84
        $this->em           = $em;
80 84
        $this->region       = $region;
81 84
        $this->uow          = $em->getUnitOfWork();
82 84
        $this->cacheLogger  = $cacheConfig->getCacheLogger();
83 84
        $this->validator    = $cacheConfig->getQueryValidator();
84 84
    }
85
86
    /**
87
     * {@inheritdoc}
88
     */
89 50
    public function get(QueryCacheKey $key, ResultSetMapping $rsm, array $hints = [])
90
    {
91 50
        if ( ! ($key->cacheMode & Cache::MODE_GET)) {
92 3
            return null;
93
        }
94
95 49
        $entry = $this->region->get($key);
96
97 49
        if ( ! $entry instanceof QueryCacheEntry) {
98 46
            return null;
99
        }
100
101 36
        if ( ! $this->validator->isValid($key, $entry)) {
102 8
            $this->region->evict($key);
103
104 8
            return null;
105
        }
106
107 31
        $result      = [];
108 31
        $entityName  = reset($rsm->aliasMap);
109 31
        $hasRelation = ( ! empty($rsm->relationMap));
110 31
        $persister   = $this->uow->getEntityPersister($entityName);
111 31
        $region      = $persister->getCacheRegion();
112 31
        $regionName  = $region->getName();
113
114 31
        $cm = $this->em->getClassMetadata($entityName);
115
116
        $generateKeys = function (array $entry) use ($cm): EntityCacheKey {
117 31
            return new EntityCacheKey($cm->rootEntityName, $entry['identifier']);
0 ignored issues
show
Bug introduced by
Accessing rootEntityName on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
118 31
        };
119
120 31
        $cacheKeys = new CollectionCacheEntry(array_map($generateKeys, $entry->result));
121 31
        $entries   = $region->getMultiple($cacheKeys);
122
123
        // @TODO - move to cache hydration component
124 31
        foreach ($entry->result as $index => $entry) {
125 31
            $entityEntry = is_array($entries) && array_key_exists($index, $entries) ? $entries[$index] : null;
126
127 31
            if ($entityEntry === null) {
128
129 2
                if ($this->cacheLogger !== null) {
130 1
                    $this->cacheLogger->entityCacheMiss($regionName, $cacheKeys->identifiers[$index]);
0 ignored issues
show
Compatibility introduced by
$cacheKeys->identifiers[$index] of type object<Doctrine\ORM\Cache\CacheKey> is not a sub-type of object<Doctrine\ORM\Cache\EntityCacheKey>. It seems like you assume a child class of the class Doctrine\ORM\Cache\CacheKey to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
131
                }
132
133 2
                return null;
134
            }
135
136 29
            if ($this->cacheLogger !== null) {
137 28
                $this->cacheLogger->entityCacheHit($regionName, $cacheKeys->identifiers[$index]);
0 ignored issues
show
Compatibility introduced by
$cacheKeys->identifiers[$index] of type object<Doctrine\ORM\Cache\CacheKey> is not a sub-type of object<Doctrine\ORM\Cache\EntityCacheKey>. It seems like you assume a child class of the class Doctrine\ORM\Cache\CacheKey to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
138
            }
139
140 29
            if ( ! $hasRelation) {
141 21
                $result[$index]  = $this->uow->createEntity($entityEntry->class, $entityEntry->resolveAssociationEntries($this->em), self::$hints);
142
143 21
                continue;
144
            }
145
146 8
            $data = $entityEntry->data;
147
148 8
            foreach ($entry['associations'] as $name => $assoc) {
149 8
                $assocPersister  = $this->uow->getEntityPersister($assoc['targetEntity']);
150 8
                $assocRegion     = $assocPersister->getCacheRegion();
151 8
                $assocMetadata   = $this->em->getClassMetadata($assoc['targetEntity']);
152
153 8
                if ($assoc['type'] & ClassMetadata::TO_ONE) {
154
155 4
                    if (($assocEntry = $assocRegion->get($assocKey = new EntityCacheKey($assocMetadata->rootEntityName, $assoc['identifier']))) === null) {
0 ignored issues
show
Bug introduced by
Accessing rootEntityName on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
156
157 1
                        if ($this->cacheLogger !== null) {
158 1
                            $this->cacheLogger->entityCacheMiss($assocRegion->getName(), $assocKey);
159
                        }
160
161 1
                        $this->uow->hydrationComplete();
162
163 1
                        return null;
164
                    }
165
166 3
                    $data[$name] = $this->uow->createEntity($assocEntry->class, $assocEntry->resolveAssociationEntries($this->em), self::$hints);
167
168 3
                    if ($this->cacheLogger !== null) {
169 3
                        $this->cacheLogger->entityCacheHit($assocRegion->getName(), $assocKey);
170
                    }
171
172 3
                    continue;
173
                }
174
175 4
                if ( ! isset($assoc['list']) || empty($assoc['list'])) {
176
                    continue;
177
                }
178
179 4
                $generateKeys = function ($id) use ($assocMetadata): EntityCacheKey {
180 4
                    return new EntityCacheKey($assocMetadata->rootEntityName, $id);
0 ignored issues
show
Bug introduced by
Accessing rootEntityName on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
181 4
                };
182
183 4
                $collection   = new PersistentCollection($this->em, $assocMetadata, new ArrayCollection());
0 ignored issues
show
Compatibility introduced by
$assocMetadata of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
184 4
                $assocKeys    = new CollectionCacheEntry(array_map($generateKeys, $assoc['list']));
185 4
                $assocEntries = $assocRegion->getMultiple($assocKeys);
186
187 4
                foreach ($assoc['list'] as $assocIndex => $assocId) {
188 4
                    $assocEntry = is_array($assocEntries) && array_key_exists($assocIndex, $assocEntries) ? $assocEntries[$assocIndex] : null;
189
190 4
                    if ($assocEntry === null) {
191 1
                        if ($this->cacheLogger !== null) {
192 1
                            $this->cacheLogger->entityCacheMiss($assocRegion->getName(), $assocKeys->identifiers[$assocIndex]);
0 ignored issues
show
Compatibility introduced by
$assocKeys->identifiers[$assocIndex] of type object<Doctrine\ORM\Cache\CacheKey> is not a sub-type of object<Doctrine\ORM\Cache\EntityCacheKey>. It seems like you assume a child class of the class Doctrine\ORM\Cache\CacheKey to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
193
                        }
194
195 1
                        $this->uow->hydrationComplete();
196
197 1
                        return null;
198
                    }
199
200 3
                    $element = $this->uow->createEntity($assocEntry->class, $assocEntry->resolveAssociationEntries($this->em), self::$hints);
201
202 3
                    $collection->hydrateSet($assocIndex, $element);
203
204 3
                    if ($this->cacheLogger !== null) {
205 3
                        $this->cacheLogger->entityCacheHit($assocRegion->getName(), $assocKeys->identifiers[$assocIndex]);
0 ignored issues
show
Compatibility introduced by
$assocKeys->identifiers[$assocIndex] of type object<Doctrine\ORM\Cache\CacheKey> is not a sub-type of object<Doctrine\ORM\Cache\EntityCacheKey>. It seems like you assume a child class of the class Doctrine\ORM\Cache\CacheKey to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
206
                    }
207
                }
208
209 3
                $data[$name] = $collection;
210
211 3
                $collection->setInitialized(true);
212
            }
213
214 6
            $result[$index] = $this->uow->createEntity($entityEntry->class, $data, self::$hints);
215
        }
216
217 27
        $this->uow->hydrationComplete();
218
219 27
        return $result;
220
    }
221
222
    /**
223
     * {@inheritdoc}
224
     */
225 57
    public function put(QueryCacheKey $key, ResultSetMapping $rsm, $result, array $hints = [])
226
    {
227 57
        if ($rsm->scalarMappings) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $rsm->scalarMappings of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
228 1
            throw new CacheException("Second level cache does not support scalar results.");
229
        }
230
231 56
        if (count($rsm->entityMappings) > 1) {
232 1
            throw new CacheException("Second level cache does not support multiple root entities.");
233
        }
234
235 55
        if ( ! $rsm->isSelect) {
236 2
            throw new CacheException("Second-level cache query supports only select statements.");
237
        }
238
239 53
        if (isset($hints[Query::HINT_FORCE_PARTIAL_LOAD]) && $hints[Query::HINT_FORCE_PARTIAL_LOAD]) {
240 1
            throw new CacheException("Second level cache does not support partial entities.");
241
        }
242
243 52
        if ( ! ($key->cacheMode & Cache::MODE_PUT)) {
244 3
            return false;
245
        }
246
247 51
        $data        = [];
248 51
        $entityName  = reset($rsm->aliasMap);
249 51
        $rootAlias   = key($rsm->aliasMap);
250 51
        $hasRelation = ( ! empty($rsm->relationMap));
251 51
        $persister   = $this->uow->getEntityPersister($entityName);
252
253 51
        if ( ! ($persister instanceof CachedPersister)) {
254 1
            throw CacheException::nonCacheableEntity($entityName);
255
        }
256
257 50
        $region = $persister->getCacheRegion();
258
259 50
        foreach ($result as $index => $entity) {
260 50
            $identifier                     = $this->uow->getEntityIdentifier($entity);
261 50
            $entityKey                      = new EntityCacheKey($entityName, $identifier);
262 50
            $data[$index]['identifier']     = $identifier;
263 50
            $data[$index]['associations']   = [];
264
265 50
            if (($key->cacheMode & Cache::MODE_REFRESH) || ! $region->contains($entityKey)) {
266
                // Cancel put result if entity put fail
267 36
                if ( ! $persister->storeEntityCache($entity, $entityKey)) {
268 1
                    return false;
269
                }
270
            }
271
272 49
            if ( ! $hasRelation) {
273 35
                continue;
274
            }
275
276
            // @TODO - move to cache hydration components
277 14
            foreach ($rsm->relationMap as $alias => $name) {
278 14
                $parentAlias  = $rsm->parentAliasMap[$alias];
279 14
                $parentClass  = $rsm->aliasMap[$parentAlias];
280 14
                $metadata     = $this->em->getClassMetadata($parentClass);
281 14
                $assoc        = $metadata->associationMappings[$name];
0 ignored issues
show
Bug introduced by
Accessing associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
282 14
                $assocValue   = $this->getAssociationValue($rsm, $alias, $entity);
283
284 14
                if ($assocValue === null) {
285 1
                    continue;
286
                }
287
288
                // root entity association
289 13
                if ($rootAlias === $parentAlias) {
290
                    // Cancel put result if association put fail
291 13
                    if ( ($assocInfo = $this->storeAssociationCache($key, $assoc, $assocValue)) === null) {
292 2
                        return false;
293
                    }
294
295 11
                    $data[$index]['associations'][$name] = $assocInfo;
296
297 11
                    continue;
298
                }
299
300
                // store single nested association
301 2
                if ( ! is_array($assocValue)) {
302
                    // Cancel put result if association put fail
303 1
                    if ($this->storeAssociationCache($key, $assoc, $assocValue) === null) {
304
                        return false;
305
                    }
306
307 1
                    continue;
308
                }
309
310
                // store array of nested association
311 1
                foreach ($assocValue as $aVal) {
312
                    // Cancel put result if association put fail
313 1
                    if ($this->storeAssociationCache($key, $assoc, $aVal) === null) {
314 12
                        return false;
315
                    }
316
                }
317
            }
318
        }
319
320 47
        return $this->region->put($key, new QueryCacheEntry($data));
321
    }
322
323
    /**
324
     * @param \Doctrine\ORM\Cache\QueryCacheKey $key
325
     * @param array                             $assoc
326
     * @param mixed                             $assocValue
327
     *
328
     * @return array|null
329
     */
330 13
    private function storeAssociationCache(QueryCacheKey $key, array $assoc, $assocValue)
331
    {
332 13
        $assocPersister = $this->uow->getEntityPersister($assoc['targetEntity']);
333 13
        $assocMetadata  = $assocPersister->getClassMetadata();
334 13
        $assocRegion    = $assocPersister->getCacheRegion();
335
336
        // Handle *-to-one associations
337 13
        if ($assoc['type'] & ClassMetadata::TO_ONE) {
338 7
            $assocIdentifier = $this->uow->getEntityIdentifier($assocValue);
339 7
            $entityKey       = new EntityCacheKey($assocMetadata->rootEntityName, $assocIdentifier);
340
341 7
            if ( ! $assocValue instanceof Proxy && ($key->cacheMode & Cache::MODE_REFRESH) || ! $assocRegion->contains($entityKey)) {
342
                // Entity put fail
343 7
                if ( ! $assocPersister->storeEntityCache($assocValue, $entityKey)) {
344 1
                    return null;
345
                }
346
            }
347
348
            return [
349 6
                'targetEntity'  => $assocMetadata->rootEntityName,
350 6
                'identifier'    => $assocIdentifier,
351 6
                'type'          => $assoc['type']
352
            ];
353
        }
354
355
        // Handle *-to-many associations
356 6
        $list = [];
357
358 6
        foreach ($assocValue as $assocItemIndex => $assocItem) {
359 6
            $assocIdentifier = $this->uow->getEntityIdentifier($assocItem);
360 6
            $entityKey       = new EntityCacheKey($assocMetadata->rootEntityName, $assocIdentifier);
361
362 6
            if (($key->cacheMode & Cache::MODE_REFRESH) || ! $assocRegion->contains($entityKey)) {
363
                // Entity put fail
364 6
                if ( ! $assocPersister->storeEntityCache($assocItem, $entityKey)) {
365 1
                    return null;
366
                }
367
            }
368
369 5
            $list[$assocItemIndex] = $assocIdentifier;
370
        }
371
372
        return [
373 5
            'targetEntity'  => $assocMetadata->rootEntityName,
374 5
            'type'          => $assoc['type'],
375 5
            'list'          => $list,
376
        ];
377
    }
378
379
    /**
380
     * @param \Doctrine\ORM\Query\ResultSetMapping $rsm
381
     * @param string                               $assocAlias
382
     * @param object                               $entity
383
     *
384
     * @return array|object
385
     */
386 15
    private function getAssociationValue(ResultSetMapping $rsm, $assocAlias, $entity)
387
    {
388 15
        $path  = [];
389 15
        $alias = $assocAlias;
390
391 15
        while (isset($rsm->parentAliasMap[$alias])) {
392 15
            $parent = $rsm->parentAliasMap[$alias];
393 15
            $field  = $rsm->relationMap[$alias];
394 15
            $class  = $rsm->aliasMap[$parent];
395
396 15
            array_unshift($path, [
397 15
                'field'  => $field,
398 15
                'class'  => $class
399
            ]
400
            );
401
402 15
            $alias = $parent;
403
        }
404
405 15
        return $this->getAssociationPathValue($entity, $path);
406
    }
407
408
    /**
409
     * @param mixed $value
410
     * @param array $path
411
     *
412
     * @return array|object|null
413
     */
414 15
    private function getAssociationPathValue($value, array $path)
415
    {
416 15
        $mapping  = array_shift($path);
417 15
        $metadata = $this->em->getClassMetadata($mapping['class']);
418 15
        $assoc    = $metadata->associationMappings[$mapping['field']];
0 ignored issues
show
Bug introduced by
Accessing associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
419 15
        $value    = $metadata->getFieldValue($value, $mapping['field']);
420
421 15
        if ($value === null) {
422 1
            return null;
423
        }
424
425 14
        if (empty($path)) {
426 14
            return $value;
427
        }
428
429
        // Handle *-to-one associations
430 3
        if ($assoc['type'] & ClassMetadata::TO_ONE) {
431 1
            return $this->getAssociationPathValue($value, $path);
432
        }
433
434 2
        $values = [];
435
436 2
        foreach ($value as $item) {
437 2
            $values[] = $this->getAssociationPathValue($item, $path);
438
        }
439
440 2
        return $values;
441
    }
442
443
    /**
444
     * {@inheritdoc}
445
     */
446 48
    public function clear()
447
    {
448 48
        return $this->region->evictAll();
449
    }
450
451
    /**
452
     * {@inheritdoc}
453
     */
454 28
    public function getRegion()
455
    {
456 28
        return $this->region;
457
    }
458
}
459