Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"spryker/propel": "^3.45.0",
"spryker/propel-orm": "^1.16.0",
"spryker/symfony": "^3.1.0",
"spryker/transfer": "^3.39.0",
"spryker/util-encoding": "^2.1.0"
},
"require-dev": {
Expand All @@ -28,8 +29,7 @@
"spryker/monolog": "*",
"spryker/queue": "*",
"spryker/silex": "^2.0.0",
"spryker/testify": "^3.58.0",
"spryker/transfer": "^3.39.0"
"spryker/testify": "^3.58.0"
},
"suggest": {
"spryker/console": "If you want to execute console commands",
Expand Down
3 changes: 3 additions & 0 deletions dependency.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
{
"include": {
"spryker/transfer": "Provides transfer objects definition with `::get*OrFail()` functionality."
},
"include-dev": {
"codeception/module-asserts": "Codeception module containing various assertions.",
"phpunit/phpunit": "Need due to compatibility minimal version package with PHP 8."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,23 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="spryker:transfer-01 http://static.spryker.com/transfer-01.xsd">

<transfer name="HydrateEventsRequest">
<property name="eventEntities" type="EventEntity[]" singular="eventEntity" description="Collection of event entities to be processed"/>
<property name="foreignKeyName" type="string" description="Name of the foreign key field used for grouping or identification"/>
<property name="additionalValueName" type="string" description="Name of the additional value field used for custom mapping or grouping"/>
</transfer>

<transfer name="HydrateEventsResponse">
<property name="idTimestampMap" type="array" associative="true" singular="id" description="Map of event entity IDs to their corresponding timestamps"/>
<property name="foreignKeyTimestampMap" type="array" associative="true" singular="foreignKey" description="Map of foreign key values to their corresponding timestamps"/>
<property name="additionalValueTimestampMap" type="array" associative="true" singular="additionalValue" description="Map of additional values to their corresponding timestamps"/>
</transfer>

<transfer name="EventEntity">
<property name="event" type="string"/>
<property name="name" type="string"/>
<property name="id" type="int"/>
<property name="timestamp" type="int"/>
<property name="foreignKeys" type="array" singular="foreignKeys"/>
<property name="modifiedColumns" type="array" singular="modifiedColumns"/>
<property name="originalValues" type="array" singular="originalValues"/>
Expand Down
16 changes: 16 additions & 0 deletions src/Spryker/Zed/EventBehavior/Business/EventBehaviorFacade.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
namespace Spryker\Zed\EventBehavior\Business;

use Generated\Shared\Transfer\EventTriggerResponseTransfer;
use Generated\Shared\Transfer\HydrateEventsRequestTransfer;
use Generated\Shared\Transfer\HydrateEventsResponseTransfer;
use Spryker\Zed\Kernel\Business\AbstractFacade;

/**
Expand Down Expand Up @@ -126,6 +128,20 @@ public function getEventTransfersAdditionalValues(array $eventTransfers, string
return $this->getFactory()->createEventEntityTransferFilter()->getEventTransfersAdditionalValues($eventTransfers, $columnName);
}

/**
* {@inheritDoc}
*
* @api
*
* @param \Generated\Shared\Transfer\HydrateEventsRequestTransfer $hydrateEventsRequestTransfer
*
* @return \Generated\Shared\Transfer\HydrateEventsResponseTransfer
*/
public function hydrateEventDataTransfer(HydrateEventsRequestTransfer $hydrateEventsRequestTransfer): HydrateEventsResponseTransfer
{
return $this->getFactory()->createEventEntityTransferFilter()->hydrateEventDataTransfer($hydrateEventsRequestTransfer);
}

/**
* {@inheritDoc}
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
namespace Spryker\Zed\EventBehavior\Business;

use Generated\Shared\Transfer\EventTriggerResponseTransfer;
use Generated\Shared\Transfer\HydrateEventsRequestTransfer;
use Generated\Shared\Transfer\HydrateEventsResponseTransfer;

interface EventBehaviorFacadeInterface
{
Expand Down Expand Up @@ -101,7 +103,7 @@ public function getEventTransfersOriginalValues(array $eventTransfers, string $c

/**
* Specification:
* - Returns field value of the specficed column in eventTransfers.
* - Returns field value of the specified column in eventTransfers.
*
* @api
*
Expand All @@ -112,6 +114,26 @@ public function getEventTransfersOriginalValues(array $eventTransfers, string $c
*/
public function getEventTransfersAdditionalValues(array $eventTransfers, string $columnName): array;

/**
* Specification:
* - Extracts timestamp information for event entities passed in the request.
* - Always returns a map of entity IDs to their respective timestamps.
* - Optionally extracts additional values with timestamps for:
* - Foreign key values if `foreignKeyName` is provided.
* - Additional values if `additionalValueName` is provided.
* - Response structure (in HydrateEventsResponseTransfer):
* - `idTimestampMap`: [id => timestamp]
* - `foreignKeyTimestampMap`: [foreignKeyValue => timestamp] (if requested)
* - `additionalValueTimestampMap`: [additionalValue => timestamp] (if requested)
*
* @api
*
* @param \Generated\Shared\Transfer\HydrateEventsRequestTransfer $hydrateEventsRequestTransfer
*
* @return \Generated\Shared\Transfer\HydrateEventsResponseTransfer
*/
public function hydrateEventDataTransfer(HydrateEventsRequestTransfer $hydrateEventsRequestTransfer): HydrateEventsResponseTransfer;

/**
* Specification:
* - Triggers events for specified resources.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@

namespace Spryker\Zed\EventBehavior\Business\Model;

use Generated\Shared\Transfer\EventEntityTransfer;
use Generated\Shared\Transfer\HydrateEventsRequestTransfer;
use Generated\Shared\Transfer\HydrateEventsResponseTransfer;

class EventEntityTransferFilter implements EventEntityTransferFilterInterface
{
/**
Expand Down Expand Up @@ -145,6 +149,93 @@ public function getEventTransfersAdditionalValues(array $eventTransfers, string
return array_unique($additionalValues);
}

/**
* @param \Generated\Shared\Transfer\HydrateEventsRequestTransfer $hydrateEventsRequestTransfer
*
* @return \Generated\Shared\Transfer\HydrateEventsResponseTransfer
*/
public function hydrateEventDataTransfer(HydrateEventsRequestTransfer $hydrateEventsRequestTransfer): HydrateEventsResponseTransfer
{
$hydrateEventsResponseTransfer = new HydrateEventsResponseTransfer();
$eventEntityTransfers = $hydrateEventsRequestTransfer->getEventEntities()->getArrayCopy();

if (count($eventEntityTransfers) === 0) {
return $hydrateEventsResponseTransfer;
}
usort($eventEntityTransfers, fn (EventEntityTransfer $a, EventEntityTransfer $b) => $a->getTimestamp() <=> $b->getTimestamp());

$hydrateEventsResponseTransfer->setIdTimestampMap($this->getIdsTimestampMap($eventEntityTransfers));

if ($hydrateEventsRequestTransfer->getAdditionalValueName()) {
$hydrateEventsResponseTransfer->setAdditionalValueTimestampMap($this->getAdditionalValueTimestampMap($eventEntityTransfers, $hydrateEventsRequestTransfer->getAdditionalValueName()));
}

if ($hydrateEventsRequestTransfer->getForeignKeyName()) {
$hydrateEventsResponseTransfer->setForeignKeyTimestampMap($this->getForeignKeyTimestampMap($eventEntityTransfers, $hydrateEventsRequestTransfer->getForeignKeyName()));
}

return $hydrateEventsResponseTransfer;
}

/**
* @param array<\Generated\Shared\Transfer\EventEntityTransfer> $eventTransfers
*
* @return array<int, int>
*/
protected function getIdsTimestampMap(array $eventTransfers): array
{
$ids = [];
foreach ($eventTransfers as $eventTransfer) {
$ids[(int)$eventTransfer->getId()] = (int)$eventTransfer->getTimestamp();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so, if we go with the easiest flow that we can check. E.g. I've updating some big form on backoffice that have more than 1 entity in it. I put new data in it and it will trigger n+1 different events. There are not a 0 chance that those events will have the same id. With this approach we are going to loose some of the events.

The same goes for other similar methods

Copy link
Contributor Author

@vol4onok vol4onok Aug 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As you see, we use the last one for a specific (unique) entity. It's part of the improvement

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok. I still have my doubts, but please double check it on QA

}

return $ids;
}

/**
* @param array<\Generated\Shared\Transfer\EventEntityTransfer> $eventTransfers
* @param string $columnName
*
* @return array<int|string, int>
*/
protected function getAdditionalValueTimestampMap(array $eventTransfers, string $columnName): array
{
$additionalValues = [];
foreach ($eventTransfers as $eventTransfer) {
$additionalValuesOfEvent = $eventTransfer->getAdditionalValues();
if (!isset($additionalValuesOfEvent[$columnName])) {
continue;
}

$additionalValues[$additionalValuesOfEvent[$columnName]] = (int)$eventTransfer->getTimestamp();
}

return $additionalValues;
}

/**
* @param array<\Generated\Shared\Transfer\EventEntityTransfer> $eventTransfers
* @param string $foreignKeyColumnName
*
* @return array<int, int>
*/
protected function getForeignKeyTimestampMap(array $eventTransfers, string $foreignKeyColumnName): array
{
$foreignKeys = [];
foreach ($eventTransfers as $eventTransfer) {
if (!isset($eventTransfer->getForeignKeys()[$foreignKeyColumnName])) {
continue;
}

$value = $eventTransfer->getForeignKeys()[$foreignKeyColumnName] ?? null;
if ($value !== null) {
$foreignKeys[(int)$value] = (int)$eventTransfer->getTimestamp();
}
}

return $foreignKeys;
}

/**
* @param array<string> $columns
* @param array<string> $modifiedColumns
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@

namespace Spryker\Zed\EventBehavior\Business\Model;

use Generated\Shared\Transfer\HydrateEventsRequestTransfer;
use Generated\Shared\Transfer\HydrateEventsResponseTransfer;

interface EventEntityTransferFilterInterface
{
/**
Expand Down Expand Up @@ -55,4 +58,11 @@ public function getEventTransfersOriginalValues(array $eventTransfers, string $c
* @return array
*/
public function getEventTransfersAdditionalValues(array $eventTransfers, string $columnName): array;

/**
* @param \Generated\Shared\Transfer\HydrateEventsRequestTransfer $hydrateEventsRequestTransfer
*
* @return \Generated\Shared\Transfer\HydrateEventsResponseTransfer
*/
public function hydrateEventDataTransfer(HydrateEventsRequestTransfer $hydrateEventsRequestTransfer): HydrateEventsResponseTransfer;
}
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,9 @@ protected function createEventResourceQueryContainerPluginIterator(EventResource
protected function triggerBulk(EventResourceQueryContainerPluginInterface $plugin, array $ids): void
{
$eventEntityTransfers = array_map(function ($id) {
return (new EventEntityTransfer())->setId($id);
return (new EventEntityTransfer())
->setId($id)
->setTimestamp(time());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so we just set time from now, but we need to compare it with the time from the message. This is not according AC

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are those changes mentioned in the changelogs?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, it's enough here because we publish specific entities, we don't modify them.

}, $ids);

$this->eventFacade->triggerBulk($plugin->getEventName(), $eventEntityTransfers);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,9 @@ protected function getIdColumnName($plugin): ?string
protected function triggerBulk(EventResourcePluginInterface $plugin, array $ids): void
{
$eventEntityTransfers = array_map(function ($id) {
return (new EventEntityTransfer())->setId($id);
return (new EventEntityTransfer())
->setId($id)
->setTimestamp(time());
}, $ids);

$this->eventFacade->triggerBulk($plugin->getEventName(), $eventEntityTransfers);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ protected function triggerEvents(array $events): int
$eventEntityTransfer->setEvent($data[EventBehavior::EVENT_CHANGE_NAME]);
$eventEntityTransfer->setName($data[EventBehavior::EVENT_CHANGE_ENTITY_NAME]);
$eventEntityTransfer->setId($id);
$eventEntityTransfer->setTimestamp($event->getCreatedAt()?->getTimestamp());
$eventEntityTransfer->setForeignKeys($data[EventBehavior::EVENT_CHANGE_ENTITY_FOREIGN_KEYS]);
if (isset($data[EventBehavior::EVENT_CHANGE_ENTITY_ORIGINAL_VALUES])) {
$eventEntityTransfer->setOriginalValues($data[EventBehavior::EVENT_CHANGE_ENTITY_ORIGINAL_VALUES]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use DateInterval;
use DateTime;
use Generated\Shared\Transfer\EventEntityTransfer;
use Generated\Shared\Transfer\HydrateEventsRequestTransfer;
use Orm\Zed\EventBehavior\Persistence\SpyEventBehaviorEntityChange;
use Orm\Zed\EventBehavior\Persistence\SpyEventBehaviorEntityChangeQuery;
use PHPUnit\Framework\MockObject\Rule\InvokedCount as InvokedCountMatcher;
Expand Down Expand Up @@ -41,6 +42,11 @@ class EventBehaviorFacadeTest extends Unit
*/
protected const ID = 'id';

/**
* @var string
*/
protected const TIMESTAMP = 'timestamp';

/**
* @var string
*/
Expand Down Expand Up @@ -217,6 +223,46 @@ public function testGetEventTransferIds(): void
$this->assertEquals($eventTransferIds, [1, 2]);
}

/**
* @return void
*/
public function testHydrateEventDataTransfer(): void
{
// Arrange
$hydrateEventsRequestTransfer = new HydrateEventsRequestTransfer();

$eventEntityTransfer = new EventEntityTransfer();
$eventEntityTransfer->setId(1);
$eventEntityTransfer->setAdditionalValues(['property1' => 'value1']);
$eventEntityTransfer->setTimestamp(102);
$eventEntityTransfer->setAdditionalValues(['property2' => 'value3']);
$hydrateEventsRequestTransfer->addEventEntity($eventEntityTransfer);

$eventEntityTransfer = new EventEntityTransfer();
$eventEntityTransfer->setId(2);
$eventEntityTransfer->setForeignKeys(['foreignKey' => 7]);
$eventEntityTransfer->setTimestamp(102);
$eventEntityTransfer->setAdditionalValues(['property2' => 'value1']);
$hydrateEventsRequestTransfer->addEventEntity($eventEntityTransfer);

$eventEntityTransfer = new EventEntityTransfer();
$eventEntityTransfer->setId(1);
$eventEntityTransfer->setForeignKeys(['foreignKey' => 5]);
$eventEntityTransfer->setTimestamp(106);
$eventEntityTransfer->setAdditionalValues(['property1' => 'value1', 'property2' => 'value2']);
$hydrateEventsRequestTransfer->addEventEntity($eventEntityTransfer);

$hydrateEventsRequestTransfer->setForeignKeyName('foreignKey');
$hydrateEventsRequestTransfer->setAdditionalValueName('property2');

$hydrateEventsResponseTransfer = $this->tester->getFacade()->hydrateEventDataTransfer($hydrateEventsRequestTransfer);

// Assert
$this->assertEquals($hydrateEventsResponseTransfer->getIdTimestampMap(), [1 => 106, 2 => 102]);
$this->assertEquals($hydrateEventsResponseTransfer->getForeignKeyTimestampMap(), [7 => 102, 5 => 106]);
$this->assertEquals($hydrateEventsResponseTransfer->getAdditionalValueTimestampMap(), ['value3' => 102, 'value1' => 102, 'value2' => 106]);
}

/**
* @return void
*/
Expand Down Expand Up @@ -347,7 +393,7 @@ public function assertTriggeredEvent(string $eventName, TransferInterface $event
$actualArray[EventBehavior::EVENT_CHANGE_ENTITY_ADDITIONAL_VALUES] = $actualArray[static::FIELD_ADDITIONAL_VALUES];
unset($actualArray[static::FIELD_ADDITIONAL_VALUES]);

$this->assertEquals($actualArray, $this->createEventData($actualArray[static::ID]));
$this->assertEquals($actualArray, $this->createEventData($actualArray[static::ID], $actualArray[static::TIMESTAMP]));
}

/**
Expand Down Expand Up @@ -435,14 +481,16 @@ protected function createLostEntityChangeEvent(string $idEntity = '123'): void

/**
* @param string $idEntity
* @param int $timestamp
*
* @return array
*/
protected function createEventData(string $idEntity = '123'): array
protected function createEventData(string $idEntity = '123', int $timestamp = 0): array
{
return [
EventBehavior::EVENT_CHANGE_ENTITY_NAME => 'name',
EventBehavior::EVENT_CHANGE_ENTITY_ID => $idEntity,
EventEntityTransfer::TIMESTAMP => $timestamp,
EventBehavior::EVENT_CHANGE_ENTITY_FOREIGN_KEYS => [1, 2, 3],
EventBehavior::EVENT_CHANGE_NAME => 'test',
EventBehavior::EVENT_CHANGE_ENTITY_MODIFIED_COLUMNS => [],
Expand Down
Loading