399 lines
12 KiB
PHP
399 lines
12 KiB
PHP
<?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";
|