Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
OrdersFixture.php
Go to the documentation of this file.
1 <?php
7 namespace Magento\Setup\Fixtures;
8 
11 
45 class OrdersFixture extends Fixture
46 {
52  const BATCH_SIZE = 1000;
53 
59  const BIG_CONFIGURABLE_TYPE = 'big_configurable';
60 
67 
74 
81 
88 
95 
102 
108  protected $priority = 135;
109 
115  private $queryTemplates;
116 
122  private $resourceConnections;
123 
127  private $storeManager;
128 
132  private $productCollectionFactory;
133 
137  private $productRepository;
138 
142  private $optionRepository;
143 
147  private $linkManagement;
148 
152  private $serializer;
153 
159  private $orderQuotesEnable = true;
160 
170  public function __construct(
171  \Magento\Store\Model\StoreManagerInterface $storeManager,
172  \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory,
173  \Magento\Catalog\Api\ProductRepositoryInterface $productRepository,
174  \Magento\ConfigurableProduct\Api\OptionRepositoryInterface $optionRepository,
175  \Magento\ConfigurableProduct\Api\LinkManagementInterface $linkManagement,
176  \Magento\Framework\Serialize\SerializerInterface $serializer,
178  ) {
179  $this->storeManager = $storeManager;
180  $this->productCollectionFactory = $productCollectionFactory;
181  $this->productRepository = $productRepository;
182  $this->optionRepository = $optionRepository;
183  $this->linkManagement = $linkManagement;
184  $this->serializer = $serializer;
185  parent::__construct($fixtureModel);
186  }
187 
197  public function execute()
198  {
199  $orderSimpleCountFrom = (int)$this->fixtureModel->getValue(
200  'order_simple_product_count_from',
201  self::ORDER_SIMPLE_PRODUCT_COUNT_FROM
202  );
203  $orderSimpleCountTo = (int)$this->fixtureModel->getValue(
204  'order_simple_product_count_to',
205  self::ORDER_SIMPLE_PRODUCT_COUNT_TO
206  );
207  $orderConfigurableCountFrom = (int)$this->fixtureModel->getValue(
208  'order_configurable_product_count_from',
209  self::ORDER_CONFIGURABLE_PRODUCT_COUNT_FROM
210  );
211  $orderConfigurableCountTo = (int)$this->fixtureModel->getValue(
212  'order_configurable_product_count_to',
213  self::ORDER_CONFIGURABLE_PRODUCT_COUNT_TO
214  );
215  $orderBigConfigurableCountFrom = (int)$this->fixtureModel->getValue(
216  'order_big_configurable_product_count_from',
217  self::ORDER_BIG_CONFIGURABLE_PRODUCT_COUNT_FROM
218  );
219  $orderBigConfigurableCountTo = (int)$this->fixtureModel->getValue(
220  'order_big_configurable_product_count_to',
221  self::ORDER_BIG_CONFIGURABLE_PRODUCT_COUNT_TO
222  );
223  $this->orderQuotesEnable = (bool)$this->fixtureModel->getValue('order_quotes_enable', true);
224 
225  $entityId = $this->getMaxEntityId(
226  'sales_order',
227  \Magento\Sales\Model\ResourceModel\Order::class,
228  'entity_id'
229  );
230  $requestedOrders = (int)$this->fixtureModel->getValue('orders', 0);
231  if ($requestedOrders - $entityId < 1) {
232  return;
233  }
234 
235  $ruleId = $this->getMaxEntityId(
236  'salesrule',
237  \Magento\SalesRule\Model\ResourceModel\Rule::class,
238  'rule_id'
239  );
240 
241  $maxItemId = $this->getMaxEntityId(
242  'sales_order_item',
243  \Magento\Sales\Model\ResourceModel\Order\Item::class,
244  'item_id'
245  );
246  $maxItemsPerOrder = $orderSimpleCountTo + ($orderConfigurableCountTo + $orderBigConfigurableCountTo) * 2;
247 
249  $itemIdSequence = $this->getItemIdSequence($maxItemId, $requestedOrders, $maxItemsPerOrder);
250 
251  $this->prepareQueryTemplates();
252 
253  $result = [];
254  foreach ($this->storeManager->getStores() as $store) {
255  $productsResult = [];
256  $this->storeManager->setCurrentStore($store->getId());
257 
258  if ($orderSimpleCountTo > 0) {
259  $productsResult[Type::TYPE_SIMPLE] = $this->prepareSimpleProducts(
260  $this->getProductIds($store, Type::TYPE_SIMPLE, $orderSimpleCountTo)
261  );
262  }
263  if ($orderConfigurableCountTo > 0) {
264  $productsResult[Configurable::TYPE_CODE] = $this->prepareConfigurableProducts(
265  $this->getProductIds($store, Configurable::TYPE_CODE, $orderConfigurableCountTo)
266  );
267  }
268  if ($orderBigConfigurableCountTo > 0) {
269  $productsResult[self::BIG_CONFIGURABLE_TYPE] = $this->prepareConfigurableProducts(
270  $this->getProductIds($store, self::BIG_CONFIGURABLE_TYPE, $orderBigConfigurableCountTo)
271  );
272  }
273 
274  $result[] = [
275  $store->getId(),
276  implode(PHP_EOL, [
277  $this->storeManager->getWebsite($store->getWebsiteId())->getName(),
278  $this->storeManager->getGroup($store->getStoreGroupId())->getName(),
279  $store->getName()
280  ]),
281  $productsResult
282  ];
283  }
284 
285  $productStoreId = function ($index) use ($result) {
286  return $result[$index % count($result)][0];
287  };
288  $productStoreName = function ($index) use ($result) {
289  return $result[$index % count($result)][1];
290  };
291  $productId = function ($entityId, $index, $type) use ($result) {
292  return $result[$entityId % count($result)][2][$type][$index]['id'];
293  };
294  $productSku = function ($entityId, $index, $type) use ($result) {
295  return $result[$entityId % count($result)][2][$type][$index]['sku'];
296  };
297  $productName = function ($entityId, $index, $type) use ($result) {
298  return $result[$entityId % count($result)][2][$type][$index]['name'];
299  };
300  $productBuyRequest = function ($entityId, $index, $type) use ($result) {
301  return $result[$entityId % count($result)][2][$type][$index]['buyRequest'];
302  };
303  $productChildBuyRequest = function ($entityId, $index, $type) use ($result) {
304  return $result[$entityId % count($result)][2][$type][$index]['childBuyRequest'];
305  };
306  $productChildId = function ($entityId, $index, $type) use ($result) {
307  return $result[$entityId % count($result)][2][$type][$index]['childId'];
308  };
309 
310  $address = [
311  '%firstName%' => 'First Name',
312  '%lastName%' => 'Last Name',
313  '%company%' => 'Company',
314  '%address%' => 'Address',
315  '%city%' => 'city',
316  '%state%' => 'Alabama',
317  '%country%' => 'US',
318  '%zip%' => '11111',
319  '%phone%' => '911'
320  ];
321 
322  $batchNumber = 0;
323  $entityId++;
324  while ($entityId <= $requestedOrders) {
325  $batchNumber++;
326  $productCount = [
327  Type::TYPE_SIMPLE => mt_rand($orderSimpleCountFrom, $orderSimpleCountTo),
328  Configurable::TYPE_CODE => mt_rand($orderConfigurableCountFrom, $orderConfigurableCountTo),
329  self::BIG_CONFIGURABLE_TYPE => mt_rand($orderBigConfigurableCountFrom, $orderBigConfigurableCountTo)
330  ];
331  $order = [
332  '%itemsPerOrder%' => array_sum($productCount),
333  '%orderNumber%' => 100000000 * $productStoreId($entityId) + $entityId,
334  '%email%' => "order_{$entityId}@example.com",
335  '%time%' => date(\Magento\Framework\Stdlib\DateTime::DATETIME_PHP_FORMAT),
336  '%productStoreId%' => $productStoreId($entityId),
337  '%productStoreName%' => $productStoreName($entityId),
338  '%entityId%' => $entityId,
339  '%ruleId%' => $ruleId,
340  ];
341  $shippingAddress = ['%orderAddressId%' => $entityId * 2 - 1, '%addressType%' => 'shipping'];
342  $billingAddress = ['%orderAddressId%' => $entityId * 2, '%addressType%' => 'billing'];
343 
344  try {
345  $this->query('quote', $order);
346  $this->query('quote_address', $order, $address, $shippingAddress);
347  $this->query('quote_address', $order, $address, $billingAddress);
348  $this->query('quote_payment', $order);
349  $this->query('quote_shipping_rate', $order, $shippingAddress);
350 
351  $this->query('eav_entity_store', $order);
352  $this->query('sales_order', $order);
353  $this->query('sales_order_address', $order, $address, $shippingAddress);
354  $this->query('sales_order_address', $order, $address, $billingAddress);
355  $this->query('sales_order_grid', $order);
356  $this->query('sales_order_payment', $order);
357  $this->query('sales_order_status_history', $order);
358 
359  for ($i = 0; $i < $productCount[Type::TYPE_SIMPLE]; $i++) {
360  $itemData = [
361  '%productId%' => $productId($entityId, $i, Type::TYPE_SIMPLE),
362  '%sku%' => $productSku($entityId, $i, Type::TYPE_SIMPLE),
363  '%name%' => $productName($entityId, $i, Type::TYPE_SIMPLE),
364  '%itemId%' => $itemIdSequence->current(),
365  '%productType%' => Type::TYPE_SIMPLE,
366  '%productOptions%' => $productBuyRequest($entityId, $i, Type::TYPE_SIMPLE),
367  '%parentItemId%' => 'null',
368  ];
369  $this->query('sales_order_item', $order, $itemData);
370  $this->query('quote_item', $order, $itemData);
371  $this->query('quote_item_option', $order, $itemData, [
372  '%code%' => 'info_buyRequest',
373  '%value%' => $this->serializer->serialize([
374  'product' => $productId($entityId, $i, Type::TYPE_SIMPLE),
375  'qty' => "1",
376  'uenc' => 'aHR0cDovL21hZ2UyLmNvbS9jYXRlZ29yeS0xLmh0bWw'
377  ])
378  ]);
379  $itemIdSequence->next();
380  }
381 
382  foreach ([Configurable::TYPE_CODE, self::BIG_CONFIGURABLE_TYPE] as $type) {
383  for ($i = 0; $i < $productCount[$type]; $i++) {
384  // Generate parent item
385  $parentItemId = $itemIdSequence->current();
386  $itemData = [
387  '%productId%' => $productId($entityId, $i, $type),
388  '%sku%' => $productSku($entityId, $i, $type),
389  '%name%' => $productName($entityId, $i, $type),
390  '%productOptions%' => $productBuyRequest($entityId, $i, $type)['order'],
391  '%itemId%' => $parentItemId,
392  '%parentItemId%' => 'null',
393  '%productType%' => Configurable::TYPE_CODE
394  ];
395  $this->query('sales_order_item', $order, $itemData);
396  $this->query('quote_item', $order, $itemData);
397  $this->query('quote_item_option', $order, $itemData, [
398  '%code%' => 'info_buyRequest',
399  '%value%' => $productBuyRequest($entityId, $i, $type)['quote']
400  ]);
401  $this->query('quote_item_option', $order, $itemData, [
402  '%code%' => 'attributes',
403  '%value%' => $productBuyRequest($entityId, $i, $type)['super_attribute']
404  ]);
405  $itemData['%productId%'] = $productChildId($entityId, $i, $type);
406  $this->query('quote_item_option', $itemData, [
407  '%code%' => "product_qty_" . $productChildId($entityId, $i, $type),
408  '%value%' => "1"
409  ]);
410  $this->query('quote_item_option', $itemData, [
411  '%code%' => "simple_product",
412  '%value%' => $productChildId($entityId, $i, $type)
413  ]);
414  $itemIdSequence->next();
415 
416  // Generate child item
417  $itemData = [
418  '%productId%' => $productChildId($entityId, $i, $type),
419  '%sku%' => $productSku($entityId, $i, $type),
420  '%name%' => $productName($entityId, $i, $type),
421  '%productOptions%' => $productChildBuyRequest($entityId, $i, $type)['order'],
422  '%itemId%' => $itemIdSequence->current(),
423  '%parentItemId%' => $parentItemId,
424  '%productType%' => Type::TYPE_SIMPLE
425  ];
426 
427  $this->query('sales_order_item', $order, $itemData);
428  $this->query('quote_item', $order, $itemData);
429  $this->query('quote_item_option', $itemData, [
430  '%code%' => "info_buyRequest",
431  '%value%' => $productChildBuyRequest($entityId, $i, $type)['quote']
432  ]);
433  $this->query('quote_item_option', $itemData, [
434  '%code%' => "parent_product_id",
435  '%value%' => $productId($entityId, $i, $type)
436  ]);
437  $itemIdSequence->next();
438  }
439  }
440  } catch (\Exception $lastException) {
441  foreach ($this->resourceConnections as $connection) {
442  if ($connection->getTransactionLevel() > 0) {
443  $connection->rollBack();
444  }
445  }
446  throw $lastException;
447  }
448 
449  if ($batchNumber >= self::BATCH_SIZE) {
450  $this->commitBatch();
451  $batchNumber = 0;
452  }
453  $entityId++;
454  }
455 
456  foreach ($this->resourceConnections as $connection) {
457  if ($connection->getTransactionLevel() > 0) {
458  $connection->commit();
459  }
460  }
461  }
462 
473  private function prepareQueryTemplates()
474  {
475  $fileName = __DIR__ . DIRECTORY_SEPARATOR . "_files" . DIRECTORY_SEPARATOR . "orders_fixture_data.json";
476  $templateData = json_decode(file_get_contents(realpath($fileName)), true);
477  foreach ($templateData as $table => $template) {
478  if (isset($template['_table'])) {
479  $table = $template['_table'];
480  unset($template['_table']);
481  }
482  if (isset($template['_resource'])) {
483  $resource = $template['_resource'];
484  unset($template['_resource']);
485  } else {
486  $resource = explode("_", $table);
487  foreach ($resource as &$item) {
488  $item = ucfirst($item);
489  }
490  $resource = "Magento\\"
491  . array_shift($resource)
492  . "\\Model\\ResourceModel\\"
493  . implode("\\", $resource);
494  }
495 
496  $tableName = $this->getTableName($table, $resource);
497 
498  $querySuffix = "";
499  if (isset($template['_query_suffix'])) {
500  $querySuffix = $template['_query_suffix'];
501  unset($template['_query_suffix']);
502  }
503 
504  $fields = implode(', ', array_keys($template));
505  $values = implode(', ', array_values($template));
506 
508  $resourceModel = $this->fixtureModel->getObjectManager()->get($resource);
509  $connection = $resourceModel->getConnection();
510  if ($connection->getTransactionLevel() == 0) {
511  $connection->beginTransaction();
512  }
513 
514  $this->queryTemplates[$table] = "INSERT INTO `{$tableName}` ({$fields}) VALUES ({$values}){$querySuffix};";
515  $this->resourceConnections[$table] = $connection;
516  }
517  }
518 
529  protected function query($table, ... $replacements)
530  {
531  if (!$this->orderQuotesEnable && strpos($table, "quote") !== false) {
532  return;
533  }
534  $query = $this->queryTemplates[$table];
535  foreach ($replacements as $data) {
536  $query = str_replace(array_keys($data), array_values($data), $query);
537  }
538 
539  $this->resourceConnections[$table]->query($query);
540  }
541 
553  private function getMaxEntityId($tableName, $resourceName, $column = 'entity_id')
554  {
555  $tableName = $this->getTableName(
556  $tableName,
557  $resourceName
558  );
559 
561  $resource = $this->fixtureModel->getObjectManager()->get($resourceName);
562  $connection = $resource->getConnection();
563  return (int)$connection->query("SELECT MAX(`{$column}`) FROM `{$tableName}`;")->fetchColumn(0);
564  }
565 
575  private function getProductIds(\Magento\Store\Api\Data\StoreInterface $store, $typeId, $limit = null)
576  {
578  $productCollection = $this->productCollectionFactory->create();
579 
580  $productCollection->addStoreFilter($store->getId());
581  $productCollection->addWebsiteFilter($store->getWebsiteId());
582 
583  // "Big%" should be replaced with a configurable value.
584  if ($typeId === self::BIG_CONFIGURABLE_TYPE) {
585  $productCollection->getSelect()->where(" type_id = '" . Configurable::TYPE_CODE . "' ");
586  $productCollection->getSelect()->where(" sku LIKE 'Big%' ");
587  } else {
588  $productCollection->getSelect()->where(" type_id = '$typeId' ");
589  $productCollection->getSelect()->where(" sku NOT LIKE 'Big%' ");
590  }
591  $ids = $productCollection->getAllIds($limit);
592  if ($limit && count($ids) < $limit) {
593  throw new \Exception('Not enough products of type: ' . $typeId);
594  }
595  return $ids;
596  }
597 
606  private function prepareSimpleProducts(array $productIds = [])
607  {
608  $productsResult = [];
609 
610  foreach ($productIds as $key => $simpleId) {
611  $simpleProduct = $this->productRepository->getById($simpleId);
612  $productsResult[$key]['id'] = $simpleId;
613  $productsResult[$key]['sku'] = $simpleProduct->getSku();
614  $productsResult[$key]['name'] = $simpleProduct->getName();
615  $productsResult[$key]['buyRequest'] = $this->serializer->serialize([
616  "info_buyRequest" => [
617  "uenc" => "aHR0cDovL21hZ2VudG8uZGV2L2NvbmZpZ3VyYWJsZS1wcm9kdWN0LTEuaHRtbA,,",
618  "product" => $simpleId,
619  "qty" => "1"
620  ]
621  ]);
622  }
623  return $productsResult;
624  }
625 
634  private function prepareConfigurableProducts(array $productIds = [])
635  {
636  $productsResult = [];
637  foreach ($productIds as $key => $configurableId) {
638  $configurableProduct = $this->productRepository->getById($configurableId);
639  $options = $this->optionRepository->getList($configurableProduct->getSku());
640  $configurableChild = $configurableProduct->getTypeInstance()->getUsedProducts($configurableProduct);
641  $configurableChild = reset($configurableChild);
642  $simpleSku = $configurableChild->getSku();
643  $simpleId = $this->productRepository->get($simpleSku)->getId();
644 
645  $attributesInfo = [];
646  $superAttribute = [];
647  foreach ($options as $option) {
648  $attributesInfo[] = [
649  "label" => $option->getLabel(),
650  "value" => $option['options']['0']['label'],
651  "option_id" => $option->getAttributeId(),
652  "option_value" => $option->getValues()[0]->getValueIndex()
653  ];
654  $superAttribute[$option->getAttributeId()] = $option->getValues()[0]->getValueIndex();
655  }
656 
657  $configurableBuyRequest = [
658  "info_buyRequest" => [
659  "uenc" => "aHR0cDovL21hZ2UyLmNvbS9jYXRlZ29yeS0xLmh0bWw",
660  "product" => $configurableId,
661  "selected_configurable_option" => $simpleId,
662  "related_product" => "",
663  "super_attribute" => $superAttribute,
664  "qty" => 1
665  ],
666  "attributes_info" => $attributesInfo,
667  "simple_name" => $configurableChild->getName(),
668  "simple_sku" => $configurableChild->getSku(),
669  ];
670  $simpleBuyRequest = [
671  "info_buyRequest" => [
672  "uenc" => "aHR0cDovL21hZ2VudG8uZGV2L2NvbmZpZ3VyYWJsZS1wcm9kdWN0LTEuaHRtbA,,",
673  "product" => $configurableId,
674  "selected_configurable_option" => $simpleId,
675  "related_product" => "",
676  "super_attribute" => $superAttribute,
677  "qty" => "1"
678  ]
679  ];
680 
681  $quoteConfigurableBuyRequest = $configurableBuyRequest['info_buyRequest'];
682  $quoteSimpleBuyRequest = $simpleBuyRequest['info_buyRequest'];
683  unset($quoteConfigurableBuyRequest['selected_configurable_option']);
684  unset($quoteSimpleBuyRequest['selected_configurable_option']);
685 
686  $productsResult[$key]['id'] = $configurableId;
687  $productsResult[$key]['sku'] = $simpleSku;
688  $productsResult[$key]['name'] = $configurableProduct->getName();
689  $productsResult[$key]['childId'] = $simpleId;
690  $productsResult[$key]['buyRequest'] = [
691  'order' => $this->serializer->serialize($configurableBuyRequest),
692  'quote' => $this->serializer->serialize($quoteConfigurableBuyRequest),
693  'super_attribute' => $this->serializer->serialize($superAttribute)
694  ];
695  $productsResult[$key]['childBuyRequest'] = [
696  'order' => $this->serializer->serialize($simpleBuyRequest),
697  'quote' => $this->serializer->serialize($quoteSimpleBuyRequest),
698  ];
699  }
700  return $productsResult;
701  }
702 
710  private function commitBatch()
711  {
712  foreach ($this->resourceConnections as $connection) {
713  if ($connection->getTransactionLevel() > 0) {
714  $connection->commit();
715  $connection->beginTransaction();
716  }
717  }
718  }
719 
723  public function getActionTitle()
724  {
725  return 'Generating orders';
726  }
727 
731  public function introduceParamLabels()
732  {
733  return [
734  'orders' => 'Orders'
735  ];
736  }
737 
746  public function getTableName($tableName, $resourceName)
747  {
749  $resource = $this->fixtureModel->getObjectManager()->get($resourceName);
750  return $resource->getConnection()->getTableName($resource->getTable($tableName));
751  }
752 
761  private function getItemIdSequence($maxItemId, $requestedOrders, $maxItemsPerOrder)
762  {
763  $requestedItems = $requestedOrders * $maxItemsPerOrder;
764  for ($i = $maxItemId + 1; $i <= $requestedItems; $i++) {
765  yield $i;
766  }
767  }
768 }
$tableName
Definition: trigger.php:13
$billingAddress
Definition: order.php:25
defined('TESTS_BP')||define('TESTS_BP' __DIR__
Definition: _bootstrap.php:60
$shippingAddress
Definition: order.php:40
$order
Definition: order.php:55
$fields
Definition: details.phtml:14
$storeManager
$values
Definition: options.phtml:88
$resource
Definition: bulk.php:12
$address
Definition: customer.php:38
$type
Definition: item.phtml:13
$fileName
Definition: translate.phtml:15
$resourceModel
Definition: tablerates.php:10
$simpleId
query($table,... $replacements)
$connection
Definition: bulk.php:13
$table
Definition: trigger.php:14
$i
Definition: gallery.phtml:31
$index
Definition: list.phtml:44
$templateData
$template
Definition: export.php:12
__construct(\Magento\Store\Model\StoreManagerInterface $storeManager, \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory, \Magento\Catalog\Api\ProductRepositoryInterface $productRepository, \Magento\ConfigurableProduct\Api\OptionRepositoryInterface $optionRepository, \Magento\ConfigurableProduct\Api\LinkManagementInterface $linkManagement, \Magento\Framework\Serialize\SerializerInterface $serializer, FixtureModel $fixtureModel)