xml – Import ponad 50 000 produktów do woocommerce trwa ponad 4 godziny
Oszaleję, próbując rozwiązać ten problem, pracuję nad tym już od 2 tygodni. Byłbym wdzięczny za wszelką pomoc.
Mam problem z importem ponad 50 000 produktów z pliku xml do woocommerce. Parsowanie xml jest szybkie, działa w mniej niż kilka minut, ale „część importu” do woocommerce jest bardzo powolna.
Próbowałem wordpress/woocommerce rest api i to było wielkie nie, za wolno. Więc teraz używam pliku PHP na serwerze, który analizuje pliki xml i przesyła je.
Otrzymuję plik XML w tym formacie:
<product>
<SKU>28760</SKU>
<Name>...</Name>
<Description>...</Description>
<Category>...</Category>
<Price>...</Price>
<Shipping>...</Shipping>
<Currency></Currency>
<Instock>yes</Instock>
<ProductUrl>...</ProductUrl>
<ImageUrl>...</ImageUrl>
<Brand>...</Brand>
<OriginalPrice>...</OriginalPrice>
<Ean>...</Ean>
<ManufacturerArticleNumber>...</ManufacturerArticleNumber>
</product>
Następnie podczas analizowania im za pomocą XMLReader w tym celu, wypychając każdy element/wiersz do tablicy tymczasowej, a następnie wywołując funkcję importu z tą tablicą.
Oto jak to działa:
- Skrypty Bash uruchamiają php import.php > run.log
- Rozpoczyna się import pliku, parsowanie xml jest szybkie, ale import jest powolny i wolniejszy po osiągnięciu około 2000 produktów.
Używam klasy WC_Product_Variable do stworzenia nowego produktu.
Oto moja funkcja „import($p, $coId, $coName)”, gdzie $p to produkt, $coId companyId i $coName nazwa firmy, z której mam plik xml.
try {
$product_exists = wc_get_product_id_by_sku($p->SKU . "-MAIN");
if ($product_exists !== 0) {
$product = wc_get_product($product_exists);
$log->info('Product already exists, skipping', ["SKU" => $p->SKU . "-MAIN"]);
} else {
$product = new WC_Product_Variable();
$product->set_name($p->Name);
$product->set_sku($p->SKU . "-MAIN");
$category = (string)$p->Category;
$categories = explode(" - ", $category);
$parent_id = 0;
foreach ($categories as $cat) {
$term = term_exists($cat, 'product_cat', $parent_id);
if (!$term) {
$term = wp_insert_term(
$cat, mn
'product_cat',
array(
'parent' => $parent_id
)
);
}
// New category id
$parent_id = $term['term_id'];
}
// Upload image to wordpress
$image_url = $p->ImageUrl;
$image_id = media_sideload_image("{$image_url}", 0, null, "id");
if (is_wp_error($image_id)) {
echo "Error uploading image \n";
} else {
$product->set_image_id($image_id);
}
$product->set_category_ids(array($parent_id));
$attributes = array();
// one available for variation attribute
$attribute = new WC_Product_Attribute();
$attribute->set_name('Company');
$attribute->set_options(array("{$coName}"));
$attribute->set_position(0);
$attribute->set_visible(true);
$attribute->set_variation(true);
$attributes[] = $attribute;
$attribute = new WC_Product_Attribute();
$attribute->set_name('Brand');
$attribute->set_options(array($p->Brand));
$attribute->set_position(0);
$attribute->set_visible(true);
$attribute->set_variation(false);
$attributes[] = $attribute;
$product->set_short_description((string)$p->Description);
$product->set_attributes($attributes);
$product->set_regular_price($p->OriginalPrice);
$product->set_sale_price($p->Price);
$product->set_stock_status("outofstock");
$product->save();
$insertTag = wp_set_object_terms($product->get_id(), $p->Brand, "product_tag", true);
if (is_wp_error($insertTag)) {
echo "Cant insert tag for product: " . $insertTag->get_error_message() . " \n";
}
}
} catch (Exception $e) {
$log->error("Can't create main product", ["Error" => $e->getMessage(), "Product SKU" => $p->SKU]);
}
try {
// now we need two variations for Magical and Non-magical Wizard hat
$variation = new WC_Product_Variation();
$variation->set_parent_id($product->get_id());
$variation->set_sku((string)$p->SKU . "-" . $coId);
$variation->set_attributes(array('company' => $coName));
$variation->set_regular_price((string)$p->OriginalPrice);
$variation->set_sale_price((string)$p->Price);
$variation->set_stock_status(($p->Instock == "no" ? "outofstock" : "instock"));
$variation->update_meta_data("brand", (string)$p->Brand);
$variation->update_meta_data("updated", (string)Date("Y-m-d H:i:s"));
$variation->save();
} catch (Exception $e) {
$log->error("Can't create variant product", ["Error" => $e->getMessage(), "Product SKU" => $p->SKU]);
}
// Check memory and CPU usage
$load = sys_getloadavg();
$memory_usage = memory_get_usage(true);
if ($load[0] > 1.5 || $memory_usage > 0.9 * $memory_limit) {
$logger->warning('Current memory and CPU usage - PAUSING 10 SEC', [
'loadavg' => $load[0],
'memory' => $memory_usage . " of " . $memory_limit,
]);
sleep(10); // Pause 10 seconds
}
Jakieś pomysły, jak mogę to przyspieszyć? W tej chwili import 25 000 produktów zajmuje około 4 godzin.
Używam VPS z 4 GB ramu, 2 procesory.
Poza tą funkcją jest to zawarte w moim pliku przed funkcją:
set_time_limit(0);
ini_set('memory_limit', '1024M');
ini_set('max_execution_time', 0);
require_once "vendor/autoload.php";
if (!defined('ABSPATH')) {
define('ABSPATH', '/opt/bitnami/projects/****/public_html/');
}
include_once ABSPATH . 'wp-load.php';
include_once ABSPATH . 'wp-includes/class-wp-error.php';
include_once ABSPATH . 'wp-admin/includes/plugin.php';
include_once ABSPATH . 'wp-admin/includes/file.php';
include_once ABSPATH . 'wp-admin/includes/image.php';
require_once(ABSPATH . 'wp-admin/includes/media.php');
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
$memory_limit = ini_get('memory_limit');
if (preg_match('/^(\d+)(.)$/', $memory_limit, $matches)) {
if ($matches[2] == 'M') {
$memory_limit = $matches[1] * 1024 * 1024; // nnnM -> nnn MB
} else if ($matches[2] == 'K') {
$memory_limit = $matches[1] * 1024; // nnnK -> nnn KB
}
}
// Create a logger
$log = new Logger('import');
$log->pushHandler(new StreamHandler('/opt/bitnami/projects/****/logs/import-' . Date("Y-m-d") . '.log', Logger::INFO));
$log->info('Init import', ["Status" => "Starting up"]);
// Log the current memory and CPU usage
$logger = new Monolog\Logger('import-usage');
$logger->pushHandler(new Monolog\Handler\StreamHandler('/opt/bitnami/projects/****/logs/import-' . Date("Y-m-d") . '.log', Monolog\Logger::DEBUG));
echo "Start import job... \n";