Add export functionality for products with images to CSV

This commit is contained in:
2025-09-09 17:44:22 +02:00
parent 602e6a92fe
commit d7fe875eb9
3 changed files with 478 additions and 2 deletions

398
export-csv.php Normal file
View File

@@ -0,0 +1,398 @@
<?php
/**
* PrestaShop 1.7.x eksport produktów z atrybutami i cechami do CSV
* Każdy wiersz = produkt bez kombinacji albo konkretna kombinacja produktu.
*/
ini_set('memory_limit', '1024M');
set_time_limit(0);
// ===== KONFIGURACJA =====
$host = "localhost";
$db = "interblue_sklep";
$user = "interblue_sklep";
$pass = "2212+#++@pSVSb4";
$prefix = "ps_"; // zmień jeśli masz inny prefix
$idLang = 1; // ID języka (sprawdź w ps_lang)
$idShop = 1; // ID sklepu (sprawdź w ps_shop)
$outFile = __DIR__ . "/export_products.csv";
$delimiter = ";"; // średnik wygodny do Excela w PL
// ========================
// --- TRYB TESTOWY NOWE ---
// Ustaw JEDNO z poniższych (drugie zostaw puste), aby wyeksportować tylko wybrany produkt:
$testProductId = null; // np. 123; lub null
$testEan = ""; // np. "5901234567890"; lub "" (puste)
// Dodatkowo możesz nadpisać przez URL/CLI: ?test_id=123 lub ?test_ean=590...
if (php_sapi_name() !== 'cli')
{
if (isset($_GET['test_id']) && is_numeric($_GET['test_id']))
{
$testProductId = (int)$_GET['test_id'];
}
if (isset($_GET['test_ean']) && $_GET['test_ean'] !== '')
{
$testEan = trim($_GET['test_ean']);
}
}
else
{
// CLI: php export.php test_id=123 lub test_ean=590...
foreach ($argv as $arg)
{
if (preg_match('/^test_id=(\d+)$/', $arg, $m)) $testProductId = (int)$m[1];
if (preg_match('/^test_ean=(.+)$/', $arg, $m)) $testEan = trim($m[1]);
}
}
// ========================
// Połączenie PDO
$pdo = new PDO("mysql:host=$host;dbname=$db;charset=utf8", $user, $pass, [
PDO::ATTR_ERRMODE => 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";