From 4f38525b6f05a3c54f8e887afd9b18e213a5115f Mon Sep 17 00:00:00 2001
From: Mikalai_Shostka <Mikalai_Shostka@epam.com>
Date: Fri, 9 Dec 2016 14:36:23 +0300
Subject: [PATCH] MAGETWO-61556: Fixture generation (Configurables, Attribute
 Set)

---
 .../Setup/Fixtures/AttributeSetsFixture.php   | 111 +++-
 .../Fixtures/ConfigurableProductsFixture.php  | 474 ++++++++++++------
 .../Setup/Fixtures/SimpleProductsFixture.php  |  12 +-
 .../Fixtures/AttributeSetsFixtureTest.php     |   4 +-
 4 files changed, 413 insertions(+), 188 deletions(-)

diff --git a/setup/src/Magento/Setup/Fixtures/AttributeSetsFixture.php b/setup/src/Magento/Setup/Fixtures/AttributeSetsFixture.php
index 5617eca9a0e..1bdffb253ae 100644
--- a/setup/src/Magento/Setup/Fixtures/AttributeSetsFixture.php
+++ b/setup/src/Magento/Setup/Fixtures/AttributeSetsFixture.php
@@ -16,12 +16,27 @@ class AttributeSetsFixture extends Fixture
      */
     protected $priority = 25;
 
+    /**
+     * Cache for attribute IDs.
+     *
+     * @var array
+     */
+    private $attributeIdsCache = [];
+
+    /**
+     * Quantity of unique attributes to generate. Zero means infinity.
+     *
+     * @var int
+     */
+    protected $uniqueAttributesQuantity = 0;
+
     /**
      * {@inheritdoc}
      */
     public function execute()
     {
-        $attributeSets = $this->fixtureModel->getValue('attribute_sets', null);
+        $this->populateUniqueAttributesQuantity();
+        $attributeSets = $this->getAttributeSetsFixtureValue();
         if ($attributeSets === null) {
             return;
         }
@@ -71,37 +86,81 @@ class AttributeSetsFixture extends Fixture
             $attributesData = array_key_exists(0, $attributeSetData['attributes']['attribute'])
                 ? $attributeSetData['attributes']['attribute'] : [$attributeSetData['attributes']['attribute']];
             foreach ($attributesData as $attributeData) {
-                //Create Attribute
-                /** @var  \Magento\Catalog\Api\Data\ProductAttributeInterfaceFactory $attributeFactory */
-                $attributeFactory = $this->fixtureModel->getObjectManager()->create(
-                    \Magento\Catalog\Api\Data\ProductAttributeInterfaceFactory::class
-                );
-
-                $optionsData = array_key_exists(0, $attributeData['options']['option'])
-                    ? $attributeData['options']['option'] : [$attributeData['options']['option']];
-                $options = [];
-                foreach ($optionsData as $optionData) {
-                    /** @var \Magento\Eav\Api\Data\AttributeOptionInterfaceFactory $optionFactory */
-                    $optionFactory = $this->fixtureModel->getObjectManager()->create(
-                        \Magento\Eav\Api\Data\AttributeOptionInterfaceFactory::class
+                if ($this->uniqueAttributesQuantity === 0
+                    || (count($this->attributeIdsCache) < $this->uniqueAttributesQuantity)) {
+                    //Create Attribute
+                    /** @var  \Magento\Catalog\Api\Data\ProductAttributeInterfaceFactory $attributeFactory */
+                    $attributeFactory = $this->fixtureModel->getObjectManager()->create(
+                        \Magento\Catalog\Api\Data\ProductAttributeInterfaceFactory::class
                     );
-                    $option = $optionFactory->create(['data' => $optionData]);
-                    $options[] = $option;
-                }
 
-                $attribute = $attributeFactory->create(['data' => $attributeData]);
-                $attribute->setOptions($options);
+                    $optionsData = array_key_exists(0, $attributeData['options']['option'])
+                        ? $attributeData['options']['option'] : [$attributeData['options']['option']];
+                    $options = [];
+                    foreach ($optionsData as $optionData) {
+                        /** @var \Magento\Eav\Api\Data\AttributeOptionInterfaceFactory $optionFactory */
+                        $optionFactory = $this->fixtureModel->getObjectManager()->create(
+                            \Magento\Eav\Api\Data\AttributeOptionInterfaceFactory::class
+                        );
+                        $option = $optionFactory->create(['data' => $optionData]);
+                        $options[] = $option;
+                    }
 
-                $result = $attributeRepository->save($attribute);
-                $attributeId = $result->getAttributeId();
+                    $attribute = $attributeFactory->create(['data' => $attributeData]);
+                    $attribute->setOptions($options);
 
+                    $result = $attributeRepository->save($attribute);
+                    $attributeId = $result->getAttributeId();
+                    $this->fillAttributeIdsCache($attributeId);
+                } else {
+                    $attributeId = $this->getAttributeIdFromCache();
+                }
                 //Associate Attribute to Attribute Set
                 $sortOrder = 3;
+
                 $attributeManagement->assign($attributeSetId, $attributeGroupId, $attributeId, $sortOrder);
             }
         }
     }
 
