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

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