Expand locations to 14, update service slugs, dynamic sitemap and footer
- Add 8 new location pages: Cheektowaga, Depew, Grand Island, Hamburg, Kenmore, Orchard Park, Tonawanda, West Seneca (locations.sqlite) - Update service slugs to floor-* pattern; repair -> restoration (services.sqlite) - Replace static sitemap.xml with dynamic sitemap.php (35 URLs, all routes) - Dynamic footer location list from SQLite (no more hardcoded links) - Reviews page: schema.org AggregateRating + Review from testimonials.sqlite - Expand schema.org areaServed to 14 cities on all pages - Add Elfsight Google Reviews widget to reviews page - Update blog.sqlite content - Drop Dockerfile COPY for deleted sitemap.xml
This commit is contained in:
@@ -12,7 +12,6 @@ RUN chmod +x /entrypoint.sh
|
||||
COPY assets /var/www/html/assets/
|
||||
COPY src /var/www/html/src/
|
||||
COPY robots.txt /var/www/html/robots.txt
|
||||
COPY sitemap.xml /var/www/html/sitemap.xml
|
||||
COPY 404.html /var/www/html/404.html
|
||||
COPY 500.html /var/www/html/500.html
|
||||
COPY .env /var/www/html/.env
|
||||
|
||||
+7
-1
@@ -42,7 +42,13 @@ http {
|
||||
client_max_body_size 16k;
|
||||
|
||||
location = /robots.txt { access_log off; try_files $uri =404; }
|
||||
location = /sitemap.xml { access_log off; try_files $uri =404; }
|
||||
location = /sitemap.xml {
|
||||
access_log off;
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME /var/www/html/src/api/sitemap.php;
|
||||
fastcgi_param QUERY_STRING "";
|
||||
fastcgi_pass 127.0.0.1:9000;
|
||||
}
|
||||
location = /404.html { internal; }
|
||||
location = /500.html { internal; }
|
||||
|
||||
|
||||
-105
@@ -1,105 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<url>
|
||||
<loc>https://floorithardwoodfloors.com/</loc>
|
||||
<lastmod>2026-05-31</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://floorithardwoodfloors.com/about/</loc>
|
||||
<lastmod>2026-05-31</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://floorithardwoodfloors.com/contact/</loc>
|
||||
<lastmod>2026-05-31</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://floorithardwoodfloors.com/reviews/</loc>
|
||||
<lastmod>2026-05-31</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://floorithardwoodfloors.com/blog/</loc>
|
||||
<lastmod>2026-05-31</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://floorithardwoodfloors.com/services/</loc>
|
||||
<lastmod>2026-05-31</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://floorithardwoodfloors.com/services/floor-installation/</loc>
|
||||
<lastmod>2026-05-31</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.85</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://floorithardwoodfloors.com/services/floor-refinishing/</loc>
|
||||
<lastmod>2026-05-31</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.85</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://floorithardwoodfloors.com/services/floor-restoration/</loc>
|
||||
<lastmod>2026-05-31</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.85</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://floorithardwoodfloors.com/services/floor-sanding/</loc>
|
||||
<lastmod>2026-05-31</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.85</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://floorithardwoodfloors.com/locations/</loc>
|
||||
<lastmod>2026-05-31</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://floorithardwoodfloors.com/locations/buffalo/</loc>
|
||||
<lastmod>2026-05-31</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.85</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://floorithardwoodfloors.com/locations/amherst/</loc>
|
||||
<lastmod>2026-05-31</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://floorithardwoodfloors.com/locations/williamsville/</loc>
|
||||
<lastmod>2026-05-31</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://floorithardwoodfloors.com/locations/clarence/</loc>
|
||||
<lastmod>2026-05-31</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://floorithardwoodfloors.com/locations/east-amherst/</loc>
|
||||
<lastmod>2026-05-31</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://floorithardwoodfloors.com/locations/lancaster/</loc>
|
||||
<lastmod>2026-05-31</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
</urlset>
|
||||
@@ -37,12 +37,14 @@
|
||||
<div class="footer-col">
|
||||
<h5>Locations</h5>
|
||||
<ul>
|
||||
<li><a href="/locations/buffalo/">Buffalo</a></li>
|
||||
<li><a href="/locations/amherst/">Amherst</a></li>
|
||||
<li><a href="/locations/williamsville/">Williamsville</a></li>
|
||||
<li><a href="/locations/clarence/">Clarence</a></li>
|
||||
<li><a href="/locations/east-amherst/">East Amherst</a></li>
|
||||
<li><a href="/locations/lancaster/">Lancaster</a></li>
|
||||
<?php
|
||||
$ldb = new SQLite3(__DIR__ . '/../data/locations.sqlite', SQLITE3_OPEN_READONLY);
|
||||
$ldb->busyTimeout(3000);
|
||||
$locs = $ldb->query("SELECT slug, city FROM locations ORDER BY city");
|
||||
while ($loc = $locs->fetchArray(SQLITE3_ASSOC)):
|
||||
?><li><a href="/locations/<?= htmlspecialchars($loc['slug'], ENT_QUOTES) ?>/"><?= htmlspecialchars($loc['city'], ENT_QUOTES) ?>, NY</a></li>
|
||||
<?php endwhile; $ldb->close(); ?>
|
||||
<li><a href="/locations/">View all service areas</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer-col">
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Executable
+58
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
$base = 'https://floorithardwoodfloors.com';
|
||||
$today = date('Y-m-d');
|
||||
$data_dir = __DIR__ . '/data';
|
||||
|
||||
function open_db(string $path): SQLite3 {
|
||||
$db = new SQLite3($path, SQLITE3_OPEN_READONLY);
|
||||
$db->busyTimeout(3000);
|
||||
return $db;
|
||||
}
|
||||
|
||||
$urls = [];
|
||||
|
||||
$urls[] = ['loc' => $base . '/', 'lastmod' => $today, 'changefreq' => 'weekly', 'priority' => '1.0'];
|
||||
|
||||
$pages_db = open_db($data_dir . '/pages.sqlite');
|
||||
$res = $pages_db->query("SELECT slug FROM pages WHERE in_nav=1 AND slug != 'home' ORDER BY nav_order");
|
||||
while ($row = $res->fetchArray(SQLITE3_ASSOC)) {
|
||||
$urls[] = ['loc' => $base . '/' . $row['slug'] . '/', 'lastmod' => $today, 'changefreq' => 'monthly', 'priority' => '0.8'];
|
||||
}
|
||||
$pages_db->close();
|
||||
|
||||
$svc_db = open_db($data_dir . '/services.sqlite');
|
||||
$res = $svc_db->query("SELECT slug FROM services ORDER BY slug");
|
||||
while ($row = $res->fetchArray(SQLITE3_ASSOC)) {
|
||||
$urls[] = ['loc' => $base . '/services/' . $row['slug'] . '/', 'lastmod' => $today, 'changefreq' => 'monthly', 'priority' => '0.85'];
|
||||
}
|
||||
$svc_db->close();
|
||||
|
||||
$loc_db = open_db($data_dir . '/locations.sqlite');
|
||||
$res = $loc_db->query("SELECT slug FROM locations ORDER BY slug");
|
||||
while ($row = $res->fetchArray(SQLITE3_ASSOC)) {
|
||||
$urls[] = ['loc' => $base . '/locations/' . $row['slug'] . '/', 'lastmod' => $today, 'changefreq' => 'monthly', 'priority' => '0.8'];
|
||||
}
|
||||
$loc_db->close();
|
||||
|
||||
$blog_db = open_db($data_dir . '/blog.sqlite');
|
||||
$res = $blog_db->query("SELECT slug FROM posts WHERE published=1 ORDER BY created_at DESC");
|
||||
while ($row = $res->fetchArray(SQLITE3_ASSOC)) {
|
||||
$urls[] = ['loc' => $base . '/blog/' . $row['slug'] . '/', 'lastmod' => $today, 'changefreq' => 'monthly', 'priority' => '0.7'];
|
||||
}
|
||||
$blog_db->close();
|
||||
|
||||
header('Content-Type: application/xml; charset=UTF-8');
|
||||
header('X-Robots-Tag: noindex');
|
||||
echo '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
|
||||
echo '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . "\n";
|
||||
foreach ($urls as $u) {
|
||||
echo " <url>\n";
|
||||
echo ' <loc>' . htmlspecialchars($u['loc'], ENT_XML1) . "</loc>\n";
|
||||
echo ' <lastmod>' . $u['lastmod'] . "</lastmod>\n";
|
||||
echo ' <changefreq>' . $u['changefreq'] . "</changefreq>\n";
|
||||
echo ' <priority>' . $u['priority'] . "</priority>\n";
|
||||
echo " </url>\n";
|
||||
}
|
||||
echo '</urlset>' . "\n";
|
||||
@@ -20,7 +20,7 @@ $page_title = $page['title'];
|
||||
$page_meta = $page['meta_description'];
|
||||
$sections = json_decode($page['sections_json'], true) ?? [];
|
||||
|
||||
$schema_json = json_encode([
|
||||
$schema_data = [
|
||||
'@context' => 'https://schema.org',
|
||||
'@type' => 'HomeAndConstructionBusiness',
|
||||
'name' => 'Floor It Hardwood Floors',
|
||||
@@ -32,9 +32,35 @@ $schema_json = json_encode([
|
||||
'addressRegion' => 'NY',
|
||||
'addressCountry' => 'US',
|
||||
],
|
||||
'areaServed' => ['Buffalo','Amherst','Williamsville','Clarence','East Amherst','Lancaster'],
|
||||
'areaServed' => ['Buffalo','Amherst','Williamsville','Clarence','East Amherst','Lancaster','Cheektowaga','Tonawanda','West Seneca','Orchard Park','Hamburg','Grand Island','Kenmore','Depew'],
|
||||
'description' => $page['meta_description'],
|
||||
], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||
];
|
||||
if ($slug === 'reviews') {
|
||||
$rdb = new SQLite3(__DIR__ . '/../data/testimonials.sqlite', SQLITE3_OPEN_READONLY);
|
||||
$rdb->busyTimeout(3000);
|
||||
$r_result = $rdb->query("SELECT quote, author, rating FROM testimonials ORDER BY id");
|
||||
$reviews = [];
|
||||
while ($row = $r_result->fetchArray(SQLITE3_ASSOC)) {
|
||||
$reviews[] = [
|
||||
'@type' => 'Review',
|
||||
'reviewBody' => $row['quote'],
|
||||
'author' => ['@type' => 'Person', 'name' => $row['author']],
|
||||
'reviewRating' => ['@type' => 'Rating', 'ratingValue' => (string)$row['rating'], 'bestRating' => '5'],
|
||||
];
|
||||
}
|
||||
$rdb->close();
|
||||
$schema_data['aggregateRating'] = [
|
||||
'@type' => 'AggregateRating',
|
||||
'ratingValue' => '4.9',
|
||||
'bestRating' => '5',
|
||||
'worstRating' => '1',
|
||||
'reviewCount' => (string)max(count($reviews), 1),
|
||||
];
|
||||
if ($reviews) {
|
||||
$schema_data['review'] = $reviews;
|
||||
}
|
||||
}
|
||||
$schema_json = json_encode($schema_data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||
|
||||
require __DIR__ . '/../components/_header.php';
|
||||
|
||||
@@ -355,6 +381,11 @@ foreach ($sections as $s) {
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<!-- Elfsight Google Reviews | floor-it -->
|
||||
<div class="elfsight-reviews-wrap" style="margin-top:2rem">
|
||||
<script src="https://elfsightcdn.com/platform.js" async></script>
|
||||
<div class="elfsight-app-b303800c-1d16-47ee-a5fc-8eaacfe67ca0" data-elfsight-app-lazy></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<?php
|
||||
|
||||
Reference in New Issue
Block a user