+    /**
+     * Get attribute ID from cache.
+     *
+     * @return int
+     */
+    private function getAttributeIdFromCache()
+    {
+        $attributeId = next($this->attributeIdsCache);
+        if ($attributeId === false) {
+            $attributeId = reset($this->attributeIdsCache);
+        }
+
+        return $attributeId;
+    }
+
+    /**
+     * Fill attribute IDs cache.
+     *
+     * @param int $attributeId
+     * @return void
+     */
+    private function fillAttributeIdsCache($attributeId)
+    {
+        if ($this->uniqueAttributesQuantity !== 0) {
+            $this->attributeIdsCache[] = $attributeId;
+        }
+    }
+
+    /**
+     * Populate quantity of unique attributes to generate.
+     *
+     * @return void
+     */
+    protected function populateUniqueAttributesQuantity()
+    {
+        $this->uniqueAttributesQuantity = $this->fixtureModel->getValue('unique_attributes_quantity', 0);
+    }
+
     /**
      * {@inheritdoc}
      */
@@ -119,4 +178,14 @@ class AttributeSetsFixture extends Fixture
             'attribute_sets' => 'Attribute Sets'
         ];
     }
+
+    /**
+     * Get attribute sets fixture value.
+     *
+     * @return array|null
+     */
+    protected function getAttributeSetsFixtureValue()
+    {
+        return $this->fixtureModel->getValue('attribute_sets', null);
+    }
 }
diff --git a/setup/src/Magento/Setup/Fixtures/ConfigurableProductsFixture.php b/setup/src/Magento/Setup/Fixtures/ConfigurableProductsFixture.php
index 2340de71cf7..9e84578281c 100644
--- a/setup/src/Magento/Setup/Fixtures/ConfigurableProductsFixture.php
+++ b/setup/src/Magento/Setup/Fixtures/ConfigurableProductsFixture.php
@@ -25,6 +25,13 @@ class ConfigurableProductsFixture extends SimpleProductsFixture
      */
     protected $searchConfig;
 
