461 lines
16 KiB
PHP
461 lines
16 KiB
PHP
<?php
|
|
/**
|
|
* MigrateFromOpenCart.php
|
|
*
|
|
* @copyright 2023 beikeshop.com - All Rights Reserved
|
|
* @link https://beikeshop.com
|
|
* @author Edward Yang <yangjin@guangda.work>
|
|
* @created 2023-01-03 18:57:53
|
|
* @modified 2023-01-03 18:57:53
|
|
*/
|
|
|
|
namespace Beike\Console\Commands;
|
|
|
|
use Beike\Admin\Services\CategoryService;
|
|
use Beike\Admin\Services\ProductService;
|
|
use Beike\Models\Brand;
|
|
use Beike\Models\Category;
|
|
use Beike\Models\CategoryDescription;
|
|
use Beike\Models\CategoryPath;
|
|
use Beike\Models\Product;
|
|
use Beike\Models\ProductDescription;
|
|
use Beike\Models\ProductSku;
|
|
use Illuminate\Console\Command;
|
|
use Illuminate\Database\ConnectionInterface;
|
|
use Illuminate\Support\Collection;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
class MigrateFromOpenCart extends Command
|
|
{
|
|
public const PER_PAGE = 1000;
|
|
|
|
public const LANG_MAPPING = [
|
|
'zh_cn' => 11,
|
|
'es' => 6,
|
|
];
|
|
|
|
protected $signature = 'migrate:oc';
|
|
|
|
protected $description = '从 OpenCart 迁移数据';
|
|
|
|
protected ConnectionInterface $ocdb;
|
|
|
|
private $ocProductVariants;
|
|
|
|
private $ocVariantDescriptions;
|
|
|
|
private $ocVariantValues;
|
|
|
|
private $ocVariantValueDescriptions;
|
|
|
|
private $ocProductImages;
|
|
|
|
private $ocProductCategories;
|
|
|
|
private int $page = 1;
|
|
|
|
public function __construct()
|
|
{
|
|
parent::__construct();
|
|
$this->ocdb = DB::connection('opencart');
|
|
}
|
|
|
|
/**
|
|
* 导入OC产品数据
|
|
*/
|
|
public function handle()
|
|
{
|
|
$this->importCategories();
|
|
$this->importBrands();
|
|
$this->importProducts();
|
|
}
|
|
|
|
/**
|
|
* 导入分类数据
|
|
*/
|
|
private function importCategories()
|
|
{
|
|
Category::query()->truncate();
|
|
CategoryDescription::query()->truncate();
|
|
CategoryPath::query()->truncate();
|
|
|
|
$this->ocdb->table('category')
|
|
->orderBy('category_id')
|
|
->chunk(self::PER_PAGE, function ($ocCategories) {
|
|
$bkCategories = [];
|
|
foreach ($ocCategories as $ocCategory) {
|
|
$bkCategories[] = [
|
|
'id' => $ocCategory->category_id,
|
|
'parent_id' => $ocCategory->parent_id,
|
|
'position' => $ocCategory->sort_order,
|
|
'active' => $ocCategory->status,
|
|
'created_at' => now(),
|
|
'updated_at' => now(),
|
|
];
|
|
}
|
|
Category::query()->insert($bkCategories);
|
|
});
|
|
|
|
$descriptions = [];
|
|
$ocDescriptions = $this->ocdb->table('category_description')->orderBy('category_id')->get();
|
|
$langMapping = array_flip(self::LANG_MAPPING);
|
|
foreach ($ocDescriptions as $description) {
|
|
$descriptions[] = [
|
|
'category_id' => $description->category_id,
|
|
'locale' => $langMapping[$description->language_id],
|
|
'name' => $description->name,
|
|
'content' => html_entity_decode($description->description, ENT_QUOTES),
|
|
'created_at' => now(),
|
|
'updated_at' => now(),
|
|
];
|
|
}
|
|
CategoryDescription::query()->insert($descriptions);
|
|
|
|
CategoryService::repairCategories();
|
|
}
|
|
|
|
/**
|
|
* 导入品牌数据
|
|
*/
|
|
private function importBrands()
|
|
{
|
|
Brand::query()->truncate();
|
|
$this->ocdb->table('manufacturer')
|
|
->orderBy('manufacturer_id')
|
|
->chunk(self::PER_PAGE, function ($ocBrands) {
|
|
$bkBrands = [];
|
|
foreach ($ocBrands as $ocBrand) {
|
|
$bkBrands[] = [
|
|
'id' => $ocBrand->manufacturer_id,
|
|
'name' => $ocBrand->name,
|
|
'first' => mb_strtoupper(mb_substr($ocBrand->name, 0, 1)),
|
|
'logo' => $ocBrand->image,
|
|
'sort_order' => $ocBrand->sort_order,
|
|
'status' => $ocBrand->status,
|
|
'created_at' => now(),
|
|
'updated_at' => now(),
|
|
];
|
|
}
|
|
Brand::query()->insert($bkBrands);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 导入产品
|
|
*/
|
|
private function importProducts()
|
|
{
|
|
$this->ocProductVariants = $this->ocdb->table('product_variant')->get()->groupBy('product_id');
|
|
$this->ocVariantDescriptions = $this->ocdb->table('variant_description')->get()->groupBy('variant_id');
|
|
$this->ocVariantValues = $this->ocdb->table('variant_value')->get()->keyBy('variant_value_id');
|
|
$this->ocVariantValueDescriptions = $this->ocdb->table('variant_value_description')->get()->groupBy('variant_value_id');
|
|
$this->ocProductImages = $this->ocdb->table('product_image')->get()->groupBy('product_id');
|
|
$this->ocProductCategories = $this->ocdb->table('product_to_category')->get()->groupBy('product_id');
|
|
|
|
$this->clearData();
|
|
$this->ocdb->table('product')
|
|
->where('parent_id', 0)
|
|
// ->where('product_id', 1894)
|
|
// ->where('sku', '10012378')
|
|
->orderBy('product_id')
|
|
->chunk(self::PER_PAGE, function ($ocProducts) {
|
|
$this->importProduct($ocProducts);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 导入单个产品
|
|
* @param Collection $ocProducts
|
|
*/
|
|
private function importProduct(Collection $ocProducts)
|
|
{
|
|
$total = $ocProducts->count();
|
|
$ocProductIds = $ocProducts->pluck('product_id');
|
|
$childProducts = $this->ocdb->table('product')->whereIn('parent_id', $ocProductIds)->get()->groupBy('parent_id');
|
|
foreach ($ocProducts as $index => $ocProduct) {
|
|
dump("Start handle Page: $this->page - {$index}/{$total}");
|
|
$ocProductId = $ocProduct->product_id;
|
|
$productVariants = $this->ocProductVariants[$ocProductId] ?? [];
|
|
$childProducts = $childProducts[$ocProductId] ?? [];
|
|
$bkProduct = $this->generateBeikeProduct($ocProduct, $productVariants, $childProducts);
|
|
(new ProductService)->create($bkProduct);
|
|
}
|
|
$this->page++;
|
|
}
|
|
|
|
/**
|
|
* 构造 beike 产品
|
|
*/
|
|
private function generateBeikeProduct($ocProduct, $productVariants, $childProducts)
|
|
{
|
|
$variables = $this->generateVariables($ocProduct, $childProducts);
|
|
$bkProduct = [
|
|
'active' => $ocProduct->status,
|
|
'brand_id' => $ocProduct->manufacturer_id,
|
|
'position' => $ocProduct->sort_order,
|
|
'tax_class_id' => $ocProduct->tax_class_id,
|
|
'variables' => json_encode($variables),
|
|
];
|
|
$bkProduct['descriptions'] = $this->generateDescriptions($ocProduct);
|
|
$bkProduct['images'] = [$ocProduct->image];
|
|
$bkProduct['skus'] = $this->generateSkus($ocProduct, $productVariants, $childProducts, $variables);
|
|
$bkProduct['categories'] = $this->generateCategories($ocProduct);
|
|
|
|
return $bkProduct;
|
|
}
|
|
|
|
/**
|
|
* 生成 beike 产品规格
|
|
* @return array[]
|
|
*/
|
|
private function generateVariables($ocProduct, $childProducts): array
|
|
{
|
|
$productIds = [$ocProduct->product_id];
|
|
foreach ($childProducts as $childProduct) {
|
|
$productIds[] = $childProduct->product_id;
|
|
}
|
|
|
|
$locales = locales();
|
|
$items = $values = [];
|
|
foreach ($productIds as $productId) {
|
|
$productVariants = $this->ocProductVariants[$productId] ?? [];
|
|
foreach ($productVariants as $productVariant) {
|
|
$productVariantId = $productVariant->variant_id;
|
|
$productVariantValueId = $productVariant->variant_value_id;
|
|
|
|
$variants = $this->ocVariantDescriptions[$productVariantId];
|
|
$variantValue = $this->ocVariantValues[$productVariantValueId];
|
|
$variantValueDescriptions = $this->ocVariantValueDescriptions[$productVariantValueId];
|
|
|
|
$variants = $variants->keyBy('language_id');
|
|
$names = [];
|
|
foreach ($locales as $locale) {
|
|
$variant = $variants[self::LANG_MAPPING[$locale['code']]];
|
|
$names[$locale['code']] = $variant->name;
|
|
}
|
|
|
|
$valueNames = [];
|
|
$variantValueDescriptions = $variantValueDescriptions->keyBy('language_id');
|
|
foreach ($locales as $locale) {
|
|
$variantValueDescription = $variantValueDescriptions[self::LANG_MAPPING[$locale['code']]];
|
|
$valueNames[$locale['code']] = $variantValueDescription->name;
|
|
}
|
|
|
|
$values[$productVariantValueId] = [
|
|
'variant_value_id' => $productVariantValueId,
|
|
'name' => $valueNames,
|
|
'image' => $variantValue->image,
|
|
];
|
|
|
|
$items[$productVariantId] = [
|
|
'variant_id' => $productVariantId,
|
|
'name' => $names,
|
|
'values' => $values,
|
|
'isImage' => (bool) $variantValue->image,
|
|
];
|
|
}
|
|
}
|
|
|
|
$items = array_values($items);
|
|
foreach ($items as $index => $item) {
|
|
$items[$index]['values'] = array_values($item['values']);
|
|
}
|
|
|
|
return $items;
|
|
}
|
|
|
|
/**
|
|
* 生成 beike 产品描述
|
|
* @param $ocProduct
|
|
* @return array
|
|
*/
|
|
private function generateDescriptions($ocProduct): array
|
|
{
|
|
$descriptions = [];
|
|
$locales = locales();
|
|
$ocDescriptions = $this->ocdb->table('product_description')
|
|
->where('product_id', $ocProduct->product_id)
|
|
->get()
|
|
->keyBy('language_id')
|
|
->toArray();
|
|
|
|
foreach ($locales as $locale) {
|
|
$ocDescription = $ocDescriptions[self::LANG_MAPPING[$locale['code']]];
|
|
$descriptions[$locale['code']] = [
|
|
'name' => $ocDescription->name,
|
|
'content' => html_entity_decode($ocDescription->description, ENT_QUOTES),
|
|
];
|
|
}
|
|
|
|
return $descriptions;
|
|
}
|
|
|
|
/**
|
|
* 生成 beike 产品 SKU
|
|
*
|
|
* @param $ocProduct
|
|
* @param $productVariants
|
|
* @param $childProducts
|
|
* @param array $variables
|
|
* @return array
|
|
*/
|
|
private function generateSkus($ocProduct, $productVariants, $childProducts, array $variables): array
|
|
{
|
|
// 简单商品
|
|
if (count($productVariants) == 0) {
|
|
return $this->generateSimpleSku($ocProduct);
|
|
}
|
|
|
|
$masterSku = $childSkus = [];
|
|
// 有主商品
|
|
if (count($productVariants) > 0) {
|
|
$masterSku = $this->generateMasterSku($ocProduct, $productVariants, $variables);
|
|
}
|
|
// 有子商品
|
|
if (count($childProducts) > 0) {
|
|
$childSkus = $this->generateChildSkus($childProducts, $variables);
|
|
}
|
|
if ($masterSku) {
|
|
array_push($childSkus, $masterSku);
|
|
}
|
|
|
|
return $childSkus;
|
|
}
|
|
|
|
/**
|
|
* 获取简单商品 SKU
|
|
* @param $ocProduct
|
|
* @return array[]
|
|
*/
|
|
private function generateSimpleSku($ocProduct): array
|
|
{
|
|
$images = $this->ocProductImages[$ocProduct->product_id] ?? [];
|
|
|
|
return [
|
|
[
|
|
'image' => $images,
|
|
'model' => $ocProduct->model,
|
|
'sku' => $ocProduct->sku,
|
|
'price' => $ocProduct->price,
|
|
'origin_price' => $ocProduct->price,
|
|
'cost_price' => $ocProduct->cost_price,
|
|
'quantity' => $ocProduct->quantity,
|
|
'variants' => null,
|
|
'position' => $ocProduct->sort_order,
|
|
'is_default' => 1,
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* 获取主商品 SKU
|
|
* @param $ocProduct
|
|
* @param $productVariants
|
|
* @param $variables
|
|
* @return array
|
|
*/
|
|
private function generateMasterSku($ocProduct, $productVariants, $variables): array
|
|
{
|
|
$variants = $this->generateVariants($productVariants, $variables);
|
|
$images = $this->ocProductImages[$ocProduct->product_id] ?? [];
|
|
|
|
return [
|
|
'image' => $images,
|
|
'model' => $ocProduct->model,
|
|
'sku' => $ocProduct->sku,
|
|
'price' => $ocProduct->price,
|
|
'origin_price' => $ocProduct->price,
|
|
'cost_price' => $ocProduct->cost_price,
|
|
'quantity' => $ocProduct->quantity,
|
|
'variants' => $variants,
|
|
'position' => $ocProduct->sort_order,
|
|
'is_default' => 1,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* 获取子商品SKU
|
|
* @param $childProducts
|
|
* @param $variables
|
|
* @return array
|
|
*/
|
|
private function generateChildSkus($childProducts, $variables): array
|
|
{
|
|
$items = [];
|
|
foreach ($childProducts as $ocProduct) {
|
|
$ocProductId = $ocProduct->product_id;
|
|
$productVariants = $this->ocProductVariants[$ocProductId] ?? [];
|
|
|
|
$images = $this->ocProductImages[$ocProductId] ?? [];
|
|
$variants = $this->generateVariants($productVariants, $variables);
|
|
$items[] = [
|
|
'image' => $images,
|
|
'model' => $ocProduct->model,
|
|
'sku' => $ocProduct->sku,
|
|
'price' => $ocProduct->price,
|
|
'origin_price' => $ocProduct->price,
|
|
'cost_price' => $ocProduct->cost_price,
|
|
'quantity' => $ocProduct->quantity,
|
|
'variants' => $variants,
|
|
'position' => $ocProduct->sort_order,
|
|
'is_default' => 0,
|
|
];
|
|
}
|
|
|
|
return $items;
|
|
}
|
|
|
|
/**
|
|
* 生成商品 SKU 多规格数据
|
|
*
|
|
* @param $productVariants
|
|
* @param $variables
|
|
* @return array
|
|
*/
|
|
private function generateVariants($productVariants, $variables): array
|
|
{
|
|
$items = [];
|
|
foreach ($productVariants as $productVariant) {
|
|
$variantId = $productVariant->variant_id;
|
|
$variantValueId = $productVariant->variant_value_id;
|
|
foreach ($variables as $index1 => $variable) {
|
|
if ($variantId == $variable['variant_id']) {
|
|
foreach ($variable['values'] as $index2 => $value) {
|
|
if ($variantValueId == $value['variant_value_id']) {
|
|
$items[$index1] = (string) $index2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return $items;
|
|
}
|
|
|
|
/**
|
|
* 生成商品分类关联
|
|
*
|
|
* @param $ocProduct
|
|
* @return array
|
|
*/
|
|
private function generateCategories($ocProduct): array
|
|
{
|
|
$ocProductCategories = $this->ocProductCategories[$ocProduct->product_id] ?? [];
|
|
if ($ocProductCategories) {
|
|
return $ocProductCategories->pluck('category_id')->toArray();
|
|
}
|
|
|
|
return [];
|
|
}
|
|
|
|
/**
|
|
* 清空产品相关数据
|
|
*/
|
|
private function clearData()
|
|
{
|
|
Product::query()->truncate();
|
|
ProductSku::query()->truncate();
|
|
ProductDescription::query()->truncate();
|
|
}
|
|
}
|