Failed Conditions
Push — 2.7 ( c036c0...266f0d )
by Jonathan
57:23 queued 50:07
created

Cache/Persister/Entity/AbstractEntityPersister.php (1 issue)

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\Persister\Entity;
22
23
use Doctrine\ORM\Cache;
24
use Doctrine\ORM\Cache\Region;
25
use Doctrine\ORM\Cache\EntityCacheKey;
26
use Doctrine\ORM\Cache\CollectionCacheKey;
27
use Doctrine\ORM\Cache\TimestampCacheKey;
28
use Doctrine\ORM\Cache\QueryCacheKey;
29
use Doctrine\ORM\Cache\Persister\CachedPersister;
30
use Doctrine\ORM\Mapping\ClassMetadata;
31
use Doctrine\ORM\PersistentCollection;
32
use Doctrine\ORM\EntityManagerInterface;
33
use Doctrine\ORM\Persisters\Entity\EntityPersister;
34
35
use Doctrine\Common\Util\ClassUtils;
36
use Doctrine\Common\Collections\Criteria;
37
38
/**
39
 * @author Fabio B. Silva <[email protected]>
40
 * @since 2.5
41
 */
42
abstract class AbstractEntityPersister implements CachedEntityPersister
43
{
44
     /**
45
     * @var \Doctrine\ORM\UnitOfWork
46
     */
47
    protected $uow;
48
49
    /**
50
     * @var \Doctrine\ORM\Mapping\ClassMetadataFactory
51
     */
52
    protected $metadataFactory;
53
54
    /**
55
     * @var \Doctrine\ORM\Persisters\Entity\EntityPersister
56
     */
57
    protected $persister;
58
59
    /**
60
     * @var \Doctrine\ORM\Mapping\ClassMetadata
61
     */
62
    protected $class;
63
64
     /**
65
     * @var array
66
     */
67
    protected $queuedCache = [];
68
69
    /**
70
     * @var \Doctrine\ORM\Cache\Region
71
     */
72
    protected $region;
73
74
    /**
75
     * @var \Doctrine\ORM\Cache\TimestampRegion
76
     */
77
    protected $timestampRegion;
78
79
    /**
80
     * @var \Doctrine\ORM\Cache\TimestampCacheKey
81
     */
82
    protected $timestampKey;
83
84
    /**
85
     * @var \Doctrine\ORM\Cache\EntityHydrator
86
     */
87
    protected $hydrator;
88
89
    /**
90
     * @var \Doctrine\ORM\Cache
91
     */
92
    protected $cache;
93
94
    /**
95
     * @var \Doctrine\ORM\Cache\Logging\CacheLogger
96
     */
97
    protected $cacheLogger;
98
99
    /**
100
     * @var string
101
     */
102
    protected $regionName;
103
104
    /**
105
     * Associations configured as FETCH_EAGER, as well as all inverse one-to-one associations.
106
     *
107
     * @var array|null
108
     */
109
    protected $joinedAssociations;
110
111
    /**
112
     * @param \Doctrine\ORM\Persisters\Entity\EntityPersister $persister The entity persister to cache.
113
     * @param \Doctrine\ORM\Cache\Region                      $region    The entity cache region.
114
     * @param \Doctrine\ORM\EntityManagerInterface            $em        The entity manager.
115
     * @param \Doctrine\ORM\Mapping\ClassMetadata             $class     The entity metadata.
116
     */
117 209
    public function __construct(EntityPersister $persister, Region $region, EntityManagerInterface $em, ClassMetadata $class)
118
    {
119 209
        $configuration  = $em->getConfiguration();
120 209
        $cacheConfig    = $configuration->getSecondLevelCacheConfiguration();
121 209
        $cacheFactory   = $cacheConfig->getCacheFactory();
122
123 209
        $this->class            = $class;
124 209
        $this->region           = $region;
125 209
        $this->persister        = $persister;
126 209
        $this->cache            = $em->getCache();
127 209
        $this->regionName       = $region->getName();
128 209
        $this->uow              = $em->getUnitOfWork();
129 209
        $this->metadataFactory  = $em->getMetadataFactory();
130 209
        $this->cacheLogger      = $cacheConfig->getCacheLogger();
131 209
        $this->timestampRegion  = $cacheFactory->getTimestampRegion();
132 209
        $this->hydrator         = $cacheFactory->buildEntityHydrator($em, $class);
133 209
        $this->timestampKey     = new TimestampCacheKey($this->class->rootEntityName);
134 209
    }
135
136
    /**
137
     * {@inheritdoc}
138
     */
139 99
    public function addInsert($entity)
140
    {
141 99
        $this->persister->addInsert($entity);
142 99
    }
143
144
    /**
145
     * {@inheritdoc}
146
     */
147 3
    public function getInserts()
148
    {
149 3
        return $this->persister->getInserts();
150
    }
151
152
    /**
153
     * {@inheritdoc}
154
     */
155 3
    public function getSelectSQL($criteria, $assoc = null, $lockMode = null, $limit = null, $offset = null, array $orderBy = null)
156
    {
157 3
        return $this->persister->getSelectSQL($criteria, $assoc, $lockMode, $limit, $offset, $orderBy);
158
    }
159
160
    /**
161
     * {@inheritDoc}
162
     */
163
    public function getCountSQL($criteria = [])
164
    {
165
        return $this->persister->getCountSQL($criteria);
166
    }
167
168
    /**
169
     * {@inheritdoc}
170
     */
171 9
    public function getInsertSQL()
172
    {
173 9
        return $this->persister->getInsertSQL();
174
    }
175
176
    /**
177
     * {@inheritdoc}
178
     */
179 17
    public function getResultSetMapping()
180
    {
181 17
        return $this->persister->getResultSetMapping();
182
    }
183
184
    /**
185
     * {@inheritdoc}
186
     */
187 3
    public function getSelectConditionStatementSQL($field, $value, $assoc = null, $comparison = null)
188
    {
189 3
        return $this->persister->getSelectConditionStatementSQL($field, $value, $assoc, $comparison);
190
    }
191
192
    /**
193
     * {@inheritdoc}
194
     */
195 4
    public function exists($entity, Criteria $extraConditions = null)
196
    {
197 4
        if (null === $extraConditions) {
198 4
            $key = new EntityCacheKey($this->class->rootEntityName, $this->class->getIdentifierValues($entity));
199
200 4
            if ($this->region->contains($key)) {
201 1
                return true;
202
            }
203
        }
204
205 4
        return $this->persister->exists($entity, $extraConditions);
206
    }
207
208
    /**
209
     * {@inheritdoc}
210
     */
211 125
    public function getCacheRegion()
212
    {
213 125
        return $this->region;
214
    }
215
216
    /**
217
     * @return \Doctrine\ORM\Cache\EntityHydrator
218
     */
219 45
    public function getEntityHydrator()
220
    {
221 45
        return $this->hydrator;
222
    }
223
224
    /**
225
     * {@inheritdoc}
226
     */
227 38
    public function storeEntityCache($entity, EntityCacheKey $key)
228
    {
229 38
        $class      = $this->class;
230 38
        $className  = ClassUtils::getClass($entity);
231
232 38
        if ($className !== $this->class->name) {
233 3
            $class = $this->metadataFactory->getMetadataFor($className);
234
        }
235
236 38
        $entry  = $this->hydrator->buildCacheEntry($class, $key, $entity);
237 38
        $cached = $this->region->put($key, $entry);
238
239 38
        if ($this->cacheLogger && $cached) {
240 24
            $this->cacheLogger->entityCachePut($this->regionName, $key);
241
        }
242
243 38
        return $cached;
244
    }
245
246
    /**
247
     * @param object $entity
248
     */
249 29
    private function storeJoinedAssociations($entity)
250
    {
251 29
        if ($this->joinedAssociations === null) {
252 29
            $associations = [];
253
254 29
            foreach ($this->class->associationMappings as $name => $assoc) {
255 23
                if (isset($assoc['cache']) &&
256 23
                    ($assoc['type'] & ClassMetadata::TO_ONE) &&
257 23
                    ($assoc['fetch'] === ClassMetadata::FETCH_EAGER || ! $assoc['isOwningSide'])) {
258
259 23
                    $associations[] = $name;
260
                }
261
            }
262
263 29
            $this->joinedAssociations = $associations;
264
        }
265
266 29
        foreach ($this->joinedAssociations as $name) {
267 2
            $assoc       = $this->class->associationMappings[$name];
268 2
            $assocEntity = $this->class->getFieldValue($entity, $name);
269
270 2
            if ($assocEntity === null) {
271
                continue;
272
            }
273
274 2
            $assocId        = $this->uow->getEntityIdentifier($assocEntity);
275 2
            $assocMetadata  = $this->metadataFactory->getMetadataFor($assoc['targetEntity']);
276 2
            $assocKey       = new EntityCacheKey($assocMetadata->rootEntityName, $assocId);
277 2
            $assocPersister = $this->uow->getEntityPersister($assoc['targetEntity']);
278
279 2
            $assocPersister->storeEntityCache($assocEntity, $assocKey);
280
        }
281 29
    }
282
283
    /**
284
     * Generates a string of currently query
285
     *
286
     * @param array   $query
287
     * @param string  $criteria
288
     * @param array   $orderBy
289
     * @param integer $limit
290
     * @param integer $offset
291
     *
292
     * @return string
293
     */
294 17
    protected function getHash($query, $criteria, array $orderBy = null, $limit = null, $offset = null)
295
    {
296 17
        list($params) = ($criteria instanceof Criteria)
297 6
            ? $this->persister->expandCriteriaParameters($criteria)
298 17
            : $this->persister->expandParameters($criteria);
299
300 17
        return sha1($query . serialize($params) . serialize($orderBy) . $limit . $offset);
301
    }
302
303
    /**
304
     * {@inheritdoc}
305
     */
306 3
    public function expandParameters($criteria)
307
    {
308 3
        return $this->persister->expandParameters($criteria);
309
    }
310
311
    /**
312
     * {@inheritdoc}
313
     */
314 3
    public function expandCriteriaParameters(Criteria $criteria)
315
    {
316 3
        return $this->persister->expandCriteriaParameters($criteria);
317
    }
318
319
    /**
320
     * {@inheritdoc}
321
     */
322 14
    public function getClassMetadata()
323
    {
324 14
        return $this->persister->getClassMetadata();
325
    }
326
327
    /**
328
     * {@inheritdoc}
329
     */
330 3
    public function getManyToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null)
