category_id,
category_year AS year,
category_maker AS make,
category_model AS model
FROM category
WHERE category_maker != ''
AND category_model != ''
AND status = '1'
ORDER BY category_year DESC, category_maker ASC, category_model ASC";
$stmtCat = $pdo->query($sqlCat);
$categories = $stmtCat->fetchAll();
foreach ($categories as $cat) {
$year = trim($cat['year']);
$make = str_replace(' ', '_', trim($cat['make']));
$model = str_replace(' ', '_', trim($cat['model']));
$catId = $cat['category_id'];
$catSlug = "{$year}-{$make}-{$model}-Parts-Lists-{$catId}";
$urls[] = [
'loc' => $baseUrl . "/products/{$catSlug}",
'lastmod' => $today,
'changefreq' => 'weekly',
'priority' => '0.8'
];
}
// 4. Query All Active Products for Detail URLs (Synced with Google Shopping Feed logic)
$sqlParts = "SELECT
i.item_id AS id,
i.item_num AS sku,
isp.item_partslink_num AS partslink,
isp.item_oem_num AS oem_number,
isp.item_applications AS applications,
COALESCE(ise.item_title, '') AS item_title,
COALESCE(ise.item_title2, '') AS item_title2,
i.item_description,
MAX(itc.category_id) AS category_id
FROM items i
LEFT JOIN items_spec isp ON i.item_id = isp.item_id
LEFT JOIN item_spec_ext ise ON i.item_id = ise.item_id
LEFT JOIN item_to_category itc ON i.item_id = itc.item_id
WHERE i.show_on_web = 1
AND i.item_price > 0
AND i.item_description IS NOT NULL
AND TRIM(i.item_description) != ''
GROUP BY i.item_id
ORDER BY i.item_id DESC";
$stmtParts = $pdo->query($sqlParts);
$parts = $stmtParts->fetchAll();
foreach ($parts as $p) {
$id = $p['id'];
$sku = !empty($p['sku']) ? trim($p['sku']) : '';
$oeNumber = '';
if (!empty($p['oem_number'])) {
$cleanOem = str_replace('##', '#', $p['oem_number']);
$oes = array_filter(explode('#', $cleanOem));
$cleanOesArr = array_map(function($val) {
return preg_replace('/[^a-zA-Z0-9\-]/', '', trim($val));
}, $oes);
$oeNumber = implode(' ', array_filter($cleanOesArr));
}
list($fullTitle, $yearStr, $makeModel, $cleanPart, $attrStr, $slugYearStr) = buildPlanBTitle(
$p['applications'],
$p['item_title'],
$p['item_title2'],
$p['item_description']
);
$nameSlug = preg_replace('/[^a-zA-Z0-9\s]/', '', $cleanPart);
$nameSlug = preg_replace('/\s+/', '-', trim($nameSlug));
$oemSlug = !empty($oeNumber) ? explode(' ', $oeNumber)[0] : 'NONE';
$partslink = !empty($p['partslink']) ? trim($p['partslink']) : '';
$plSlug = !empty($partslink) ? preg_replace('/[^a-zA-Z0-9\-]/', '', $partslink) : (!empty($sku) ? preg_replace('/[^a-zA-Z0-9\-]/', '', $sku) : 'PART');
$ymmSlug = 'ALL-ALL-ALL';
$finalSlugYear = !empty($slugYearStr) ? $slugYearStr : $yearStr;
if (!empty($makeModel)) {
$ymmSlug = preg_replace('/[^a-zA-Z0-9\s\-]/', '', "{$finalSlugYear} {$makeModel}");
$ymmSlug = preg_replace('/\s+/', '-', trim($ymmSlug));
}
$catId = !empty($p['category_id']) ? (int)$p['category_id'] : 0;
$productSlug = "{$id}-{$ymmSlug}-{$nameSlug}-{$oemSlug}-{$plSlug}-{$catId}";
$urls[] = [
'loc' => $baseUrl . "/products_spec/{$productSlug}",
'lastmod' => $today,
'changefreq' => 'monthly',
'priority' => '0.7'
];
}
} catch (Exception $e) {
// Fallback gracefully if database fails
}
}
// Generate XML Output
$maxUrlsPerSitemap = 50000;
$chunks = array_chunk($urls, $maxUrlsPerSitemap);
$isCliOrGenerate = php_sapi_name() === 'cli' || isset($_GET['generate']);
$xmlDirs = [dirname(__DIR__), dirname(__DIR__) . '/dist'];
if (count($chunks) <= 1) {
$xml = new SimpleXMLElement('');
foreach ($urls as $u) {
$urlTag = $xml->addChild('url');
$urlTag->addChild('loc', htmlspecialchars($u['loc']));
$urlTag->addChild('lastmod', $u['lastmod']);
$urlTag->addChild('changefreq', $u['changefreq']);
$urlTag->addChild('priority', $u['priority']);
}
if ($isCliOrGenerate) {
foreach ($xmlDirs as $dir) {
if (is_dir($dir)) {
$xmlPath = $dir . '/sitemap.xml';
$xml->asXML($xmlPath);
}
}
echo "Sitemap generated successfully in web and web/dist (" . count($urls) . " URLs)\n";
} else {
echo $xml->asXML();
}
} else {
$indexXml = new SimpleXMLElement('');
foreach ($chunks as $index => $chunk) {
$sitemapIndex = $index + 1;
$sitemapFilename = "sitemap-{$sitemapIndex}.xml";
$xml = new SimpleXMLElement('');
foreach ($chunk as $u) {
$urlTag = $xml->addChild('url');
$urlTag->addChild('loc', htmlspecialchars($u['loc']));
$urlTag->addChild('lastmod', $u['lastmod']);
$urlTag->addChild('changefreq', $u['changefreq']);
$urlTag->addChild('priority', $u['priority']);
}
if ($isCliOrGenerate) {
foreach ($xmlDirs as $dir) {
if (is_dir($dir)) {
$xml->asXML($dir . '/' . $sitemapFilename);
}
}
}
$sitemapTag = $indexXml->addChild('sitemap');
$sitemapTag->addChild('loc', htmlspecialchars($baseUrl . '/' . $sitemapFilename));
$sitemapTag->addChild('lastmod', $today);
}
if ($isCliOrGenerate) {
foreach ($xmlDirs as $dir) {
if (is_dir($dir)) {
$indexPath = $dir . '/sitemap.xml';
$indexXml->asXML($indexPath);
}
}
echo "Sitemap Index generated successfully in web and web/dist with " . count($chunks) . " sub-sitemaps (Total URLs: " . count($urls) . ")\n";
} else {
echo $indexXml->asXML();
}
}