diff --git a/application/Espo/Core/FieldProcessing/Relation/LinkMultipleSaveProcessor.php b/application/Espo/Core/FieldProcessing/Relation/LinkMultipleSaveProcessor.php new file mode 100644 index 0000000000..4a2996ad0d --- /dev/null +++ b/application/Espo/Core/FieldProcessing/Relation/LinkMultipleSaveProcessor.php @@ -0,0 +1,199 @@ +entityManager = $entityManager; + } + + public function process(Entity $entity, string $name, array $options): void + { + $entityType = $entity->getEntityType(); + + $repository = $this->entityManager->getRDBRepository($entityType); + + $idListAttribute = $name . 'Ids'; + $columnsAttribute = $name . 'Columns'; + + $defs = $this->entityManager->getDefs()->getEntity($entityType); + + $skipCreate = $options['skipLinkMultipleCreate'] ?? false; + $skipRemove = $options['skipLinkMultipleRemove'] ?? false; + $skipUpdate = $options['skipLinkMultipleUpdate'] ?? false; + + if ($entity->isNew()) { + $skipRemove = true; + $skipUpdate = true; + } + + if ($entity->has($idListAttribute)) { + $specifiedIdList = $entity->get($idListAttribute); + } + else if ($entity->has($columnsAttribute)) { + $skipRemove = true; + + $specifiedIdList = array_keys( + get_object_vars( + $entity->get($columnsAttribute) ?? (object) [] + ) + ); + } + else { + return; + } + + if (!is_array($specifiedIdList)) { + return; + } + + $toRemoveIdList = []; + $existingIdList = []; + $toUpdateIdList = []; + $toCreateIdList = []; + + $existingColumnsData = (object) []; + + $columns = null; + + if ($defs->hasField($name)) { + $columns = $defs->getField($name)->getParam('columns'); + } + + $columnData = !empty($columns) ? + $entity->get($columnsAttribute) : + null; + + if (!$skipRemove || !$skipUpdate) { + $foreignEntityList = $repository->getRelation($entity, $name)->find(); + + foreach ($foreignEntityList as $foreignEntity) { + $existingIdList[] = $foreignEntity->id; + + if (empty($columns)) { + continue; + } + + $data = (object) []; + + foreach ($columns as $columnName => $columnField) { + $foreignId = $foreignEntity->id; + + $data->$columnName = $foreignEntity->get($columnField); + } + + $existingColumnsData->$foreignId = $data; + + if (!$entity->isNew()) { + $entity->setFetched($columnsAttribute, $existingColumnsData); + } + } + } + + if (!$entity->isNew()) { + if ($entity->has($idListAttribute) && !$entity->hasFetched($idListAttribute)) { + $entity->setFetched($idListAttribute, $existingIdList); + } + + if ($entity->has($columnsAttribute) && !empty($columns)) { + $entity->setFetched($columnsAttribute, $existingColumnsData); + } + } + + foreach ($existingIdList as $id) { + if (!in_array($id, $specifiedIdList)) { + if (!$skipRemove) { + $toRemoveIdList[] = $id; + } + + continue; + } + + if ($skipUpdate || empty($columns)) { + continue; + } + + foreach ($columns as $columnName => $columnField) { + if (!isset($columnData->$id) || !is_object($columnData->$id)) { + continue; + } + + if ( + property_exists($columnData->$id, $columnName) + && + ( + !property_exists($existingColumnsData->$id, $columnName) + || + $columnData->$id->$columnName !== $existingColumnsData->$id->$columnName + ) + ) { + $toUpdateIdList[] = $id; + } + } + } + + if (!$skipCreate) { + foreach ($specifiedIdList as $id) { + if (!in_array($id, $existingIdList)) { + $toCreateIdList[] = $id; + } + } + } + + foreach ($toCreateIdList as $id) { + $data = null; + + if (!empty($columns) && isset($columnData->$id)) { + $data = (array) $columnData->$id; + } + + $repository->getRelation($entity, $name)->relateById($id, $data); + } + + foreach ($toRemoveIdList as $id) { + $repository->getRelation($entity, $name)->unrelateById($id); + } + + foreach ($toUpdateIdList as $id) { + $data = (array) $columnData->$id; + + $repository->getRelation($entity, $name)->updateColumnsById($id, (array) $data); + } + } +} diff --git a/application/Espo/Core/FieldProcessing/Relation/SaveProcessor.php b/application/Espo/Core/FieldProcessing/Relation/SaveProcessor.php new file mode 100644 index 0000000000..2bef1e9011 --- /dev/null +++ b/application/Espo/Core/FieldProcessing/Relation/SaveProcessor.php @@ -0,0 +1,403 @@ +entityManager = $entityManager; + $this->linkMultipleSaveProcessor = $linkMultipleSaveProcessor; + } + + public function process(Entity $entity, array $options): void + { + $this->processMany($entity, $options); + $this->processHasOne($entity, $options); + $this->processBelongsToHasOne($entity, $options); + + return; + + foreach ($entity->getRelationList() as $name) { + $type = $entity->getRelationType($name); + + $foreignEntityType = $entity->getRelationParam($name, 'entity'); + $foreignKey = $entity->getRelationParam($name, 'foreignKey'); + + /*if (in_array($type, $manyRelationTypeList)) { + $idListAttribute = $name . 'Ids'; + $columnsAttribute = $name . 'Columns'; + + if ($entity->has($idListAttribute) || $entity->has($columnsAttribute)) { + $this->processLinkMultipleFieldSave($entity, $name, $options); + } + + continue; + }*/ + + if ($type === $entity::HAS_ONE) { + /*if (!$foreignEntityType || !$foreignKey) { + continue; + }*/ + + /*$noSave = $this->metadata + ->get(['entityDefs', $entity->getEntityType(), 'fields', $name, 'noSave']); + + if ($noSave) { + continue; + }*/ + + $idAttribute = $name . 'Id'; + + if (!$entity->has($idAttribute)) { + continue; + } + + + continue; + } + + if ($type === $entity::BELONGS_TO) { + if (!$entity->get($name . 'Id')) { + continue; + } + + if (!$entity->isAttributeChanged($name . 'Id')) { + continue; + } + + $foreignLink = $entity->getRelationParam($name, 'foreign'); + + if ( + $this->metadata->get( + ['entityDefs', $foreignEntityType, 'links', $foreignLink, 'type'] + ) === $entity::HAS_ONE + ) { + $anotherEntity = $this + ->select(['id']) + ->where([ + $name . 'Id' => $entity->get($name . 'Id'), + 'id!=' => $entity->id, + ]) + ->findOne(); + + if ($anotherEntity) { + $anotherEntity->set($name . 'Id', null); + + $this->entityManager->saveEntity($anotherEntity, [ + 'skipAll' => true, + ]); + } + } + + continue; + } + } + } + + private function processMany(Entity $entity, array $options): void + { + $entityType = $entity->getEntityType(); + + foreach ($this->getManyRelationList($entityType) as $name) { + $this->processManyItem($entity, $name, $options); + } + } + + private function processManyItem(Entity $entity, string $name, array $options): void + { + $idListAttribute = $name . 'Ids'; + $columnsAttribute = $name . 'Columns'; + + if (!$entity->has($idListAttribute) && !$entity->has($columnsAttribute)) { + return; + } + + $this->linkMultipleSaveProcessor->process($entity, $name, $options); + } + + private function processHasOne(Entity $entity, array $options): void + { + $entityType = $entity->getEntityType(); + + foreach ($this->getHasOneRelationList($entityType) as $name) { + $this->processHasOneItem($entity, $name); + } + } + + private function processHasOneItem(Entity $entity, string $name): void + { + $entityType = $entity->getEntityType(); + + $idAttribute = $name . 'Id'; + + if (!$entity->has($idAttribute)) { + return; + } + + $id = $entity->get($idAttribute); + + $defs = $this->entityManager->getDefs()->getEntity($entityType); + + $relationDefs = $defs->getRelation($name); + + $foreignKey = $relationDefs->getForeignKey(); + $foreignEntityType = $relationDefs->getForeignEntityType(); + + $previousForeignEntity = $this->entityManager + ->getRDBRepository($foreignEntityType) + ->select(['id']) + ->where([ + $foreignKey => $entity->getId(), + ]) + ->findOne(); + + if ($previousForeignEntity) { + if (!$entity->isNew()) { + $entity->setFetched($idAttribute, $previousForeignEntity->getId()); + } + + if (!$id) { + $previousForeignEntity->set($foreignKey, null); + + $this->entityManager->saveEntity($previousForeignEntity, [ + 'skipAll' => true, + ]); + } + } + else if (!$entity->isNew()) { + $entity->setFetched($idAttribute, null); + } + + if ($id) { + $this->entityManager + ->getRDBRepository($entityType) + ->getRelation($entity, $name) + ->relateById($id); + } + } + + private function processBelongsToHasOne(Entity $entity, array $options): void + { + $entityType = $entity->getEntityType(); + + foreach ($this->getBelongsToHasOneRelationList($entityType) as $name) { + $this->processBelongsToHasOneItem($entity, $name); + } + } + + private function processBelongsToHasOneItem(Entity $entity, string $name): void + { + $entityType = $entity->getEntityType(); + + $idAttribute = $name . 'Id'; + + if (!$entity->get($idAttribute)) { + return; + } + + if (!$entity->isAttributeChanged($idAttribute)) { + return; + } + + $anotherEntity = $this->entityManager + ->getRDBRepository($entityType) + ->select(['id']) + ->where([ + $idAttribute => $entity->get($idAttribute), + 'id!=' => $entity->getId(), + ]) + ->findOne(); + + if (!$anotherEntity) { + return; + } + + $anotherEntity->set($idAttribute, null); + + $this->entityManager->saveEntity($anotherEntity, [ + 'skipAll' => true, + ]); + } + + private function getManyRelationList(string $entityType): array + { + if (array_key_exists($entityType, $this->manyRelationListMapCache)) { + return $this->manyRelationListMapCache[$entityType]; + } + + $typeList = [ + Entity::HAS_MANY, + Entity::MANY_MANY, + Entity::HAS_CHILDREN, + ]; + + $defs = $this->entityManager->getDefs()->getEntity($entityType); + + $list = []; + + foreach ($defs->getRelationNameList() as $name) { + $type = $defs->getRelation($name)->getType(); + + if (!in_array($type, $typeList)) { + continue; + } + + if (!$defs->hasAttribute($name . 'Ids')) { + continue; + } + + if ($defs->hasField($name) && $defs->getField($name)->getParam('noSave')) { + continue; + } + + $list[] = $name; + } + + $this->manyRelationListMapCache[$entityType] = $list; + + return $list; + } + + private function getHasOneRelationList(string $entityType): array + { + if (array_key_exists($entityType, $this->hasOneRelationListMapCache)) { + return $this->hasOneRelationListMapCache[$entityType]; + } + + $ormDefs = $this->entityManager->getDefs(); + + $defs = $ormDefs->getEntity($entityType); + + $list = []; + + foreach ($defs->getRelationNameList() as $name) { + $relationDefs = $defs->getRelation($name); + + $type = $relationDefs->getType(); + + if ($type !== Entity::HAS_ONE) { + continue; + } + + if (!$relationDefs->hasForeignEntityType()) { + continue; + } + + if (!$relationDefs->hasForeignKey()) { + continue; + } + + if (!$defs->hasAttribute($name . 'Id')) { + continue; + } + + if ($defs->hasField($name) && $defs->getField($name)->getParam('noSave')) { + continue; + } + + $list[] = $name; + } + + $this->hasOneRelationListMapCache[$entityType] = $list; + + return $list; + } + + private function getBelongsToHasOneRelationList(string $entityType): array + { + if (array_key_exists($entityType, $this->belongsToHasOneRelationListMapCache)) { + return $this->belongsToHasOneRelationListMapCache[$entityType]; + } + + $ormDefs = $this->entityManager->getDefs(); + + $defs = $ormDefs->getEntity($entityType); + + $list = []; + + foreach ($defs->getRelationNameList() as $name) { + $relationDefs = $defs->getRelation($name); + + $type = $relationDefs->getType(); + + if ($type !== Entity::BELONGS_TO) { + continue; + } + + if (!$relationDefs->hasForeignRelationName()) { + continue; + } + + if (!$relationDefs->hasForeignEntityType()) { + continue; + } + + if (!$defs->hasAttribute($name . 'Id')) { + continue; + } + + $foreignEntityType = $relationDefs->getForeignEntityType(); + $foreignRelationName = $relationDefs->getForeignRelationName(); + + $foreignType = $ormDefs + ->getEntity($foreignEntityType) + ->getRelation($foreignRelationName) + ->getType(); + + if ($foreignType !== Entity::HAS_ONE) { + continue; + } + + $list[] = $name; + } + + $this->belongsToHasOneRelationListMapCache[$entityType] = $list; + + return $list; + } +} diff --git a/application/Espo/Core/FieldProcessing/SaveProcessor.php b/application/Espo/Core/FieldProcessing/SaveProcessor.php index 31d1f7e2d1..553c587837 100644 --- a/application/Espo/Core/FieldProcessing/SaveProcessor.php +++ b/application/Espo/Core/FieldProcessing/SaveProcessor.php @@ -34,6 +34,7 @@ use Espo\Core\ORM\Entity; use Espo\Core\{ FieldProcessing\EmailAddress\SaveProcessor as EmailAddressSaveProcessor, FieldProcessing\PhoneNumber\SaveProcessor as PhoneNumberSaveProcessor, + FieldProcessing\Relation\SaveProcessor as RelationSaveProcessor, }; /** @@ -45,17 +46,22 @@ class SaveProcessor private $phoneNumberSaveProcessor; + private $relationSaveProcessor; + public function __construct( EmailAddressSaveProcessor $emailAddressSaveProcessor, - PhoneNumberSaveProcessor $phoneNumberSaveProcessor + PhoneNumberSaveProcessor $phoneNumberSaveProcessor, + RelationSaveProcessor $relationSaveProcessor ) { $this->emailAddressSaveProcessor = $emailAddressSaveProcessor; $this->phoneNumberSaveProcessor = $phoneNumberSaveProcessor; + $this->relationSaveProcessor = $relationSaveProcessor; } public function process(Entity $entity, array $options): void { $this->emailAddressSaveProcessor->process($entity, $options); $this->phoneNumberSaveProcessor->process($entity, $options); + $this->relationSaveProcessor->process($entity, $options); } } diff --git a/application/Espo/Core/Repositories/Database.php b/application/Espo/Core/Repositories/Database.php index 5830225df1..0047d3f3f6 100644 --- a/application/Espo/Core/Repositories/Database.php +++ b/application/Espo/Core/Repositories/Database.php @@ -139,7 +139,11 @@ class Database extends RDBRepository ]; $this->hookManager->process( - $this->entityType, 'afterMassRelate', $entity, $options, $hookData + $this->entityType, + 'afterMassRelate', + $entity, + $options, + $hookData ); } } @@ -151,11 +155,14 @@ class Database extends RDBRepository if (!$this->hooksDisabled && empty($options['skipHooks'])) { if (is_string($foreign)) { $foreignId = $foreign; + $foreignEntityType = $entity->getRelationParam($relationName, 'entity'); if ($foreignEntityType) { $foreign = $this->getEntityManager()->getEntity($foreignEntityType); - $foreign->id = $foreignId; + + $foreign->set('id', $foreignId); + $foreign->setAsFetched(); } } @@ -213,7 +220,6 @@ class Database extends RDBRepository parent::afterSave($entity, $options); if (!$this->processFieldsAfterSaveDisabled) { - $this->processSpecifiedRelationsSave($entity, $options); $this->processFileFieldsSave($entity); $this->processArrayFieldsSave($entity); $this->processWysiwygFieldsSave($entity); @@ -227,6 +233,7 @@ class Database extends RDBRepository public function save(Entity $entity, array $options = []): void { $nowString = date('Y-m-d H:i:s', time()); + $restoreData = []; if ( @@ -262,7 +269,8 @@ class Database extends RDBRepository } } } - } else { + } + else { if (empty($options['silent']) && empty($options['skipModifiedBy'])) { if ($entity->hasAttribute('modifiedAt')) { $entity->set('modifiedAt', $nowString); @@ -427,275 +435,4 @@ class Database extends RDBRepository } } } - - public function processLinkMultipleFieldSave(Entity $entity, string $link, array $options = []) - { - $name = $link; - - $idListAttribute = $link . 'Ids'; - $columnsAttribute = $link . 'Columns'; - - if ($this->getMetadata()->get("entityDefs." . $entity->getEntityType() . ".fields.{$name}.noSave")) { - return; - } - - $skipCreate = false; - $skipRemove = false; - $skipUpdate = false; - - if (!empty($options['skipLinkMultipleCreate'])) { - $skipCreate = true; - } - - if (!empty($options['skipLinkMultipleRemove'])) { - $skipRemove = true; - } - - if (!empty($options['skipLinkMultipleUpdate'])) { - $skipUpdate = true; - } - - if ($entity->isNew()) { - $skipRemove = true; - $skipUpdate = true; - } - - if ($entity->has($idListAttribute)) { - $specifiedIdList = $entity->get($idListAttribute); - } - else if ($entity->has($columnsAttribute)) { - $skipRemove = true; - $specifiedIdList = []; - - foreach ($entity->get($columnsAttribute) as $id => $d) { - $specifiedIdList[] = $id; - } - } else { - return; - } - - if (!is_array($specifiedIdList)) { - return; - } - - $toRemoveIdList = []; - $existingIdList = []; - $toUpdateIdList = []; - $toCreateIdList = []; - $existingColumnsData = (object) []; - - $columns = $this->getMetadata()->get(['entityDefs', $entity->getEntityType(), 'fields', $name, 'columns']); - - if (!empty($columns)) { - $columnData = $entity->get($columnsAttribute); - } - - if (!$skipRemove || !$skipUpdate) { - $foreignEntityList = $this->getRelation($entity, $name)->find(); - - if ($foreignEntityList) { - foreach ($foreignEntityList as $foreignEntity) { - $existingIdList[] = $foreignEntity->id; - - if (!empty($columns)) { - $data = (object) []; - - foreach ($columns as $columnName => $columnField) { - $foreignId = $foreignEntity->id; - $data->$columnName = $foreignEntity->get($columnField); - } - - $existingColumnsData->$foreignId = $data; - - if (!$entity->isNew()) { - $entity->setFetched($columnsAttribute, $existingColumnsData); - } - } - } - } - } - - if (!$entity->isNew()) { - if ($entity->has($idListAttribute) && !$entity->hasFetched($idListAttribute)) { - $entity->setFetched($idListAttribute, $existingIdList); - } - - if ($entity->has($columnsAttribute) && !empty($columns)) { - $entity->setFetched($columnsAttribute, $existingColumnsData); - } - } - - foreach ($existingIdList as $id) { - if (!in_array($id, $specifiedIdList)) { - if (!$skipRemove) { - $toRemoveIdList[] = $id; - } - } - else { - if (!$skipUpdate && !empty($columns)) { - foreach ($columns as $columnName => $columnField) { - if (isset($columnData->$id) && is_object($columnData->$id)) { - if ( - property_exists($columnData->$id, $columnName) - && - ( - !property_exists($existingColumnsData->$id, $columnName) - || - $columnData->$id->$columnName !== $existingColumnsData->$id->$columnName - ) - ) { - $toUpdateIdList[] = $id; - } - } - } - } - } - } - - if (!$skipCreate) { - foreach ($specifiedIdList as $id) { - if (!in_array($id, $existingIdList)) { - $toCreateIdList[] = $id; - } - } - } - - foreach ($toCreateIdList as $id) { - $data = null; - - if (!empty($columns) && isset($columnData->$id)) { - $data = (array) $columnData->$id; - } - - $this->getRelation($entity, $name)->relateById($id, $data); - } - - foreach ($toRemoveIdList as $id) { - $this->getRelation($entity, $name)->unrelateById($id); - } - - foreach ($toUpdateIdList as $id) { - $data = (array) $columnData->$id; - - $this->getRelation($entity, $name)->updateColumnsById($id, (array) $data); - } - } - - protected function processSpecifiedRelationsSave(Entity $entity, array $options = []) - { - $relationTypeList = [ - $entity::HAS_MANY, - $entity::MANY_MANY, - $entity::HAS_CHILDREN, - ]; - - foreach ($entity->getRelationList() as $name) { - - $type = $entity->getRelationType($name); - - $foreignEntityType = $entity->getRelationParam($name, 'entity'); - $foreignKey = $entity->getRelationParam($name, 'foreignKey'); - - if (in_array($type, $relationTypeList)) { - $idListAttribute = $name . 'Ids'; - $columnsAttribute = $name . 'Columns'; - - if ($entity->has($idListAttribute) || $entity->has($columnsAttribute)) { - $this->processLinkMultipleFieldSave($entity, $name, $options); - } - - continue; - } - - if ($type === $entity::HAS_ONE) { - if (!$foreignEntityType || !$foreignKey) { - continue; - } - - $noSave = $this->getMetadata()->get( - ['entityDefs', $entity->getEntityType(), 'fields', $name, 'noSave'] - ); - - if ($noSave) { - continue; - } - - $idAttribute = $name . 'Id'; - - if (!$entity->has($idAttribute)) { - continue; - } - - $where = []; - - $where[$foreignKey] = $entity->id; - - $previousForeignEntity = $this->getEntityManager() - ->getRepository($foreignEntityType) - ->select(['id']) - ->where($where) - ->findOne(); - - if ($previousForeignEntity) { - if (!$entity->isNew()) { - $entity->setFetched($idAttribute, $previousForeignEntity->id); - } - - if (!$entity->get($idAttribute)) { - $previousForeignEntity->set($foreignKey, null); - - $this->getEntityManager()->saveEntity($previousForeignEntity, ['skipAll' => true]); - } - } else { - if (!$entity->isNew()) { - $entity->setFetched($idAttribute, null); - } - } - - if ($entity->get($idAttribute)) { - $relateResult = $this->relate($entity, $name, $entity->get($idAttribute)); - - if (!$relateResult) { - $entity->set($idAttribute, null); - } - } - - continue; - } - - if ($type === $entity::BELONGS_TO) { - if (!$entity->get($name . 'Id')) { - continue; - } - - if (!$entity->isAttributeChanged($name . 'Id')) { - continue; - } - - $foreignLink = $entity->getRelationParam($name, 'foreign'); - - if ( - $this->getMetadata()->get( - ['entityDefs', $foreignEntityType, 'links', $foreignLink, 'type'] - ) === $entity::HAS_ONE - ) { - $anotherEntity = $this - ->select(['id']) - ->where([ - $name . 'Id' => $entity->get($name . 'Id'), - 'id!=' => $entity->id, - ]) - ->findOne(); - - if ($anotherEntity) { - $anotherEntity->set($name . 'Id', null); - - $this->getEntityManager()->saveEntity($anotherEntity, ['skipAll' => true]); - } - } - - continue; - } - } - } } diff --git a/tests/integration/Espo/Core/FieldProcessing/RelationTest.php b/tests/integration/Espo/Core/FieldProcessing/RelationTest.php new file mode 100644 index 0000000000..18922da98c --- /dev/null +++ b/tests/integration/Espo/Core/FieldProcessing/RelationTest.php @@ -0,0 +1,192 @@ +getContainer()->get('entityManager'); + + $contact1 = $entityManager->createEntity('Contact', []); + $contact2 = $entityManager->createEntity('Contact', []); + $contact3 = $entityManager->createEntity('Contact', []); + + $opportunity = $entityManager->createEntity('Opportunity', [ + 'contactsIds' => [ + $contact1->getId(), + $contact2->getId(), + ], + 'contactsColumns' => (object) [ + $contact1->getId() => (object) [ + 'role' => 'Decision Maker', + ], + ], + ]); + + $opportunity = $entityManager->getEntity('Opportunity', $opportunity->getId()); + + $this->assertEquals( + self::sortArray([ + $contact1->getId(), + $contact2->getId(), + ]), + self::sortArray($opportunity->getLinkMultipleIdList('contacts')) + ); + + $column1 = $entityManager + ->getRDBRepository('Opportunity') + ->getRelation($opportunity, 'contacts') + ->getColumn($contact1, 'role'); + + $this->assertEquals('Decision Maker', $column1); + + $opportunity->set([ + 'contactsIds' => [ + $contact2->getId(), + $contact3->getId(), + ], + 'contactsColumns' => (object) [ + $contact2->getId() => (object) [ + 'role' => null, + ], + $contact3->getId() => (object) [ + 'role' => 'Evaluator', + ], + ], + ]); + + $entityManager->saveEntity($opportunity); + + $opportunity = $entityManager->getEntity('Opportunity', $opportunity->getId()); + + $this->assertEquals( + self::sortArray([ + $contact2->getId(), + $contact3->getId(), + ]), + self::sortArray($opportunity->getLinkMultipleIdList('contacts')) + ); + + $column3 = $entityManager + ->getRDBRepository('Opportunity') + ->getRelation($opportunity, 'contacts') + ->getColumn($contact3, 'role'); + + $this->assertEquals('Evaluator', $column3); + + $opportunity = $entityManager->getEntity('Opportunity', $opportunity->getId()); + + $opportunity->set([ + 'contactsColumns' => (object) [ + $contact3->getId() => (object) [ + 'role' => 'Decision Maker', + ], + ], + ]); + + $entityManager->saveEntity($opportunity); + + $column3 = $entityManager + ->getRDBRepository('Opportunity') + ->getRelation($opportunity, 'contacts') + ->getColumn($contact3, 'role'); + + $this->assertEquals('Decision Maker', $column3); + } + + public function testHasOne(): void + { + /* @var $entityManager EntityManager */ + $entityManager = $this->getContainer()->get('entityManager'); + + $lead1 = $entityManager->createEntity('Lead', []); + $lead2 = $entityManager->createEntity('Lead', []); + + $account = $entityManager->createEntity('Account', []); + + $lead1->set('createdAccountId', $account->getId()); + + $entityManager->saveEntity($lead1); + + $lead2->set('createdAccountId', $account->getId()); + + $entityManager->saveEntity($lead2); + + $lead1 = $entityManager->getEntity('Lead', $lead1->getId()); + + $this->assertNull($lead1->get('createdAccountId')); + + $lead2 = $entityManager->getEntity('Lead', $lead2->getId()); + + $this->assertEquals($account->getId(), $lead2->get('createdAccountId')); + } + + public function testBelongsToHasOne(): void + { + /* @var $entityManager EntityManager */ + $entityManager = $this->getContainer()->get('entityManager'); + + $lead1 = $entityManager->createEntity('Lead', []); + $lead2 = $entityManager->createEntity('Lead', []); + + $account = $entityManager->createEntity('Account', [ + 'originalLeadId' => $lead1->getId(), + ]); + + $entityManager->saveEntity($account); + + $account = $entityManager->getEntity('Account', $account->getId()); + + $account->set([ + 'originalLeadId' => $lead2->getId(), + ]); + + $entityManager->saveEntity($account); + + $lead2 = $entityManager->getEntity('Lead', $lead2->getId()); + + $this->assertEquals($account->getId(), $lead2->get('createdAccountId')); + + $lead1 = $entityManager->getEntity('Lead', $lead1->getId()); + + $this->assertEquals(null, $lead1->get('createdAccountId')); + } + + private static function sortArray(array $array): array + { + sort($array); + + return $array; + } +}