public function getOrCreateDocuments($className, $nodes, array &$hints = array())
{
$refresh = isset($hints['refresh']) ? $hints['refresh'] : false;
$locale = isset($hints['locale']) ? $hints['locale'] : null;
$fallback = isset($hints['fallback']) ? $hints['fallback'] : isset($locale);
$documents = array();
$overrideLocalValuesOids = array();
$strategies = array();
$nodesByStrategy = array();
$allLocales = array();
//prepare array of document ordered by the nodes path
$existingDocuments = 0;
foreach ($nodes as $node) {
$requestedClassName = $className;
try {
$actualClassName = $this->documentClassMapper->getClassName($this->dm, $node, $className);
} catch (ClassMismatchException $e) {
// ignore class mismatch, just skip that one
continue;
}
$id = $node->getPath();
$class = $this->dm->getClassMetadata($actualClassName);
// prepare first, add later when fine
$document = $this->getDocumentById($id);
if ($document) {
if (!$refresh) {
++$existingDocuments;
$documents[$id] = $document;
} else {
$overrideLocalValuesOids[$id] = spl_object_hash($document);
}
try {
$this->validateClassName($document, $requestedClassName);
} catch (ClassMismatchException $e) {
continue;
}
} else {
$document = $class->newInstance();
// delay registering the new document until children proxy have been created
$overrideLocalValuesOids[$id] = false;
}
$documents[$id] = $document;
if ($this->isDocumentTranslatable($class)) {
$currentStrategy = $this->dm->getTranslationStrategy($class->translator);
$localesToTry = $this->dm->getLocaleChooserStrategy()->getFallbackLocales($document, $class, $locale);
foreach ($localesToTry as $localeToTry) {
$allLocales[$localeToTry] = $localeToTry;
}
$strategies[$class->name] = $currentStrategy;
$nodesByStrategy[$class->name][] = $node;
}
}
foreach ($nodesByStrategy as $strategyClass => $nodesForLocale) {
if (!$strategies[$strategyClass] instanceof TranslationNodesWarmer) {
continue;
}
$strategies[$strategyClass]->getTranslationsForNodes($nodesForLocale, $allLocales, $this->session);
}
// return early
if (count($documents) === $existingDocuments) {
return $documents;
}
foreach ($nodes as $node) {
$id = $node->getPath();
$document = $this->getDocumentById($id) ?: (isset($documents[$id]) ? $documents[$id] : null);
if (!$document) {
continue;
}
$documents[$id] = $document;
$class = $this->dm->getClassMetadata(get_class($document));
$documentState = array();
$nonMappedData = array();
// second param is false to get uuid rather than dereference reference properties to node instances
$properties = $node->getPropertiesValues(null, false);
foreach ($class->fieldMappings as $fieldName) {
$mapping = $class->mappings[$fieldName];
if (isset($properties[$mapping['property']])) {
if (true === $mapping['multivalue']) {
if (isset($mapping['assoc'])) {
$documentState[$fieldName] = $this->createAssoc($properties, $mapping);
} else {
$documentState[$fieldName] = (array) $properties[$mapping['property']];
}
} else {
$documentState[$fieldName] = $properties[$mapping['property']];
}
} elseif (true === $mapping['multivalue']) {
$documentState[$mapping['property']] = array();
}
}
if ($class->node) {
$documentState[$class->node] = $node;
}
if ($class->nodename) {
$documentState[$class->nodename] = $node->getName();
}
if ($class->identifier) {
$documentState[$class->identifier] = $node->getPath();
}
if (!isset($hints['prefetch']) || $hints['prefetch']) {
$this->getPrefetchHelper()->prefetchReferences($class, $node);
}
// initialize inverse side collections
foreach ($class->referenceMappings as $fieldName) {
$mapping = $class->mappings[$fieldName];
if ($mapping['type'] === ClassMetadata::MANY_TO_ONE) {
if (!$node->hasProperty($mapping['property'])) {
continue;
}
try {
$referencedNode = $node->getProperty($mapping['property'])->getNode();
$proxy = $this->getOrCreateProxyFromNode($referencedNode, $locale);
if (isset($mapping['targetDocument']) && !$proxy instanceof $mapping['targetDocument']) {
throw new PHPCRException("Unexpected class for referenced document at '{$referencedNode->getPath()}'. Expected '{$mapping['targetDocument']}' but got '" . ClassUtils::getClass($proxy) . "'.");
}
} catch (RepositoryException $e) {
if ($e instanceof ItemNotFoundException || isset($hints['ignoreHardReferenceNotFound'])) {
// a weak reference or an old version can have lost references
$proxy = null;
} else {
throw new PHPCRException($e->getMessage(), 0, $e);
}
}
$documentState[$fieldName] = $proxy;
} elseif ($mapping['type'] === ClassMetadata::MANY_TO_MANY) {
$referencedNodes = array();
if ($node->hasProperty($mapping['property'])) {
foreach ($node->getProperty($mapping['property'])->getString() as $reference) {
$referencedNodes[] = $reference;
}
}
$targetDocument = isset($mapping['targetDocument']) ? $mapping['targetDocument'] : null;
$coll = new ReferenceManyCollection($this->dm, $document, $mapping['property'], $referencedNodes, $targetDocument, $locale, $this->getReferenceManyCollectionTypeFromMetadata($mapping));
$documentState[$fieldName] = $coll;
}
}
if (!isset($hints['prefetch']) || $hints['prefetch']) {
if ($class->translator) {
try {
$prefetchLocale = $locale ?: $this->dm->getLocaleChooserStrategy()->getLocale();
} catch (InvalidArgumentException $e) {
throw new InvalidArgumentException($e->getMessage() . ' but document ' . $class->name . ' is mapped with translations.');
}
} else {
$prefetchLocale = null;
}
$this->getPrefetchHelper()->prefetchHierarchy($class, $node, $prefetchLocale);
}
if ($class->parentMapping && $node->getDepth() > 0) {
// do not map parent to self if we are at root
$documentState[$class->parentMapping] = $this->getOrCreateProxyFromNode($node->getParent(), $locale);
}
if ($class->depthMapping) {
$documentState[$class->depthMapping] = $node->getDepth();
}
foreach ($class->childMappings as $fieldName) {
$mapping = $class->mappings[$fieldName];
$documentState[$fieldName] = $node->hasNode($mapping['nodeName']) ? $this->getOrCreateProxyFromNode($node->getNode($mapping['nodeName']), $locale) : null;
}
foreach ($class->childrenMappings as $fieldName) {
$mapping = $class->mappings[$fieldName];
$documentState[$fieldName] = new ChildrenCollection($this->dm, $document, $mapping['filter'], $mapping['fetchDepth'], $locale);
}
foreach ($class->referrersMappings as $fieldName) {
$mapping = $class->mappings[$fieldName];
// get the reference type strategy (weak or hard) on the fly, as we
// can not do it in ClassMetadata
$referringMeta = $this->dm->getClassMetadata($mapping['referringDocument']);
$referringField = $referringMeta->mappings[$mapping['referencedBy']];
$documentState[$fieldName] = new ReferrersCollection($this->dm, $document, $referringField['strategy'], $referringField['property'], $locale, $mapping['referringDocument']);
}
foreach ($class->mixedReferrersMappings as $fieldName) {
$mapping = $class->mappings[$fieldName];
$documentState[$fieldName] = new ImmutableReferrersCollection($this->dm, $document, $mapping['referenceType'], $locale);
}
// when not set then not needed
if (!isset($overrideLocalValuesOids[$id])) {
continue;
}
if (!$overrideLocalValuesOids[$id]) {
// registering the document needs to be delayed until the children proxies where created
$overrideLocalValuesOids[$id] = $this->registerDocument($document, $id);
}
$this->nonMappedData[$overrideLocalValuesOids[$id]] = $nonMappedData;
foreach ($class->reflFields as $fieldName => $reflFields) {
$value = isset($documentState[$fieldName]) ? $documentState[$fieldName] : null;
$reflFields->setValue($document, $value);
$this->originalData[$overrideLocalValuesOids[$id]][$fieldName] = $value;
}
// Load translations
$this->doLoadTranslation($document, $class, $locale, $fallback, $refresh);
if ($invoke = $this->eventListenersInvoker->getSubscribedSystems($class, Event::postLoad)) {
$this->eventListenersInvoker->invoke($class, Event::postLoad, $document, new LifecycleEventArgs($document, $this->dm), $invoke);
}
}
return $documents;
}