['1'], ]; $dryRun = in_array('--dry-run', $argv, true); $brandFile = dirname(__DIR__) . '/resources/catalog/known_brands.php'; if (!is_file($brandFile)) { fwrite(STDERR, "Known brand file not found: {$brandFile}\n"); exit(1); } $brands = require $brandFile; if (!is_array($brands)) { fwrite(STDERR, "Known brand file must return an array.\n"); exit(1); } $dotenv = Dotenv\Dotenv::createImmutable(dirname(__DIR__)); $dotenv->safeLoad(); $dsn = sprintf( 'mysql:host=%s;port=%s;dbname=%s;charset=%s', $_ENV['DB_HOST'] ?? '127.0.0.1', $_ENV['DB_PORT'] ?? '3306', $_ENV['DB_DATABASE'] ?? '', $_ENV['DB_CHARSET'] ?? 'utf8mb4' ); $pdo = new PDO( $dsn, $_ENV['DB_USERNAME'] ?? '', $_ENV['DB_PASSWORD'] ?? '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ] ); function normalize_list(mixed $value): array { if (!is_array($value)) { return []; } $items = []; foreach ($value as $item) { $text = trim((string)$item); if ($text !== '') { $items[] = $text; } } return array_values(array_unique($items)); } function decode_json_list(mixed $value): array { if (is_array($value)) { return normalize_list($value); } if (!is_string($value) || trim($value) === '') { return []; } $decoded = json_decode($value, true); return normalize_list(is_array($decoded) ? $decoded : []); } function stable_json_list(array $value): string { $items = normalize_list($value); sort($items, SORT_STRING); return json_encode($items, JSON_UNESCAPED_UNICODE); } function load_enabled_categories(PDO $pdo): array { $rows = $pdo->query( "SELECT id, name, code, supported_service_types FROM catalog_categories WHERE is_enabled = 1 ORDER BY sort_order ASC, id ASC" )->fetchAll(); $byCode = []; $byName = []; $byId = []; foreach ($rows as $row) { $category = [ 'id' => (int)$row['id'], 'name' => (string)$row['name'], 'code' => (string)$row['code'], 'supported_service_types' => decode_json_list($row['supported_service_types'] ?? null), ]; $byId[$category['id']] = $category; $byCode[strtolower($category['code'])] = $category; $byName[$category['name']] = $category; } return [$byCode, $byName, $byId]; } function match_categories(array $brand, array $categoriesByCode, array $categoriesByName): array { $matched = []; foreach (normalize_list($brand['category_codes'] ?? []) as $code) { $key = strtolower($code); if (isset($categoriesByCode[$key])) { $matched[$categoriesByCode[$key]['id']] = $categoriesByCode[$key]; } } foreach (normalize_list($brand['category_names'] ?? []) as $name) { if (isset($categoriesByName[$name])) { $matched[$categoriesByName[$name]['id']] = $categoriesByName[$name]; } } return $matched; } function infer_supported_service_types(array $categories): array { $serviceTypes = []; foreach ($categories as $category) { foreach ($category['supported_service_types'] as $serviceType) { $serviceTypes[$serviceType] = $serviceType; } } if (!$serviceTypes) { $serviceTypes['anxinyan'] = 'anxinyan'; } return array_values($serviceTypes); } function find_existing_brand(PDO $pdo, string $code): ?array { $stmt = $pdo->prepare('SELECT * FROM catalog_brands WHERE code = ? LIMIT 1'); $stmt->execute([$code]); $row = $stmt->fetch(); if ($row) { return $row; } foreach (LEGACY_CODE_ALIASES[$code] ?? [] as $legacyCode) { $stmt->execute([$legacyCode]); $legacy = $stmt->fetch(); if ($legacy) { return $legacy; } } return null; } function existing_relation_ids(PDO $pdo, int $brandId): array { $stmt = $pdo->prepare('SELECT category_id FROM catalog_brand_categories WHERE brand_id = ?'); $stmt->execute([$brandId]); $ids = []; foreach ($stmt->fetchAll() as $row) { $ids[(int)$row['category_id']] = true; } return $ids; } [$categoriesByCode, $categoriesByName, $categoriesById] = load_enabled_categories($pdo); $stats = [ 'brands_total' => count($brands), 'inserted' => 0, 'updated' => 0, 'enabled_existing' => 0, 'legacy_code_repaired' => 0, 'relations_inserted' => 0, 'skipped_no_enabled_category' => 0, 'skipped_invalid' => 0, ]; $now = date('Y-m-d H:i:s'); $insertBrand = $pdo->prepare( 'INSERT INTO catalog_brands (name, en_name, code, logo, sort_order, is_enabled, supported_service_types, created_at, updated_at) VALUES (?, ?, ?, ?, ?, 1, ?, ?, ?)' ); $updateBrand = $pdo->prepare( 'UPDATE catalog_brands SET name = ?, en_name = ?, code = ?, sort_order = ?, is_enabled = 1, supported_service_types = ?, updated_at = ? WHERE id = ?' ); $insertRelation = $pdo->prepare( 'INSERT INTO catalog_brand_categories (brand_id, category_id, created_at) VALUES (?, ?, ?)' ); if (!$dryRun) { $pdo->beginTransaction(); } try { foreach ($brands as $brand) { $code = trim((string)($brand['code'] ?? '')); $name = trim((string)($brand['name'] ?? '')); $enName = trim((string)($brand['en_name'] ?? '')); if ($code === '' || $name === '' || $enName === '') { $stats['skipped_invalid']++; continue; } $matchedCategories = match_categories($brand, $categoriesByCode, $categoriesByName); if (!$matchedCategories) { $stats['skipped_no_enabled_category']++; continue; } $serviceTypes = infer_supported_service_types($matchedCategories); $serviceTypesJson = stable_json_list($serviceTypes); $sortOrder = (int)($brand['sort_order'] ?? 0); $existing = find_existing_brand($pdo, $code); if ($existing) { $brandId = (int)$existing['id']; $wasDisabled = (int)$existing['is_enabled'] !== 1; $wasLegacyCode = (string)$existing['code'] !== $code; $existingJson = stable_json_list(decode_json_list($existing['supported_service_types'] ?? null)); $needsUpdate = $wasDisabled || $wasLegacyCode || (string)$existing['name'] !== $name || (string)$existing['en_name'] !== $enName || (int)$existing['sort_order'] !== $sortOrder || $existingJson !== $serviceTypesJson; if ($needsUpdate) { $stats['updated']++; if ($wasDisabled) { $stats['enabled_existing']++; } if ($wasLegacyCode) { $stats['legacy_code_repaired']++; } if (!$dryRun) { $updateBrand->execute([$name, $enName, $code, $sortOrder, $serviceTypesJson, $now, $brandId]); } } } else { $stats['inserted']++; if ($dryRun) { $brandId = 0; } else { $insertBrand->execute([$name, $enName, $code, '', $sortOrder, $serviceTypesJson, $now, $now]); $brandId = (int)$pdo->lastInsertId(); } } $existingCategoryIds = $brandId > 0 ? existing_relation_ids($pdo, $brandId) : []; foreach (array_keys($matchedCategories) as $categoryId) { if (isset($existingCategoryIds[(int)$categoryId])) { continue; } $stats['relations_inserted']++; if (!$dryRun && $brandId > 0) { $insertRelation->execute([$brandId, (int)$categoryId, $now]); } } } if (!$dryRun) { $pdo->commit(); } } catch (Throwable $e) { if (!$dryRun && $pdo->inTransaction()) { $pdo->rollBack(); } fwrite(STDERR, "IMPORT_KNOWN_BRANDS_FAILED\n"); fwrite(STDERR, $e->getMessage() . PHP_EOL); exit(1); } echo $dryRun ? "DRY_RUN\n" : "IMPORT_KNOWN_BRANDS_OK\n"; echo "ENABLED_CATEGORIES=" . count($categoriesById) . PHP_EOL; foreach ($stats as $key => $value) { echo strtoupper($key) . '=' . $value . PHP_EOL; }