diff --git a/app/Http/Controllers/MagentoMigrationController.php b/app/Http/Controllers/MagentoMigrationController.php index 80ec00c..ae8e16e 100644 --- a/app/Http/Controllers/MagentoMigrationController.php +++ b/app/Http/Controllers/MagentoMigrationController.php @@ -31,6 +31,7 @@ public function index() $m1AttributesMissingInM2 = $this->migrationService->getM1AttributesMissingInM2(); $m1AttributeGroups = $this->migrationService->getMagento1AttributeGroups(); $m2AttributeGroups = $this->migrationService->getMagento2AttributeGroups(); + $m1AttributeGroupsMissingInM2 = $this->migrationService->getM1AttributeGroupsMissingInM2(); return view('migration.index', [ 'm1Stores' => $m1Stores, @@ -44,6 +45,7 @@ public function index() 'm1AttributesMissingInM2' => $m1AttributesMissingInM2, 'm1AttributeGroups' => $m1AttributeGroups, 'm2AttributeGroups' => $m2AttributeGroups, + 'm1AttributeGroupsMissingInM2' => $m1AttributeGroupsMissingInM2, ]); } @@ -198,6 +200,26 @@ public function migrateAttribute(Request $request, $attributeId) } } + /** + * Migrate an attribute group from Magento 1 to Magento 2 + */ + public function migrateAttributeGroup(Request $request, $groupId, $setId) + { + try { + $result = $this->migrationService->migrateAttributeGroup($groupId, $setId); + + return response()->json($result, $result['success'] ? 200 : 400); + + } catch (\Exception $e) { + Log::error('Attribute group migration error: ' . $e->getMessage()); + + return response()->json([ + 'success' => false, + 'message' => 'Migration failed: ' . $e->getMessage(), + ], 500); + } + } + /** * Execute the migration */ diff --git a/app/Services/MagentoCategoryMigrationService.php b/app/Services/MagentoCategoryMigrationService.php index ae1a876..df05442 100644 --- a/app/Services/MagentoCategoryMigrationService.php +++ b/app/Services/MagentoCategoryMigrationService.php @@ -1290,6 +1290,195 @@ public function getMagento2AttributeGroups() } } + /** + * Get attribute groups that exist in Magento 1 but are missing in Magento 2 + */ + public function getM1AttributeGroupsMissingInM2() + { + try { + $m1Groups = $this->getMagento1AttributeGroups(); + $m2Groups = $this->getMagento2AttributeGroups(); + + // Create a map of M2 groups by set name and group name + $m2GroupMap = []; + foreach ($m2Groups as $m2Group) { + $key = ($m2Group->attribute_set_name ?? 'Default') . '|' . ($m2Group->attribute_group_name ?? ''); + $m2GroupMap[$key] = true; + } + + // Filter M1 groups that don't exist in M2 + $missingGroups = $m1Groups->filter(function ($group) use ($m2GroupMap) { + $key = ($group->attribute_set_name ?? 'Default') . '|' . ($group->attribute_group_name ?? ''); + return !isset($m2GroupMap[$key]); + }); + + return $missingGroups->values(); + } catch (Exception $e) { + Log::error('Error fetching missing attribute groups: ' . $e->getMessage()); + return collect([]); + } + } + + /** + * Migrate an attribute group from Magento 1 to Magento 2 + */ + public function migrateAttributeGroup($m1GroupId, $m1SetId) + { + try { + // Get M1 entity type ID + $m1EntityTypeId = DB::connection($this->magento1Connection) + ->table($this->magento1Prefix . 'eav_entity_type') + ->where('entity_type_code', 'catalog_category') + ->value('entity_type_id'); + + if (!$m1EntityTypeId) { + return ['success' => false, 'message' => 'Magento 1 entity type not found']; + } + + // Get M1 attribute group + $m1Group = DB::connection($this->magento1Connection) + ->table($this->magento1Prefix . 'eav_attribute_group') + ->where('attribute_group_id', $m1GroupId) + ->where('attribute_set_id', $m1SetId) + ->first(); + + if (!$m1Group) { + return ['success' => false, 'message' => 'Attribute group not found in Magento 1']; + } + + // Get M1 attribute set + $m1Set = DB::connection($this->magento1Connection) + ->table($this->magento1Prefix . 'eav_attribute_set') + ->where('attribute_set_id', $m1SetId) + ->where('entity_type_id', $m1EntityTypeId) + ->first(); + + if (!$m1Set) { + return ['success' => false, 'message' => 'Attribute set not found in Magento 1']; + } + + // Get M2 entity type ID + $m2EntityTypeId = DB::connection($this->magento2Connection) + ->table($this->magento2Prefix . 'eav_entity_type') + ->where('entity_type_code', 'catalog_category') + ->value('entity_type_id'); + + if (!$m2EntityTypeId) { + return ['success' => false, 'message' => 'Magento 2 entity type not found']; + } + + // Find or create the corresponding attribute set in M2 + $m2Set = DB::connection($this->magento2Connection) + ->table($this->magento2Prefix . 'eav_attribute_set') + ->where('entity_type_id', $m2EntityTypeId) + ->where('attribute_set_name', $m1Set->attribute_set_name) + ->first(); + + if (!$m2Set) { + // Create the attribute set in M2 + $m2SetId = DB::connection($this->magento2Connection) + ->table($this->magento2Prefix . 'eav_attribute_set') + ->insertGetId([ + 'entity_type_id' => $m2EntityTypeId, + 'attribute_set_name' => $m1Set->attribute_set_name, + 'sort_order' => $m1Set->sort_order ?? 0, + ]); + $m2Set = (object)['attribute_set_id' => $m2SetId]; + } else { + $m2SetId = $m2Set->attribute_set_id; + } + + // Check if group already exists in M2 + $existingGroup = DB::connection($this->magento2Connection) + ->table($this->magento2Prefix . 'eav_attribute_group') + ->where('attribute_set_id', $m2SetId) + ->where('attribute_group_name', $m1Group->attribute_group_name) + ->first(); + + if ($existingGroup) { + return ['success' => false, 'message' => 'Attribute group already exists in Magento 2']; + } + + DB::connection($this->magento2Connection)->beginTransaction(); + + // Create the attribute group in M2 + $m2GroupId = DB::connection($this->magento2Connection) + ->table($this->magento2Prefix . 'eav_attribute_group') + ->insertGetId([ + 'attribute_set_id' => $m2SetId, + 'attribute_group_name' => $m1Group->attribute_group_name, + 'sort_order' => $m1Group->sort_order ?? 0, + ]); + + // Get attributes from M1 group + $m1Attributes = DB::connection($this->magento1Connection) + ->table($this->magento1Prefix . 'eav_entity_attribute') + ->where('attribute_set_id', $m1SetId) + ->where('attribute_group_id', $m1GroupId) + ->orderBy('sort_order') + ->get(); + + // Migrate attributes to M2 group (if they exist in M2) + $migratedCount = 0; + foreach ($m1Attributes as $m1EntityAttr) { + // Get M1 attribute code + $m1Attribute = DB::connection($this->magento1Connection) + ->table($this->magento1Prefix . 'eav_attribute') + ->where('attribute_id', $m1EntityAttr->attribute_id) + ->first(); + + if ($m1Attribute) { + // Find corresponding M2 attribute by code + $m2Attribute = DB::connection($this->magento2Connection) + ->table($this->magento2Prefix . 'eav_attribute') + ->where('entity_type_id', $m2EntityTypeId) + ->where('attribute_code', $m1Attribute->attribute_code) + ->first(); + + if ($m2Attribute) { + // Check if already in entity_attribute + $exists = DB::connection($this->magento2Connection) + ->table($this->magento2Prefix . 'eav_entity_attribute') + ->where('attribute_set_id', $m2SetId) + ->where('attribute_group_id', $m2GroupId) + ->where('attribute_id', $m2Attribute->attribute_id) + ->exists(); + + if (!$exists) { + DB::connection($this->magento2Connection) + ->table($this->magento2Prefix . 'eav_entity_attribute') + ->insert([ + 'entity_type_id' => $m2EntityTypeId, + 'attribute_set_id' => $m2SetId, + 'attribute_group_id' => $m2GroupId, + 'attribute_id' => $m2Attribute->attribute_id, + 'sort_order' => $m1EntityAttr->sort_order ?? 0, + ]); + $migratedCount++; + } + } + } + } + + DB::connection($this->magento2Connection)->commit(); + + return [ + 'success' => true, + 'message' => 'Attribute group migrated successfully', + 'group_name' => $m1Group->attribute_group_name, + 'm2_group_id' => $m2GroupId, + 'attributes_migrated' => $migratedCount, + ]; + + } catch (Exception $e) { + if (isset($this->magento2Connection)) { + DB::connection($this->magento2Connection)->rollBack(); + } + Log::error("Error migrating attribute group {$m1GroupId}: " . $e->getMessage()); + return ['success' => false, 'message' => 'Failed to migrate attribute group: ' . $e->getMessage()]; + } + } + /** * Recursively delete all children of a category */ diff --git a/resources/views/migration/index.blade.php b/resources/views/migration/index.blade.php index 0582247..6e73d8b 100644 --- a/resources/views/migration/index.blade.php +++ b/resources/views/migration/index.blade.php @@ -758,11 +758,19 @@ Attribute Set Attributes Sort Order + Actions - + @foreach($m1AttributeGroups as $group) - + @php + $groupKey = ($group->attribute_set_name ?? 'Default') . '|' . ($group->attribute_group_name ?? ''); + $isMissing = $m1AttributeGroupsMissingInM2->contains(function($missingGroup) use ($groupKey) { + $missingKey = ($missingGroup->attribute_set_name ?? 'Default') . '|' . ($missingGroup->attribute_group_name ?? ''); + return $missingKey === $groupKey; + }); + @endphp + {{ $group->attribute_group_id }} {{ $group->attribute_group_name ?? 'N/A' }} {{ $group->attribute_set_name ?? 'N/A' }} @@ -770,6 +778,19 @@ {{ $group->attribute_count ?? 0 }} {{ $group->sort_order ?? 'N/A' }} + + @if($isMissing) + + @else + ✓ Exists + @endif + @endforeach @@ -1316,6 +1337,64 @@ function migrateAttribute(attributeId, attributeCode) { }); } + function migrateAttributeGroup(groupId, setId, groupName) { + if (!confirm(`Are you sure you want to migrate the attribute group "${groupName}" from Magento 1 to Magento 2?\n\nThis will also migrate any attributes in this group that exist in Magento 2.`)) { + return; + } + + const button = document.getElementById(`migrate-group-btn-${groupId}-${setId}`); + const originalText = button.textContent; + button.disabled = true; + button.textContent = 'Migrating...'; + button.style.background = '#999'; + button.style.cursor = 'not-allowed'; + + const url = `{{ route("migration.migrate-attribute-group", ["groupId" => ":groupId", "setId" => ":setId"]) }}` + .replace(':groupId', groupId) + .replace(':setId', setId); + + fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-CSRF-TOKEN': '{{ csrf_token() }}' + } + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + // Update the row + const row = document.getElementById(`group-row-${groupId}-${setId}`); + if (row) { + row.style.opacity = '0.5'; + row.style.background = '#d4edda'; + + // Update button to show success + button.textContent = 'Migrated ✓'; + button.style.background = '#28a745'; + button.disabled = true; + } + const message = data.attributes_migrated !== undefined + ? `Attribute group migrated successfully! ${data.attributes_migrated} attribute(s) were also migrated to the group.` + : 'Attribute group migrated successfully!'; + alert(message); + } else { + button.disabled = false; + button.textContent = originalText; + button.style.background = '#1976D2'; + button.style.cursor = 'pointer'; + alert('Error: ' + (data.message || 'Failed to migrate attribute group')); + } + }) + .catch(error => { + button.disabled = false; + button.textContent = originalText; + button.style.background = '#1976D2'; + button.style.cursor = 'pointer'; + alert('Error: ' + error.message); + }); + } + // Render tree structure function renderTree(container, nodes, source = 'm2') { nodes.forEach(node => { diff --git a/routes/web.php b/routes/web.php index 1aa93dd..4230653 100644 --- a/routes/web.php +++ b/routes/web.php @@ -17,4 +17,5 @@ Route::delete('/category/{categoryId}', [MagentoMigrationController::class, 'deleteCategory'])->name('migration.delete-category'); Route::put('/category/{categoryId}/rename', [MagentoMigrationController::class, 'renameCategory'])->name('migration.rename-category'); Route::post('/attribute/{attributeId}/migrate', [MagentoMigrationController::class, 'migrateAttribute'])->name('migration.migrate-attribute'); + Route::post('/attribute-group/{groupId}/{setId}/migrate', [MagentoMigrationController::class, 'migrateAttributeGroup'])->name('migration.migrate-attribute-group'); });