-
-
Notifications
You must be signed in to change notification settings - Fork 96
/
PrefetchHelper.php
131 lines (119 loc) · 4.54 KB
/
PrefetchHelper.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
<?php
namespace Doctrine\ODM\PHPCR\Tools\Helper;
use Doctrine\ODM\PHPCR\DocumentManagerInterface;
use Doctrine\ODM\PHPCR\Mapping\ClassMetadata;
use PHPCR\NodeInterface;
use PHPCR\Util\PathHelper;
/**
* This helper collects information about what nodes will be loaded when
* creating a document proxy and allows to load them in one go, even for a
* collection.
*
* The trick is to gather as many paths and UUID as possible to fetch them in a
* single call. Once the transport cached them, we can use normal PHPCR calls
* to access them, keeping the code readable.
*
* @author David Buchmann <mail@davidbu.ch>
*/
class PrefetchHelper
{
/**
* @param NodeInterface[] $nodes
*/
public function prefetch(DocumentManagerInterface $dm, iterable $nodes, ?string $locale = null): void
{
if (0 === count($nodes)) {
return;
}
$uuids = [];
$paths = [];
$documentClassMapper = $dm->getConfiguration()->getDocumentClassMapper();
foreach ($nodes as $node) {
$className = $documentClassMapper->getClassName($dm, $node);
$class = $dm->getClassMetadata($className);
if (!$locale && $class->translator) {
$locale = $dm->getLocaleChooserStrategy()->getLocale();
}
$uuids = array_merge($uuids, $this->collectPrefetchReferences($class, $node));
$paths = array_merge($paths, $this->collectPrefetchHierarchy($class, $node, $locale));
}
if (count($uuids) > 0) {
$node->getSession()->getNodesByIdentifier($uuids); /* @phpstan-ignore-line */
}
if (count($paths) > 0) {
$node->getSession()->getNodes($paths); /* @phpstan-ignore-line */
}
}
/**
* Prefetch all mapped ReferenceOne fields.
*
* @param NodeInterface $node the node to prefetch parent and children for
*/
public function prefetchReferences(ClassMetadata $class, NodeInterface $node): void
{
$prefetch = $this->collectPrefetchReferences($class, $node);
if (count($prefetch)) {
$node->getSession()->getNodesByIdentifier($prefetch);
}
}
/**
* Prefetch all Child mappings and the ParentDocument if mapping exist.
*
* @param NodeInterface $node the node to prefetch parent and children for
*/
public function prefetchHierarchy(ClassMetadata $class, NodeInterface $node, ?string $locale = null): void
{
$prefetch = $this->collectPrefetchHierarchy($class, $node, $locale);
if (count($prefetch)) {
$node->getSession()->getNodes($prefetch);
}
}
/**
* Gather all UUIDs to pre-fetch nodes in MANY_TO_ONE mappings.
*
* @param NodeInterface $node the node to prefetch parent and children for
*
* @return string[] list of UUID to fetch in one go
*/
public function collectPrefetchReferences(ClassMetadata $class, NodeInterface $node): array
{
$refNodeUUIDs = [];
foreach ($class->referenceMappings as $fieldName) {
$mapping = $class->mappings[$fieldName];
if (!$node->hasProperty($mapping['property'])) {
continue;
}
if ($mapping['type'] & ClassMetadata::MANY_TO_ONE
&& 'path' !== $mapping['strategy']
) {
$refNodeUUIDs[] = $node->getProperty($mapping['property'])->getString();
}
}
return $refNodeUUIDs;
}
/**
* Gather the parent and all child mappings so they can be fetched in one
* go.
*
* @param NodeInterface $node the node to prefetch parent and children for
* @param string|null $locale the locale to also prefetch the translation
* child if applicable
*
* @return string[] list of absolute paths to nodes that should be prefetched
*/
public function collectPrefetchHierarchy(ClassMetadata $class, NodeInterface $node, ?string $locale = null): array
{
$prefetch = [];
if ($class->parentMapping && $node->getDepth() > 0) {
$prefetch[] = PathHelper::getParentPath($node->getPath());
}
foreach ($class->childMappings as $fieldName) {
$childName = $class->mappings[$fieldName]['nodeName'];
$prefetch[] = PathHelper::absolutizePath($childName, $node->getPath());
}
if ($locale && count($prefetch) && 'child' === $class->translator) {
$prefetch[] = $node->getPath().'/phpcr_locale:'.$locale;
}
return $prefetch;
}
}