mirror of
https://github.com/myronblair/tomsjavajive
synced 2026-06-30 17:50:32 -05:00
SEO overhaul: product schema, dynamic sitemap, favicon, og-image fix
- product.php: set metaTitle, metaDescription, canonicalUrl, ogImage, ogType=product, productSchema (JSON-LD with price/availability/reviews), and breadcrumbs variables for header.php to consume - sitemap.php: dynamic XML sitemap generated from DB — includes all 30 active products + static pages; robots.txt now points here - header.php: fix favicon links (favicon.ico in root + icon-192.png); fix productSchema output (was double-encoding via json_encode) - robots.txt: point Sitemap directive to /sitemap.php Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+5
-3
@@ -54,7 +54,7 @@ $customerUser = $isLoggedIn ? CustomerAuth::getUser() : null;
|
||||
</script>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($productSchema)): ?>
|
||||
<script type="application/ld+json"><?= json_encode($productSchema,JSON_UNESCAPED_SLASHES) ?></script>
|
||||
<script type="application/ld+json"><?= $productSchema ?></script>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- PWA Meta Tags -->
|
||||
@@ -78,8 +78,10 @@ $customerUser = $isLoggedIn ? CustomerAuth::getUser() : null;
|
||||
<link rel="stylesheet" href="/assets/css/style.css?v=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/assets/css/style.css') ?>">
|
||||
|
||||
<!-- Favicon -->
|
||||
<link rel="icon" href="/assets/images/logo.png" type="image/png">
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<link rel="icon" type="image/png" sizes="192x192" href="/assets/images/icon-192.png">
|
||||
<link rel="apple-touch-icon" sizes="192x192" href="/assets/images/icon-192.png">
|
||||
|
||||
<?php if (strpos($_SERVER['REQUEST_URI'], 'payment') !== false || strpos($_SERVER['REQUEST_URI'], 'checkout') !== false): ?>
|
||||
<script src="https://js.stripe.com/v3/"></script>
|
||||
<?php endif; ?>
|
||||
|
||||
+47
-3
@@ -23,9 +23,6 @@ if (!$product) {
|
||||
exit;
|
||||
}
|
||||
|
||||
$pageTitle = $product['name'] . " - Tom's Java Jive";
|
||||
$pageDescription = truncate(strip_tags($product['description']), 160);
|
||||
|
||||
// Get product reviews
|
||||
$reviews = db()->fetchAll(
|
||||
"SELECT * FROM reviews WHERE product_id = :id AND is_approved = 1 ORDER BY created_at DESC LIMIT 10",
|
||||
@@ -49,6 +46,53 @@ $mainImage = !empty($images) ? $images[0] : '/assets/images/placeholder-product.
|
||||
$salePrice = $product['sale_price'];
|
||||
$price = $product['price'];
|
||||
$inStock = $product['stock'] > 0;
|
||||
$displayPrice = $salePrice ?? $price;
|
||||
|
||||
// SEO meta
|
||||
$categoryLabel = ucfirst($product['category'] ?? 'Coffee');
|
||||
$metaTitle = $product['name'] . ' ' . $categoryLabel . " Coffee | Tom's Java Jive";
|
||||
$metaDescription = truncate(strip_tags($product['description'] ?? ''), 155)
|
||||
?: $product['name'] . ' flavored artisan coffee beans freshly roasted in Weatherford, TX. Available in whole bean and ground. Ships nationwide.';
|
||||
$metaKeywords = strtolower($product['name']) . ' coffee, ' . strtolower($categoryLabel) . ' coffee, flavored coffee beans, artisan coffee, buy coffee online';
|
||||
$canonicalUrl = 'https://tomsjavajive.com/product.php?id=' . $productId;
|
||||
$ogType = 'product';
|
||||
$ogImage = (strpos($mainImage, 'http') === 0) ? $mainImage : 'https://tomsjavajive.com' . $mainImage;
|
||||
|
||||
// Structured data — Product schema
|
||||
$productSchemaData = [
|
||||
'@context' => 'https://schema.org',
|
||||
'@type' => 'Product',
|
||||
'name' => $product['name'] . ' ' . $categoryLabel . ' Coffee',
|
||||
'url' => $canonicalUrl,
|
||||
'image' => $ogImage,
|
||||
'description' => $metaDescription,
|
||||
'brand' => ['@type' => 'Brand', 'name' => "Tom's Java Jive"],
|
||||
'offers' => [
|
||||
'@type' => 'Offer',
|
||||
'priceCurrency' => 'USD',
|
||||
'price' => number_format((float) $displayPrice, 2, '.', ''),
|
||||
'availability' => $inStock ? 'https://schema.org/InStock' : 'https://schema.org/OutOfStock',
|
||||
'url' => $canonicalUrl,
|
||||
'seller' => ['@type' => 'Organization', 'name' => "Tom's Java Jive"],
|
||||
],
|
||||
];
|
||||
if ($reviewCount > 0) {
|
||||
$productSchemaData['aggregateRating'] = [
|
||||
'@type' => 'AggregateRating',
|
||||
'ratingValue' => round($avgRating, 1),
|
||||
'reviewCount' => $reviewCount,
|
||||
'bestRating' => 5,
|
||||
'worstRating' => 1,
|
||||
];
|
||||
}
|
||||
$productSchema = json_encode($productSchemaData, JSON_UNESCAPED_SLASHES);
|
||||
|
||||
// Breadcrumbs
|
||||
$breadcrumbs = [
|
||||
['name' => 'Home', 'url' => 'https://tomsjavajive.com/'],
|
||||
['name' => 'Shop', 'url' => 'https://tomsjavajive.com/shop.php'],
|
||||
['name' => $product['name'], 'url' => $canonicalUrl],
|
||||
];
|
||||
|
||||
require_once __DIR__ . '/includes/header.php';
|
||||
?>
|
||||
|
||||
+1
-1
@@ -8,4 +8,4 @@ Disallow: /checkout.php
|
||||
Disallow: /payment.php
|
||||
Disallow: /config/
|
||||
Disallow: /install/
|
||||
Sitemap: https://tomsjavajive.com/sitemap.xml
|
||||
Sitemap: https://tomsjavajive.com/sitemap.php
|
||||
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
/**
|
||||
* Tom's Java Jive - Dynamic XML Sitemap
|
||||
*/
|
||||
require_once __DIR__ . '/includes/functions.php';
|
||||
|
||||
header('Content-Type: application/xml; charset=utf-8');
|
||||
header('X-Robots-Tag: noindex');
|
||||
|
||||
$base = 'https://tomsjavajive.com';
|
||||
$now = date('Y-m-d');
|
||||
|
||||
$products = db()->fetchAll(
|
||||
"SELECT product_id, updated_at FROM products WHERE is_active = 1 ORDER BY created_at DESC"
|
||||
);
|
||||
|
||||
echo '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
|
||||
echo '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . "\n";
|
||||
|
||||
// Static pages
|
||||
$staticPages = [
|
||||
['loc' => '/', 'priority' => '1.0', 'changefreq' => 'weekly'],
|
||||
['loc' => '/shop.php', 'priority' => '0.9', 'changefreq' => 'daily'],
|
||||
['loc' => '/about.php', 'priority' => '0.6', 'changefreq' => 'monthly'],
|
||||
['loc' => '/contact.php', 'priority' => '0.5', 'changefreq' => 'monthly'],
|
||||
['loc' => '/login.php', 'priority' => '0.3', 'changefreq' => 'monthly'],
|
||||
['loc' => '/register.php','priority' => '0.3', 'changefreq' => 'monthly'],
|
||||
];
|
||||
|
||||
foreach ($staticPages as $p) {
|
||||
$loc = htmlspecialchars($base . $p['loc']);
|
||||
echo " <url><loc>{$loc}</loc><lastmod>{$now}</lastmod><changefreq>{$p['changefreq']}</changefreq><priority>{$p['priority']}</priority></url>\n";
|
||||
}
|
||||
|
||||
// Product pages
|
||||
foreach ($products as $product) {
|
||||
$loc = htmlspecialchars($base . '/product.php?id=' . $product['product_id']);
|
||||
$lastmod = substr($product['updated_at'] ?? $now, 0, 10);
|
||||
echo " <url><loc>{$loc}</loc><lastmod>{$lastmod}</lastmod><changefreq>weekly</changefreq><priority>0.8</priority></url>\n";
|
||||
}
|
||||
|
||||
echo '</urlset>';
|
||||
Reference in New Issue
Block a user