diff --git a/app/Http/Controllers/MagentoMigrationController.php b/app/Http/Controllers/MagentoMigrationController.php index 5aa212e..43a07b3 100644 --- a/app/Http/Controllers/MagentoMigrationController.php +++ b/app/Http/Controllers/MagentoMigrationController.php @@ -68,6 +68,50 @@ public function getMagento1Categories() ]); } + /** + * Get Magento 1 category tree + */ + public function getMagento1CategoryTree() + { + try { + $categories = $this->migrationService->getMagento1Categories(); + $tree = $this->migrationService->buildCategoryTreeHierarchy($categories); + + return response()->json([ + 'success' => true, + 'tree' => $tree, + ]); + } catch (\Exception $e) { + Log::error('Error fetching M1 category tree: ' . $e->getMessage()); + return response()->json([ + 'success' => false, + 'message' => 'Failed to fetch category tree: ' . $e->getMessage(), + ], 500); + } + } + + /** + * Get Magento 2 category tree + */ + public function getMagento2CategoryTree() + { + try { + $categories = $this->migrationService->getMagento2Categories(); + $tree = $this->migrationService->buildCategoryTreeHierarchy($categories); + + return response()->json([ + 'success' => true, + 'tree' => $tree, + ]); + } catch (\Exception $e) { + Log::error('Error fetching M2 category tree: ' . $e->getMessage()); + return response()->json([ + 'success' => false, + 'message' => 'Failed to fetch category tree: ' . $e->getMessage(), + ], 500); + } + } + /** * Execute the migration */ diff --git a/app/Services/MagentoCategoryMigrationService.php b/app/Services/MagentoCategoryMigrationService.php index 1d92355..0ac843a 100644 --- a/app/Services/MagentoCategoryMigrationService.php +++ b/app/Services/MagentoCategoryMigrationService.php @@ -152,6 +152,156 @@ public function getMagento1Categories($storeId = null) } } + /** + * Get all categories from Magento 2 for a specific store + */ + public function getMagento2Categories($storeId = null) + { + try { + // Get entity type ID + $entityTypeId = DB::connection($this->magento2Connection) + ->table($this->magento2Prefix . 'eav_entity_type') + ->where('entity_type_code', 'catalog_category') + ->value('entity_type_id'); + + if (!$entityTypeId) { + return collect([]); + } + + // Get attribute IDs + $nameAttributeId = DB::connection($this->magento2Connection) + ->table($this->magento2Prefix . 'eav_attribute') + ->where('entity_type_id', $entityTypeId) + ->where('attribute_code', 'name') + ->value('attribute_id'); + + $isActiveAttributeId = DB::connection($this->magento2Connection) + ->table($this->magento2Prefix . 'eav_attribute') + ->where('entity_type_id', $entityTypeId) + ->where('attribute_code', 'is_active') + ->value('attribute_id'); + + $urlKeyAttributeId = DB::connection($this->magento2Connection) + ->table($this->magento2Prefix . '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->magento2Connection) + ->table($this->magento2Prefix . '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->magento2Connection) + ->table($this->magento2Prefix . 'catalog_category_entity_varchar') + ->where('attribute_id', $nameAttributeId) + ->where('store_id', $targetStoreId) + ->pluck('value', 'entity_id') + ->toArray(); + } + + if ($isActiveAttributeId) { + $isActiveValues = DB::connection($this->magento2Connection) + ->table($this->magento2Prefix . 'catalog_category_entity_int') + ->where('attribute_id', $isActiveAttributeId) + ->where('store_id', $targetStoreId) + ->pluck('value', 'entity_id') + ->toArray(); + } + + if ($urlKeyAttributeId) { + $urlKeyValues = DB::connection($this->magento2Connection) + ->table($this->magento2Prefix . '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 2 categories: ' . $e->getMessage()); + return collect([]); + } + } + + /** + * Build hierarchical category tree structure + */ + public function buildCategoryTreeHierarchy($categories) + { + $categoryMap = []; + $rootCategories = []; + + // First pass: create a map of all categories with children array + foreach ($categories as $category) { + $categoryMap[$category->entity_id] = [ + 'id' => $category->entity_id, + 'name' => $category->name ?? 'Unnamed Category', + 'parent_id' => $category->parent_id, + 'level' => $category->level ?? 0, + 'position' => $category->position ?? 0, + 'is_active' => $category->is_active ?? 0, + 'path' => $category->path ?? '', + 'children' => [] + ]; + } + + // Second pass: build the tree structure by assigning children to parents + foreach ($categoryMap as $id => $category) { + $parentId = $category['parent_id']; + + // Skip if parent is self (prevent circular references) + if ($parentId == $id) { + $rootCategories[] = &$categoryMap[$id]; + continue; + } + + // Check if this is a root category (parent is 0, 1, or doesn't exist in map) + if ($parentId == 0 || $parentId == 1 || !isset($categoryMap[$parentId])) { + $rootCategories[] = &$categoryMap[$id]; + } else { + // Add as child of parent + $categoryMap[$parentId]['children'][] = &$categoryMap[$id]; + } + } + + // Sort function for categories + $sortCategories = function(&$categories) use (&$sortCategories) { + usort($categories, function($a, $b) { + return $a['position'] <=> $b['position']; + }); + // Recursively sort children + foreach ($categories as &$category) { + if (!empty($category['children'])) { + $sortCategories($category['children']); + } + } + }; + + // Sort root categories and all children recursively + $sortCategories($rootCategories); + + return $rootCategories; + } + /** * Migrate categories from Magento 1 to Magento 2 */ diff --git a/resources/views/migration/index.blade.php b/resources/views/migration/index.blade.php index 19da52e..efd5ee7 100644 --- a/resources/views/migration/index.blade.php +++ b/resources/views/migration/index.blade.php @@ -251,6 +251,154 @@ color: #666; margin-top: 5px; } + + /* Tabs */ + .tabs { + display: flex; + border-bottom: 2px solid #ddd; + margin-bottom: 20px; + background: #f8f9fa; + border-radius: 8px 8px 0 0; + } + + .tab { + padding: 15px 30px; + cursor: pointer; + border: none; + background: transparent; + font-size: 1em; + font-weight: 600; + color: #666; + transition: all 0.3s; + border-bottom: 3px solid transparent; + margin-bottom: -2px; + } + + .tab:hover { + background: #e9ecef; + color: #333; + } + + .tab.active { + color: #667eea; + border-bottom-color: #667eea; + background: white; + } + + .tab-content { + display: none; + } + + .tab-content.active { + display: block; + } + + /* Tree View */ + .tree-container { + background: white; + border: 1px solid #ddd; + border-radius: 6px; + padding: 20px; + max-height: 600px; + overflow-y: auto; + } + + .tree-node { + margin: 5px 0; + } + + .tree-node-item { + display: flex; + align-items: center; + padding: 8px 12px; + border-radius: 4px; + cursor: pointer; + user-select: none; + transition: background 0.2s; + } + + .tree-node-item:hover { + background: #f0f0f0; + } + + .tree-toggle { + width: 20px; + height: 20px; + display: inline-flex; + align-items: center; + justify-content: center; + margin-right: 8px; + font-size: 12px; + color: #666; + cursor: pointer; + } + + .tree-toggle.expanded::before { + content: '▼'; + } + + .tree-toggle.collapsed::before { + content: '▶'; + } + + .tree-toggle.leaf { + width: 20px; + margin-right: 8px; + } + + .tree-label { + flex: 1; + display: flex; + align-items: center; + gap: 10px; + } + + .tree-label-text { + font-weight: 500; + color: #333; + } + + .tree-badge { + padding: 2px 8px; + border-radius: 12px; + font-size: 0.75em; + font-weight: 600; + } + + .tree-badge.active { + background: #d4edda; + color: #155724; + } + + .tree-badge.inactive { + background: #f8d7da; + color: #721c24; + } + + .tree-children { + margin-left: 28px; + border-left: 2px solid #e0e0e0; + padding-left: 12px; + display: none; + } + + .tree-children.expanded { + display: block; + } + + .tree-loading { + text-align: center; + padding: 40px; + color: #666; + } + + .tree-error { + background: #f8d7da; + color: #721c24; + padding: 15px; + border-radius: 6px; + margin: 10px 0; + }
@@ -261,6 +409,14 @@View category hierarchies from Magento 1 and Magento 2
+ +