vendor/shopware/elasticsearch/Product/CustomFieldUpdater.php line 43

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Elasticsearch\Product;
  3. use Elasticsearch\Client;
  4. use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityWrittenContainerEvent;
  5. use Shopware\Core\Framework\Log\Package;
  6. use Shopware\Core\System\CustomField\CustomFieldDefinition;
  7. use Shopware\Core\System\CustomField\CustomFieldTypes;
  8. use Shopware\Elasticsearch\Framework\ElasticsearchHelper;
  9. use Shopware\Elasticsearch\Framework\ElasticsearchOutdatedIndexDetector;
  10. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  11. /**
  12. * @deprecated tag:v6.5.0 - reason:becomes-internal - EventSubscribers will become internal in v6.5.0
  13. */
  14. #[Package('core')]
  15. class CustomFieldUpdater implements EventSubscriberInterface
  16. {
  17. private ElasticsearchOutdatedIndexDetector $indexDetector;
  18. private Client $client;
  19. private ElasticsearchHelper $elasticsearchHelper;
  20. /**
  21. * @internal
  22. */
  23. public function __construct(ElasticsearchOutdatedIndexDetector $indexDetector, Client $client, ElasticsearchHelper $elasticsearchHelper)
  24. {
  25. $this->indexDetector = $indexDetector;
  26. $this->client = $client;
  27. $this->elasticsearchHelper = $elasticsearchHelper;
  28. }
  29. public static function getSubscribedEvents(): array
  30. {
  31. return [
  32. EntityWrittenContainerEvent::class => 'onNewCustomFieldCreated',
  33. ];
  34. }
  35. public function onNewCustomFieldCreated(EntityWrittenContainerEvent $containerEvent): void
  36. {
  37. $event = $containerEvent->getEventByEntityName(CustomFieldDefinition::ENTITY_NAME);
  38. if ($event === null) {
  39. return;
  40. }
  41. if (!$this->elasticsearchHelper->allowIndexing()) {
  42. return;
  43. }
  44. $newCreatedFields = [];
  45. foreach ($event->getWriteResults() as $writeResult) {
  46. $existence = $writeResult->getExistence();
  47. if ($existence && $existence->exists()) {
  48. continue;
  49. }
  50. /** @var array<mixed> $esType */
  51. $esType = self::getTypeFromCustomFieldType($writeResult->getProperty('type'));
  52. $newCreatedFields[(string) $writeResult->getProperty('name')] = $esType;
  53. }
  54. if (\count($newCreatedFields) === 0) {
  55. return;
  56. }
  57. $this->createNewFieldsInIndices($newCreatedFields);
  58. }
  59. /**
  60. * @deprecated tag:v6.5.0 - Return type will be changed to not nullable - reason:return-type-change
  61. *
  62. * @return array<mixed>|null
  63. */
  64. public static function getTypeFromCustomFieldType(string $type): ?array
  65. {
  66. switch ($type) {
  67. case CustomFieldTypes::INT:
  68. return [
  69. 'type' => 'long',
  70. ];
  71. case CustomFieldTypes::FLOAT:
  72. return [
  73. 'type' => 'double',
  74. ];
  75. case CustomFieldTypes::BOOL:
  76. return [
  77. 'type' => 'boolean',
  78. ];
  79. case CustomFieldTypes::DATETIME:
  80. return [
  81. 'type' => 'date',
  82. 'format' => 'yyyy-MM-dd HH:mm:ss.000',
  83. 'ignore_malformed' => true,
  84. ];
  85. case CustomFieldTypes::PRICE:
  86. case CustomFieldTypes::JSON:
  87. return [
  88. 'type' => 'object',
  89. 'dynamic' => true,
  90. ];
  91. case CustomFieldTypes::HTML:
  92. case CustomFieldTypes::TEXT:
  93. return [
  94. 'type' => 'text',
  95. ];
  96. case CustomFieldTypes::COLORPICKER:
  97. case CustomFieldTypes::ENTITY:
  98. case CustomFieldTypes::MEDIA:
  99. case CustomFieldTypes::SELECT:
  100. case CustomFieldTypes::SWITCH:
  101. default:
  102. return [
  103. 'type' => 'keyword',
  104. ];
  105. }
  106. }
  107. /**
  108. * @param array<string, array<mixed>> $newCreatedFields
  109. */
  110. private function createNewFieldsInIndices(array $newCreatedFields): void
  111. {
  112. $indices = $this->indexDetector->getAllUsedIndices();
  113. foreach ($indices as $indexName) {
  114. $body = [
  115. 'properties' => [
  116. 'customFields' => [
  117. 'properties' => $newCreatedFields,
  118. ],
  119. ],
  120. ];
  121. // For some reason, we need to include the includes to prevent merge conflicts.
  122. // This error can happen for example after updating from version <6.4.
  123. $current = $this->client->indices()->get(['index' => $indexName]);
  124. $includes = $current[$indexName]['mappings']['_source']['includes'] ?? [];
  125. if ($includes !== []) {
  126. $body['_source'] = [
  127. 'includes' => $includes,
  128. ];
  129. }
  130. $this->client->indices()->putMapping([
  131. 'index' => $indexName,
  132. 'body' => $body,
  133. ]);
  134. }
  135. }
  136. }