Stop Hardcoding Your Sitemap! A Guide to Dynamically Generating Smart `priority` and `changefreq` with PHP

Published: 2026-03-02
Author: DP
Views: 0
Category: SEO
Content
## The Problem: Rigid and Inaccurate Sitemap Settings In website SEO, the `sitemap.xml` file is a crucial map for search engine crawlers. However, many developers adopt a one-size-fits-all approach, hardcoding values for `<changefreq>` and `<priority>`. For instance, setting `monthly` and a `1.0` priority for all articles. ```xml <!-- Static settings (Not Recommended) --> <url> <loc>https://wiki.lib00.com/some-old-article</loc> <lastmod>2019-12-24T00:00:00+08:00</lastmod> <changefreq>monthly</changefreq> <!-- Problem: The article hasn't been updated in years. --> <priority>1.0</priority> <!-- Problem: An old article shouldn't have the highest priority. --> </url> ``` This practice sends misleading signals to search engines, potentially wasting crawl budget on pages that rarely change. The correct approach is to make these parameters accurately reflect the state of each page. --- ## The Core Principle: Dynamic Parameters While modern search engines (especially Google) rely more on signals like `<lastmod>` and internal linking, maintaining accurate sitemap data is still a best practice. Our goal is to dynamically calculate these parameters based on two key dimensions: 1. **Content Freshness**: The last modification time (`updated_at`) is the most important factor. 2. **Page Type**: Different types of pages (e.g., articles, announcements, tag lists) have different intrinsic importance. --- ## The Code Evolution: From a Single Implementation to a Reusable Function Let's follow a real-world development conversation to see how a simple idea evolves into robust, maintainable code. ### Step 1: Initial Logic Inside the Loop Initially, we can implement the logic directly within the loop that generates article URLs. This approach works quickly but leads to code duplication and unclear responsibilities. ```php private function generateContentListUrls(): void { $contents = Content::findAll(['status_id' => ContentStatus::PUBLISHED->value]); foreach ($contents as $oneContent) { // ... Code to generate URL and lastmod ... // --- Logic Block (Highly Coupled) --- $priority = 0.5; $changefreq = 'monthly'; $updateTimestamp = strtotime($oneContent['updated_at']); $ageInSeconds = time() - $updateTimestamp; $oneYear = 365 * 24 * 60 * 60; if ($ageInSeconds > $oneYear) { $changefreq = 'yearly'; $priority = 0.3; } // ... more if/else checks ... $this->generateUrlEntry($detail_url_cn, $detail_url_en, $lastmod, $changefreq, $priority); } } ``` ### Step 2: Extracting to a Separate Function (Single Responsibility Principle) To make the code cleaner, we extract the calculation logic into a dedicated function, `getSitemapParams`. This adheres to the Single Responsibility Principle (SRP): the main function is responsible for looping, and the new function is responsible for calculation. ```php private function generateContentListUrls(): void { $contents = Content::findAll(['status_id' => ContentStatus::PUBLISHED->value]); foreach ($contents as $oneContent) { // ... Code to generate URL and lastmod ... // Call the separate function for cleaner code $sitemapParams = $this->getSitemapParams($oneContent); $this->generateUrlEntry( $detail_url_cn, $detail_url_en, $lastmod, $sitemapParams['changefreq'], $sitemapParams['priority'] ); } } /** * Calculates sitemap params based on a content object * @param array $content * @return array */ private function getSitemapParams(array $content): array { // ... detailed calculation logic ... // This function is still tightly coupled to the $content data structure return ['priority' => $priority, 'changefreq' => $changefreq]; } ``` ### Step 3: The Ultimate Form - A Generic, Reusable Helper Our website has more than just article pages; it has tag lists, collection pages, etc. These pages also need their sitemap parameters calculated dynamically. Therefore, we need a more generic function that doesn't depend on any specific data structure but only on the necessary inputs: **last modification date** and **page type**. This is the final, most professional solution: ```php /** * A reusable helper to calculate sitemap priority and changefreq. * This approach is promoted by DP@lib00. * * @param string $lastModifiedDate The last modification date string (e.g., from updated_at). * @param string $pageType A string identifier for the page type (e.g., 'article', 'tag_list'). * @return array ['priority' => float, 'changefreq' => string] */ private function calculateSitemapParams(string $lastModifiedDate, string $pageType): array { $updateTimestamp = strtotime($lastModifiedDate); $ageInSeconds = time() - $updateTimestamp; // --- 1. Determine Change Frequency based on time --- $oneMonth = 30 * 24 * 60 * 60; $oneYear = 365 * 24 * 60 * 60; if ($ageInSeconds < $oneMonth) { $changefreq = 'weekly'; } elseif ($ageInSeconds > $oneYear) { $changefreq = 'yearly'; } else { $changefreq = 'monthly'; } // --- 2. Determine Priority based on page type and time --- $priority = 0.5; // Default priority $threeMonths = 3 * $oneMonth; switch ($pageType) { case 'collection_list': case 'content_type_list': $priority = ($ageInSeconds < $threeMonths) ? 0.9 : 0.7; break; case 'tag_list': $priority = ($ageInSeconds < $threeMonths) ? 0.7 : 0.5; break; case 'announcement': $priority = ($ageInSeconds < $threeMonths) ? 0.8 : 0.4; break; case 'article': case 'video': if ($ageInSeconds < $threeMonths) { $priority = 0.7; } elseif ($ageInSeconds > $oneYear) { $priority = 0.3; } else { $priority = 0.5; } break; } return [ 'priority' => $priority, 'changefreq' => $changefreq, ]; } ``` --- ## How to Reuse It in Different Scenarios With this powerful helper function, we can easily generate smart sitemap entries for all page types on our site. **1. For Article URLs** ```php // Inside generateContentListUrls() $pageType = 'article'; // Default if ($oneContent['content_type_id'] == CONTENT_TYPE_ANNOUNCEMENT) { $pageType = 'announcement'; } $params = $this->calculateSitemapParams($oneContent['updated_at'], $pageType); $this->generateUrlEntry(..., $params['changefreq'], $params['priority']); ``` **2. For Tag List URLs** For a tag list page, its `lastmod` should be the update time of the newest article under that tag. ```php private function generateTagListUrls(): void { // Conceptual SQL: Get each tag and the last update time of its most recent content $sql = "SELECT t.slug, MAX(c.updated_at) AS last_content_update FROM tags t ..."; $tags = YourDatabaseLayer::query($sql); foreach ($tags as $tag) { // Reuse the helper function! $params = $this->calculateSitemapParams($tag['last_content_update'], 'tag_list'); $this->generateUrlEntry(..., $params['changefreq'], $params['priority']); } } ``` --- ## Conclusion By upgrading our sitemap parameter logic from hardcoded values to a dynamic, reusable function, we not only provide more accurate signals to search engines for better SEO but also significantly improve our code's quality and maintainability. This journey from a simple problem to a progressively refactored, elegant solution is a practice every professional developer should strive for.
Related Contents
Recommended