added migrate attributes

This commit is contained in:
Chris Rosenau 2025-11-08 15:43:12 -07:00
parent 64ddd5bb01
commit fb21111ab2
4 changed files with 305 additions and 0 deletions

View File

@ -28,6 +28,7 @@ public function index()
$m2CategoriesNotInM1 = $this->migrationService->getM2CategoriesNotInM1();
$m1Attributes = $this->migrationService->getMagento1Attributes();
$m2Attributes = $this->migrationService->getMagento2Attributes();
$m1AttributesMissingInM2 = $this->migrationService->getM1AttributesMissingInM2();
$m1AttributeGroups = $this->migrationService->getMagento1AttributeGroups();
$m2AttributeGroups = $this->migrationService->getMagento2AttributeGroups();
@ -40,6 +41,7 @@ public function index()
'm2CategoriesNotInM1' => $m2CategoriesNotInM1,
'm1Attributes' => $m1Attributes,
'm2Attributes' => $m2Attributes,
'm1AttributesMissingInM2' => $m1AttributesMissingInM2,
'm1AttributeGroups' => $m1AttributeGroups,
'm2AttributeGroups' => $m2AttributeGroups,
]);
@ -176,6 +178,26 @@ public function renameCategory(Request $request, $categoryId)
}
}
/**
* Migrate an attribute from Magento 1 to Magento 2
*/
public function migrateAttribute(Request $request, $attributeId)
{
try {
$result = $this->migrationService->migrateAttribute($attributeId);
return response()->json($result, $result['success'] ? 200 : 400);
} catch (\Exception $e) {
Log::error('Attribute migration error: ' . $e->getMessage());
return response()->json([
'success' => false,
'message' => 'Migration failed: ' . $e->getMessage(),
], 500);
}
}
/**
* Execute the migration
*/

View File