331
    {
332 3
        return $this->persister->getManyToManyCollection($assoc, $sourceEntity, $offset, $limit);
333
    }
334
335
    /**
336
     * {@inheritdoc}
337
     */
338 3
    public function getOneToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null)
339
    {
340 3
        return $this->persister->getOneToManyCollection($assoc, $sourceEntity, $offset, $limit);
341
    }
342
343
    /**
344
     * {@inheritdoc}
345
     */
346 3
    public function getOwningTable($fieldName)
347
    {
348 3
        return $this->persister->getOwningTable($fieldName);
349
    }
350
351
    /**
352
     * {@inheritdoc}
353
     */
354 99
    public function executeInserts()
355
    {
356 99
        $this->queuedCache['insert'] = $this->persister->getInserts();
357
358 99
        return $this->persister->executeInserts();
359
    }
360
361
    /**
362
     * {@inheritdoc}
363
     */
364 6
    public function load(array $criteria, $entity = null, $assoc = null, array $hints = [], $lockMode = null, $limit = null, array $orderBy = null)
365
    {
366 6
        if ($entity !== null || $assoc !== null || ! empty($hints) || $lockMode !== null) {
367 3
            return $this->persister->load($criteria, $entity, $assoc, $hints, $lockMode, $limit, $orderBy);
368
        }
369
370
        //handle only EntityRepository#findOneBy
371 3
        $query      = $this->persister->getSelectSQL($criteria, null, null, $limit, null, $orderBy);
372 3
        $hash       = $this->getHash($query, $criteria, null, null, null);
373 3
        $rsm        = $this->getResultSetMapping();
374 3
        $queryKey   = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL, $this->timestampKey);
