From f279528c566b6f63571de9783d54eca3fd7aa19f Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Sat, 21 Dec 2024 13:58:31 +0200 Subject: [PATCH] record: early before create/update hooks --- .../Espo/Core/Record/Hook/Provider.php | 2 + application/Espo/Core/Record/Hook/Type.php | 3 ++ application/Espo/Core/Record/HookManager.php | 54 +++++++++++++++++++ application/Espo/Core/Record/Service.php | 4 ++ .../Espo/Core/Utils/Metadata/Builder.php | 2 + schema/metadata/recordDefs.json | 14 +++++ 6 files changed, 79 insertions(+) diff --git a/application/Espo/Core/Record/Hook/Provider.php b/application/Espo/Core/Record/Hook/Provider.php index b241213c61..52d0883e9e 100644 --- a/application/Espo/Core/Record/Hook/Provider.php +++ b/application/Espo/Core/Record/Hook/Provider.php @@ -47,8 +47,10 @@ class Provider /** @var array */ private $typeInterfaceListMap = [ Type::BEFORE_READ => [ReadHook::class], + Type::EARLY_BEFORE_CREATE => [CreateHook::class, SaveHook::class], Type::BEFORE_CREATE => [CreateHook::class, SaveHook::class], Type::AFTER_CREATE => [CreateHook::class, SaveHook::class], + Type::EARLY_BEFORE_UPDATE => [UpdateHook::class, SaveHook::class], Type::BEFORE_UPDATE => [UpdateHook::class, SaveHook::class], Type::AFTER_UPDATE => [UpdateHook::class, SaveHook::class], Type::BEFORE_DELETE => [DeleteHook::class], diff --git a/application/Espo/Core/Record/Hook/Type.php b/application/Espo/Core/Record/Hook/Type.php index a8b7a7bb88..960644e296 100644 --- a/application/Espo/Core/Record/Hook/Type.php +++ b/application/Espo/Core/Record/Hook/Type.php @@ -33,6 +33,9 @@ class Type { public const BEFORE_READ = 'beforeRead'; + public const EARLY_BEFORE_CREATE = 'earlyBeforeCreate'; + public const EARLY_BEFORE_UPDATE = 'earlyBeforeUpdate'; + public const BEFORE_CREATE = 'beforeCreate'; public const BEFORE_UPDATE = 'beforeUpdate'; public const BEFORE_DELETE = 'beforeDelete'; diff --git a/application/Espo/Core/Record/HookManager.php b/application/Espo/Core/Record/HookManager.php index 38ccb3eec3..0c237bed02 100644 --- a/application/Espo/Core/Record/HookManager.php +++ b/application/Espo/Core/Record/HookManager.php @@ -48,6 +48,24 @@ class HookManager public function __construct(private Provider $provider) {} + /** + * @throws BadRequest + * @throws Forbidden + * @throws Conflict + */ + public function processEarlyBeforeCreate(Entity $entity, CreateParams $params): void + { + foreach ($this->getEarlyBeforeCreateHookList($entity->getEntityType()) as $hook) { + if ($hook instanceof SaveHook) { + $hook->process($entity); + + continue; + } + + $hook->process($entity, $params); + } + } + /** * @throws BadRequest * @throws Forbidden @@ -91,6 +109,24 @@ class HookManager } } + /** + * @throws BadRequest + * @throws Forbidden + * @throws Conflict + */ + public function processEarlyBeforeUpdate(Entity $entity, UpdateParams $params): void + { + foreach ($this->getEarlyBeforeUpdateHookList($entity->getEntityType()) as $hook) { + if ($hook instanceof SaveHook) { + $hook->process($entity); + + continue; + } + + $hook->process($entity, $params); + } + } + /** * @throws BadRequest * @throws Forbidden @@ -188,6 +224,15 @@ class HookManager return $this->provider->getList($entityType, Type::BEFORE_READ); } + /** + * @return (CreateHook|SaveHook)[] + */ + private function getEarlyBeforeCreateHookList(string $entityType): array + { + /** @var (CreateHook|SaveHook)[] */ + return $this->provider->getList($entityType, Type::EARLY_BEFORE_CREATE); + } + /** * @return (CreateHook|SaveHook)[] */ @@ -206,6 +251,15 @@ class HookManager return $this->provider->getList($entityType, Type::AFTER_CREATE); } + /** + * @return (UpdateHook|SaveHook)[] + */ + private function getEarlyBeforeUpdateHookList(string $entityType): array + { + /** @var (UpdateHook|SaveHook)[] */ + return $this->provider->getList($entityType, Type::EARLY_BEFORE_UPDATE); + } + /** * @return (UpdateHook|SaveHook)[] */ diff --git a/application/Espo/Core/Record/Service.php b/application/Espo/Core/Record/Service.php index 405ce7e159..6a27d82bdc 100644 --- a/application/Espo/Core/Record/Service.php +++ b/application/Espo/Core/Record/Service.php @@ -635,6 +635,8 @@ class Service implements Crud, $entity->set($data); $this->populateDefaults($entity, $data); + $this->getRecordHookManager()->processEarlyBeforeCreate($entity, $params); + $this->processValidation($entity, $data); $this->checkEntityCreateAccess($entity); $this->processAssignmentCheck($entity); @@ -719,6 +721,8 @@ class Service implements Crud, $entity->set($data); + $this->getRecordHookManager()->processEarlyBeforeUpdate($entity, $params); + $this->processValidation($entity, $data); $this->processAssignmentCheck($entity); $this->getLinkCheck()->processFields($entity); diff --git a/application/Espo/Core/Utils/Metadata/Builder.php b/application/Espo/Core/Utils/Metadata/Builder.php index 2edb29a919..95a5cd54fb 100644 --- a/application/Espo/Core/Utils/Metadata/Builder.php +++ b/application/Espo/Core/Utils/Metadata/Builder.php @@ -62,7 +62,9 @@ class Builder ['recordDefs', self::ANY_KEY, 'updateInputFilterClassNameList'], ['recordDefs', self::ANY_KEY, 'outputFilterClassNameList'], ['recordDefs', self::ANY_KEY, 'beforeReadHookClassNameList'], + ['recordDefs', self::ANY_KEY, 'earlyBeforeCreateHookClassNameList'], ['recordDefs', self::ANY_KEY, 'beforeCreateHookClassNameList'], + ['recordDefs', self::ANY_KEY, 'earlyBeforeUpdateHookClassNameList'], ['recordDefs', self::ANY_KEY, 'beforeUpdateHookClassNameList'], ['recordDefs', self::ANY_KEY, 'beforeDeleteHookClassNameList'], ['recordDefs', self::ANY_KEY, 'afterCreateHookClassNameList'], diff --git a/schema/metadata/recordDefs.json b/schema/metadata/recordDefs.json index f6c61e3067..72c63c73bc 100644 --- a/schema/metadata/recordDefs.json +++ b/schema/metadata/recordDefs.json @@ -198,6 +198,13 @@ "type": "string" } }, + "earlyBeforeCreateHookClassNameList": { + "description": "Early-before-create hooks. Run before validation. Should implement the Espo\\Core\\Record\\Hook\\CreateHook or Espo\\Core\\Record\\Hook\\SaveHook interface. As of v9.0.", + "type": "array", + "items": { + "type": "string" + } + }, "beforeCreateHookClassNameList": { "description": "Before-create hooks. Should implement the Espo\\Core\\Record\\Hook\\CreateHook or Espo\\Core\\Record\\Hook\\SaveHook interface.", "type": "array", @@ -212,6 +219,13 @@ "type": "string" } }, + "earlyBeforeUpdateHookClassNameList": { + "description": "Early-before-update hooks. Run before validation. Should implement the Espo\\Core\\Record\\Hook\\UpdateHook or Espo\\Core\\Record\\Hook\\SaveHook interface. As of v9.0.", + "type": "array", + "items": { + "type": "string" + } + }, "beforeUpdateHookClassNameList": { "description": "Before-update hooks. Should implement the Espo\\Core\\Record\\Hook\\UpdateHook or Espo\\Core\\Record\\Hook\\SaveHook interface.", "type": "array",