+    /**
+     * Variations count.
+     *
+     * @var int
+     */
+    protected $variationsCount;
+
     //@codingStandardsIgnoreStart
     /**
      * Get CSV template headers
@@ -169,7 +176,7 @@ class ConfigurableProductsFixture extends SimpleProductsFixture
     )
     {
         return [
-            'sku' => 'Configurable Product %s' . $suffix,
+            'sku' => $this->getConfigurableProductSkuPattern() . $suffix,
             'store_view_code' => '',
             'attribute_set_code' => $attributeSetClosure,
             'additional_attributes' => $additionalAttributesClosure,
@@ -225,8 +232,8 @@ class ConfigurableProductsFixture extends SimpleProductsFixture
             'updated_at' => '2013-10-25 15:12:39',
             'upsell_tgtr_position_behavior' => '',
             'upsell_tgtr_position_limit' => '',
-            'url_key' => "configurable-product-%s{$suffix}",
-            'url_path' => "configurable-product-%s{$suffix}",
+            'url_key' => $this->getUrlKeyPrefix() . "{$suffix}",
+            'url_path' => $this->getUrlKeyPrefix() . "{$suffix}",
             'visibility' => 'Catalog, Search',
             'weight' => '',
             'qty' => 333,
@@ -305,7 +312,7 @@ class ConfigurableProductsFixture extends SimpleProductsFixture
         $data = [];
         for ($i = 1; $i <= $optionsNumber; $i++) {
             $productData = [
-                'sku' => "Configurable Product %s-option {$i}{$suffix}",
+                'sku' => $this->getConfigurableOptionSkuPattern() . "{$i}{$suffix}",
                 'store_view_code' => '',
                 'attribute_set_code' => $attributeSetClosure,
                 'additional_attributes' => $additionalAttributesClosure,
@@ -361,8 +368,8 @@ class ConfigurableProductsFixture extends SimpleProductsFixture
                 'updated_at' => '2013-10-25 15:12:32',
                 'upsell_tgtr_position_behavior' => '',
                 'upsell_tgtr_position_limit' => '',
-                'url_key' => "simple-of-configurable-product-{$suffix}-%s-option-{$i}",
-                'url_path' => "simple-of-configurable-product-{$suffix}-%s-option-{$i}",
+                'url_key' => $this->getOptionUrlKeyPrefix() . "-{$suffix}-%s-option-{$i}",
+                'url_path' => $this->getOptionUrlKeyPrefix() . "-{$suffix}-%s-option-{$i}",
                 'variations' => '',
                 'variations_1382710717' => '',
                 'variations_1382710773' => '',
@@ -431,7 +438,7 @@ class ConfigurableProductsFixture extends SimpleProductsFixture
      */
     public function execute()
     {
-        $configurableProductsCount = $this->fixtureModel->getValue('configurable_products', 0);
+        $configurableProductsCount = $this->getConfigurableProductsValue();
         if (!$configurableProductsCount) {
             return;
         }
@@ -440,170 +447,147 @@ class ConfigurableProductsFixture extends SimpleProductsFixture
         $maxAmountOfWordsShortDescription = $this->getSearchConfigValue('max_amount_of_words_short_description');
         $minAmountOfWordsDescription = $this->getSearchConfigValue('min_amount_of_words_description');
         $minAmountOfWordsShortDescription = $this->getSearchConfigValue('min_amount_of_words_short_description');
+        $configurableProductsWithAttributes = [];
 
-        $attributes = $this->getAttributes();
-        $searchTerms = $this->getSearchTerms();
-        $this->fixtureModel->resetObjectManager();
-        $result = $this->getCategoriesAndWebsites();
-        $variationCount = $this->fixtureModel->getValue('configurable_products_variation', 3);
-        $result = array_values($result);
-        $dataGenerator = new DataGenerator(realpath(__DIR__ . '/' . 'dictionary.csv'));
-
-        $productWebsiteClosure = function ($index) use ($result) {
-            return $result[$index % count($result)][0];
-        };
-        $productCategoryClosure = function ($index) use ($result) {
-            return $result[$index % count($result)][2] . '/' . $result[$index % count($result)][1];
-        };
-        $shortDescriptionClosure = function ($index)
-        use (
-            $searchTerms,
-            $simpleProductsCount,
-            $configurableProductsCount,
-            $dataGenerator,
-            $maxAmountOfWordsShortDescription,
-            $minAmountOfWordsShortDescription
-        )
-        {
-            $count = $searchTerms === null
-                ? 0
-                : round(
-                    $searchTerms[$index % count($searchTerms)]['count'] * (
-                        $configurableProductsCount / ($simpleProductsCount + $configurableProductsCount)
-                    )
-                );
-            mt_srand($index);
-            return $dataGenerator->generate(
-                $minAmountOfWordsShortDescription,
-                $maxAmountOfWordsShortDescription,
-                'shortDescription-' . $index
-            ) . ($index <= ($count * count($searchTerms)) ? ' '
-                . $searchTerms[$index % count($searchTerms)]['term'] : '');
-        };
-        $descriptionClosure = function ($index)
-        use (
-            $searchTerms,
-            $simpleProductsCount,
-            $configurableProductsCount,
-            $dataGenerator,
-            $maxAmountOfWordsDescription,
-            $minAmountOfWordsDescription
-        )
-        {
-            $count = $searchTerms === null
-                ? 0
-                : round(
-                    $searchTerms[$index % count($searchTerms)]['count'] * (
-                        $configurableProductsCount / ($simpleProductsCount + $configurableProductsCount)
-                    )
-                );
-            mt_srand($index);
-            return $dataGenerator->generate(
-                $minAmountOfWordsDescription,
-                $maxAmountOfWordsDescription,
-                'description-' . $index
-            ) . ($index <= ($count * count($searchTerms))
-                ? ' ' . $searchTerms[$index % count($searchTerms)]['term'] : '');
-        };
-        $priceClosure = function($index) {
-            mt_srand($index);
-            switch (mt_rand(0,3)) {
-                case 0: return 9.99;
-                case 1: return 5;
-                case 2: return 1;
-                case 3: return mt_rand(1,10000)/10;
-            }
-        };
-        $attributeSetClosure = function($index) use ($attributes, $result) {
-            mt_srand($index);
-            $attributeSet =  (count(array_keys($attributes)) > (($index - 1) % count($result))
-                ? array_keys($attributes)[mt_rand(0, count(array_keys($attributes)) - 1)] : 'Default');
-            return $attributeSet;
-        };
-        $variationClosure = function($index, $variationIndex) use ($attributes, $result, $variationCount) {
-            mt_srand($index);
-            $attributeSetCode = (count(array_keys($attributes)) > (($index - 1) % count($result))
-                ? array_keys($attributes)[mt_rand(0, count(array_keys($attributes)) - 1)] : 'Default');
-            $skus = [];
-            for ($i=1; $i <= $variationCount; $i++) {
-                $skus[] = 'sku=Configurable Product ' . $index . '-option ' . $i;
-            }
-            $values = [];
-            if ($attributeSetCode == 'Default') {
-                for ($i=1; $i <= $variationCount; $i++) {
-                    $values[] =  'configurable_variation=option ' . $i;
-                }
+        if ($this->getAdditionalConfigurableProductsVariations() === null) {
+            $configurableProductsWithAttributes[$configurableProductsCount]
+                = $this->getConfigurableProductsVariationsValue();
+        } else {
+            if (strpos($this->getAdditionalConfigurableProductsVariations(), ',')) {
+                $variations = explode(',', $this->getAdditionalConfigurableProductsVariations());
             } else {
-                for ($i=$variationCount; $i > 0; $i--) {
-                    $attributeValues = '';
-                    foreach ($attributes[$attributeSetCode] as $attribute) {
-                        $attributeValues = $attributeValues . $attribute['name'] . "=" .
-                            $attribute['values'][($variationIndex - $i) % count($attribute['values'])] . ",";
-                    }
-                    $values [] = $attributeValues;
-                }
+                $variations = [$this->getAdditionalConfigurableProductsVariations()];
             }
-            $variations = [];
-            for ($i=0; $i < $variationCount; $i++) {
-                $variations[] = trim(implode(",",[$skus[$i],$values[$i]]), ",");
+
+            foreach ($variations as $variation) {
+                $value = explode(':', $variation);
+                $configurableProductsWithAttributes[$value[0]] = $value[1];
             }
-            return implode("|",$variations);
-        };
-        $additionalAttributesClosure = function($index, $variationIndex) use ($attributes, $result) {
-            $attributeValues = '';
-            mt_srand($index);
-            $attributeSetCode = (count(array_keys($attributes)) > (($index - 1) % count($result))
-                ? array_keys($attributes)[mt_rand(0, count(array_keys($attributes)) - 1)] : 'Default');
-            if ($attributeSetCode !== 'Default' ) {
-                foreach ($attributes[$attributeSetCode] as $attribute) {
-                    $attributeValues = $attributeValues . $attribute['name'] . "=" .
-                        $attribute['values'][$variationIndex %  count($attribute['values'])] . ",";
+        }
+
+        foreach ($configurableProductsWithAttributes as $productsCount => $variationsCount) {
+            $this->variationsCount = $variationsCount;
+            $configurableProductsCount = $productsCount;
+            $attributes = $this->getAttributes();
+            $searchTerms = $this->getSearchTerms();
+            $this->fixtureModel->resetObjectManager();
+            $result = $this->getCategoriesAndWebsites();
+            $result = array_values($result);
+            $dataGenerator = new DataGenerator(realpath(__DIR__ . '/' . 'dictionary.csv'));
+
+            $productWebsiteClosure = function ($index) use ($result) {
+                return $result[$index % count($result)][0];
+            };
+            $productCategoryClosure = function ($index) use ($result) {
+                return $result[$index % count($result)][2] . '/' . $result[$index % count($result)][1];
+            };
+            $shortDescriptionClosure = function ($index)
+            use (
+                $searchTerms,
+                $simpleProductsCount,
+                $configurableProductsCount,
+                $dataGenerator,
+                $maxAmountOfWordsShortDescription,
+                $minAmountOfWordsShortDescription
+            ) {
+                $count = $searchTerms === null
+                    ? 0
+                    : round(
+                        $searchTerms[$index % count($searchTerms)]['count'] * (
+                            $configurableProductsCount / ($simpleProductsCount + $configurableProductsCount)
+                        )
+                    );
+                mt_srand($index);
+                return $dataGenerator->generate(
+                    $minAmountOfWordsShortDescription,
+                    $maxAmountOfWordsShortDescription,
+                    'shortDescription-' . $index
+                ) . ($index <= ($count * count($searchTerms)) ? ' '
+                    . $searchTerms[$index % count($searchTerms)]['term'] : '');
+            };
+            $descriptionClosure = function ($index)
+            use (
+                $searchTerms,
+                $simpleProductsCount,
+                $configurableProductsCount,
+                $dataGenerator,
+                $maxAmountOfWordsDescription,
+                $minAmountOfWordsDescription
+            ) {
+                $count = $searchTerms === null
+                    ? 0
+                    : round(
+                        $searchTerms[$index % count($searchTerms)]['count'] * (
+                            $configurableProductsCount / ($simpleProductsCount + $configurableProductsCount)
+                        )
+                    );
+                mt_srand($index);
+                return $dataGenerator->generate(
+                    $minAmountOfWordsDescription,
+                    $maxAmountOfWordsDescription,
+                    'description-' . $index
+                ) . ($index <= ($count * count($searchTerms))
+                    ? ' ' . $searchTerms[$index % count($searchTerms)]['term'] : '');
+            };
+            $priceClosure = function ($index) {
+                mt_srand($index);
+                switch (mt_rand(0, 3)) {
+                    case 0:
+                        return 9.99;
+                    case 1:
+                        return 5;
+                    case 2:
+                        return 1;
+                    case 3:
+                        return mt_rand(1, 10000) / 10;
                 }
-            }
-            return trim($attributeValues, ",");
-        };
-        /**
-         * Create configurable products
-         */
-        $pattern = new Pattern();
-        $pattern->setHeaders($this->getHeaders());
-        $pattern->setRowsSet(
-            $this->getRows(
-                $productCategoryClosure,
-                $productWebsiteClosure,
-                $shortDescriptionClosure,
-                $descriptionClosure,
-                $priceClosure,
-                $attributeSetClosure,
-                $additionalAttributesClosure,
-                $variationClosure,
-                $variationCount
-            )
-        );
+            };
+            $attributeSetClosure = $this->getAttributeSetClosure($attributes, $result);
+            $variationClosure = $this->getVariationsClosure($attributes, $result, $variationsCount);
+            $additionalAttributesClosure = $this->getAdditionalAttributesClosure($attributes, $result);
+            /**
+             * Create configurable products
+             */
+            $pattern = new Pattern();
+            $pattern->setHeaders($this->getHeaders());
+            $pattern->setRowsSet(
+                $this->getRows(
+                    $productCategoryClosure,
+                    $productWebsiteClosure,
+                    $shortDescriptionClosure,
+                    $descriptionClosure,
+                    $priceClosure,
+                    $attributeSetClosure,
+                    $additionalAttributesClosure,
+                    $variationClosure,
+                    $variationsCount
+                )
+            );
 
-        /** @var \Magento\ImportExport\Model\Import $import */
-        $import = $this->fixtureModel->getObjectManager()->create(
-            \Magento\ImportExport\Model\Import::class,
-            [
-                'data' => [
-                    'entity' => 'catalog_product',
-                    'behavior' => 'append',
-                    'validation_strategy' => 'validation-stop-on-errors',
-                ],
-            ]
-        );
+            /** @var \Magento\ImportExport\Model\Import $import */
+            $import = $this->fixtureModel->getObjectManager()->create(
+                \Magento\ImportExport\Model\Import::class,
+                [
+                    'data' => [
+                        'entity' => 'catalog_product',
+                        'behavior' => 'append',
+                        'validation_strategy' => 'validation-stop-on-errors',
+                    ],
+                ]
+            );
 
-        $source = $this->fixtureModel->getObjectManager()->create(
-            Generator::class,
-            ['rowPattern' => $pattern, 'count' => $configurableProductsCount]
-        );
-        // it is not obvious, but the validateSource() will actually save import queue data to DB
-        if (!$import->validateSource($source)) {
-            throw new \Exception($import->getFormatedLogTrace());
-        }
-        // this converts import queue into actual entities
-        if (!$import->importSource()) {
-            throw new \Exception($import->getFormatedLogTrace());
+            $source = $this->fixtureModel->getObjectManager()->create(
+                Generator::class,
+                ['rowPattern' => $pattern, 'count' => $configurableProductsCount]
+            );
+            // it is not obvious, but the validateSource() will actually save import queue data to DB
+            if (!$import->validateSource($source)) {
+                throw new \Exception($import->getFormatedLogTrace());
+            }
+            // this converts import queue into actual entities
+            if (!$import->importSource()) {
+                throw new \Exception($import->getFormatedLogTrace());
+            }
         }
     }
     // @codingStandardsIgnoreEnd
@@ -674,4 +658,166 @@ class ConfigurableProductsFixture extends SimpleProductsFixture
         }
         return $result;
     }
+
+    /**
+     * Get configurable products value.
+     *
+     * @return int
+     */
+    protected function getConfigurableProductsValue()
+    {
+        return $this->fixtureModel->getValue('configurable_products', 0);
+    }
+
+    /**
+     * Get configurable products variations value.
+     *
+     * @return int
+     */
+    protected function getConfigurableProductsVariationsValue()
+    {
+        return $this->fixtureModel->getValue('configurable_products_variation', 3);
+    }
+
+    /**
+     * Get attribute set closure
+     *
+     * @param array $attributes
+     * @param array $result
+     * @return \Closure
+     */
+    protected function getAttributeSetClosure(array $attributes, array $result)
+    {
+        return function ($index) use ($attributes, $result) {
+            mt_srand($index);
+            $attributeSet =  (count(array_keys($attributes)) > (($index - 1) % count($result))
+                ? array_keys($attributes)[mt_rand(0, count(array_keys($attributes)) - 1)] : 'Default');
+            return $attributeSet;
+        };
+    }
+
+    /**
+     * Get additional attributes closure.
+     *
+     * @param array $attributes
+     * @param array $result
+     * @return \Closure
+     */
+    protected function getAdditionalAttributesClosure(array $attributes, array $result)
+    {
+        return function ($index, $variationIndex) use ($attributes, $result) {
+            $attributeValues = '';
+            mt_srand($index);
+            $attributeSetCode = (count(array_keys($attributes)) > (($index - 1) % count($result))
+                ? array_keys($attributes)[mt_rand(0, count(array_keys($attributes)) - 1)] : 'Default');
+            if ($attributeSetCode !== 'Default') {
+                foreach ($attributes[$attributeSetCode] as $attribute) {
+                    $attributeValues = $attributeValues . $attribute['name'] . "=" .
+                        $attribute['values'][$variationIndex %  count($attribute['values'])] . ",";
+                }
+            }
+            return trim($attributeValues, ",");
+        };
+    }
+
+    /**
+     * Get variations closure.
+     *
+     * @param array $attributes
+     * @param array $result
+     * @param int $variationCount
+     * @return \Closure
+     */
+    protected function getVariationsClosure(array $attributes, array $result, $variationCount)
+    {
+        return function ($index, $variationIndex) use ($attributes, $result, $variationCount) {
+            mt_srand($index);
+            $attributeSetCode = (count(array_keys($attributes)) > (($index - 1) % count($result))
+                ? array_keys($attributes)[mt_rand(0, count(array_keys($attributes)) - 1)] : 'Default');
+            $skus = [];
+            for ($i = 1; $i <= $variationCount; $i++) {
+                $skus[] = 'sku=' . sprintf($this->getConfigurableOptionSkuPattern(), $index) . $i;
+            }
+            $values = [];
+            if ($attributeSetCode == 'Default') {
+                for ($i = 1; $i <= $variationCount; $i++) {
+                    $values[] = 'configurable_variation=option ' . $i;
+                }
+            } else {
+                for ($i = $variationCount; $i > 0; $i--) {
+                    $attributeValues = '';
+                    foreach ($attributes[$attributeSetCode] as $attribute) {
+                        $attributeValues = $attributeValues . $attribute['name'] . "=" .
+                            $attribute['values'][($variationIndex - $i) % count($attribute['values'])] . ",";
+                    }
+                    $values [] = $attributeValues;
+                }
+            }
+            $variations = [];
+            for ($i = 0; $i < $variationCount; $i++) {
+                $variations[] = trim(implode(",", [$skus[$i], $values[$i]]), ",");
+            }
+            return implode("|", $variations);
+        };
+    }
+
+    /**
+     * Get configurable product sku pattern.
+     *
+     * @return string
+     */
+    private function getConfigurableProductSkuPattern()
+    {
+        return 'Configurable Product ' . $this->getConfigurableProductPrefix() . ' %s';
+    }
+
+    /**
+     * Get configurable option sku pattern.
+     *
+     * @return string
+     */
+    protected function getConfigurableOptionSkuPattern()
+    {
+        return 'Configurable Product ' . $this->getConfigurableProductPrefix() . '%s-option';
+    }
+
+    /**
+     * Get url key prefix.
+     *
+     * @return string
+     */
+    private function getUrlKeyPrefix()
+    {
+        return 'configurable-product' . $this->getConfigurableProductPrefix() . '-%s';
+    }
+
+    /**
+     * Get option url key prefix.
+     *
+     * @return string
+     */
+    private function getOptionUrlKeyPrefix()
+    {
+        return 'simple-of-configurable-product' . $this->getConfigurableProductPrefix();
+    }
+
+    /**
+     * Get additional configurations for configurable products.
+     *
+     * @return string|null
+     */
+    private function getAdditionalConfigurableProductsVariations()
+    {
+        return $this->fixtureModel->getValue('configurable_products_variations', null);
+    }
+
+    /**
+     * Get configurable product prefix.
+     *
+     * @return string
+     */
+    protected function getConfigurableProductPrefix()
+    {
+        return '';
+    }
 }
diff --git a/setup/src/Magento/Setup/Fixtures/SimpleProductsFixture.php b/setup/src/Magento/Setup/Fixtures/SimpleProductsFixture.php
index fbb28d35269..43270676cb9 100644
--- a/setup/src/Magento/Setup/Fixtures/SimpleProductsFixture.php
+++ b/setup/src/Magento/Setup/Fixtures/SimpleProductsFixture.php
@@ -235,7 +235,7 @@ class SimpleProductsFixture extends Fixture
      */
     protected function getAttributes()
     {
-        $attributeSets = $this->fixtureModel->getValue('attribute_sets', null);
+        $attributeSets = $this->getAttributeSets();
         $attributes = [];
 
         if ($attributeSets !== null && array_key_exists('attribute_set', $attributeSets)) {
@@ -337,4 +337,14 @@ class SimpleProductsFixture extends Fixture
         }
         return $searchTerms;
     }
+
+    /**
+     * Get attribute sets.
+     *
+     * @return array|null
+     */
+    private function getAttributeSets()
+    {
+        return $this->fixtureModel->getValue('attribute_sets', null);
+    }
 }
diff --git a/setup/src/Magento/Setup/Test/Unit/Fixtures/AttributeSetsFixtureTest.php b/setup/src/Magento/Setup/Test/Unit/Fixtures/AttributeSetsFixtureTest.php
index 674f444fbde..2192f4e84e2 100644
--- a/setup/src/Magento/Setup/Test/Unit/Fixtures/AttributeSetsFixtureTest.php
+++ b/setup/src/Magento/Setup/Test/Unit/Fixtures/AttributeSetsFixtureTest.php
@@ -202,7 +202,7 @@ class AttributeSetsFixtureTest extends \PHPUnit_Framework_TestCase
             ->willReturn($optionFactoryMock);
 
         $this->fixtureModelMock
-            ->expects($this->once())
+            ->expects($this->any())
             ->method('getValue')
             ->willReturn($attributeSets);
 
@@ -267,7 +267,7 @@ class AttributeSetsFixtureTest extends \PHPUnit_Framework_TestCase
             ->method('getObjectManager')
             ->will($this->returnValue($objectManagerMock));
         $this->fixtureModelMock
-            ->expects($this->once())
+            ->expects($this->any())
             ->method('getValue')
             ->willReturn(null);
 
-- 
GitLab