@ -1030,6 +1030,154 @@ public function getMagento2Attributes()
}
}
/**
* Get attributes that exist in Magento 1 but are missing in Magento 2
*/
public function getM1AttributesMissingInM2()
{
try {
$m1Attributes = $this->getMagento1Attributes();
$m2Attributes = $this->getMagento2Attributes();
// Get all M2 attribute codes
$m2AttributeCodes = $m2Attributes->pluck('attribute_code')->toArray();
// Filter M1 attributes that don't exist in M2
$missingAttributes = $m1Attributes->filter(function ($attr) use ($m2AttributeCodes) {
return !in_array($attr->attribute_code, $m2AttributeCodes);
});
return $missingAttributes->values();
} catch (Exception $e) {
Log::error('Error fetching missing attributes: ' . $e->getMessage());
return collect([]);
}
}
/**
* Migrate an attribute from Magento 1 to Magento 2
*/
public function migrateAttribute($m1AttributeId)
{
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 full attribute data from M1
$m1Attribute = DB::connection($this->magento1Connection)
->table($this->magento1Prefix . 'eav_attribute')
->where('attribute_id', $m1AttributeId)
->where('entity_type_id', $m1EntityTypeId)
->first();
if (!$m1Attribute) {
return ['success' => false, 'message' => 'Attribute not found in Magento 1'];
}
// Check if attribute already exists in M2
$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'];
}
$existingAttribute = DB::connection($this->magento2Connection)
->table($this->magento2Prefix . 'eav_attribute')
->where('entity_type_id', $m2EntityTypeId)
->where('attribute_code', $m1Attribute->attribute_code)
->first();
if ($existingAttribute) {
return ['success' => false, 'message' => 'Attribute already exists in Magento 2'];
}
DB::connection($this->magento2Connection)->beginTransaction();
// Insert attribute into M2
$m2AttributeId = DB::connection($this->magento2Connection)
->table($this->magento2Prefix . 'eav_attribute')
->insertGetId([
'entity_type_id' => $m2EntityTypeId,
'attribute_code' => $m1Attribute->attribute_code,
'attribute_model' => $m1Attribute->attribute_model ?? null,
'backend_model' => $m1Attribute->backend_model ?? null,
'backend_type' => $m1Attribute->backend_type,
'backend_table' => $m1Attribute->backend_table ?? null,
'frontend_model' => $m1Attribute->frontend_model ?? null,
'frontend_input' => $m1Attribute->frontend_input ?? null,
'frontend_label' => $m1Attribute->frontend_label ?? null,
'frontend_class' => $m1Attribute->frontend_class ?? null,
'source_model' => $m1Attribute->source_model ?? null,
'is_required' => $m1Attribute->is_required ?? 0,
'is_user_defined' => $m1Attribute->is_user_defined ?? 1,
'default_value' => $m1Attribute->default_value ?? null,
'is_unique' => $m1Attribute->is_unique ?? 0,
'note' => $m1Attribute->note ?? null,
]);
// Add attribute to default attribute set if it exists
$defaultAttributeSet = DB::connection($this->magento2Connection)
->table($this->magento2Prefix . 'eav_attribute_set')
->where('entity_type_id', $m2EntityTypeId)
->where('attribute_set_name', 'Default')
->first();
if ($defaultAttributeSet) {
$defaultGroup = DB::connection($this->magento2Connection)
->table($this->magento2Prefix . 'eav_attribute_group')
->where('attribute_set_id', $defaultAttributeSet->attribute_set_id)
->orderBy('sort_order')
->first();
if ($defaultGroup) {
// Get max sort order for this group
$maxSortOrder = DB::connection($this->magento2Connection)
->table($this->magento2Prefix . 'eav_entity_attribute')
->where('attribute_set_id', $defaultAttributeSet->attribute_set_id)
->where('attribute_group_id', $defaultGroup->attribute_group_id)
->max('sort_order') ?? 0;
// Add attribute to entity_attribute table
DB::connection($this->magento2Connection)
->table($this->magento2Prefix . 'eav_entity_attribute')
->insert([
'entity_type_id' => $m2EntityTypeId,
'attribute_set_id' => $defaultAttributeSet->attribute_set_id,
'attribute_group_id' => $defaultGroup->attribute_group_id,
'attribute_id' => $m2AttributeId,
'sort_order' => $maxSortOrder + 10,
]);
}
}
DB::connection($this->magento2Connection)->commit();
return [
'success' => true,
'message' => 'Attribute migrated successfully',
'attribute_code' => $m1Attribute->attribute_code,
'm2_attribute_id' => $m2AttributeId,
];
} catch (Exception $e) {
if (isset($this->magento2Connection)) {
DB::connection($this->magento2Connection)->rollBack();
}
Log::error("Error migrating attribute {$m1AttributeId}: " . $e->getMessage());
return ['success' => false, 'message' => 'Failed to migrate attribute: ' . $e->getMessage()];
}
}
/**
* Get attribute groups from Magento 1
*/

View File

