Add export functionality for products with images to CSV
This commit is contained in:
398
export-csv.php
Normal file
398
export-csv.php
Normal 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";
|
||||
Reference in New Issue
Block a user