{"id":59,"date":"2025-01-24T10:53:23","date_gmt":"2025-01-24T10:53:23","guid":{"rendered":"https:\/\/magendoo.ro\/insights\/?p=59"},"modified":"2025-01-24T10:53:23","modified_gmt":"2025-01-24T10:53:23","slug":"product-import-strategies-in-magento-2-part-2-the-hybrid-approach","status":"publish","type":"post","link":"https:\/\/magendoo.ro\/insights\/product-import-strategies-in-magento-2-part-2-the-hybrid-approach\/","title":{"rendered":"Product Import Strategies in Magento 2 &#8211; Part 2: The Hybrid Approach"},"content":{"rendered":"\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Introduction<\/h2>\n\n\n\n<p>Following our exploration of the DTO approach in Part 1, we&#8217;ll now dive into the Hybrid approach for product imports. This method combines programmatic product creation with CSV data enrichment, offering flexibility while maintaining control over the core product structure.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">System Architecture<\/h2>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"496\" src=\"https:\/\/magendoo.ro\/insights\/wp-content\/uploads\/2025\/01\/hybrid-1-1024x496.png\" alt=\"\" class=\"wp-image-60\" srcset=\"https:\/\/magendoo.ro\/insights\/wp-content\/uploads\/2025\/01\/hybrid-1-1024x496.png 1024w, https:\/\/magendoo.ro\/insights\/wp-content\/uploads\/2025\/01\/hybrid-1-300x145.png 300w, https:\/\/magendoo.ro\/insights\/wp-content\/uploads\/2025\/01\/hybrid-1-768x372.png 768w, https:\/\/magendoo.ro\/insights\/wp-content\/uploads\/2025\/01\/hybrid-1-1536x744.png 1536w, https:\/\/magendoo.ro\/insights\/wp-content\/uploads\/2025\/01\/hybrid-1.png 2032w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Implementation<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">1. Base Product Generator<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>namespace Vendor\\Module\\Model;\n\nclass BaseProductGenerator\n{\n    private $productFactory;\n    private $attributeSetRepository;\n    private $storeManager;\n\n    public function createBaseProduct(string $sku): ProductInterface\n    {\n        $product = $this-&gt;productFactory-&gt;create();\n\n        $product-&gt;setSku($sku)\n            -&gt;setAttributeSetId($this-&gt;getDefaultAttributeSetId())\n            -&gt;setStatus(\\Magento\\Catalog\\Model\\Product\\Attribute\\Source\\Status::STATUS_ENABLED)\n            -&gt;setTypeId(\\Magento\\Catalog\\Model\\Product\\Type::TYPE_SIMPLE)\n            -&gt;setVisibility(\\Magento\\Catalog\\Model\\Product\\Visibility::VISIBILITY_BOTH)\n            -&gt;setWebsiteIds($this-&gt;getWebsiteIds());\n\n        return $product;\n    }\n\n    private function getDefaultAttributeSetId(): int\n    {\n        return $this-&gt;attributeSetRepository-&gt;get(4)-&gt;getAttributeSetId();\n    }\n\n    private function getWebsiteIds(): array\n    {\n        return array_keys($this-&gt;storeManager-&gt;getWebsites());\n    }\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">2. Product Enricher<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>namespace Vendor\\Module\\Model;\n\nclass ProductEnricher\n{\n    private $productRepository;\n    private $mediaProcessor;\n    private $attributeProcessor;\n\n    public function enrichFromCsv(ProductInterface $product, array $data): void\n    {\n        $this-&gt;processBasicAttributes($product, $data);\n        $this-&gt;processCustomAttributes($product, $data);\n        $this-&gt;processCategories($product, $data);\n        $this-&gt;processImages($product, $data);\n        $this-&gt;processInventory($product, $data);\n\n        $this-&gt;productRepository-&gt;save($product);\n    }\n\n    private function processBasicAttributes(ProductInterface $product, array $data): void\n    {\n        $basicAttributes = &#91;\n            'name' =&gt; $data&#91;'name'] ?? '',\n            'price' =&gt; $data&#91;'price'] ?? null,\n            'description' =&gt; $data&#91;'description'] ?? '',\n            'short_description' =&gt; $data&#91;'short_description'] ?? ''\n        ];\n\n        foreach ($basicAttributes as $code =&gt; $value) {\n            if ($value !== null) {\n                $product-&gt;setData($code, $value);\n            }\n        }\n    }\n\n    private function processCustomAttributes(ProductInterface $product, array $data): void\n    {\n        foreach ($data as $code =&gt; $value) {\n            if ($this-&gt;attributeProcessor-&gt;isCustomAttribute($code)) {\n                $this-&gt;attributeProcessor-&gt;processAttribute($product, $code, $value);\n            }\n        }\n    }\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">3. Import Service<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>namespace Vendor\\Module\\Service;\n\nclass HybridImportService\n{\n    private $baseGenerator;\n    private $enricher;\n    private $indexerRegistry;\n    private $logger;\n\n    public function import(string $filePath): ImportResult\n    {\n        $result = new ImportResult();\n\n        try {\n            $this-&gt;disableIndexers();\n\n            foreach ($this-&gt;readCsvInChunks($filePath) as $row) {\n                try {\n                    $product = $this-&gt;baseGenerator-&gt;createBaseProduct($row&#91;'sku']);\n                    $this-&gt;enricher-&gt;enrichFromCsv($product, $row);\n                    $result-&gt;addSuccess($row&#91;'sku']);\n                } catch (\\Exception $e) {\n                    $result-&gt;addError($row&#91;'sku'], $e-&gt;getMessage());\n                    $this-&gt;logger-&gt;error($e-&gt;getMessage(), &#91;'sku' =&gt; $row&#91;'sku']]);\n                }\n            }\n\n            $this-&gt;reindexAffectedProducts($result-&gt;getSuccessful());\n        } catch (\\Exception $e) {\n            $result-&gt;setFatalError($e-&gt;getMessage());\n        } finally {\n            $this-&gt;enableIndexers();\n        }\n\n        return $result;\n    }\n\n    private function disableIndexers(): void\n    {\n        foreach ($this-&gt;getIndexers() as $indexer) {\n            $indexer-&gt;setScheduled(true);\n        }\n    }\n\n    private function reindexAffectedProducts(array $skus): void\n    {\n        \/\/ Reindex only affected products\n    }\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Advanced Features<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">1. Attribute Processing<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>namespace Vendor\\Module\\Model;\n\nclass AttributeProcessor\n{\n    private $eavConfig;\n    private $storeManager;\n\n    public function processAttribute(ProductInterface $product, string $code, $value): void\n    {\n        $attribute = $this-&gt;eavConfig-&gt;getAttribute('catalog_product', $code);\n\n        switch ($attribute-&gt;getFrontendInput()) {\n            case 'select':\n                $this-&gt;processSelectAttribute($product, $attribute, $value);\n                break;\n            case 'multiselect':\n                $this-&gt;processMultiselectAttribute($product, $attribute, $value);\n                break;\n            \/\/ Handle other attribute types\n        }\n    }\n\n    private function processSelectAttribute(ProductInterface $product, $attribute, $value): void\n    {\n        $optionId = $this-&gt;getOptionId($attribute, $value);\n        $product-&gt;setData($attribute-&gt;getAttributeCode(), $optionId);\n    }\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">2. Transaction Management<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>namespace Vendor\\Module\\Model;\n\nclass TransactionManager\n{\n    private $resource;\n\n    public function execute(callable $callback): void\n    {\n        $connection = $this-&gt;resource-&gt;getConnection();\n        $connection-&gt;beginTransaction();\n\n        try {\n            $callback();\n            $connection-&gt;commit();\n        } catch (\\Exception $e) {\n            $connection-&gt;rollBack();\n            throw $e;\n        }\n    }\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Best Practices<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">1. Performance Optimization<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Use bulk save operations<\/li>\n\n\n\n<li>Manage indexers effectively<\/li>\n\n\n\n<li>Implement batch processing<\/li>\n\n\n\n<li>Cache attribute options and configurations<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">2. Error Recovery<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>namespace Vendor\\Module\\Model;\n\nclass ErrorRecovery\n{\n    private $logger;\n    private $emailNotifier;\n\n    public function handleError(\\Exception $e, array $context): void\n    {\n        $this-&gt;logger-&gt;critical($e-&gt;getMessage(), $context);\n\n        if ($this-&gt;isRecoverable($e)) {\n            $this-&gt;attemptRecovery($context);\n        } else {\n            $this-&gt;notifyAdmin($e, $context);\n        }\n    }\n\n    private function attemptRecovery(array $context): void\n    {\n        \/\/ Implement recovery logic\n        $this-&gt;cleanupTemporaryFiles();\n        $this-&gt;restoreIndexers();\n        $this-&gt;unlockImportProcess();\n    }\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Advantages of Hybrid Approach<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Control<\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Fine-grained control over product structure<\/li>\n\n\n\n<li>Flexible attribute handling<\/li>\n\n\n\n<li>Custom validation rules<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Performance<\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Optimized for large datasets<\/li>\n\n\n\n<li>Better memory management<\/li>\n\n\n\n<li>Controlled indexing<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Reliability<\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Transaction support<\/li>\n\n\n\n<li>Error recovery<\/li>\n\n\n\n<li>Data consistency<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Limitations<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Complexity<\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>More complex implementation<\/li>\n\n\n\n<li>Requires careful planning<\/li>\n\n\n\n<li>Higher maintenance overhead<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Learning Curve<\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Team needs to understand both approaches<\/li>\n\n\n\n<li>More documentation required<\/li>\n\n\n\n<li>Complex debugging<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">When to Use Hybrid Approach<\/h2>\n\n\n\n<p>This approach is ideal when:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Handling complex product types<\/li>\n\n\n\n<li>Dealing with large datasets<\/li>\n\n\n\n<li>Need fine-grained control<\/li>\n\n\n\n<li>Have specific business rules<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Next Steps<\/h2>\n\n\n\n<p>In Part 3, we&#8217;ll explore the Middleware approach, which uses a lightweight service to handle data transformation and image processing before import.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>Stay tuned for the final part of our series where we&#8217;ll dive into using middleware for product imports in Magento 2.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">magento2 #php #ecommerce #dataImport #adobecommerce #programming<\/h1>\n","protected":false},"excerpt":{"rendered":"<p>Introduction Following our exploration of the DTO approach in Part 1, we&#8217;ll now dive into the Hybrid approach for product imports. This method combines programmatic product creation with CSV data enrichment, offering flexibility while maintaining control over the core product structure. System Architecture Implementation 1. Base Product Generator 2. Product Enricher 3. Import Service Advanced [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"site-container-style":"default","site-container-layout":"default","site-sidebar-layout":"default","disable-article-header":"default","disable-site-header":"default","disable-site-footer":"default","disable-content-area-spacing":"default","footnotes":""},"categories":[1,9],"tags":[],"class_list":["post-59","post","type-post","status-publish","format-standard","hentry","category-general","category-magento-2"],"_links":{"self":[{"href":"https:\/\/magendoo.ro\/insights\/wp-json\/wp\/v2\/posts\/59","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/magendoo.ro\/insights\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/magendoo.ro\/insights\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/magendoo.ro\/insights\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/magendoo.ro\/insights\/wp-json\/wp\/v2\/comments?post=59"}],"version-history":[{"count":1,"href":"https:\/\/magendoo.ro\/insights\/wp-json\/wp\/v2\/posts\/59\/revisions"}],"predecessor-version":[{"id":61,"href":"https:\/\/magendoo.ro\/insights\/wp-json\/wp\/v2\/posts\/59\/revisions\/61"}],"wp:attachment":[{"href":"https:\/\/magendoo.ro\/insights\/wp-json\/wp\/v2\/media?parent=59"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/magendoo.ro\/insights\/wp-json\/wp\/v2\/categories?post=59"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/magendoo.ro\/insights\/wp-json\/wp\/v2\/tags?post=59"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}