627 lines
28 KiB
PHP
627 lines
28 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
class AdditionalController extends Controller
|
|
{
|
|
protected $magento1Connection = 'magento1';
|
|
protected $magento2Connection = 'magento2';
|
|
protected $magento1Prefix;
|
|
protected $magento2Prefix;
|
|
|
|
/** Default attribute code for frontpage tabs (can be overridden in request) */
|
|
protected $frontpageTabsAttributeCode = 'frontpage_tabs';
|
|
|
|
public function __construct()
|
|
{
|
|
$this->magento1Prefix = config('database.connections.magento1.prefix', '');
|
|
$this->magento2Prefix = config('database.connections.magento2.prefix', '');
|
|
}
|
|
|
|
/**
|
|
* Compare M1 text attributes (potential tab content) against M2 mgs_protabs config.
|
|
* Returns per-scope breakdown of what is configured and what is missing.
|
|
*/
|
|
public function compareProtabs(Request $request)
|
|
{
|
|
try {
|
|
// --- M2: gather all Protabs entries and website/store names ---
|
|
$m2Tabs = DB::connection($this->magento2Connection)
|
|
->table('mgs_protabs')
|
|
->orderBy('scope')
|
|
->orderBy('scope_id')
|
|
->orderBy('position')
|
|
->get();
|
|
|
|
$m2Websites = DB::connection($this->magento2Connection)
|
|
->table('store_website')
|
|
->whereNotIn('website_id', [0])
|
|
->whereRaw("name NOT LIKE '%Admin%'")
|
|
->pluck('name', 'website_id');
|
|
|
|
$m2Stores = DB::connection($this->magento2Connection)
|
|
->table('store')
|
|
->where('store_id', '>', 0)
|
|
->pluck('name', 'store_id');
|
|
|
|
// Tabs keyed by "scope:scope_id" for easy lookup
|
|
$tabsByScope = [];
|
|
foreach ($m2Tabs as $tab) {
|
|
$key = $tab->scope . ':' . $tab->scope_id;
|
|
$tabsByScope[$key][] = (array) $tab;
|
|
}
|
|
|
|
// --- M1: find all catalog_product text attributes with values ---
|
|
$entityTypeIdM1 = DB::connection($this->magento1Connection)
|
|
->table($this->magento1Prefix . 'eav_entity_type')
|
|
->where('entity_type_code', 'catalog_product')
|
|
->value('entity_type_id');
|
|
|
|
// Get all text-backend attributes that actually have product values
|
|
$m1TextAttrs = DB::connection($this->magento1Connection)
|
|
->table($this->magento1Prefix . 'eav_attribute as a')
|
|
->join($this->magento1Prefix . 'catalog_product_entity_text as v', 'a.attribute_id', '=', 'v.attribute_id')
|
|
->where('a.entity_type_id', $entityTypeIdM1)
|
|
->select('a.attribute_code', 'a.frontend_label', DB::raw('COUNT(v.value_id) as value_count'))
|
|
->whereNotNull('v.value')
|
|
->where('v.value', '!=', '')
|
|
->groupBy('a.attribute_id', 'a.attribute_code', 'a.frontend_label')
|
|
->having('value_count', '>', 0)
|
|
->orderBy('a.attribute_code')
|
|
->get()
|
|
->keyBy('attribute_code');
|
|
|
|
// --- Build per-scope comparison ---
|
|
// Collect all unique scopes from existing tabs
|
|
$scopes = $m2Tabs->map(fn($t) => ['scope' => $t->scope, 'scope_id' => $t->scope_id])
|
|
->unique(fn($s) => $s['scope'] . ':' . $s['scope_id'])
|
|
->values();
|
|
|
|
$comparison = [];
|
|
foreach ($scopes as $scope) {
|
|
$key = $scope['scope'] . ':' . $scope['scope_id'];
|
|
$existing = $tabsByScope[$key] ?? [];
|
|
$existingAttrCodes = collect($existing)
|
|
->where('tab_type', 'attribute')
|
|
->pluck('value')
|
|
->filter()
|
|
->flip(); // use as set
|
|
|
|
// Find M1 text attrs with values not in this scope's protabs
|
|
$missing = [];
|
|
foreach ($m1TextAttrs as $attrCode => $attr) {
|
|
if (!isset($existingAttrCodes[$attrCode])) {
|
|
$missing[] = [
|
|
'attribute_code' => $attrCode,
|
|
'frontend_label' => $attr->frontend_label ?? $attrCode,
|
|
'value_count' => $attr->value_count,
|
|
'suggested_title' => ucwords(str_replace('_', ' ', $attrCode)),
|
|
'suggested_pos' => count($existing) + count($missing) + 1,
|
|
];
|
|
}
|
|
}
|
|
|
|
$scopeLabel = match ($scope['scope']) {
|
|
'default' => 'Default (all stores)',
|
|
'websites' => 'Website: ' . ($m2Websites[$scope['scope_id']] ?? 'ID ' . $scope['scope_id']),
|
|
'stores' => 'Store: ' . ($m2Stores[$scope['scope_id']] ?? 'ID ' . $scope['scope_id']),
|
|
default => $scope['scope'] . ' ' . $scope['scope_id'],
|
|
};
|
|
|
|
$comparison[] = [
|
|
'scope' => $scope['scope'],
|
|
'scope_id' => $scope['scope_id'],
|
|
'scope_label' => $scopeLabel,
|
|
'existing' => $existing,
|
|
'missing' => $missing,
|
|
];
|
|
}
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'comparison' => $comparison,
|
|
'm1_text_attr_count' => $m1TextAttrs->count(),
|
|
]);
|
|
} catch (\Throwable $e) {
|
|
Log::error('Protabs compare error: ' . $e->getMessage());
|
|
return response()->json(['success' => false, 'message' => $e->getMessage()], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Insert one or more missing tabs into M2 mgs_protabs.
|
|
* Accepts an array of tab objects: [{title, tab_type, value, position, scope, scope_id}, ...]
|
|
*/
|
|
public function syncProtabs(Request $request)
|
|
{
|
|
$tabs = $request->input('tabs', []);
|
|
if (empty($tabs)) {
|
|
return response()->json(['success' => false, 'message' => 'No tabs provided.'], 400);
|
|
}
|
|
|
|
$inserted = 0;
|
|
$skipped = 0;
|
|
$errors = 0;
|
|
$log = [];
|
|
|
|
foreach ($tabs as $tab) {
|
|
$scope = $tab['scope'] ?? 'default';
|
|
$scopeId = (int)($tab['scope_id'] ?? 0);
|
|
$value = trim($tab['value'] ?? '');
|
|
$title = trim($tab['title'] ?? '');
|
|
$tabType = $tab['tab_type'] ?? 'attribute';
|
|
$pos = (int)($tab['position'] ?? 99);
|
|
|
|
if ($value === '' || $title === '') {
|
|
$log[] = "Skipped entry with empty value or title.";
|
|
$skipped++;
|
|
continue;
|
|
}
|
|
|
|
// Check if already exists for this scope
|
|
$exists = DB::connection($this->magento2Connection)
|
|
->table('mgs_protabs')
|
|
->where('scope', $scope)
|
|
->where('scope_id', $scopeId)
|
|
->where('tab_type', $tabType)
|
|
->where('value', $value)
|
|
->exists();
|
|
|
|
if ($exists) {
|
|
$log[] = "Tab '{$title}' ({$value}) already exists for {$scope}:{$scopeId}, skipped.";
|
|
$skipped++;
|
|
continue;
|
|
}
|
|
|
|
try {
|
|
DB::connection($this->magento2Connection)
|
|
->table('mgs_protabs')
|
|
->insert([
|
|
'title' => $title,
|
|
'tab_type' => $tabType,
|
|
'value' => $value,
|
|
'position' => $pos,
|
|
'scope' => $scope,
|
|
'scope_id' => $scopeId,
|
|
]);
|
|
$inserted++;
|
|
$log[] = "Added tab '{$title}' ({$value}) to {$scope}:{$scopeId} at position {$pos}.";
|
|
} catch (\Throwable $e) {
|
|
$errors++;
|
|
$log[] = "Error adding '{$value}': " . $e->getMessage();
|
|
Log::error('Protabs sync error: ' . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
return response()->json([
|
|
'success' => $errors === 0,
|
|
'message' => "Protabs sync: {$inserted} added, {$skipped} skipped, {$errors} errors.",
|
|
'inserted' => $inserted,
|
|
'skipped' => $skipped,
|
|
'errors' => $errors,
|
|
'log' => $log,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Show the Additional tools page
|
|
*/
|
|
public function index()
|
|
{
|
|
$frontpageTabsInfo = $this->getFrontpageTabsAttributeInfo();
|
|
return view('additional.index', [
|
|
'frontpageTabsInfo' => $frontpageTabsInfo,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Get info about the frontpage tabs attribute in M1 and M2 (for display / discovery)
|
|
*/
|
|
protected function getFrontpageTabsAttributeInfo()
|
|
{
|
|
$info = ['m1' => null, 'm2' => null, 'suggested_codes' => []];
|
|
try {
|
|
$entityTypeIdM1 = DB::connection($this->magento1Connection)
|
|
->table($this->magento1Prefix . 'eav_entity_type')
|
|
->where('entity_type_code', 'catalog_product')
|
|
->value('entity_type_id');
|
|
$entityTypeIdM2 = DB::connection($this->magento2Connection)
|
|
->table($this->magento2Prefix . 'eav_entity_type')
|
|
->where('entity_type_code', 'catalog_product')
|
|
->value('entity_type_id');
|
|
if (!$entityTypeIdM1 || !$entityTypeIdM2) {
|
|
return $info;
|
|
}
|
|
|
|
foreach (['frontpage_tabs', 'front_tabs', 'product_tabs'] as $code) {
|
|
$m1 = DB::connection($this->magento1Connection)
|
|
->table($this->magento1Prefix . 'eav_attribute')
|
|
->where('entity_type_id', $entityTypeIdM1)
|
|
->where('attribute_code', $code)
|
|
->select('attribute_id', 'attribute_code', 'backend_type', 'frontend_label')
|
|
->first();
|
|
$m2 = DB::connection($this->magento2Connection)
|
|
->table($this->magento2Prefix . 'eav_attribute')
|
|
->where('entity_type_id', $entityTypeIdM2)
|
|
->where('attribute_code', $code)
|
|
->select('attribute_id', 'attribute_code', 'backend_type', 'frontend_label')
|
|
->first();
|
|
if ($m1) {
|
|
$info['m1'] = $m1;
|
|
$info['m2'] = $m2;
|
|
break;
|
|
}
|
|
if ($m1 || $m2) {
|
|
$info['suggested_codes'][] = $code;
|
|
}
|
|
}
|
|
|
|
if (!$info['m1']) {
|
|
$suggested = DB::connection($this->magento1Connection)
|
|
->table($this->magento1Prefix . 'eav_attribute')
|
|
->where('entity_type_id', $entityTypeIdM1)
|
|
->where(function ($q) {
|
|
$q->where('attribute_code', 'like', '%frontpage%')
|
|
->orWhere('attribute_code', 'like', '%front%tab%')
|
|
->orWhere('attribute_code', 'like', '%tab%');
|
|
})
|
|
->pluck('attribute_code')
|
|
->toArray();
|
|
$info['suggested_codes'] = array_unique(array_merge($info['suggested_codes'], $suggested));
|
|
}
|
|
} catch (\Throwable $e) {
|
|
Log::warning('Frontpage tabs attribute info: ' . $e->getMessage());
|
|
}
|
|
return $info;
|
|
}
|
|
|
|
/**
|
|
* Return diagnostic info for an attribute code (M1/M2 existence, backend_type, value row counts)
|
|
*/
|
|
public function attributeDiagnostic(Request $request)
|
|
{
|
|
$attributeCode = trim((string) $request->input('attribute_code', '')) ?: 'frontpage_tabs';
|
|
try {
|
|
$entityTypeIdM1 = DB::connection($this->magento1Connection)
|
|
->table($this->magento1Prefix . 'eav_entity_type')
|
|
->where('entity_type_code', 'catalog_product')
|
|
->value('entity_type_id');
|
|
$entityTypeIdM2 = DB::connection($this->magento2Connection)
|
|
->table($this->magento2Prefix . 'eav_entity_type')
|
|
->where('entity_type_code', 'catalog_product')
|
|
->value('entity_type_id');
|
|
if (!$entityTypeIdM1 || !$entityTypeIdM2) {
|
|
return response()->json(['success' => false, 'message' => 'Could not resolve entity type.', 'm1' => null, 'm2' => null]);
|
|
}
|
|
$attrM1 = DB::connection($this->magento1Connection)
|
|
->table($this->magento1Prefix . 'eav_attribute')
|
|
->where('entity_type_id', $entityTypeIdM1)
|
|
->where('attribute_code', $attributeCode)
|
|
->select('attribute_id', 'attribute_code', 'backend_type', 'frontend_input', 'frontend_label')
|
|
->first();
|
|
$attrM2 = DB::connection($this->magento2Connection)
|
|
->table($this->magento2Prefix . 'eav_attribute')
|
|
->where('entity_type_id', $entityTypeIdM2)
|
|
->where('attribute_code', $attributeCode)
|
|
->select('attribute_id', 'attribute_code', 'backend_type', 'frontend_input', 'frontend_label')
|
|
->first();
|
|
$diagnostic = ['attribute_code' => $attributeCode, 'm1' => null, 'm2' => null];
|
|
if ($attrM1) {
|
|
$diagnostic['m1'] = (array) $attrM1;
|
|
$bt = $attrM1->backend_type ?? 'varchar';
|
|
$tables = ['varchar', 'text', 'int', 'decimal', 'datetime'];
|
|
$diagnostic['m1']['value_counts_by_table'] = [];
|
|
foreach ($tables as $t) {
|
|
try {
|
|
$c = DB::connection($this->magento1Connection)
|
|
->table($this->magento1Prefix . 'catalog_product_entity_' . $t)
|
|
->where('attribute_id', $attrM1->attribute_id)
|
|
->count();
|
|
$diagnostic['m1']['value_counts_by_table'][$t] = $c;
|
|
} catch (\Throwable $e) {
|
|
$diagnostic['m1']['value_counts_by_table'][$t] = 'error';
|
|
}
|
|
}
|
|
}
|
|
if ($attrM2) {
|
|
$diagnostic['m2'] = (array) $attrM2;
|
|
}
|
|
return response()->json(['success' => true, 'diagnostic' => $diagnostic]);
|
|
} catch (\Throwable $e) {
|
|
Log::warning('Attribute diagnostic: ' . $e->getMessage());
|
|
return response()->json(['success' => false, 'message' => $e->getMessage(), 'diagnostic' => null], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sync frontpage tabs from Magento 1 to Magento 2 by SKU
|
|
* Scans all M1 products by SKU and ensures M2 products have the same frontpage tabs value(s)
|
|
*/
|
|
public function syncFrontpageTabs(Request $request)
|
|
{
|
|
$attributeCode = $request->input('attribute_code', $this->frontpageTabsAttributeCode);
|
|
$dryRun = $request->boolean('dry_run', false);
|
|
|
|
try {
|
|
$entityTypeIdM1 = DB::connection($this->magento1Connection)
|
|
->table($this->magento1Prefix . 'eav_entity_type')
|
|
->where('entity_type_code', 'catalog_product')
|
|
->value('entity_type_id');
|
|
$entityTypeIdM2 = DB::connection($this->magento2Connection)
|
|
->table($this->magento2Prefix . 'eav_entity_type')
|
|
->where('entity_type_code', 'catalog_product')
|
|
->value('entity_type_id');
|
|
if (!$entityTypeIdM1 || !$entityTypeIdM2) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Could not resolve catalog_product entity type in M1 or M2.',
|
|
'updated' => 0,
|
|
'skipped' => 0,
|
|
'errors' => 0,
|
|
'log' => [],
|
|
], 400);
|
|
}
|
|
|
|
$attrM1 = DB::connection($this->magento1Connection)
|
|
->table($this->magento1Prefix . 'eav_attribute')
|
|
->where('entity_type_id', $entityTypeIdM1)
|
|
->where('attribute_code', $attributeCode)
|
|
->first();
|
|
if (!$attrM1) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => "Attribute '{$attributeCode}' not found in Magento 1.",
|
|
'updated' => 0,
|
|
'skipped' => 0,
|
|
'errors' => 0,
|
|
'log' => [],
|
|
], 400);
|
|
}
|
|
|
|
$attrM2 = DB::connection($this->magento2Connection)
|
|
->table($this->magento2Prefix . 'eav_attribute')
|
|
->where('entity_type_id', $entityTypeIdM2)
|
|
->where('attribute_code', $attributeCode)
|
|
->first();
|
|
if (!$attrM2) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => "Attribute '{$attributeCode}' not found in Magento 2. Create the attribute in M2 first.",
|
|
'updated' => 0,
|
|
'skipped' => 0,
|
|
'errors' => 0,
|
|
'log' => [],
|
|
], 400);
|
|
}
|
|
|
|
$backendType = $attrM1->backend_type ?? 'varchar';
|
|
if (!in_array($backendType, ['varchar', 'text', 'int', 'decimal', 'datetime'], true)) {
|
|
$backendType = 'varchar';
|
|
}
|
|
$backendTypesToTry = ['varchar', 'text', 'int', 'decimal', 'datetime'];
|
|
$m1ValueTable = null;
|
|
$m2ValueTable = null;
|
|
$resolvedBackendType = null;
|
|
foreach ($backendTypesToTry as $bt) {
|
|
$t1 = $this->magento1Prefix . 'catalog_product_entity_' . $bt;
|
|
$count = 0;
|
|
try {
|
|
$count = DB::connection($this->magento1Connection)
|
|
->table($t1)
|
|
->where('attribute_id', $attrM1->attribute_id)
|
|
->count();
|
|
} catch (\Throwable $e) {
|
|
continue;
|
|
}
|
|
if ($count > 0) {
|
|
$resolvedBackendType = $bt;
|
|
$m1ValueTable = $t1;
|
|
$m2ValueTable = $this->magento2Prefix . 'catalog_product_entity_' . $bt;
|
|
break;
|
|
}
|
|
}
|
|
if ($m1ValueTable === null) {
|
|
$m1ValueTable = $this->magento1Prefix . 'catalog_product_entity_' . $backendType;
|
|
$m2ValueTable = $this->magento2Prefix . 'catalog_product_entity_' . $backendType;
|
|
$resolvedBackendType = $backendType;
|
|
}
|
|
|
|
$m1ValueRowCount = 0;
|
|
try {
|
|
$m1ValueRowCount = DB::connection($this->magento1Connection)
|
|
->table($m1ValueTable)
|
|
->where('attribute_id', $attrM1->attribute_id)
|
|
->count();
|
|
} catch (\Throwable $e) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => "Magento 1 value table for attribute '{$attributeCode}' (backend_type {$resolvedBackendType}) not found or error: " . $e->getMessage(),
|
|
'updated' => 0,
|
|
'skipped' => 0,
|
|
'errors' => 0,
|
|
'log' => [],
|
|
'diagnostic' => ['backend_type' => $resolvedBackendType, 'm1_value_table' => $m1ValueTable],
|
|
], 400);
|
|
}
|
|
if ($m1ValueRowCount === 0) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => "No values found in Magento 1 for attribute '{$attributeCode}'. No products have this attribute set in M1. Check that the attribute is assigned to product attribute sets and that products have values saved.",
|
|
'updated' => 0,
|
|
'skipped' => 0,
|
|
'errors' => 0,
|
|
'log' => [],
|
|
'diagnostic' => [
|
|
'backend_type' => $resolvedBackendType,
|
|
'm1_value_table' => $m1ValueTable,
|
|
'm1_value_row_count' => 0,
|
|
],
|
|
], 400);
|
|
}
|
|
|
|
$skuAttrIdM1 = DB::connection($this->magento1Connection)
|
|
->table($this->magento1Prefix . 'eav_attribute')
|
|
->where('entity_type_id', $entityTypeIdM1)
|
|
->where('attribute_code', 'sku')
|
|
->value('attribute_id');
|
|
$skuAttrIdM2 = DB::connection($this->magento2Connection)
|
|
->table($this->magento2Prefix . 'eav_attribute')
|
|
->where('entity_type_id', $entityTypeIdM2)
|
|
->where('attribute_code', 'sku')
|
|
->value('attribute_id');
|
|
if (!$skuAttrIdM1 || !$skuAttrIdM2) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Could not resolve SKU attribute in M1 or M2.',
|
|
'updated' => 0,
|
|
'skipped' => 0,
|
|
'errors' => 0,
|
|
'log' => [],
|
|
], 400);
|
|
}
|
|
|
|
$m1ProductsWithTabs = DB::connection($this->magento1Connection)
|
|
->table($this->magento1Prefix . 'catalog_product_entity as e')
|
|
->leftJoin($this->magento1Prefix . 'catalog_product_entity_varchar as sku_v', function ($join) use ($skuAttrIdM1) {
|
|
$join->on('e.entity_id', '=', 'sku_v.entity_id')
|
|
->where('sku_v.attribute_id', '=', $skuAttrIdM1);
|
|
})
|
|
->join($m1ValueTable . ' as vt', function ($join) use ($attrM1) {
|
|
$join->on('e.entity_id', '=', 'vt.entity_id')
|
|
->where('vt.attribute_id', '=', $attrM1->attribute_id);
|
|
})
|
|
->where(function ($q) {
|
|
$q->whereNotNull('sku_v.value')
|
|
->orWhereNotNull('e.sku');
|
|
})
|
|
->selectRaw('e.entity_id as m1_entity_id, TRIM(COALESCE(sku_v.value, e.sku)) as sku, vt.store_id, vt.value')
|
|
->get();
|
|
|
|
if ($m1ProductsWithTabs->isEmpty()) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => "Attribute '{$attributeCode}' has {$m1ValueRowCount} value row(s) in M1, but no rows matched when joining with products (by SKU). Possible causes: SKU is stored in a different store_id in M1, or the value table does not match the attribute's backend_type.",
|
|
'updated' => 0,
|
|
'skipped' => 0,
|
|
'errors' => 0,
|
|
'log' => [],
|
|
'diagnostic' => [
|
|
'backend_type' => $resolvedBackendType,
|
|
'm1_value_table' => $m1ValueTable,
|
|
'm1_value_row_count' => $m1ValueRowCount,
|
|
],
|
|
], 400);
|
|
}
|
|
|
|
$m2SkuToEntityId = DB::connection($this->magento2Connection)
|
|
->table($this->magento2Prefix . 'catalog_product_entity as e')
|
|
->leftJoin($this->magento2Prefix . 'catalog_product_entity_varchar as sku_v', function ($join) use ($skuAttrIdM2) {
|
|
$join->on('e.entity_id', '=', 'sku_v.entity_id')
|
|
->where('sku_v.attribute_id', '=', $skuAttrIdM2);
|
|
})
|
|
->where(function ($q) {
|
|
$q->whereNotNull('sku_v.value')
|
|
->orWhereNotNull('e.sku');
|
|
})
|
|
->selectRaw('e.entity_id, TRIM(COALESCE(sku_v.value, e.sku)) as sku')
|
|
->pluck('e.entity_id', 'sku');
|
|
|
|
$log = [];
|
|
$updated = 0;
|
|
$skipped = 0;
|
|
$skippedNoM2 = 0;
|
|
$skippedAlreadyMatch = 0;
|
|
$errors = 0;
|
|
|
|
foreach ($m1ProductsWithTabs as $row) {
|
|
$sku = trim((string) $row->sku);
|
|
$m2EntityId = $m2SkuToEntityId->get($sku);
|
|
if ($m2EntityId === null) {
|
|
$skipped++;
|
|
$skippedNoM2++;
|
|
$log[] = "SKU {$sku}: no M2 product, skipped.";
|
|
continue;
|
|
}
|
|
$existing = DB::connection($this->magento2Connection)
|
|
->table($m2ValueTable)
|
|
->where('entity_id', $m2EntityId)
|
|
->where('attribute_id', $attrM2->attribute_id)
|
|
->where('store_id', $row->store_id)
|
|
->first();
|
|
if ($existing && (string) $existing->value === (string) $row->value) {
|
|
$skipped++;
|
|
$skippedAlreadyMatch++;
|
|
$log[] = "SKU {$sku} (store {$row->store_id}): M2 value already matches M1, skipped.";
|
|
continue;
|
|
}
|
|
if ($dryRun) {
|
|
$updated++;
|
|
$log[] = "SKU {$sku} (store {$row->store_id}): would set value.";
|
|
continue;
|
|
}
|
|
try {
|
|
if ($existing) {
|
|
DB::connection($this->magento2Connection)
|
|
->table($m2ValueTable)
|
|
->where('entity_id', $m2EntityId)
|
|
->where('attribute_id', $attrM2->attribute_id)
|
|
->where('store_id', $row->store_id)
|
|
->update(['value' => $row->value]);
|
|
} else {
|
|
DB::connection($this->magento2Connection)
|
|
->table($m2ValueTable)
|
|
->insert([
|
|
'attribute_id' => $attrM2->attribute_id,
|
|
'store_id' => $row->store_id,
|
|
'entity_id' => $m2EntityId,
|
|
'value' => $row->value,
|
|
]);
|
|
}
|
|
$updated++;
|
|
$log[] = "SKU {$sku} (store {$row->store_id}): synced.";
|
|
} catch (\Throwable $e) {
|
|
$errors++;
|
|
$log[] = "SKU {$sku}: error - " . $e->getMessage();
|
|
Log::error("Sync frontpage tabs SKU {$sku}: " . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
$m1SampleSkus = $m1ProductsWithTabs->take(5)->pluck('sku')->unique()->values()->toArray();
|
|
$diagnostic = [
|
|
'backend_type' => $resolvedBackendType,
|
|
'm1_value_row_count' => $m1ValueRowCount,
|
|
'm1_products_with_value' => $m1ProductsWithTabs->count(),
|
|
'skipped_no_m2_product' => $skippedNoM2,
|
|
'skipped_already_match' => $skippedAlreadyMatch,
|
|
'm1_sample_skus' => $m1SampleSkus,
|
|
];
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => $dryRun
|
|
? "Dry run: {$updated} product/store rows would be updated, {$skipped} skipped."
|
|
: "Synced frontpage tabs: {$updated} updated, {$skipped} skipped, {$errors} errors.",
|
|
'updated' => $updated,
|
|
'skipped' => $skipped,
|
|
'errors' => $errors,
|
|
'log' => array_slice($log, -100),
|
|
'diagnostic' => $diagnostic,
|
|
]);
|
|
|
|
} catch (\Throwable $e) {
|
|
Log::error('Sync frontpage tabs error: ' . $e->getMessage());
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Sync failed: ' . $e->getMessage(),
|
|
'updated' => 0,
|
|
'skipped' => 0,
|
|
'errors' => 0,
|
|
'log' => [],
|
|
], 500);
|
|
}
|
|
}
|
|
}
|