Skip to content

Commit a9d98e0

Browse files
authored
Merge pull request #233 from ProcessMaker/FOUR-26911
FOUR-26911 Implement Catch Event Message Mapping
2 parents f021157 + 37d3273 commit a9d98e0

13 files changed

Lines changed: 412 additions & 34 deletions

src/ProcessMaker/Nayra/Bpmn/CatchEventTrait.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ protected function initCatchEventTrait()
3030
{
3131
$this->setProperty(CatchEventInterface::BPMN_PROPERTY_EVENT_DEFINITIONS, new Collection);
3232
$this->setProperty(CatchEventInterface::BPMN_PROPERTY_PARALLEL_MULTIPLE, false);
33+
$this->setProperty(CatchEventInterface::BPMN_PROPERTY_DATA_OUTPUT_ASSOCIATION, new Collection);
3334
}
3435

3536
/**
@@ -42,6 +43,11 @@ public function getEventDefinitions()
4243
return $this->getProperty(CatchEventInterface::BPMN_PROPERTY_EVENT_DEFINITIONS);
4344
}
4445

46+
public function getDataOutputAssociations()
47+
{
48+
return $this->getProperty(CatchEventInterface::BPMN_PROPERTY_DATA_OUTPUT_ASSOCIATION);
49+
}
50+
4551
/**
4652
* Register catch events.
4753
*
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
namespace ProcessMaker\Nayra\Bpmn;
4+
5+
use ProcessMaker\Nayra\Contracts\Bpmn\DataOutputAssociationInterface;
6+
7+
/**
8+
* DataOutputAssociation class for handling data output associations in BPMN processes
9+
*/
10+
class DataOutputAssociation implements DataOutputAssociationInterface
11+
{
12+
use BaseTrait;
13+
14+
protected function initDataOutputAssociation()
15+
{
16+
$this->properties[static::BPMN_PROPERTY_ASSIGNMENT] = new Collection;
17+
}
18+
19+
public function getSource()
20+
{
21+
return $this->getProperty(static::BPMN_PROPERTY_SOURCES_REF);
22+
}
23+
24+
public function getTarget()
25+
{
26+
return $this->getProperty(static::BPMN_PROPERTY_TARGET_REF);
27+
}
28+
29+
public function getTransformation()
30+
{
31+
return $this->getProperty(static::BPMN_PROPERTY_TRANSFORMATION);
32+
}
33+
34+
public function getAssignments()
35+
{
36+
return $this->getProperty(static::BPMN_PROPERTY_ASSIGNMENT);
37+
}
38+
}

src/ProcessMaker/Nayra/Bpmn/DataStoreTrait.php

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -113,21 +113,21 @@ public function getDotData($path, $default = null)
113113
{
114114
$keys = explode('.', $path);
115115
$current = $this->data;
116-
116+
117117
// Navigate through the path
118118
foreach ($keys as $key) {
119119
// Handle numeric keys for arrays
120120
if (is_numeric($key)) {
121121
$key = (int) $key;
122122
}
123-
123+
124124
if (!isset($current[$key])) {
125125
return $default;
126126
}
127-
127+
128128
$current = $current[$key];
129129
}
130-
130+
131131
return $current;
132132
}
133133

@@ -142,31 +142,34 @@ public function getDotData($path, $default = null)
142142
public function setDotData($path, $value)
143143
{
144144
$keys = explode('.', $path);
145+
$firstKey = $keys[0];
145146
$current = &$this->data;
146-
147+
147148
// Navigate to the parent of the target key
148149
for ($i = 0; $i < count($keys) - 1; $i++) {
149150
$key = $keys[$i];
150-
151+
151152
// Handle numeric keys for arrays
152153
if (is_numeric($key)) {
153154
$key = (int) $key;
154155
}
155-
156+
156157
if (!isset($current[$key]) || !is_array($current[$key])) {
157158
$current[$key] = [];
158159
}
159160
$current = &$current[$key];
160161
}
161-
162+
162163
// Set the final value
163164
$finalKey = $keys[count($keys) - 1];
164165
if (is_numeric($finalKey)) {
165166
$finalKey = (int) $finalKey;
166167
}
167-
168+
168169
$current[$finalKey] = $value;
169-
170+
// Keep compatibility with putData method (required by PM Core)
171+
$this->putData($firstKey, $this->data[$firstKey]);
172+
170173
return $this;
171174
}
172175
}

src/ProcessMaker/Nayra/Bpmn/Models/MessageEventDefinition.php

Lines changed: 55 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
namespace ProcessMaker\Nayra\Bpmn\Models;
44