375 3
        $queryCache = $this->cache->getQueryCache($this->regionName);
376 3
        $result     = $queryCache->get($queryKey, $rsm);
377
378 3
        if ($result !== null) {
379 3
            if ($this->cacheLogger) {
380 3
                $this->cacheLogger->queryCacheHit($this->regionName, $queryKey);
381
            }
382
383 3
            return $result[0];
384
        }
385
386 3
        if (($result = $this->persister->load($criteria, $entity, $assoc, $hints, $lockMode, $limit, $orderBy)) === null) {
387
            return null;
388
        }
389
390 3
        $cached = $queryCache->put($queryKey, $rsm, [$result]);
391
392 3
        if ($this->cacheLogger) {
393 3
            if ($result) {
394 3
                $this->cacheLogger->queryCacheMiss($this->regionName, $queryKey);
395
            }
396
397 3
            if ($cached) {
398 3
                $this->cacheLogger->queryCachePut($this->regionName, $queryKey);
399
            }
400
        }
401
402 3
        return $result;
403
    }
404
405
    /**
406
     * {@inheritdoc}
407
     */
408 8
    public function loadAll(array $criteria = [], array $orderBy = null, $limit = null, $offset = null)
409
    {
410 8
        $query      = $this->persister->getSelectSQL($criteria, null, null, $limit, $offset, $orderBy);
411 8
        $hash       = $this->getHash($query, $criteria, null, null, null);
412 8
        $rsm        = $this->getResultSetMapping();
413 8
        $queryKey   = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL, $this->timestampKey);
