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([]); } } /** * 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([]); } } /** * Find categories in Magento 2 that don't exist in Magento 1 * Compares by category name (case-insensitive) */ public function getM2CategoriesNotInM1() { try { $m1Categories = $this->getMagento1Categories(); $m2Categories = $this->getMagento2Categories(); // Create a set of M1 category names (case-insensitive, normalized) $m1CategoryNames = $m1Categories->map(function($cat) { return strtolower(trim($cat->name ?? '')); })->filter(function($name) { return !empty($name); })->unique()->toArray(); // Get all M2 categories to build a map for root category lookup $m2CategoryMap = []; foreach ($m2Categories as $cat) { $m2CategoryMap[$cat->entity_id] = $cat; } // Find M2 categories that don't have a matching name in M1 $missingCategories = $m2Categories->filter(function($m2Cat) use ($m1CategoryNames) { $m2Name = strtolower(trim($m2Cat->name ?? '')); return !empty($m2Name) && !in_array($m2Name, $m1CategoryNames); })->map(function($m2Cat) use ($m2CategoryMap) { // Extract root category ID from path (typically second element: 1/2/3/4 -> 2) $rootCategoryId = null; $rootCategoryName = 'N/A'; if (!empty($m2Cat->path)) { $pathParts = explode('/', $m2Cat->path); // Root category is typically at index 1 (second element) if (count($pathParts) > 1 && isset($pathParts[1])) { $rootCategoryId = (int)$pathParts[1]; // Get root category name if it exists in our map if (isset($m2CategoryMap[$rootCategoryId])) { $rootCategoryName = $m2CategoryMap[$rootCategoryId]->name ?? "ID: {$rootCategoryId}"; } else { $rootCategoryName = "ID: {$rootCategoryId}"; } } } // Add root category info to the category object $m2Cat->root_category_id = $rootCategoryId; $m2Cat->root_category_name = $rootCategoryName; return $m2Cat; })->values(); return $missingCategories; } catch (Exception $e) { Log::error('Error finding M2 categories not in M1: ' . $e->getMessage()); return collect([]); } } protected function getCategoryProductCounts($categoryIds, $source = 'm2') { if (empty($categoryIds)) { return []; } try { $connection = $source === 'm1' ? $this->magento1Connection : $this->magento2Connection; $prefix = $source === 'm1' ? $this->magento1Prefix : $this->magento2Prefix; $tableName = $prefix . 'catalog_category_product'; // Check if table exists try { $productCounts = DB::connection($connection) ->table($tableName) ->whereIn('category_id', $categoryIds) ->select('category_id', DB::raw('COUNT(product_id) as product_count')) ->groupBy('category_id') ->pluck('product_count', 'category_id') ->toArray(); } catch (Exception $e) { // Table might not exist, return empty array Log::warning("Table {$tableName} might not exist: " . $e->getMessage()); return []; } return $productCounts; } catch (Exception $e) { Log::error("Error getting product counts: " . $e->getMessage()); return []; } } /** * Build hierarchical category tree structure */ public function buildCategoryTreeHierarchy($categories, $source = 'm2') { $categoryMap = []; $rootCategories = []; // Get all category IDs $categoryIds = $categories->pluck('entity_id')->toArray(); // Get product counts for all categories $productCounts = $this->getCategoryProductCounts($categoryIds, $source); // First pass: create a map of all categories with children array foreach ($categories as $category) { $categoryId = $category->entity_id; $categoryMap[$categoryId] = [ 'id' => $categoryId, '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 ?? '', 'product_count' => $productCounts[$categoryId] ?? 0, '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 */ public function migrateCategories($storeMapping = []) { $this->storeMapping = $storeMapping; $this->migrationLog = []; $this->categoryMapping = []; $this->addedCount = 0; $this->existingCount = 0; 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); } } // Update children_count for all affected parent categories $this->updateChildrenCounts(); // 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), 'added_count' => $this->addedCount, 'existing_count' => $this->existingCount, '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; } /** * Find existing category in Magento 2 by name and parent */ protected function findExistingCategoryInM2($categoryName, $parentId) { if (!$categoryName) { return null; } try { // Get name attribute ID for M2 $entityTypeId = DB::connection($this->magento2Connection) ->table($this->magento2Prefix . 'eav_entity_type') ->where('entity_type_code', 'catalog_category') ->value('entity_type_id'); if (!$entityTypeId) { return null; } $nameAttributeId = DB::connection($this->magento2Connection) ->table($this->magento2Prefix . 'eav_attribute') ->where('entity_type_id', $entityTypeId) ->where('attribute_code', 'name') ->value('attribute_id'); if (!$nameAttributeId) { return null; } // Find categories with the same name and parent (checking default store view) $existingCategories = DB::connection($this->magento2Connection) ->table($this->magento2Prefix . 'catalog_category_entity', 'e') ->join($this->magento2Prefix . 'catalog_category_entity_varchar as v', function($join) use ($nameAttributeId) { $join->on('e.entity_id', '=', 'v.entity_id') ->where('v.attribute_id', '=', $nameAttributeId) ->where('v.store_id', '=', 0); }) ->where('v.value', '=', $categoryName) ->where('e.parent_id', '=', $parentId) ->select('e.entity_id') ->first(); return $existingCategories ? $existingCategories->entity_id : null; } catch (Exception $e) { Log::error("Error finding existing category in M2: " . $e->getMessage()); return null; } } /** * Migrate a single category */ protected function migrateCategory($m1Category, $parentId = null) { try { // Check if category already exists in mapping (already processed in this migration) 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; } // Check if category already exists in M2 database $existingM2CategoryId = null; if ($m1Category->name) { $existingM2CategoryId = $this->findExistingCategoryInM2($m1Category->name, $m2ParentId); } if ($existingM2CategoryId) { // Category already exists in M2, use existing ID $m2EntityId = $existingM2CategoryId; $this->existingCount++; $this->migrationLog[] = "Found existing category: ID {$m1Category->entity_id} -> {$m2EntityId} ({$m1Category->name})"; } else { // Category doesn't exist, create new one $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]); $this->addedCount++; $this->migrationLog[] = "Added new category: ID {$m1Category->entity_id} -> {$m2EntityId} ({$m1Category->name})"; } // Store mapping $this->categoryMapping[$m1Category->entity_id] = $m2EntityId; 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; } } /** * Update children_count for all parent categories */ protected function updateChildrenCounts() { try { // Get all unique parent IDs from migrated categories $parentIds = DB::connection($this->magento2Connection) ->table($this->magento2Prefix . 'catalog_category_entity') ->whereIn('entity_id', array_values($this->categoryMapping)) ->distinct() ->pluck('parent_id') ->toArray(); // Update children_count for each parent foreach ($parentIds as $parentId) { if ($parentId > 0) { $childrenCount = DB::connection($this->magento2Connection) ->table($this->magento2Prefix . 'catalog_category_entity') ->where('parent_id', $parentId) ->count(); DB::connection($this->magento2Connection) ->table($this->magento2Prefix . 'catalog_category_entity') ->where('entity_id', $parentId) ->update(['children_count' => $childrenCount]); } } } catch (Exception $e) { Log::error("Error updating children counts: " . $e->getMessage()); // Don't throw, just log the error } } /** * 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; } /** * Rename a Magento 2 category */ public function renameCategory($categoryId, $newName, $storeId = 0) { try { // Validate category exists $category = DB::connection($this->magento2Connection) ->table($this->magento2Prefix . 'catalog_category_entity') ->where('entity_id', $categoryId) ->first(); if (!$category) { return [ 'success' => false, 'message' => 'Category not found', ]; } // Prevent renaming system root if ($categoryId == 0 || $categoryId == 1) { return [ 'success' => false, 'message' => 'Cannot rename system root category', ]; } // Validate name $newName = trim($newName); if (empty($newName)) { return [ 'success' => false, 'message' => 'Category name cannot be empty', ]; } // Get name attribute ID $attributeIds = $this->getMagento2AttributeIds(); if (!isset($attributeIds['name'])) { return [ 'success' => false, 'message' => 'Name attribute not found', ]; } DB::connection($this->magento2Connection)->beginTransaction(); // Update name attribute for the specified store (default store = 0) $this->insertCategoryAttribute( $categoryId, $attributeIds['name'], $storeId, $newName ); DB::connection($this->magento2Connection)->commit(); return [ 'success' => true, 'message' => 'Category renamed successfully', 'new_name' => $newName, ]; } catch (Exception $e) { if (isset($this->magento2Connection)) { DB::connection($this->magento2Connection)->rollBack(); } Log::error("Error renaming category {$categoryId}: " . $e->getMessage()); return [ 'success' => false, 'message' => 'Failed to rename category: ' . $e->getMessage(), ]; } } /** * 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; } /** * Delete a category and all its children recursively */ public function deleteCategory($categoryId, $source = 'm2') { try { $connection = $source === 'm1' ? $this->magento1Connection : $this->magento2Connection; $prefix = $source === 'm1' ? $this->magento1Prefix : $this->magento2Prefix; // Get category information $category = DB::connection($connection) ->table($prefix . 'catalog_category_entity') ->where('entity_id', $categoryId) ->first(); if (!$category) { return [ 'success' => false, 'message' => 'Category not found', ]; } // Prevent deletion of system root (ID 0 or 1) if ($categoryId == 0 || $categoryId == 1) { return [ 'success' => false, 'message' => 'Cannot delete system root category', ]; } DB::connection($connection)->beginTransaction(); // Recursively delete all children first $this->deleteCategoryChildren($categoryId, $connection, $prefix); // Now delete the category itself $this->deleteCategoryData($categoryId, $connection, $prefix); // Update parent's children_count if parent exists if ($category->parent_id > 0 && $category->parent_id != 1) { $parentChildrenCount = DB::connection($connection) ->table($prefix . 'catalog_category_entity') ->where('parent_id', $category->parent_id) ->count(); DB::connection($connection) ->table($prefix . 'catalog_category_entity') ->where('entity_id', $category->parent_id) ->update(['children_count' => $parentChildrenCount]); } DB::connection($connection)->commit(); return [ 'success' => true, 'message' => 'Category and all subcategories deleted successfully', ]; } catch (Exception $e) { if (isset($connection)) { DB::connection($connection)->rollBack(); } Log::error("Error deleting category {$categoryId}: " . $e->getMessage()); return [ 'success' => false, 'message' => 'Failed to delete category: ' . $e->getMessage(), ]; } } /** * 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; } /** * Recursively delete all children of a category */ protected function deleteCategoryChildren($categoryId, $connection, $prefix) { // Get all children $children = DB::connection($connection) ->table($prefix . 'catalog_category_entity') ->where('parent_id', $categoryId) ->get(); // Recursively delete each child foreach ($children as $child) { $this->deleteCategoryChildren($child->entity_id, $connection, $prefix); $this->deleteCategoryData($child->entity_id, $connection, $prefix); } } /** * Delete category data (attributes and entity) */ protected function deleteCategoryData($categoryId, $connection, $prefix) { // Get entity type ID $entityTypeId = DB::connection($connection) ->table($prefix . 'eav_entity_type') ->where('entity_type_code', 'catalog_category') ->value('entity_type_id'); if ($entityTypeId) { // Delete attribute values from all attribute tables $attributeTables = ['varchar', 'int', 'text', 'decimal', 'datetime']; foreach ($attributeTables as $tableType) { $table = $prefix . 'catalog_category_entity_' . $tableType; try { DB::connection($connection) ->table($table) ->where('entity_id', $categoryId) ->delete(); } catch (Exception $e) { // Table might not exist, continue Log::warning("Table {$table} might not exist or has no data: " . $e->getMessage()); } } // Delete category product associations try { DB::connection($connection) ->table($prefix . 'catalog_category_product') ->where('category_id', $categoryId) ->delete(); } catch (Exception $e) { Log::warning("Table {$prefix}catalog_category_product might not exist: " . $e->getMessage()); } } // Delete the category entity DB::connection($connection) ->table($prefix . 'catalog_category_entity') ->where('entity_id', $categoryId) ->delete(); } }