diff --git a/application/Espo/Core/FieldProcessing/GeneralLoadProcessor.php b/application/Espo/Core/FieldProcessing/GeneralLoadProcessor.php new file mode 100644 index 0000000000..9dc543f09f --- /dev/null +++ b/application/Espo/Core/FieldProcessing/GeneralLoadProcessor.php @@ -0,0 +1,93 @@ +injectableFactory = $injectableFactory; + $this->metadata = $metadata; + } + + public function process(Entity $entity): void + { + foreach ($this->createProcessorList($entity->getEntityType()) as $processor) { + $processor->process($entity); + } + } + + /** + * @return LoadProcessor[] + */ + private function createProcessorList(string $entityType): array + { + $list = []; + + foreach ($this->getProcessorClassNameList($entityType) as $className) { + $list[] = $this->createProcessor($className); + } + + return $list; + } + + /** + * @return string[] + */ + private function getProcessorClassNameList(string $entityType): array + { + $list = $this->metadata + ->get(['app', 'fieldProcessing', 'loadProcessorClassNameList']) ?? []; + + $additionalList = $this->metadata + ->get(['recordDefs', $entityType, 'loadProcessorClassNameList']) ?? []; + + return array_merge($list, $additionalList); + } + + private function createProcessor(string $className): LoadProcessor + { + return $this->injectableFactory->create($className); + } +} diff --git a/application/Espo/Core/FieldProcessing/Link/LoadProcessor.php b/application/Espo/Core/FieldProcessing/Link/LoadProcessor.php new file mode 100644 index 0000000000..6fa6d24970 --- /dev/null +++ b/application/Espo/Core/FieldProcessing/Link/LoadProcessor.php @@ -0,0 +1,96 @@ +ormDefs = $ormDefs; + } + + public function process(Entity $entity): void + { + foreach ($this->getFieldList($entity->getEntityType()) as $field) { + $entity->loadLinkField($field); + } + } + + /** + * @return string[] + */ + private function getFieldList(string $entityType): array + { + if (array_key_exists($entityType, $this->fieldListCacheMap)) { + return $this->fieldListCacheMap[$entityType]; + } + + $list = []; + + $entityDefs = $this->ormDefs->getEntity($entityType); + + foreach ($entityDefs->getFieldList() as $fieldDefs) { + if ($fieldDefs->getType() !== 'link') { + continue; + } + + if ($fieldDefs->getParam('noLoad')) { + continue; + } + + $name = $fieldDefs->getName(); + + if (!$entityDefs->hasRelation($name)) { + continue; + } + + if ($entityDefs->getRelation($name)->getType() !== Entity::HAS_ONE) { + continue; + } + + $list[] = $name; + } + + $this->fieldListCacheMap[$entityType] = $list; + + return $list; + } +} diff --git a/application/Espo/Core/FieldProcessing/LinkMultiple/LoadProcessor.php b/application/Espo/Core/FieldProcessing/LinkMultiple/LoadProcessor.php new file mode 100644 index 0000000000..e4dcd8818e --- /dev/null +++ b/application/Espo/Core/FieldProcessing/LinkMultiple/LoadProcessor.php @@ -0,0 +1,106 @@ +ormDefs = $ormDefs; + } + + public function process(Entity $entity): void + { + $entityType = $entity->getEntityType(); + + foreach ($this->getFieldList($entityType) as $field) { + $columns = $this->ormDefs + ->getEntity($entityType) + ->getField($field) + ->getParam('columns'); + + $entity->loadLinkMultipleField($field, $columns); + } + } + + /** + * @return string[] + */ + private function getFieldList(string $entityType): array + { + if (array_key_exists($entityType, $this->fieldListCacheMap)) { + return $this->fieldListCacheMap[$entityType]; + } + + $list = []; + + $entityDefs = $this->ormDefs->getEntity($entityType); + + foreach ($entityDefs->getFieldList() as $fieldDefs) { + if ( + $fieldDefs->getType() !== 'linkMultiple' && + $fieldDefs->getType() !== 'attachmentMultiple' + ) { + continue; + } + + if ($fieldDefs->getParam('noLoad')) { + continue; + } + + if ($fieldDefs->isNotStorable()) { + continue; + } + + $name = $fieldDefs->getName(); + + if (!$entityDefs->hasRelation($name)) { + continue; + } + + $list[] = $name; + } + + $this->fieldListCacheMap[$entityType] = $list; + + return $list; + } +} diff --git a/application/Espo/Core/FieldProcessing/LinkParent/LoadProcessor.php b/application/Espo/Core/FieldProcessing/LinkParent/LoadProcessor.php new file mode 100644 index 0000000000..91ce4fb7ac --- /dev/null +++ b/application/Espo/Core/FieldProcessing/LinkParent/LoadProcessor.php @@ -0,0 +1,84 @@ +ormDefs = $ormDefs; + } + + public function process(Entity $entity): void + { + foreach ($this->getFieldList($entity->getEntityType()) as $field) { + $entity->loadParentNameField($field); + } + } + + /** + * @return string[] + */ + private function getFieldList(string $entityType): array + { + if (array_key_exists($entityType, $this->fieldListCacheMap)) { + return $this->fieldListCacheMap[$entityType]; + } + + $list = []; + + $entityDefs = $this->ormDefs->getEntity($entityType); + + foreach ($entityDefs->getFieldList() as $fieldDefs) { + if ($fieldDefs->getType() !== 'linkParent') { + continue; + } + + $name = $fieldDefs->getName(); + + $list[] = $name; + } + + $this->fieldListCacheMap[$entityType] = $list; + + return $list; + } +} diff --git a/application/Espo/Core/FieldProcessing/LinkParent/TargetLoadProcessor.php b/application/Espo/Core/FieldProcessing/LinkParent/TargetLoadProcessor.php new file mode 100644 index 0000000000..921cdcd08b --- /dev/null +++ b/application/Espo/Core/FieldProcessing/LinkParent/TargetLoadProcessor.php @@ -0,0 +1,85 @@ +entityManager = $entityManager; + } + + public function process(Entity $entity): void + { + $targetType = $entity->get('targetType'); + $targetId = $entity->get('targetId'); + + if (!$targetType || !$targetId) { + return; + } + + if (!$this->entityManager->hasRepository($targetType)) { + return; + } + + $query = $this->entityManager + ->getQueryBuilder() + ->select() + ->from($targetType) + ->withDeleted() + ->where([ + 'id' => $targetId, + ]) + ->build(); + + $target = $this->entityManager + ->getRDBRepository($targetType) + ->clone($query) + ->findOne(); + + if (!$target) { + return; + } + + if (!$target->get('name')) { + return; + } + + $entity->set('targetName', $target->get('name')); + } +} diff --git a/application/Espo/Core/FieldProcessing/LoadProcessor.php b/application/Espo/Core/FieldProcessing/LoadProcessor.php new file mode 100644 index 0000000000..50e51c86b5 --- /dev/null +++ b/application/Espo/Core/FieldProcessing/LoadProcessor.php @@ -0,0 +1,40 @@ +streamService = $streamService; + $this->metadata = $metadata; + $this->user = $user; + $this->acl = $acl; + $this->config = $config; + } + + public function process(Entity $entity): void + { + $this->processIsFollowed($entity); + $this->processFollowers($entity); + } + + public function processIsFollowed(Entity $entity): void + { + if (!$entity->hasAttribute('isFollowed')) { + return; + } + + $isFollowed = $this->streamService->checkIsFollowed($entity); + + $entity->set('isFollowed', $isFollowed); + } + + public function processFollowers(Entity $entity): void + { + if ($this->user->isPortal()) { + return; + } + + if (!$this->metadata->get(['scopes', $entity->getEntityType(), 'stream'])) { + return; + } + + if (!$this->acl->checkEntityStream($entity)) { + return; + } + + $limit = $this->config->get('recordFollowersLoadLimit') ?? self::FOLLOWERS_LIMIT; + + $data = $this->streamService->getEntityFollowers($entity, 0, $limit); + + if (!$data) { + return; + } + + $entity->set('followersIds', $data['idList']); + $entity->set('followersNames', $data['nameMap']); + } +} diff --git a/application/Espo/Resources/metadata/app/fieldProcessing.json b/application/Espo/Resources/metadata/app/fieldProcessing.json new file mode 100644 index 0000000000..539896d10d --- /dev/null +++ b/application/Espo/Resources/metadata/app/fieldProcessing.json @@ -0,0 +1,8 @@ +{ + "loadProcessorClassNameList": [ + "Espo\\Core\\FieldProcessing\\Link\\LoadProcessor", + "Espo\\Core\\FieldProcessing\\LinkMultiple\\LoadProcessor", + "Espo\\Core\\FieldProcessing\\LinkParent\\LoadProcessor", + "Espo\\Core\\FieldProcessing\\Misc\\StreamLoadProcessor" + ] +} diff --git a/application/Espo/Resources/metadata/app/metadata.json b/application/Espo/Resources/metadata/app/metadata.json index 9329c5a34c..df7292486c 100644 --- a/application/Espo/Resources/metadata/app/metadata.json +++ b/application/Espo/Resources/metadata/app/metadata.json @@ -20,6 +20,7 @@ ["app", "select"], ["app", "massActions", "__ANY__", "implementationClassName"], ["app", "actions", "__ANY__", "implementationClassName"], + ["app", "fieldProcessing"], ["selectDefs"], ["recordDefs"], ["aclDefs"], diff --git a/application/Espo/Resources/metadata/recordDefs/ActionHistoryRecord.json b/application/Espo/Resources/metadata/recordDefs/ActionHistoryRecord.json new file mode 100644 index 0000000000..28b30d47e7 --- /dev/null +++ b/application/Espo/Resources/metadata/recordDefs/ActionHistoryRecord.json @@ -0,0 +1,5 @@ +{ + "loadProcessorClassNameList": [ + "Espo\\Core\\FieldProcessing\\LinkParent\\TargetLoadProcessor" + ] +} diff --git a/application/Espo/Services/Record.php b/application/Espo/Services/Record.php index f48ec6224e..2ae8c0b0dd 100644 --- a/application/Espo/Services/Record.php +++ b/application/Espo/Services/Record.php @@ -65,6 +65,7 @@ use Espo\Core\{ Action\Data as ActionData, Action\ActionFactory, FieldValidation\Params as FieldValidationParams, + FieldProcessing\GeneralLoadProcessor, }; use Espo\Tools\{ @@ -200,8 +201,6 @@ class Record implements Crud, const MAX_SELECT_TEXT_ATTRIBUTE_LENGTH = 5000; - const FOLLOWERS_LIMIT = 4; - const FIND_DUPLICATES_LIMIT = 10; protected $selectBuilderFactory; @@ -538,61 +537,6 @@ class Record implements Crud, return $this->streamService; } - protected function loadIsFollowed(Entity $entity): void - { - if ($this->getStreamService()->checkIsFollowed($entity)) { - $entity->set('isFollowed', true); - } - else { - $entity->set('isFollowed', false); - } - } - - protected function loadFollowers(Entity $entity) - { - if ($this->getUser()->isPortal()) { - return; - } - - if (!$this->getMetadata()->get(['scopes', $entity->getEntityType(), 'stream'])) { - return; - } - - if (!$this->getAcl()->check($entity, 'stream')) { - return; - } - - $data = $this->getStreamService()->getEntityFollowers($entity, 0, self::FOLLOWERS_LIMIT); - - if ($data) { - $entity->set('followersIds', $data['idList']); - $entity->set('followersNames', $data['nameMap']); - } - } - - protected function loadLinkMultipleFields(Entity $entity) - { - $fieldDefs = $this->getMetadata()->get(['entityDefs', $entity->getEntityType(), 'fields']) ?? []; - - foreach ($fieldDefs as $field => $defs) { - if ( - isset($defs['type']) && - in_array($defs['type'], ['linkMultiple', 'attachmentMultiple']) && - empty($defs['noLoad']) && - empty($defs['notStorable']) && - $entity->hasRelation($field) - ) { - $columns = null; - - if (!empty($defs['columns'])) { - $columns = $defs['columns']; - } - - $entity->loadLinkMultipleField($field, $columns); - } - } - } - public function loadLinkMultipleFieldsForList(Entity $entity, $selectAttributeList) { foreach ($selectAttributeList as $attribute) { @@ -612,43 +556,12 @@ class Record implements Crud, } } - protected function loadLinkFields(Entity $entity) - { - $fieldDefs = $this->getMetadata()->get('entityDefs.' . $entity->getEntityType() . '.fields', []); - $linkDefs = $this->getMetadata()->get('entityDefs.' . $entity->getEntityType() . '.links', []); - - foreach ($fieldDefs as $field => $defs) { - if (isset($defs['type']) && $defs['type'] === 'link') { - if (!empty($defs['noLoad'])) { - continue; - } - - if (empty($linkDefs[$field])) { - continue; - } - - if (empty($linkDefs[$field]['type'])) { - continue; - } - - if ($linkDefs[$field]['type'] !== 'hasOne') { - continue; - } - - $entity->loadLinkField($field); - } - } - } - protected function loadParentNameFields(Entity $entity) { $fieldDefs = $this->getMetadata()->get('entityDefs.' . $entity->getEntityType() . '.fields', []); foreach ($fieldDefs as $field => $defs) { if (isset($defs['type']) && $defs['type'] == 'linkParent') { - $parentId = $entity->get($field . 'Id'); - $parentType = $entity->get($field . 'Type'); - $entity->loadParentNameField($field); } } @@ -746,13 +659,19 @@ class Record implements Crud, } } + private function createLoadProcessor(): GeneralLoadProcessor + { + return $this->injectableFactory->create(GeneralLoadProcessor::class); + } + public function loadAdditionalFields(Entity $entity) { - $this->loadLinkFields($entity); - $this->loadLinkMultipleFields($entity); - $this->loadParentNameFields($entity); - $this->loadIsFollowed($entity); - $this->loadFollowers($entity); + $loadProcessor = $this->createLoadProcessor(); + + $loadProcessor->process($entity); + + //$this->loadParentNameFields($entity); + $this->loadEmailAddressField($entity); $this->loadPhoneNumberField($entity); $this->loadNotJoinedLinkFields($entity);