414 8
        $queryCache = $this->cache->getQueryCache($this->regionName);
415 8
        $result     = $queryCache->get($queryKey, $rsm);
416
417 8
        if ($result !== null) {
418 5
            if ($this->cacheLogger) {
419 5
                $this->cacheLogger->queryCacheHit($this->regionName, $queryKey);
420
            }
421
422 5
            return $result;
423
        }
424
425 8
        $result = $this->persister->loadAll($criteria, $orderBy, $limit, $offset);
426 8
        $cached = $queryCache->put($queryKey, $rsm, $result);
427
428 8
        if ($this->cacheLogger) {
429 5
            if ($result) {
430 5
                $this->cacheLogger->queryCacheMiss($this->regionName, $queryKey);
431
            }
432
433 5
            if ($cached) {
434 5
                $this->cacheLogger->queryCachePut($this->regionName, $queryKey);
435
            }
436
        }
437
438 8
        return $result;
439
    }
440
441
    /**
442
     * {@inheritdoc}
443
     */
444 48
    public function loadById(array $identifier, $entity = null)
445
    {
446 48
        $cacheKey   = new EntityCacheKey($this->class->rootEntityName, $identifier);
447 48
        $cacheEntry = $this->region->get($cacheKey);
448 48
        $class      = $this->class;
449
450 48
        if ($cacheEntry !== null) {
451 40
            if ($cacheEntry->class !== $this->class->name) {
452 3
                $class = $this->metadataFactory->getMetadataFor($cacheEntry->class);
453
            }
454
455 40
            if (($entity = $this->hydrator->loadCacheEntry($class, $cacheKey, $cacheEntry, $entity)) !== null) {
456 40
                if ($this->cacheLogger) {
457 40
                    $this->cacheLogger->entityCacheHit($this->regionName, $cacheKey);
458
                }
459
460 40
                return $entity;
461
            }
462
        }
463
464 33
        $entity = $this->persister->loadById($identifier, $entity);
465
466 33
        if ($entity === null) {
467 2
            return null;
468
        }
469
470 33
        $class      = $this->class;
471 33
        $className  = ClassUtils::getClass($entity);
472
473 33
        if ($className !== $this->class->name) {
474 4
            $class = $this->metadataFactory->getMetadataFor($className);
475
        }
476
477 33
        $cacheEntry = $this->hydrator->buildCacheEntry($class, $cacheKey, $entity);
478 33
        $cached     = $this->region->put($cacheKey, $cacheEntry);
479
480 33
        if ($cached && (null === $this->joinedAssociations || $this->joinedAssociations)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->joinedAssociations 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...
481 29
            $this->storeJoinedAssociations($entity);
482
        }
483
484 33
        if ($this->cacheLogger) {
485 30
            if ($cached) {
486 29
                $this->cacheLogger->entityCachePut($this->regionName, $cacheKey);
487
            }
488
489 30
            $this->cacheLogger->entityCacheMiss($this->regionName, $cacheKey);
490
        }
491
492 33
        return $entity;
493
    }
494
495
    /**
496
     * {@inheritDoc}
497
     */
498 2
    public function count($criteria = [])
499
    {
500 2
        return $this->persister->count($criteria);
501
    }
502
503
    /**
504
     * {@inheritdoc}
505
     */
506 6
    public function loadCriteria(Criteria $criteria)
507
    {
508 6
        $orderBy     = $criteria->getOrderings();
509 6
        $limit       = $criteria->getMaxResults();
510 6
        $offset      = $criteria->getFirstResult();
511 6
        $query       = $this->persister->getSelectSQL($criteria);
512 6
        $hash        = $this->getHash($query, $criteria, $orderBy, $limit, $offset);
513 6
        $rsm         = $this->getResultSetMapping();
514 6
        $queryKey    = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL, $this->timestampKey);
515 6
        $queryCache  = $this->cache->getQueryCache($this->regionName);
516 6
        $cacheResult = $queryCache->get($queryKey, $rsm);
517
518 6
        if ($cacheResult !== null) {
519 3
            if ($this->cacheLogger) {
520 3
                $this->cacheLogger->queryCacheHit($this->regionName, $queryKey);
521
            }
522
523 3
            return $cacheResult;
524
        }
