vendor/shopware/core/Content/Rule/DataAbstractionLayer/RulePayloadUpdater.php line 99

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Content\Rule\DataAbstractionLayer;
  3. use Doctrine\DBAL\Connection;
  4. use Shopware\Core\Content\Rule\DataAbstractionLayer\Indexing\ConditionTypeNotFound;
  5. use Shopware\Core\Framework\App\Event\AppScriptConditionEvents;
  6. use Shopware\Core\Framework\DataAbstractionLayer\Doctrine\FetchModeHelper;
  7. use Shopware\Core\Framework\DataAbstractionLayer\Doctrine\RetryableQuery;
  8. use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityWrittenEvent;
  9. use Shopware\Core\Framework\Log\Package;
  10. use Shopware\Core\Framework\Rule\Collector\RuleConditionRegistry;
  11. use Shopware\Core\Framework\Rule\Container\AndRule;
  12. use Shopware\Core\Framework\Rule\Container\ContainerInterface;
  13. use Shopware\Core\Framework\Rule\Rule;
  14. use Shopware\Core\Framework\Rule\ScriptRule;
  15. use Shopware\Core\Framework\Uuid\Uuid;
  16. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  17. /**
  18. * @deprecated tag:v6.5.0 - reason:becomes-internal - EventSubscribers will become internal in v6.5.0
  19. */
  20. #[Package('business-ops')]
  21. class RulePayloadUpdater implements EventSubscriberInterface
  22. {
  23. private Connection $connection;
  24. private RuleConditionRegistry $ruleConditionRegistry;
  25. /**
  26. * @internal
  27. */
  28. public function __construct(Connection $connection, RuleConditionRegistry $ruleConditionRegistry)
  29. {
  30. $this->connection = $connection;
  31. $this->ruleConditionRegistry = $ruleConditionRegistry;
  32. }
  33. public static function getSubscribedEvents(): array
  34. {
  35. return [
  36. AppScriptConditionEvents::APP_SCRIPT_CONDITION_WRITTEN_EVENT => 'updatePayloads',
  37. ];
  38. }
  39. /**
  40. * @param list<string> $ids
  41. *
  42. * @return array<string, array{payload: string|null, invalid: bool}>
  43. */
  44. public function update(array $ids): array
  45. {
  46. $conditions = $this->connection->fetchAllAssociative(
  47. 'SELECT LOWER(HEX(rc.rule_id)) as array_key, rc.*, rs.script, rs.identifier, rs.updated_at as lastModified
  48. FROM rule_condition rc
  49. LEFT JOIN app_script_condition rs ON rc.script_id = rs.id AND rs.active = 1
  50. WHERE rc.rule_id IN (:ids)
  51. ORDER BY rc.rule_id',
  52. ['ids' => Uuid::fromHexToBytesList($ids)],
  53. ['ids' => Connection::PARAM_STR_ARRAY]
  54. );
  55. $rules = FetchModeHelper::group($conditions);
  56. $update = new RetryableQuery(
  57. $this->connection,
  58. $this->connection->prepare('UPDATE `rule` SET payload = :payload, invalid = :invalid WHERE id = :id')
  59. );
  60. $updated = [];
  61. /** @var string $id */
  62. foreach ($rules as $id => $rule) {
  63. $invalid = false;
  64. $serialized = null;
  65. try {
  66. $nested = $this->buildNested($rule, null);
  67. //ensure the root rule is an AndRule
  68. $nested = new AndRule($nested);
  69. $serialized = serialize($nested);
  70. } catch (ConditionTypeNotFound $exception) {
  71. $invalid = true;
  72. } finally {
  73. $update->execute([
  74. 'id' => Uuid::fromHexToBytes($id),
  75. 'payload' => $serialized,
  76. 'invalid' => (int) $invalid,
  77. ]);
  78. }
  79. $updated[$id] = ['payload' => $serialized, 'invalid' => $invalid];
  80. }
  81. return $updated;
  82. }
  83. public function updatePayloads(EntityWrittenEvent $event): void
  84. {
  85. $ruleIds = $this->connection->fetchFirstColumn(
  86. 'SELECT DISTINCT rc.rule_id
  87. FROM rule_condition rc
  88. INNER JOIN app_script_condition rs ON rc.script_id = rs.id
  89. WHERE rs.id IN (:ids)',
  90. ['ids' => Uuid::fromHexToBytesList(array_values($event->getIds()))],
  91. ['ids' => Connection::PARAM_STR_ARRAY]
  92. );
  93. if (empty($ruleIds)) {
  94. return;
  95. }
  96. $this->update(Uuid::fromBytesToHexList($ruleIds));
  97. }
  98. /**
  99. * @param array<string, mixed> $rules
  100. *
  101. * @return list<Rule>
  102. */
  103. private function buildNested(array $rules, ?string $parentId): array
  104. {
  105. $nested = [];
  106. foreach ($rules as $rule) {
  107. if ($rule['parent_id'] !== $parentId) {
  108. continue;
  109. }
  110. if (!$this->ruleConditionRegistry->has($rule['type'])) {
  111. throw new ConditionTypeNotFound($rule['type']);
  112. }
  113. $ruleClass = $this->ruleConditionRegistry->getRuleClass($rule['type']);
  114. $object = new $ruleClass();
  115. if ($object instanceof ScriptRule) {
  116. $object->assign([
  117. 'script' => $rule['script'] ?? '',
  118. 'lastModified' => $rule['lastModified'] ? new \DateTimeImmutable($rule['lastModified']) : null,
  119. 'identifier' => $rule['identifier'] ?? null,
  120. 'values' => $rule['value'] ? json_decode($rule['value'], true) : [],
  121. ]);
  122. $nested[] = $object;
  123. continue;
  124. }
  125. if ($rule['value'] !== null) {
  126. $object->assign(json_decode($rule['value'], true));
  127. }
  128. if ($object instanceof ContainerInterface) {
  129. $children = $this->buildNested($rules, $rule['id']);
  130. foreach ($children as $child) {
  131. $object->addRule($child);
  132. }
  133. }
  134. $nested[] = $object;
  135. }
  136. return $nested;
  137. }
  138. }