From bb091a0301a5fc6a1c5429e424a9bf96b6cdb22f Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Tue, 9 Apr 2024 13:07:45 +0300 Subject: [PATCH] foreign attributes unset and foregn names load --- application/Espo/Core/ORM/Entity.php | 51 +++++++++---- .../Espo/Core/Record/Access/LinkCheck.php | 76 +++++++++++++++++-- application/Espo/Core/Record/Service.php | 25 ++++++ 3 files changed, 133 insertions(+), 19 deletions(-) diff --git a/application/Espo/Core/ORM/Entity.php b/application/Espo/Core/ORM/Entity.php index 85cae9721f..95f1a38847 100644 --- a/application/Espo/Core/ORM/Entity.php +++ b/application/Espo/Core/ORM/Entity.php @@ -79,15 +79,25 @@ class Entity extends BaseEntity throw new LogicException("Called `loadParentNameField` on non-link-parent field `$field`."); } - $parentId = $this->get($field . 'Id'); + $idAttribute = $field . 'Id'; + $nameAttribute = $field . 'Name'; + + $parentId = $this->get($idAttribute); $parentType = $this->get($field . 'Type'); if (!$this->entityManager) { throw new LogicException("No entity-manager."); } + $toSetFetched = !$this->isNew() && !$this->hasFetched($idAttribute); + if (!$parentId || !$parentType) { - $this->set($field . 'Name', null); + /** @noinspection PhpRedundantOptionalArgumentInspection */ + $this->set($nameAttribute, null); + + if ($toSetFetched) { + $this->setFetched($nameAttribute, null); + } return; } @@ -105,13 +115,13 @@ class Entity extends BaseEntity ->where(['id' => $parentId]) ->findOne(); - if ($foreignEntity) { - $this->set($field . 'Name', $foreignEntity->get('name')); + $entityName = $foreignEntity ? $foreignEntity->get('name') : null; - return; + $this->set($nameAttribute, $entityName); + + if ($toSetFetched) { + $this->setFetched($nameAttribute, $entityName); } - - $this->set($field . 'Name', null); } /** @@ -262,21 +272,34 @@ class Entity extends BaseEntity } $idsAttribute = $field . 'Ids'; + $namesAttribute = $field . 'Names'; + $typesAttribute = $field . 'Types'; + $columnsAttribute = $field . 'Columns'; + + $toSetFetched = !$this->isNew() && !$this->hasFetched($idsAttribute); $this->set($idsAttribute, $ids); + $this->set($namesAttribute, $names); - if (!$this->isNew() && !$this->hasFetched($idsAttribute)) { + if ($toSetFetched) { $this->setFetched($idsAttribute, $ids); + $this->setFetched($namesAttribute, $names); } - $this->set($field . 'Names', $names); - if ($hasType) { - $this->set($field . 'Types', $types); + $this->set($typesAttribute, $types); + + if ($toSetFetched) { + $this->setFetched($typesAttribute, $types); + } } if (!empty($columns)) { - $this->set($field . 'Columns', $columnsData); + $this->set($columnsAttribute, $columnsData); + + if ($toSetFetched) { + $this->setFetched($columnsAttribute, $columnsData); + } } } @@ -317,13 +340,15 @@ class Entity extends BaseEntity } $idAttribute = $field . 'Id'; + $nameAttribute = $field . 'Name'; if (!$this->isNew() && !$this->hasFetched($idAttribute)) { $this->setFetched($idAttribute, $entityId); + $this->setFetched($nameAttribute, $entityName); } $this->set($idAttribute, $entityId); - $this->set($field . 'Name', $entityName); + $this->set($nameAttribute, $entityName); } /** diff --git a/application/Espo/Core/Record/Access/LinkCheck.php b/application/Espo/Core/Record/Access/LinkCheck.php index e1dc5c0064..adc8413f5c 100644 --- a/application/Espo/Core/Record/Access/LinkCheck.php +++ b/application/Espo/Core/Record/Access/LinkCheck.php @@ -48,10 +48,13 @@ use Espo\ORM\Defs\FieldDefs; use Espo\ORM\Defs\RelationDefs; use Espo\ORM\Entity; use Espo\ORM\EntityManager; +use Espo\ORM\Type\AttributeType; use Espo\ORM\Type\RelationType; +use stdClass; /** * Check access for record linking. When linking directly through relationships or via link fields. + * Also loads foreign name attributes. */ class LinkCheck { @@ -135,6 +138,7 @@ class LinkCheck /** @var string[] $oldIds */ $oldIds = $entity->getFetched($attribute) ?? []; + $setIds = $ids; $ids = array_values(array_diff($ids, $oldIds)); $removedIds = array_values(array_diff($oldIds, $ids)); @@ -148,9 +152,25 @@ class LinkCheck continue; } + $namesAttribute = $name . 'Names'; + + $names = $this->prepareNames($entity, $namesAttribute, $setIds); + foreach ($ids as $id) { - $this->processLinkedRecordsCheckItem($entity, $relationDefs, $id); + $foreignEntity = $this->processLinkedRecordsCheckItem($entity, $relationDefs, $id); + + if ($foreignEntity) { + $names->$id = $foreignEntity->get('name'); + } } + + $namesAttributeDefs = $entityDefs->tryGetAttribute($namesAttribute); + + if (!$namesAttributeDefs || !$namesAttributeDefs->getParam('isLinkMultipleNameMap')) { + continue; + } + + $entity->set($namesAttribute, $names); } } @@ -190,13 +210,13 @@ class LinkCheck RelationDefs $defs, string $id, bool $isOne = false - ): void { + ): ?Entity { $entityType = $entity->getEntityType(); $link = $defs->getName(); if ($this->getParam($entityType, $link, 'linkCheckDisabled')) { - return; + return null; } $foreignEntityType = null; @@ -206,7 +226,7 @@ class LinkCheck } if (!$foreignEntityType && !$defs->hasForeignEntityType()) { - return; + return null; } $foreignEntityType ??= $defs->getForeignEntityType(); @@ -226,10 +246,12 @@ class LinkCheck $toSkip = $this->linkForeignAccessCheck($isOne, $entityType, $link, $foreignEntity); if ($toSkip) { - return; + return $foreignEntity; } $this->linkEntityAccessCheck($entity, $foreignEntity, $link); + + return $foreignEntity; } /** @@ -548,6 +570,7 @@ class LinkCheck foreach ($entityDefs->getRelationList() as $relationDefs) { $name = $relationDefs->getName(); $attribute = $name . 'Id'; + $nameAttribute = $name . 'Name'; if ( !in_array($relationDefs->getType(), $typeList) || @@ -562,7 +585,26 @@ class LinkCheck $id = $entity->get($attribute); - $this->processLinkedRecordsCheckItem($entity, $relationDefs, $id, true); + $foreignEntity = $this->processLinkedRecordsCheckItem($entity, $relationDefs, $id, true); + + if (!$foreignEntity) { + continue; + } + + $nameAttributeDefs = $entityDefs->tryGetAttribute($nameAttribute); + + if (!$nameAttributeDefs) { + return; + } + + if ( + $nameAttributeDefs->getType() === AttributeType::FOREIGN || + $nameAttributeDefs->isNotStorable() + ) { + $foreignName = $relationDefs->getParam('foreignName') ?? 'name'; + + $entity->set($nameAttribute, $foreignEntity->get($foreignName)); + } } } @@ -633,4 +675,26 @@ class LinkCheck return $defaultAttributes->$attribute ?? null; } + + /** + * @param string[] $setIds + */ + private function prepareNames(Entity $entity, string $namesAttribute, array $setIds): stdClass + { + $oldNames = $entity->getFetched($namesAttribute); + + if (!$oldNames instanceof stdClass) { + $oldNames = (object) []; + } + + $names = (object) []; + + foreach ($setIds as $id) { + if (isset($oldNames->$id)) { + $names->$id = $oldNames->$id; + } + } + + return $names; + } } diff --git a/application/Espo/Core/Record/Service.php b/application/Espo/Core/Record/Service.php index fb9a6337dd..3c08cbc879 100644 --- a/application/Espo/Core/Record/Service.php +++ b/application/Espo/Core/Record/Service.php @@ -69,6 +69,7 @@ use Espo\ORM\Entity; use Espo\ORM\Repository\RDBRepository; use Espo\ORM\Collection; use Espo\ORM\Query\Part\WhereClause; +use Espo\ORM\Type\AttributeType; use Espo\Tools\Stream\Service as StreamService; use Espo\Entities\User; @@ -582,6 +583,30 @@ class Service implements Crud, unset($data->$attribute); } } + + $this->filterInputForeignAttributes($data); + } + + private function filterInputForeignAttributes(stdClass $data): void + { + $entityDefs = $this->entityManager->getDefs()->tryGetEntity($this->entityType); + + if (!$entityDefs) { + return; + } + + foreach ($entityDefs->getAttributeList() as $attributeDefs) { + if ( + $attributeDefs->getType() !== AttributeType::FOREIGN && + !$attributeDefs->getParam('isLinkMultipleNameMap') + ) { + continue; + } + + $attribute = $attributeDefs->getName(); + + unset($data->$attribute); + } } private function filterInputSystemAttributes(stdClass $data): void