525
526 6
        $result = $this->persister->loadCriteria($criteria);
527 6
        $cached = $queryCache->put($queryKey, $rsm, $result);
528
529 6
        if ($this->cacheLogger) {
530 3
            if ($result) {
531 3
                $this->cacheLogger->queryCacheMiss($this->regionName, $queryKey);
532
            }
533
534 3
            if ($cached) {
535 3
                $this->cacheLogger->queryCachePut($this->regionName, $queryKey);
536
            }
537
        }
538
539 6
        return $result;
540
    }
541
542
    /**
543
     * {@inheritdoc}
544
     */
545 7
    public function loadManyToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll)
546
    {
547 7
        $persister = $this->uow->getCollectionPersister($assoc);
548 7
        $hasCache  = ($persister instanceof CachedPersister);
549 7
        $key       = null;
550
551 7
        if ( ! $hasCache) {
552 3
            return $this->persister->loadManyToManyCollection($assoc, $sourceEntity, $coll);
553
        }
554
555 4
        $ownerId = $this->uow->getEntityIdentifier($coll->getOwner());
556 4
        $key     = $this->buildCollectionCacheKey($assoc, $ownerId);
557 4
        $list    = $persister->loadCollectionCache($coll, $key);
558
559 4
        if ($list !== null) {
560 4
            if ($this->cacheLogger) {
561 4
                $this->cacheLogger->collectionCacheHit($persister->getCacheRegion()->getName(), $key);
562
            }
563
564 4
            return $list;
565
        }
566
567 2
        $list = $this->persister->loadManyToManyCollection($assoc, $sourceEntity, $coll);
568
569 2
        $persister->storeCollectionCache($key, $list);
570
571 2
        if ($this->cacheLogger) {
572 2
            $this->cacheLogger->collectionCacheMiss($persister->getCacheRegion()->getName(), $key);
573
        }
574
575 2
        return $list;
576
    }
577
578
    /**
579
     * {@inheritdoc}
580
     */
581 17
    public function loadOneToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll)
582
    {
583 17
        $persister = $this->uow->getCollectionPersister($assoc);
584 17
        $hasCache  = ($persister instanceof CachedPersister);
585
586 17
        if ( ! $hasCache) {
587 3
            return $this->persister->loadOneToManyCollection($assoc, $sourceEntity, $coll);
588
        }
589
590 14
        $ownerId = $this->uow->getEntityIdentifier($coll->getOwner());
591 14
        $key     = $this->buildCollectionCacheKey($assoc, $ownerId);
592 14
        $list    = $persister->loadCollectionCache($coll, $key);
593
594 14
        if ($list !== null) {
595 11
            if ($this->cacheLogger) {
596 11
                $this->cacheLogger->collectionCacheHit($persister->getCacheRegion()->getName(), $key);
597
            }
598
599 11
            return $list;
600
        }
601
602 13
        $list = $this->persister->loadOneToManyCollection($assoc, $sourceEntity, $coll);
603
604 13
        $persister->storeCollectionCache($key, $list);
605
606 13
        if ($this->cacheLogger) {
607 13
            $this->cacheLogger->collectionCacheMiss($persister->getCacheRegion()->getName(), $key);
608
        }
609
610 13
        return $list;
611
    }
612
613
    /**
614
     * {@inheritdoc}
615
     */
616 5
    public function loadOneToOneEntity(array $assoc, $sourceEntity, array $identifier = [])
617
    {
618 5
        return $this->persister->loadOneToOneEntity($assoc, $sourceEntity, $identifier);
619
    }
620
621
    /**
622
     * {@inheritdoc}
623
     */
624 3
    public function lock(array $criteria, $lockMode)
625
    {
626 3
        $this->persister->lock($criteria, $lockMode);
627 3
    }
628
629
    /**
630
     * {@inheritdoc}
631
     */
632 3
    public function refresh(array $id, $entity, $lockMode = null)
633
    {
634 3
        $this->persister->refresh($id, $entity, $lockMode);
635 3
    }
636
637
    /**
638
     * @param array $association
639
     * @param array $ownerId
640
     *
641
     * @return CollectionCacheKey
642
     */
643 18
    protected function buildCollectionCacheKey(array $association, $ownerId)
644
    {
645
        /** @var ClassMetadata $metadata */
646 18
        $metadata = $this->metadataFactory->getMetadataFor($association['sourceEntity']);
647
648 18
        return new CollectionCacheKey($metadata->rootEntityName, $association['fieldName'], $ownerId);
649
    }
650
}
651