private function executeInserts($documents)
{
// sort the documents to insert parents first but maintain child order
$oids = array();
foreach ($documents as $oid => $document) {
if (!$this->contains($oid)) {
continue;
}
$oids[$oid] = $this->getDocumentId($document);
}
$order = array_flip(array_values($oids));
uasort($oids, function ($a, $b) use($order) {
// compute the node depths
$aCount = substr_count($a, '/');
$bCount = substr_count($b, '/');
// ensure that the original order is maintained for nodes with the same depth
if ($aCount === $bCount) {
return $order[$a] < $order[$b] ? -1 : 1;
}
return $aCount < $bCount ? -1 : 1;
});
$associationChangesets = $associationUpdates = array();
foreach ($oids as $oid => $id) {
$document = $documents[$oid];
$class = $this->dm->getClassMetadata(get_class($document));
// PHPCR does not validate nullable unless we would start to
// generate custom node types, which we at the moment don't.
// the ORM can delegate this validation to the relational database
// that is using a strict schema
foreach ($class->fieldMappings as $fieldName) {
if (!isset($this->documentChangesets[$oid]['fields'][$fieldName]) && !$class->isNullable($fieldName) && !$this->isAutocreatedProperty($class, $fieldName)) {
throw new PHPCRException(sprintf('Field "%s" of class "%s" is not nullable', $fieldName, $class->name));
}
}
$parentNode = $this->session->getNode(PathHelper::getParentPath($id));
$this->validateChildClass($parentNode, $class);
$nodename = PathHelper::getNodeName($id);
$node = $parentNode->addNode($nodename, $class->nodeType);
if ($class->node) {
$this->originalData[$oid][$class->node] = $node;
}
if ($class->nodename) {
$this->originalData[$oid][$class->nodename] = $nodename;
}
try {
$node->addMixin('phpcr:managed');
} catch (NoSuchNodeTypeException $e) {
throw new PHPCRException('Register phpcr:managed node type first. See https://github.com/doctrine/phpcr-odm/wiki/Custom-node-type-phpcr:managed');
}
foreach ($class->mixins as $mixin) {
$node->addMixin($mixin);
}
if ($class->identifier) {
$class->setIdentifierValue($document, $id);
}
if ($class->node) {
$class->reflFields[$class->node]->setValue($document, $node);
}
if ($class->nodename) {
// make sure this reflects the id generator strategy generated id
$class->reflFields[$class->nodename]->setValue($document, $node->getName());
}
// make sure this reflects the id generator strategy generated id
if ($class->parentMapping && !$class->reflFields[$class->parentMapping]->getValue($document)) {
$class->reflFields[$class->parentMapping]->setValue($document, $this->getOrCreateProxyFromNode($parentNode, $this->getCurrentLocale($document, $class)));
}
if ($this->writeMetadata) {
$this->documentClassMapper->writeMetadata($this->dm, $node, $class->name);
}
$this->setMixins($class, $node, $document);
$fields = isset($this->documentChangesets[$oid]['fields']) ? $this->documentChangesets[$oid]['fields'] : array();
foreach ($fields as $fieldName => $fieldValue) {
// Ignore translatable fields (they will be persisted by the translation strategy)
if (in_array($fieldName, $class->translatableFields)) {
continue;
}
if (in_array($fieldName, $class->fieldMappings)) {
$mapping = $class->mappings[$fieldName];
$type = PropertyType::valueFromName($mapping['type']);
if (null === $fieldValue) {
continue;
}
if ($mapping['multivalue'] && $fieldValue) {
$fieldValue = (array) $fieldValue;
if (isset($mapping['assoc'])) {
$fieldValue = $this->processAssoc($node, $mapping, $fieldValue);
}
}
$node->setProperty($mapping['property'], $fieldValue, $type);
} elseif (in_array($fieldName, $class->referenceMappings) || in_array($fieldName, $class->referrersMappings)) {
$associationUpdates[$oid] = $document;
//populate $associationChangesets to force executeUpdates($associationUpdates)
//to only update association fields
$data = isset($associationChangesets[$oid]['fields']) ? $associationChangesets[$oid]['fields'] : array();
$data[$fieldName] = array(null, $fieldValue);
$associationChangesets[$oid] = array('fields' => $data);
}
}
$this->doSaveTranslation($document, $node, $class);
if ($invoke = $this->eventListenersInvoker->getSubscribedSystems($class, Event::postPersist)) {
$this->eventListenersInvoker->invoke($class, Event::postPersist, $document, new LifecycleEventArgs($document, $this->dm), $invoke);
}
}
$this->documentChangesets = array_merge($this->documentChangesets, $associationChangesets);
$this->executeUpdates($associationUpdates, false);
}