@ -821,6 +821,71 @@
</div>
</div>
<!-- Missing Attributes Section -->
<div class="section" style="margin-top: 30px;">
<h2>⚠️ Magento 1 Attributes Missing in Magento 2</h2>
<p>These attributes exist in Magento 1 but do not have a matching attribute code in Magento 2:</p>
@if($m1AttributesMissingInM2->count() > 0)
<div style="margin-top: 15px; max-height: 400px; overflow-y: auto;">
<table style="width: 100%; border-collapse: collapse; background: white; border-radius: 6px;">
<thead>
<tr style="background: #f8f9fa; border-bottom: 2px solid #dee2e6;">
<th style="padding: 12px; text-align: left; font-weight: 600; color: #333;">ID</th>
<th style="padding: 12px; text-align: left; font-weight: 600; color: #333;">Attribute Code</th>
<th style="padding: 12px; text-align: left; font-weight: 600; color: #333;">Label</th>
<th style="padding: 12px; text-align: left; font-weight: 600; color: #333;">Type</th>
<th style="padding: 12px; text-align: left; font-weight: 600; color: #333;">Input</th>
<th style="padding: 12px; text-align: left; font-weight: 600; color: #333;">Required</th>
<th style="padding: 12px; text-align: left; font-weight: 600; color: #333;">User Defined</th>
<th style="padding: 12px; text-align: left; font-weight: 600; color: #333;">Actions</th>
</tr>
</thead>
<tbody id="missingAttributesTableBody">
@foreach($m1AttributesMissingInM2 as $attr)
<tr id="attr-row-{{ $attr->attribute_id }}" style="border-bottom: 1px solid #dee2e6;">
<td style="padding: 10px 12px;">{{ $attr->attribute_id }}</td>
<td style="padding: 10px 12px; font-weight: 500; color: #667eea;">{{ $attr->attribute_code }}</td>
<td style="padding: 10px 12px;">{{ $attr->frontend_label ?? 'N/A' }}</td>
<td style="padding: 10px 12px; color: #666;">{{ $attr->backend_type ?? 'N/A' }}</td>
<td style="padding: 10px 12px; color: #666;">{{ $attr->frontend_input ?? 'N/A' }}</td>
<td style="padding: 10px 12px;">
@if($attr->is_required ?? 0)
<span style="color: #d32f2f; font-weight: 600;">Yes</span>
@else
<span style="color: #666;">No</span>
@endif
</td>
<td style="padding: 10px 12px;">
@if($attr->is_user_defined ?? 0)
<span style="color: #1976D2; font-weight: 600;">Yes</span>
@else
<span style="color: #666;">No</span>
@endif
</td>
<td style="padding: 10px 12px;">
<button
class="btn btn-primary"
onclick="migrateAttribute({{ $attr->attribute_id }}, '{{ $attr->attribute_code }}')"
id="migrate-btn-{{ $attr->attribute_id }}"
style="padding: 6px 12px; font-size: 0.85em; background: #1976D2; color: white; border: none; border-radius: 4px; cursor: pointer;">
Migrate
</button>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
<div id="missingAttributesCount" style="margin-top: 15px; padding: 12px; background: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;">
<strong>Total:</strong> <span id="missingAttributesTotal">{{ $m1AttributesMissingInM2->count() }}</span> {{ Str::plural('attribute', $m1AttributesMissingInM2->count()) }} found in Magento 1 but not in Magento 2.
</div>
@else
<div style="margin-top: 15px; padding: 15px; background: #d4edda; border-left: 4px solid #28a745; border-radius: 4px; color: #155724;">
All Magento 1 attributes have matching attribute codes in Magento 2.
</div>
@endif
</div>
<!-- Category Attributes Section -->
<div class="section" style="margin-top: 30px;">
<h2>📋 Category Attributes</h2>
@ -1182,6 +1247,75 @@ function deleteCategory(categoryId, categoryName, source) {
});
}
function migrateAttribute(attributeId, attributeCode) {
if (!confirm(`Are you sure you want to migrate the attribute "${attributeCode}" from Magento 1 to Magento 2?`)) {
return;
}
const button = document.getElementById(`migrate-btn-${attributeId}`);
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", ["attributeId" => ":id"]) }}`.replace(':id', attributeId);
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': '{{ csrf_token() }}'
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
// Remove the row from the table
const row = document.getElementById(`attr-row-${attributeId}`);
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;
// Update count
const totalSpan = document.getElementById('missingAttributesTotal');
if (totalSpan) {
const currentCount = parseInt(totalSpan.textContent);
if (currentCount > 0) {
totalSpan.textContent = currentCount - 1;
const countDiv = document.getElementById('missingAttributesCount');
if (countDiv && currentCount - 1 === 0) {
countDiv.innerHTML = '<strong>Total:</strong> 0 attributes found in Magento 1 but not in Magento 2.';
} else if (countDiv) {
const plural = (currentCount - 1) === 1 ? 'attribute' : 'attributes';
countDiv.innerHTML = `<strong>Total:</strong> ${currentCount - 1} ${plural} found in Magento 1 but not in Magento 2.`;
}
}
}
}
alert('Attribute migrated successfully!');
} else {
button.disabled = false;
button.textContent = originalText;
button.style.background = '#1976D2';
button.style.cursor = 'pointer';
alert('Error: ' + (data.message || 'Failed to migrate attribute'));
}
})
.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 => {

View File

@ -16,4 +16,5 @@
Route::get('/magento2-category-tree', [MagentoMigrationController::class, 'getMagento2CategoryTree'])->name('migration.magento2-category-tree');
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');
});