migrate/app/Services/MagentoCategoryMigrationSer...

466 lines
16 KiB
PHP

<?php
namespace App\Services;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Exception;
class MagentoCategoryMigrationService
{
protected $magento1Connection;
protected $magento2Connection;
protected $magento1Prefix;
protected $magento2Prefix;
protected $storeMapping = [];
protected $categoryMapping = [];
protected $migrationLog = [];
public function __construct()
{
$this->magento1Connection = 'magento1';
$this->magento2Connection = 'magento2';
$this->magento1Prefix = config('database.connections.magento1.prefix', '');
$this->magento2Prefix = config('database.connections.magento2.prefix', '');
}
/**
* Get all stores from Magento 1
*/
public function getMagento1Stores()
{
try {
$stores = DB::connection($this->magento1Connection)
->table($this->magento1Prefix . 'core_store')
->select('store_id', 'code', 'name', 'website_id', 'group_id')
->where('store_id', '>', 0)
->get();
return $stores;
} catch (Exception $e) {
Log::error('Error fetching Magento 1 stores: ' . $e->getMessage());
return collect([]);
}
}
/**
* Get all stores from Magento 2
*/
public function getMagento2Stores()
{
try {
$stores = DB::connection($this->magento2Connection)
->table($this->magento2Prefix . 'store')
->select('store_id', 'code', 'name', 'website_id')
->where('store_id', '>', 0)
->get();
return $stores;
} catch (Exception $e) {
Log::error('Error fetching Magento 2 stores: ' . $e->getMessage());
return collect([]);
}
}
/**
* Get all categories from Magento 1 for a specific store
*/
public function getMagento1Categories($storeId = null)
{
try {
// Get entity type ID
$entityTypeId = DB::connection($this->magento1Connection)
->table($this->magento1Prefix . 'eav_entity_type')
->where('entity_type_code', 'catalog_category')
->value('entity_type_id');
if (!$entityTypeId) {
return collect([]);
}
// Get attribute IDs
$nameAttributeId = DB::connection($this->magento1Connection)
->table($this->magento1Prefix . 'eav_attribute')
->where('entity_type_id', $entityTypeId)
->where('attribute_code', 'name')
->value('attribute_id');
$isActiveAttributeId = DB::connection($this->magento1Connection)
->table($this->magento1Prefix . 'eav_attribute')
->where('entity_type_id', $entityTypeId)
->where('attribute_code', 'is_active')
->value('attribute_id');
$urlKeyAttributeId = DB::connection($this->magento1Connection)
->table($this->magento1Prefix . 'eav_attribute')
->where('entity_type_id', $entityTypeId)
->where('attribute_code', 'url_key')
->value('attribute_id');
$targetStoreId = $storeId ?? 0;
// Get base category data
$categories = DB::connection($this->magento1Connection)
->table($this->magento1Prefix . 'catalog_category_entity')
->select('entity_id', 'parent_id', 'level', 'path', 'position')
->orderBy('level')
->orderBy('position')
->get();
// Get attribute values
$nameValues = [];
$isActiveValues = [];
$urlKeyValues = [];
if ($nameAttributeId) {
$nameValues = DB::connection($this->magento1Connection)
->table($this->magento1Prefix . 'catalog_category_entity_varchar')
->where('attribute_id', $nameAttributeId)
->where('store_id', $targetStoreId)
->pluck('value', 'entity_id')
->toArray();
}
if ($isActiveAttributeId) {
$isActiveValues = DB::connection($this->magento1Connection)
->table($this->magento1Prefix . 'catalog_category_entity_int')
->where('attribute_id', $isActiveAttributeId)
->where('store_id', $targetStoreId)
->pluck('value', 'entity_id')
->toArray();
}
if ($urlKeyAttributeId) {
$urlKeyValues = DB::connection($this->magento1Connection)
->table($this->magento1Prefix . 'catalog_category_entity_varchar')
->where('attribute_id', $urlKeyAttributeId)
->where('store_id', $targetStoreId)
->pluck('value', 'entity_id')
->toArray();
}
// Combine data
return $categories->map(function($category) use ($nameValues, $isActiveValues, $urlKeyValues) {
$category->name = $nameValues[$category->entity_id] ?? null;
$category->is_active = $isActiveValues[$category->entity_id] ?? null;
$category->url_key = $urlKeyValues[$category->entity_id] ?? null;
return $category;
});
} catch (Exception $e) {
Log::error('Error fetching Magento 1 categories: ' . $e->getMessage());
return collect([]);
}
}
/**
* Migrate categories from Magento 1 to Magento 2
*/
public function migrateCategories($storeMapping = [])
{
$this->storeMapping = $storeMapping;
$this->migrationLog = [];
$this->categoryMapping = [];
try {
DB::connection($this->magento2Connection)->beginTransaction();
// Get root category ID for Magento 2 (usually 2)
$rootCategoryId = $this->getMagento2RootCategoryId();
// Get all categories from Magento 1
$m1Categories = $this->getMagento1Categories();
// Build category tree
$categoryTree = $this->buildCategoryTree($m1Categories);
// Migrate categories level by level
foreach ($categoryTree as $level => $categories) {
foreach ($categories as $m1Category) {
$this->migrateCategory($m1Category, $rootCategoryId);
}
}
// Migrate category attributes for each store
foreach ($this->storeMapping as $m1StoreId => $m2StoreId) {
$this->migrateCategoryAttributes($m1StoreId, $m2StoreId);
}
DB::connection($this->magento2Connection)->commit();
return [
'success' => true,
'message' => 'Categories migrated successfully',
'migrated_count' => count($this->categoryMapping),
'log' => $this->migrationLog
];
} catch (Exception $e) {
DB::connection($this->magento2Connection)->rollBack();
Log::error('Category migration error: ' . $e->getMessage());
return [
'success' => false,
'message' => 'Migration failed: ' . $e->getMessage(),
'log' => $this->migrationLog
];
}
}
/**
* Build category tree organized by level
*/
protected function buildCategoryTree($categories)
{
$tree = [];
foreach ($categories as $category) {
$level = $category->level ?? 1;
if (!isset($tree[$level])) {
$tree[$level] = [];
}
$tree[$level][] = $category;
}
ksort($tree);
return $tree;
}
/**
* Migrate a single category
*/
protected function migrateCategory($m1Category, $parentId = null)
{
try {
// Check if category already exists
if (isset($this->categoryMapping[$m1Category->entity_id])) {
return $this->categoryMapping[$m1Category->entity_id];
}
// Determine parent ID
if ($m1Category->parent_id == 1 || $m1Category->parent_id == 0) {
$m2ParentId = $parentId ?? $this->getMagento2RootCategoryId();
} else {
$m2ParentId = $this->categoryMapping[$m1Category->parent_id] ?? $parentId;
}
// Insert category entity
$m2EntityId = DB::connection($this->magento2Connection)
->table($this->magento2Prefix . 'catalog_category_entity')
->insertGetId([
'attribute_set_id' => 3, // Default category attribute set
'parent_id' => $m2ParentId,
'created_at' => now(),
'updated_at' => now(),
'path' => '',
'position' => $m1Category->position ?? 0,
'level' => $m1Category->level ?? 1,
'children_count' => 0,
]);
// Update path
$path = $this->buildCategoryPath($m2EntityId, $m2ParentId);
DB::connection($this->magento2Connection)
->table($this->magento2Prefix . 'catalog_category_entity')
->where('entity_id', $m2EntityId)
->update(['path' => $path]);
// Store mapping
$this->categoryMapping[$m1Category->entity_id] = $m2EntityId;
$this->migrationLog[] = "Migrated category ID {$m1Category->entity_id} -> {$m2EntityId} ({$m1Category->name})";
return $m2EntityId;
} catch (Exception $e) {
Log::error("Error migrating category {$m1Category->entity_id}: " . $e->getMessage());
$this->migrationLog[] = "ERROR: Failed to migrate category ID {$m1Category->entity_id}: " . $e->getMessage();
throw $e;
}
}
/**
* Build category path
*/
protected function buildCategoryPath($entityId, $parentId)
{
if ($parentId == $this->getMagento2RootCategoryId()) {
return "1/{$parentId}/{$entityId}";
}
$parent = DB::connection($this->magento2Connection)
->table($this->magento2Prefix . 'catalog_category_entity')
->where('entity_id', $parentId)
->first();
if ($parent && $parent->path) {
return $parent->path . '/' . $entityId;
}
return "1/{$parentId}/{$entityId}";
}
/**
* Migrate category attributes for a specific store
*/
protected function migrateCategoryAttributes($m1StoreId, $m2StoreId)
{
try {
// Get attribute IDs for Magento 2
$attributeIds = $this->getMagento2AttributeIds();
// Get categories with their attributes from Magento 1
$m1Categories = $this->getMagento1Categories($m1StoreId);
foreach ($m1Categories as $m1Category) {
if (!isset($this->categoryMapping[$m1Category->entity_id])) {
continue;
}
$m2EntityId = $this->categoryMapping[$m1Category->entity_id];
// Migrate name
if ($m1Category->name) {
$this->insertCategoryAttribute(
$m2EntityId,
$attributeIds['name'],
$m2StoreId,
$m1Category->name
);
}
// Migrate is_active
if ($m1Category->is_active !== null) {
$this->insertCategoryAttribute(
$m2EntityId,
$attributeIds['is_active'],
$m2StoreId,
$m1Category->is_active
);
}
// Migrate url_key
if ($m1Category->url_key) {
$this->insertCategoryAttribute(
$m2EntityId,
$attributeIds['url_key'],
$m2StoreId,
$m1Category->url_key
);
}
}
$this->migrationLog[] = "Migrated attributes for store mapping: M1 Store {$m1StoreId} -> M2 Store {$m2StoreId}";
} catch (Exception $e) {
Log::error("Error migrating category attributes: " . $e->getMessage());
$this->migrationLog[] = "ERROR: Failed to migrate attributes for store {$m1StoreId}: " . $e->getMessage();
throw $e;
}
}
/**
* Insert category attribute value
*/
protected function insertCategoryAttribute($entityId, $attributeId, $storeId, $value)
{
// Determine which table to use based on attribute type
$attribute = DB::connection($this->magento2Connection)
->table($this->magento2Prefix . 'eav_attribute')
->where('attribute_id', $attributeId)
->first();
if (!$attribute) {
return;
}
$table = $this->magento2Prefix . 'catalog_category_entity_' . $attribute->backend_type;
// Check if value already exists
$exists = DB::connection($this->magento2Connection)
->table($table)
->where('entity_id', $entityId)
->where('attribute_id', $attributeId)
->where('store_id', $storeId)
->exists();
if ($exists) {
DB::connection($this->magento2Connection)
->table($table)
->where('entity_id', $entityId)
->where('attribute_id', $attributeId)
->where('store_id', $storeId)
->update(['value' => $value]);
} else {
DB::connection($this->magento2Connection)
->table($table)
->insert([
'attribute_id' => $attributeId,
'store_id' => $storeId,
'entity_id' => $entityId,
'value' => $value,
]);
}
}
/**
* Get Magento 2 attribute IDs
*/
protected function getMagento2AttributeIds()
{
$entityTypeId = DB::connection($this->magento2Connection)
->table($this->magento2Prefix . 'eav_entity_type')
->where('entity_type_code', 'catalog_category')
->value('entity_type_id');
$attributes = DB::connection($this->magento2Connection)
->table($this->magento2Prefix . 'eav_attribute')
->where('entity_type_id', $entityTypeId)
->whereIn('attribute_code', ['name', 'is_active', 'url_key'])
->pluck('attribute_id', 'attribute_code')
->toArray();
return $attributes;
}
/**
* Get Magento 2 root category ID
*/
protected function getMagento2RootCategoryId()
{
// Root category is usually ID 2 in Magento 2
$rootId = DB::connection($this->magento2Connection)
->table($this->magento2Prefix . 'catalog_category_entity')
->where('level', 0)
->where('parent_id', 0)
->value('entity_id');
return $rootId ?: 2;
}
/**
* Test database connections
*/
public function testConnections()
{
$results = [
'magento1' => false,
'magento2' => false,
];
try {
DB::connection($this->magento1Connection)->select('SELECT 1');
$results['magento1'] = true;
} catch (Exception $e) {
$results['magento1_error'] = $e->getMessage();
}
try {
DB::connection($this->magento2Connection)->select('SELECT 1');
$results['magento2'] = true;
} catch (Exception $e) {
$results['magento2_error'] = $e->getMessage();
}
return $results;
}
}