added migrate attribute groups

This commit is contained in:
Chris Rosenau 2025-11-08 15:47:07 -07:00
parent fb21111ab2
commit acef61c43d
4 changed files with 293 additions and 2 deletions

View File

@ -31,6 +31,7 @@ public function index()
$m1AttributesMissingInM2 = $this->migrationService->getM1AttributesMissingInM2(); $m1AttributesMissingInM2 = $this->migrationService->getM1AttributesMissingInM2();
$m1AttributeGroups = $this->migrationService->getMagento1AttributeGroups(); $m1AttributeGroups = $this->migrationService->getMagento1AttributeGroups();
$m2AttributeGroups = $this->migrationService->getMagento2AttributeGroups(); $m2AttributeGroups = $this->migrationService->getMagento2AttributeGroups();
$m1AttributeGroupsMissingInM2 = $this->migrationService->getM1AttributeGroupsMissingInM2();
return view('migration.index', [ return view('migration.index', [
'm1Stores' => $m1Stores, 'm1Stores' => $m1Stores,
@ -44,6 +45,7 @@ public function index()
'm1AttributesMissingInM2' => $m1AttributesMissingInM2, 'm1AttributesMissingInM2' => $m1AttributesMissingInM2,
'm1AttributeGroups' => $m1AttributeGroups, 'm1AttributeGroups' => $m1AttributeGroups,
'm2AttributeGroups' => $m2AttributeGroups, '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 * Execute the migration
*/ */

View File

@ -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 * Recursively delete all children of a category
*/ */

View File

@ -758,11 +758,19 @@
<th style="padding: 10px; text-align: left; font-weight: 600; color: #333; font-size: 0.9em;">Attribute Set</th> <th style="padding: 10px; text-align: left; font-weight: 600; color: #333; font-size: 0.9em;">Attribute Set</th>
<th style="padding: 10px; text-align: left; font-weight: 600; color: #333; font-size: 0.9em;">Attributes</th> <th style="padding: 10px; text-align: left; font-weight: 600; color: #333; font-size: 0.9em;">Attributes</th>
<th style="padding: 10px; text-align: left; font-weight: 600; color: #333; font-size: 0.9em;">Sort Order</th> <th style="padding: 10px; text-align: left; font-weight: 600; color: #333; font-size: 0.9em;">Sort Order</th>
<th style="padding: 10px; text-align: left; font-weight: 600; color: #333; font-size: 0.9em;">Actions</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody id="m1AttributeGroupsTableBody">
@foreach($m1AttributeGroups as $group) @foreach($m1AttributeGroups as $group)
<tr style="border-bottom: 1px solid #e0e0e0;"> @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
<tr id="group-row-{{ $group->attribute_group_id }}-{{ $group->attribute_set_id }}" style="border-bottom: 1px solid #e0e0e0;">
<td style="padding: 8px 10px; font-size: 0.85em;">{{ $group->attribute_group_id }}</td> <td style="padding: 8px 10px; font-size: 0.85em;">{{ $group->attribute_group_id }}</td>
<td style="padding: 8px 10px; font-size: 0.85em; font-weight: 500; color: #667eea;">{{ $group->attribute_group_name ?? 'N/A' }}</td> <td style="padding: 8px 10px; font-size: 0.85em; font-weight: 500; color: #667eea;">{{ $group->attribute_group_name ?? 'N/A' }}</td>
<td style="padding: 8px 10px; font-size: 0.85em; color: #666;">{{ $group->attribute_set_name ?? 'N/A' }}</td> <td style="padding: 8px 10px; font-size: 0.85em; color: #666;">{{ $group->attribute_set_name ?? 'N/A' }}</td>
@ -770,6 +778,19 @@
<span style="font-weight: 600; color: #667eea;">{{ $group->attribute_count ?? 0 }}</span> <span style="font-weight: 600; color: #667eea;">{{ $group->attribute_count ?? 0 }}</span>
</td> </td>
<td style="padding: 8px 10px; font-size: 0.85em; color: #666;">{{ $group->sort_order ?? 'N/A' }}</td> <td style="padding: 8px 10px; font-size: 0.85em; color: #666;">{{ $group->sort_order ?? 'N/A' }}</td>
<td style="padding: 8px 10px; font-size: 0.85em;">
@if($isMissing)
<button
class="btn btn-primary"
onclick="migrateAttributeGroup({{ $group->attribute_group_id }}, {{ $group->attribute_set_id }}, '{{ $group->attribute_group_name ?? 'N/A' }}')"
id="migrate-group-btn-{{ $group->attribute_group_id }}-{{ $group->attribute_set_id }}"
style="padding: 6px 12px; font-size: 0.85em; background: #1976D2; color: white; border: none; border-radius: 4px; cursor: pointer;">
Migrate
</button>
@else
<span style="color: #28a745; font-size: 0.85em;"> Exists</span>
@endif
</td>
</tr> </tr>
@endforeach @endforeach
</tbody> </tbody>
@ -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 // Render tree structure
function renderTree(container, nodes, source = 'm2') { function renderTree(container, nodes, source = 'm2') {
nodes.forEach(node => { nodes.forEach(node => {

View File

@ -17,4 +17,5 @@
Route::delete('/category/{categoryId}', [MagentoMigrationController::class, 'deleteCategory'])->name('migration.delete-category'); Route::delete('/category/{categoryId}', [MagentoMigrationController::class, 'deleteCategory'])->name('migration.delete-category');
Route::put('/category/{categoryId}/rename', [MagentoMigrationController::class, 'renameCategory'])->name('migration.rename-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/{attributeId}/migrate', [MagentoMigrationController::class, 'migrateAttribute'])->name('migration.migrate-attribute');
Route::post('/attribute-group/{groupId}/{setId}/migrate', [MagentoMigrationController::class, 'migrateAttributeGroup'])->name('migration.migrate-attribute-group');
}); });