Magento1CustomerReader.php 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. <?php
  2. namespace App\Services\Asteria;
  3. use Illuminate\Support\Collection;
  4. use Illuminate\Support\Facades\DB;
  5. class Magento1CustomerReader
  6. {
  7. private const CUSTOMER_CODES = [
  8. 'firstname',
  9. 'lastname',
  10. 'dob',
  11. 'gender',
  12. 'telephone',
  13. 'password_hash',
  14. 'default_billing',
  15. 'default_shipping',
  16. ];
  17. private const ADDRESS_CODES = [
  18. 'firstname',
  19. 'lastname',
  20. 'company',
  21. 'street',
  22. 'city',
  23. 'region',
  24. 'postcode',
  25. 'country_id',
  26. 'telephone',
  27. ];
  28. private Magento1Schema $schema;
  29. public function __construct(private string $connection = 'asteria')
  30. {
  31. $this->schema = new Magento1Schema($connection);
  32. }
  33. /**
  34. * @return Collection<int, array<string, mixed>>
  35. */
  36. public function fetchCustomers(int $afterEntityId, int $limit): Collection
  37. {
  38. $query = DB::connection($this->connection)
  39. ->table('customer_entity')
  40. ->where('entity_id', '>', $afterEntityId)
  41. ->orderBy('entity_id')
  42. ->limit($limit);
  43. $staticColumns = $this->existingColumns('customer_entity', array_merge(
  44. ['entity_id', 'email', 'is_active', 'created_at', 'updated_at', 'group_id'],
  45. self::CUSTOMER_CODES
  46. ));
  47. $rows = $query->get($staticColumns);
  48. if ($rows->isEmpty()) {
  49. return collect();
  50. }
  51. $eav = $this->loadEavValues(
  52. 'customer',
  53. 'customer_entity',
  54. $rows->pluck('entity_id')->all(),
  55. self::CUSTOMER_CODES
  56. );
  57. return $rows->map(function ($row) use ($eav) {
  58. $id = (int) $row->entity_id;
  59. $merged = array_merge($eav[$id] ?? [], (array) $row);
  60. $merged['entity_id'] = $id;
  61. return $merged;
  62. })->values();
  63. }
  64. /**
  65. * @param array<int, int> $customerEntityIds
  66. * @return Collection<int, array<string, mixed>>
  67. */
  68. public function fetchAddresses(array $customerEntityIds): Collection
  69. {
  70. if ($customerEntityIds === []) {
  71. return collect();
  72. }
  73. $staticColumns = $this->existingColumns('customer_address_entity', array_merge(
  74. ['entity_id', 'parent_id', 'created_at', 'is_active'],
  75. self::ADDRESS_CODES
  76. ));
  77. $rows = DB::connection($this->connection)
  78. ->table('customer_address_entity')
  79. ->whereIn('parent_id', $customerEntityIds)
  80. ->orderBy('entity_id')
  81. ->get($staticColumns);
  82. if ($rows->isEmpty()) {
  83. return collect();
  84. }
  85. $eav = $this->loadEavValues(
  86. 'customer_address',
  87. 'customer_address_entity',
  88. $rows->pluck('entity_id')->all(),
  89. self::ADDRESS_CODES
  90. );
  91. return $rows->map(function ($row) use ($eav) {
  92. $id = (int) $row->entity_id;
  93. $merged = array_merge($eav[$id] ?? [], (array) $row);
  94. $merged['entity_id'] = $id;
  95. $merged['parent_id'] = (int) $row->parent_id;
  96. return $merged;
  97. })->values();
  98. }
  99. /**
  100. * @param array<int, string> $codes
  101. * @return array<int, array<string, mixed>>
  102. */
  103. private function loadEavValues(
  104. string $entityTypeCode,
  105. string $valueTablePrefix,
  106. array $entityIds,
  107. array $codes
  108. ): array {
  109. if ($entityIds === []) {
  110. return [];
  111. }
  112. $attributes = $this->attributeMap($entityTypeCode, $codes);
  113. if ($attributes === []) {
  114. return [];
  115. }
  116. $byType = [];
  117. foreach ($attributes as $code => $attribute) {
  118. $type = $attribute->backend_type ?? 'varchar';
  119. if ($type === 'static' || $type === '') {
  120. continue;
  121. }
  122. $byType[$type][(int) $attribute->attribute_id] = $code;
  123. }
  124. $values = [];
  125. foreach ($byType as $type => $idToCode) {
  126. $table = $valueTablePrefix.'_'.$type;
  127. if (! $this->schema->hasTable($table)) {
  128. continue;
  129. }
  130. $rows = DB::connection($this->connection)
  131. ->table($table)
  132. ->select('entity_id', 'attribute_id', 'value')
  133. ->whereIn('entity_id', $entityIds)
  134. ->whereIn('attribute_id', array_keys($idToCode))
  135. ->get();
  136. foreach ($rows as $row) {
  137. $code = $idToCode[(int) $row->attribute_id] ?? null;
  138. if ($code === null || $row->value === null || $row->value === '') {
  139. continue;
  140. }
  141. $values[(int) $row->entity_id][$code] = $row->value;
  142. }
  143. }
  144. return $values;
  145. }
  146. /**
  147. * @param array<int, string> $codes
  148. * @return array<string, object>
  149. */
  150. private function attributeMap(string $entityTypeCode, array $codes): array
  151. {
  152. $query = DB::connection($this->connection)
  153. ->table('eav_attribute')
  154. ->select('attribute_id', 'attribute_code', 'backend_type')
  155. ->whereIn('attribute_code', $codes);
  156. if ($this->schema->hasTable('eav_entity_type')) {
  157. $typeId = DB::connection($this->connection)
  158. ->table('eav_entity_type')
  159. ->where('entity_type_code', $entityTypeCode)
  160. ->value('entity_type_id');
  161. if ($typeId) {
  162. $query->where('entity_type_id', $typeId);
  163. }
  164. }
  165. return $query->get()->keyBy('attribute_code')->all();
  166. }
  167. /**
  168. * @param array<int, string> $candidates
  169. * @return array<int, string>
  170. */
  171. private function existingColumns(string $table, array $candidates): array
  172. {
  173. return $this->schema->existingColumns($table, $candidates);
  174. }
  175. }