Introduction
Following our exploration of the DTO approach in Part 1, we’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
namespace Vendor\Module\Model;
class BaseProductGenerator
{
private $productFactory;
private $attributeSetRepository;
private $storeManager;
public function createBaseProduct(string $sku): ProductInterface
{
$product = $this->productFactory->create();
$product->setSku($sku)
->setAttributeSetId($this->getDefaultAttributeSetId())
->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED)
->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE)
->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH)
->setWebsiteIds($this->getWebsiteIds());
return $product;
}
private function getDefaultAttributeSetId(): int
{
return $this->attributeSetRepository->get(4)->getAttributeSetId();
}
private function getWebsiteIds(): array
{
return array_keys($this->storeManager->getWebsites());
}
}
2. Product Enricher
namespace Vendor\Module\Model;
class ProductEnricher
{
private $productRepository;
private $mediaProcessor;
private $attributeProcessor;
public function enrichFromCsv(ProductInterface $product, array $data): void
{
$this->processBasicAttributes($product, $data);
$this->processCustomAttributes($product, $data);
$this->processCategories($product, $data);
$this->processImages($product, $data);
$this->processInventory($product, $data);
$this->productRepository->save($product);
}
private function processBasicAttributes(ProductInterface $product, array $data): void
{
$basicAttributes = [
'name' => $data['name'] ?? '',
'price' => $data['price'] ?? null,
'description' => $data['description'] ?? '',
'short_description' => $data['short_description'] ?? ''
];
foreach ($basicAttributes as $code => $value) {
if ($value !== null) {
$product->setData($code, $value);
}
}
}
private function processCustomAttributes(ProductInterface $product, array $data): void
{
foreach ($data as $code => $value) {
if ($this->attributeProcessor->isCustomAttribute($code)) {
$this->attributeProcessor->processAttribute($product, $code, $value);
}
}
}
}
3. Import Service
namespace Vendor\Module\Service;
class HybridImportService
{
private $baseGenerator;
private $enricher;
private $indexerRegistry;
private $logger;
public function import(string $filePath): ImportResult
{
$result = new ImportResult();
try {
$this->disableIndexers();
foreach ($this->readCsvInChunks($filePath) as $row) {
try {
$product = $this->baseGenerator->createBaseProduct($row['sku']);
$this->enricher->enrichFromCsv($product, $row);
$result->addSuccess($row['sku']);
} catch (\Exception $e) {
$result->addError($row['sku'], $e->getMessage());
$this->logger->error($e->getMessage(), ['sku' => $row['sku']]);
}
}
$this->reindexAffectedProducts($result->getSuccessful());
} catch (\Exception $e) {
$result->setFatalError($e->getMessage());
} finally {
$this->enableIndexers();
}
return $result;
}
private function disableIndexers(): void
{
foreach ($this->getIndexers() as $indexer) {
$indexer->setScheduled(true);
}
}
private function reindexAffectedProducts(array $skus): void
{
// Reindex only affected products
}
}
Advanced Features
1. Attribute Processing
namespace Vendor\Module\Model;
class AttributeProcessor
{
private $eavConfig;
private $storeManager;
public function processAttribute(ProductInterface $product, string $code, $value): void
{
$attribute = $this->eavConfig->getAttribute('catalog_product', $code);
switch ($attribute->getFrontendInput()) {
case 'select':
$this->processSelectAttribute($product, $attribute, $value);
break;
case 'multiselect':
$this->processMultiselectAttribute($product, $attribute, $value);
break;
// Handle other attribute types
}
}
private function processSelectAttribute(ProductInterface $product, $attribute, $value): void
{
$optionId = $this->getOptionId($attribute, $value);
$product->setData($attribute->getAttributeCode(), $optionId);
}
}
2. Transaction Management
namespace Vendor\Module\Model;
class TransactionManager
{
private $resource;
public function execute(callable $callback): void
{
$connection = $this->resource->getConnection();
$connection->beginTransaction();
try {
$callback();
$connection->commit();
} catch (\Exception $e) {
$connection->rollBack();
throw $e;
}
}
}
Best Practices
1. Performance Optimization
- Use bulk save operations
- Manage indexers effectively
- Implement batch processing
- Cache attribute options and configurations
2. Error Recovery
namespace Vendor\Module\Model;
class ErrorRecovery
{
private $logger;
private $emailNotifier;
public function handleError(\Exception $e, array $context): void
{
$this->logger->critical($e->getMessage(), $context);
if ($this->isRecoverable($e)) {
$this->attemptRecovery($context);
} else {
$this->notifyAdmin($e, $context);
}
}
private function attemptRecovery(array $context): void
{
// Implement recovery logic
$this->cleanupTemporaryFiles();
$this->restoreIndexers();
$this->unlockImportProcess();
}
}
Advantages of Hybrid Approach
- Control
- Fine-grained control over product structure
- Flexible attribute handling
- Custom validation rules
- Performance
- Optimized for large datasets
- Better memory management
- Controlled indexing
- Reliability
- Transaction support
- Error recovery
- Data consistency
Limitations
- Complexity
- More complex implementation
- Requires careful planning
- Higher maintenance overhead
- Learning Curve
- Team needs to understand both approaches
- More documentation required
- Complex debugging
When to Use Hybrid Approach
This approach is ideal when:
- Handling complex product types
- Dealing with large datasets
- Need fine-grained control
- Have specific business rules
Next Steps
In Part 3, we’ll explore the Middleware approach, which uses a lightweight service to handle data transformation and image processing before import.
Stay tuned for the final part of our series where we’ll dive into using middleware for product imports in Magento 2.