src/Core/Content/Rule/DataAbstractionLayer/RulePayloadUpdater.php line 93

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