55
use ProcessMaker\Nayra\Bpmn\EventDefinitionTrait;
6+
use ProcessMaker\Nayra\Contracts\Bpmn\CatchEventInterface;
7+
use ProcessMaker\Nayra\Contracts\Bpmn\CollectionInterface;
8+
use ProcessMaker\Nayra\Contracts\Bpmn\DataStoreInterface;
69
use ProcessMaker\Nayra\Contracts\Bpmn\EventDefinitionInterface;
710
use ProcessMaker\Nayra\Contracts\Bpmn\FlowNodeInterface;
811
use ProcessMaker\Nayra\Contracts\Bpmn\MessageEventDefinitionInterface;
@@ -90,47 +93,78 @@ public function assertsRule(EventDefinitionInterface $event, FlowNodeInterface $
9093
public function execute(EventDefinitionInterface $event, FlowNodeInterface $target, ExecutionInstanceInterface $instance = null, TokenInterface $token = null)
9194
{
9295
$throwEvent = $token->getOwnerElement();
93-
$this->evaluateMessagePayload($throwEvent, $token, $instance);
96+
$this->executeMessageMapping($throwEvent, $target, $instance, $token);
9497
return $this;
9598
}
9699

97100
/**
98-
* Evaluate the message payload
101+
* Map a message payload from a ThrowEvent through an optional CatchEvent mapping
102+
* into the instance data store.
99103
*
100104
* @param ThrowEventInterface $throwEvent
105+
* @param CatchEventInterface $catchEvent
106+
* @param ExecutionInstanceInterface $instance
101107
* @param TokenInterface $token
102-
* @param ExecutionInstanceInterface $targetInstance
108+
*
109+
* @return void
110+
*/
111+
private function executeMessageMapping(ThrowEventInterface $throwEvent, CatchEventInterface $catchEvent, ExecutionInstanceInterface $instance, TokenInterface $token): void
112+
{
113+
$sourceMaps = $throwEvent->getDataInputAssociations();
114+
$targetMaps = $catchEvent->getDataOutputAssociations();
115+
$targetStore = $instance->getDataStore();
116+
117+
// Source of data is the token's instance store if present; otherwise a fresh store.
118+
$sourceStore = $token->getInstance()?->getDataStore() ?? new DataStore();
119+
120+
// If target mappings exist we stage into a buffer; otherwise write straight to the instance store.
121+
$bufferStore = !count($targetMaps) ? $targetStore : new DataStore();
122+
123+
// 1) Source mappings: source → buffer/instance
124+
$this->evaluateMessagePayload($sourceMaps, $sourceStore, $bufferStore);
125+
126+
// 2) Optional target mappings: buffer → instance
127+
if (count($targetMaps)) {
128+
$this->evaluateMessagePayload($targetMaps, $bufferStore, $targetStore);
129+
}
130+
}
131+
132+
/**
133+
* Evaluate the message payload
134+
*
135+
* @param CollectionInterface $associations
136+
* @param DataStoreInterface $sourceStore
137+
* @param DataStoreInterface $targetStore
138+
*
103139
* @return void
104140
*/
105-
private function evaluateMessagePayload(ThrowEventInterface $throwEvent, TokenInterface $token, ExecutionInstanceInterface $targetInstance)
141+
private function evaluateMessagePayload(CollectionInterface $associations, DataStoreInterface $sourceStore, DataStoreInterface $targetStore): void
106142
{
107-
// Initialize message payload
108-
$payload = [];
109-
$associations = $throwEvent->getDataInputAssociations();
110-
// Get data from source token instance or empty one if not found
111-
$sourceDataStore = $token->getInstance()?->getDataStore() ?? new DataStore();
143+
$assignments = [];
112144

113-
// Associate data inputs to message payload
114145
foreach ($associations as $association) {
115-
$data = $sourceDataStore->getData();
116146
$source = $association->getSource();
117147
$target = $association->getTarget();
118148

119-
// Add reference to source
120149
$hasSource = $source && $source->getName();
121150
$hasTarget = $target && $target->getName();
122-
$data['sourceRef'] = $hasSource ? $sourceDataStore->getDotData($source->getName()) : null;
123151

124-
// Apply transformation
125-
$this->applyTransformation($association, $data, $payload, $hasTarget, $hasSource);
152+
// Base data always starts from full source store
153+
$data = $sourceStore->getData();
154+
155+
// Optionally add a direct reference to the source value
156+
if ($hasSource) {
157+
$data['sourceRef'] = $sourceStore->getDotData($source->getName());
158+
}
126159

127-
// Evaluate assignments
128-
$this->evaluateAssignments($association, $data, $payload);
160+
// Transformation and assignments build up the assignments list
161+
$this->applyTransformation($association, $data, $assignments, $hasTarget, $hasSource);
162+
$this->evaluateAssignments($association, $data, $assignments);
129163
}
130-
// Update data into target $instance
131-
$dataStore = $targetInstance->getDataStore();
132-
foreach ($payload as $load) {
133-
$dataStore->setDotData($load['key'], $load['value']);
164+
165+
// Flush all assignments into target store
166+
foreach ($assignments as $assignment) {
167+
$targetStore->setDotData($assignment['key'], $assignment['value']);
134168
}
135169
}
136170

src/ProcessMaker/Nayra/Contracts/Bpmn/CatchEventInterface.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ interface CatchEventInterface extends EventInterface
1616

1717
const EVENT_CATCH_TOKEN_ARRIVES = 'CatchEventTokenArrives';
1818

19+
const BPMN_PROPERTY_DATA_OUTPUT = 'dataOutput';
20+
21+
const BPMN_PROPERTY_DATA_OUTPUT_SET = 'outputSet';
22+
23+
const BPMN_PROPERTY_DATA_OUTPUT_ASSOCIATION = 'dataOutputAssociation';
24+
1925
/**
2026
* Get EventDefinitions that are triggers expected for a catch Event.
2127
*
@@ -57,4 +63,11 @@ public function execute(EventDefinitionInterface $event, ExecutionInstanceInterf
5763
* @return StateInterface
5864
*/
5965
public function getActiveState();
66+
67+
/**
68+
* Get the data output associations.
69+
*
70+
* @return DataOutputAssociationInterface[]
71+
*/
72+
public function getDataOutputAssociations();
6073
}

src/ProcessMaker/Nayra/Contracts/Bpmn/CollectionInterface.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33
namespace ProcessMaker\Nayra\Contracts\Bpmn;
44

55
use SeekableIterator;
6+
use Countable;
67

78
/**
89
* CollectionInterface
910
*/
10-
interface CollectionInterface extends SeekableIterator
11+
interface CollectionInterface extends SeekableIterator, Countable
1112
{
1213
/**
1314
* Count the elements of the collection.

src/ProcessMaker/Nayra/Contracts/Bpmn/DataAssociationInterface.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@
77
*/
88
interface DataAssociationInterface extends EntityInterface
99
{
10+
const BPMN_PROPERTY_ASSIGNMENT = 'assignment';
11+
const BPMN_PROPERTY_SOURCES_REF = 'sourceRef';
12+
const BPMN_PROPERTY_TARGET_REF = 'targetRef';
13+
const BPMN_PROPERTY_TRANSFORMATION = 'transformation';
14+
1015
/**
1116
* Get the source of the data association.
1217
*

src/ProcessMaker/Nayra/Contracts/Bpmn/DataOutputAssociationInterface.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@
77
*/
88
interface DataOutputAssociationInterface extends DataAssociationInterface
99
{
10+
1011
}

src/ProcessMaker/Nayra/Contracts/Bpmn/ThrowEventInterface.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ interface ThrowEventInterface extends EventInterface
1818

1919
const EVENT_THROW_TOKEN_CONSUMED = 'ThrowEventTokenConsumed';
2020

21+
const BPMN_PROPERTY_DATA_INPUT = 'dataInput';
22+
23+
const BPMN_PROPERTY_DATA_INPUT_SET = 'inputSet';
24+
25+
const BPMN_PROPERTY_DATA_INPUT_ASSOCIATION = 'dataInputAssociation';
26+
2127
/**
2228
* Get Data Inputs for the throw Event.
2329
*

src/ProcessMaker/Nayra/RepositoryTrait.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use InvalidArgumentException;
66
use ProcessMaker\Nayra\Bpmn\Assignment;
77
use ProcessMaker\Nayra\Bpmn\DataInputAssociation;
8+
use ProcessMaker\Nayra\Bpmn\DataOutputAssociation;
89
use ProcessMaker\Nayra\Bpmn\Lane;
910
use ProcessMaker\Nayra\Bpmn\LaneSet;
1011
use ProcessMaker\Nayra\Bpmn\Models\Activity;
@@ -493,4 +494,9 @@ public function createAssignment()
493494
{
494495
return new Assignment();
495496
}
497+
498+
public function createDataOutputAssociation()
499+
{
500+
return new DataOutputAssociation();
501+
}
496502
}

0 commit comments

Comments
 (0)