diff --git a/.vscode/ftp-kr.sync.cache.json b/.vscode/ftp-kr.sync.cache.json index a773bf70..f187889d 100644 --- a/.vscode/ftp-kr.sync.cache.json +++ b/.vscode/ftp-kr.sync.cache.json @@ -98,13 +98,13 @@ }, "google-merchant_id-1.xml": { "type": "-", - "size": 18781656, + "size": 18784618, "lmtime": 0, "modified": true }, ".htaccess": { "type": "-", - "size": 87138, + "size": 87360, "lmtime": 0, "modified": true }, diff --git a/export-csv.php b/export-csv.php new file mode 100644 index 00000000..ed6ff1e8 --- /dev/null +++ b/export-csv.php @@ -0,0 +1,398 @@ + PDO::ERRMODE_EXCEPTION, + PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true, // <— włącz buffer +]); + +// Otwórz CSV +$fh = fopen($outFile, "w"); +if (!$fh) die("Nie mogę utworzyć pliku CSV: {$outFile}\n"); + +// (Opcjonalnie) BOM dla Excela na Windows +fwrite($fh, "\xEF\xBB\xBF"); + +// Nagłówek +$header = [ + 'id_product', + 'id_product_attribute', + 'product_name', + 'manufacturer', + 'default_category', + 'active', + 'price_tax_excl', + 'quantity', + 'reference', + 'ean13', + 'weight', + 'comb_reference', + 'comb_ean13', + 'comb_weight', + 'comb_quantity', + 'attributes', // "Grupa: Wartość | Grupa2: Wartość2" + 'features' // "Cecha: Wartość | Cecha2: Wartość2" +]; +fputcsv($fh, $header, $delimiter); + +// ===== POMOCNICZE FUNKCJE ===== +function getProductFeatures(PDO $pdo, $prefix, $idLang, $idProduct): array +{ + // Fallback: weź tłumaczenie w :id_lang, a jeśli go nie ma – w minimalnym (dowolnym) id_lang + $sql = " + SELECT + COALESCE(fl.name, fl_any.name, '') AS feature_name, + COALESCE(fvl.value, fvl_any.value, '') AS feature_value + FROM {$prefix}feature_product fp + /* nazwa cechy w żądanym języku */ + LEFT JOIN {$prefix}feature_lang fl + ON fl.id_feature = fp.id_feature + AND fl.id_lang = :id_lang + /* wybór jakiegokolwiek dostępnego języka dla cechy */ + LEFT JOIN ( + SELECT id_feature, MIN(id_lang) AS any_lang + FROM {$prefix}feature_lang + GROUP BY id_feature + ) fl_pick + ON fl_pick.id_feature = fp.id_feature + LEFT JOIN {$prefix}feature_lang fl_any + ON fl_any.id_feature = fp.id_feature + AND fl_any.id_lang = fl_pick.any_lang + + /* wartość cechy */ + JOIN {$prefix}feature_value fv + ON fv.id_feature_value = fp.id_feature_value + + /* wartość w żądanym języku */ + LEFT JOIN {$prefix}feature_value_lang fvl + ON fvl.id_feature_value = fv.id_feature_value + AND fvl.id_lang = :id_lang + /* fallback: jakikolwiek język dla wartości */ + LEFT JOIN ( + SELECT id_feature_value, MIN(id_lang) AS any_lang + FROM {$prefix}feature_value_lang + GROUP BY id_feature_value + ) fvl_pick + ON fvl_pick.id_feature_value = fv.id_feature_value + LEFT JOIN {$prefix}feature_value_lang fvl_any + ON fvl_any.id_feature_value = fv.id_feature_value + AND fvl_any.id_lang = fvl_pick.any_lang + + WHERE fp.id_product = :id_product + ORDER BY feature_name ASC + "; + + $stmt = $pdo->prepare($sql); + $stmt->execute([ + ':id_lang' => (int)$idLang, + ':id_product' => (int)$idProduct, + ]); + + $out = []; + while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) + { + $fname = trim((string)($row['feature_name'] ?? '')); + $fval = trim((string)($row['feature_value'] ?? '')); + if ($fname === '' && $fval === '') continue; + if ($fname === '') $fname = 'Cecha'; + $out[$fname] = $fval; + } + return $out; +} + + +function getProductCombinations(PDO $pdo, $prefix, $idLang, $idShop, $idProduct): array +{ + $sqlComb = " + SELECT pa.id_product_attribute, + pa.reference AS comb_reference, + pa.ean13 AS comb_ean13, + pa.weight AS comb_weight, + COALESCE(sa.quantity, 0) AS comb_quantity + FROM {$prefix}product_attribute pa + LEFT JOIN {$prefix}stock_available sa + ON sa.id_product = pa.id_product + AND sa.id_product_attribute = pa.id_product_attribute + AND (sa.id_shop = :id_shop OR sa.id_shop IS NULL) + WHERE pa.id_product = :id_product + ORDER BY pa.id_product_attribute ASC + "; + $st = $pdo->prepare($sqlComb); + $st->execute([':id_product' => (int)$idProduct, ':id_shop' => (int)$idShop]); + $combinations = $st->fetchAll(PDO::FETCH_ASSOC); + if (!$combinations) return []; + + $ids = array_column($combinations, 'id_product_attribute'); + $in = implode(',', array_map('intval', $ids)); + + $sqlAttrs = " + SELECT pac.id_product_attribute, + agl.name AS group_name, + al.name AS attr_name + FROM {$prefix}product_attribute_combination pac + JOIN {$prefix}attribute a + ON a.id_attribute = pac.id_attribute + JOIN {$prefix}attribute_lang al + ON al.id_attribute = a.id_attribute AND al.id_lang = :id_lang + JOIN {$prefix}attribute_group_lang agl + ON agl.id_attribute_group = a.id_attribute_group AND agl.id_lang = :id_lang + WHERE pac.id_product_attribute IN ($in) + ORDER BY agl.name, al.name + "; + $st2 = $pdo->prepare($sqlAttrs); + $st2->execute([':id_lang' => (int)$idLang]); + + $attrMap = []; + while ($r = $st2->fetch(PDO::FETCH_ASSOC)) + { + $ipa = (int)$r['id_product_attribute']; + $pair = trim($r['group_name']) . ": " . trim($r['attr_name']); + $attrMap[$ipa][] = $pair; + } + + foreach ($combinations as &$c) + { + $ipa = (int)$c['id_product_attribute']; + $pairs = $attrMap[$ipa] ?? []; + $c['attributes_str'] = implode(' | ', $pairs); + } + return $combinations; +} + +// ===== FUNKCJA PISZĄCA WIERSZE DO CSV ===== +function writeProductRowsToCsv($fh, $prod, $featuresStr, $combinations, $delimiter) +{ + if ($combinations) + { + foreach ($combinations as $c) + { + $line = [ + $prod['id_product'], + $c['id_product_attribute'], + + $prod['product_name'], + $prod['manufacturer'], + $prod['default_category'], + (int)$prod['active'], + $prod['price_tax_excl'], + $prod['quantity'], + $prod['reference'], + $prod['ean13'], + $prod['weight'], + + $c['comb_reference'], + $c['comb_ean13'], + $c['comb_weight'], + $c['comb_quantity'], + $c['attributes_str'], + $featuresStr, + ]; + fputcsv($fh, $line, $delimiter); + } + } + else + { + $line = [ + $prod['id_product'], + 0, + + $prod['product_name'], + $prod['manufacturer'], + $prod['default_category'], + (int)$prod['active'], + $prod['price_tax_excl'], + $prod['quantity'], + $prod['reference'], + $prod['ean13'], + $prod['weight'], + + '', + '', + '', + '', + '', // attributes + $featuresStr, + ]; + fputcsv($fh, $line, $delimiter); + } +} + +// ===== ZAPYTANIE BAZOWE (wspólne) ===== +$baseSelect = " + SELECT + p.id_product, + COALESCE(pl.name, CONCAT('Produkt #', p.id_product)) AS product_name, + p.reference, + p.ean13, + p.weight, + ps.price AS price_tax_excl, + ps.active, + COALESCE(sa.quantity, 0) AS quantity, + m.name AS manufacturer, + cl.name AS default_category + FROM {$prefix}product p + /* KLUCZOWA ZMIANA: LEFT JOIN + warunek po id_shop */ + LEFT JOIN {$prefix}product_lang pl + ON pl.id_product = p.id_product + AND pl.id_lang = :id_lang + AND pl.id_shop = :id_shop + JOIN {$prefix}product_shop ps + ON ps.id_product = p.id_product + AND ps.id_shop = :id_shop + LEFT JOIN {$prefix}stock_available sa + ON sa.id_product = p.id_product + AND sa.id_product_attribute = 0 + AND (sa.id_shop = :id_shop OR sa.id_shop IS NULL) + LEFT JOIN {$prefix}manufacturer m + ON m.id_manufacturer = p.id_manufacturer + LEFT JOIN {$prefix}category_lang cl + ON cl.id_category = p.id_category_default + AND cl.id_lang = :id_lang +"; + +// ===== TRYB TESTOWY: tylko jeden produkt po id_product lub EAN ===== +if ($testProductId || $testEan !== "") +{ + if ($testEan !== "" && !$testProductId) + { + $stmtId = $pdo->prepare("SELECT id_product FROM {$prefix}product WHERE ean13 = :ean LIMIT 1"); + $stmtId->execute([':ean' => $testEan]); + $foundId = (int)$stmtId->fetchColumn(); + if ($foundId) $testProductId = $foundId; + } + + if (!$testProductId) + { + fclose($fh); + die("Nie znaleziono produktu dla podanego EAN.\n"); + } + + echo "Tryb testowy – eksport tylko produktu id_product={$testProductId}" . ($testEan ? " (EAN={$testEan})" : "") . "\n"; + + $sqlOne = $baseSelect . " WHERE p.id_product = :pid LIMIT 1"; + $stmt = $pdo->prepare($sqlOne); + $stmt->bindValue(':id_lang', (int)$idLang, PDO::PARAM_INT); + $stmt->bindValue(':id_shop', (int)$idShop, PDO::PARAM_INT); + $stmt->bindValue(':pid', (int)$testProductId, PDO::PARAM_INT); + $stmt->execute(); + + $prod = $stmt->fetch(PDO::FETCH_ASSOC); + $stmt->closeCursor(); + if ($prod) + { + $featuresMap = getProductFeatures($pdo, $prefix, $idLang, (int)$prod['id_product']); + $featuresStr = ''; + if ($featuresMap) + { + $pairs = []; + foreach ($featuresMap as $k => $v) $pairs[] = trim($k) . ': ' . trim($v); + $featuresStr = implode(' | ', $pairs); + } + $combinations = getProductCombinations($pdo, $prefix, $idLang, $idShop, (int)$prod['id_product']); + writeProductRowsToCsv($fh, $prod, $featuresStr, $combinations, $delimiter); + echo "✅ Zapisano rekord(y) produktu testowego do CSV.\n"; + } + else + { + echo "Brak produktu o id_product={$testProductId}.\n"; + } + + fclose($fh); + echo "✅ Zapisano CSV: {$outFile}\n"; + exit; +} + +// ===== PEŁNY EKSPORT (paginacja) ===== +$sqlCount = "SELECT COUNT(*) FROM {$prefix}product"; +$total = (int)$pdo->query($sqlCount)->fetchColumn(); +echo "Znaleziono produktów: {$total}\n"; + +$limit = 1000; +$offset = 0; + +while (true) +{ + $sqlProducts = $baseSelect . " ORDER BY p.id_product ASC LIMIT :limit OFFSET :offset"; + $stmt = $pdo->prepare($sqlProducts); + $stmt->bindValue(':id_lang', (int)$idLang, PDO::PARAM_INT); + $stmt->bindValue(':id_shop', (int)$idShop, PDO::PARAM_INT); + $stmt->bindValue(':limit', (int)$limit, PDO::PARAM_INT); + $stmt->bindValue(':offset', (int)$offset, PDO::PARAM_INT); + $stmt->execute(); + + $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); + $stmt->closeCursor(); + if (!$rows) break; + + foreach ($rows as $prod) + { + $idProduct = (int)$prod['id_product']; + + $featuresMap = getProductFeatures($pdo, $prefix, $idLang, $idProduct); + $featuresStr = ''; + if ($featuresMap) + { + $pairs = []; + foreach ($featuresMap as $k => $v) $pairs[] = trim($k) . ': ' . trim($v); + $featuresStr = implode(' | ', $pairs); + } + + $combinations = getProductCombinations($pdo, $prefix, $idLang, $idShop, $idProduct); + + writeProductRowsToCsv($fh, $prod, $featuresStr, $combinations, $delimiter); + } + + $offset += $limit; + echo "Przetworzono: {$offset} / {$total}\n"; +} + +fclose($fh); +echo "✅ Zapisano CSV: {$outFile}\n"; diff --git a/export.php b/export.php new file mode 100644 index 00000000..ac700387 --- /dev/null +++ b/export.php @@ -0,0 +1,78 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + +// Pobierz produkty +if ($testEan !== "") { + $sql = "SELECT p.id_product, p.ean13 + FROM {$prefix}product p + WHERE p.ean13 = :ean"; + $stmt = $pdo->prepare($sql); + $stmt->execute(['ean' => $testEan]); + $products = $stmt->fetchAll(PDO::FETCH_ASSOC); + echo "Tryb testowy – eksportuję tylko produkt o EAN: {$testEan}\n
"; +} else { + $sql = "SELECT p.id_product, p.ean13 + FROM {$prefix}product p + WHERE p.ean13 IS NOT NULL AND p.ean13 <> ''"; + $products = $pdo->query($sql)->fetchAll(PDO::FETCH_ASSOC); + echo "Eksportuję wszystkie produkty\n
"; +} + +foreach ($products as $prod) { + $ean = $prod['ean13']; + $id_product = (int)$prod['id_product']; + + // Pobierz zdjęcia produktu + $sqlImg = "SELECT id_image + FROM {$prefix}image + WHERE id_product = :id_product + ORDER BY position ASC"; + $stmt = $pdo->prepare($sqlImg); + $stmt->execute(['id_product' => $id_product]); + $images = $stmt->fetchAll(PDO::FETCH_ASSOC); + + if (!$images) { + echo "Brak zdjęć dla EAN: {$ean}\n
"; + continue; + } + + $i = 1; + foreach ($images as $img) { + $id_image = (int)$img['id_image']; + + // Ścieżka do pliku (PrestaShop – struktura katalogów np. 123 -> /1/2/3/123.jpg) + $path = implode('/', str_split((string)$id_image)) . "/" . $id_image . ".jpg"; + $src = __DIR__ . "/img/p/" . $path; + + if (file_exists($src)) { + $dest = $exportDir . "/" . $ean . "_" . $i . ".jpg"; + copy($src, $dest); + echo "Zapisano: $dest\n
"; + } else { + echo "Brak pliku: $src\n
"; + } + $i++; + } +} + +echo "✅ Eksport zakończony.\n
"; +?> \ No newline at end of file