One rates table, dozens of ways people want to see it, a live headline number, a full table, a trend graph, a value calculator, per metal, per city. Building a page for each combination by hand was never an option. The engine that made it manageable is WordPress shortcodes with a city attribute, and by the end the plugin exposed thirteen of them from one data core. This post is that engine.
A shortcode is a bracket tag an editor types into any page, and the plugin renders it. The whole pattern is registration plus an attribute-parsing renderer:
add_shortcode('silver_rate_live', 'srt_sc_silver_live');
add_shortcode('silver_rate_table', 'srt_sc_silver_table');
add_shortcode('silver_rate_graph', 'srt_sc_silver_graph');
// ...gold variants, calculators, comparison, zakat_calculator, rate_alerts
function srt_sc_silver_live($atts) {
$a = shortcode_atts(array('city' => 'Pakistan'), $atts);
$rate = srt_get_latest_rate($a['city']);
if (!$rate) { return '<p>Rates are being updated.</p>'; }
return sprintf(
'<div class="srt-live"><span class="srt-price">PKR %s</span>
<span class="srt-unit">per tola, %s</span></div>',
number_format((float) $rate->silver),
esc_html($a['city'])
);
}
shortcode_atts is the line doing the quiet work, it merges whatever the editor typed with defaults, so [silver_rate_live] renders national rates while [silver_rate_live city=”Multan”] renders Multan’s, same code, no per-city functions. Every one of the thirteen shortcodes follows the identical shape, parse attributes with defaults, fetch through the shared data functions, render one focused block, and the graph and comparison shortcodes accept a second attribute, days, for the history window.
The composability is the payoff. A city page is now a composition, not a build, [silver_rate_live city=”Karachi”], then the table, then [silver_rate_graph city=”Karachi” days=”60″], then the calculator, four lines of brackets producing a complete page, and fifteen cities times two metals stopped being thirty development tasks. Two disciplines kept the engine trustworthy. Shortcodes never query the database directly, they all go through the srt_ data functions, so caching or schema changes touch one layer. And every shortcode has an empty-state, the rates are being updated paragraph, because a shortcode that renders nothing, or worse, a PHP notice, when data is missing turns every page it lives on into a broken page.
A few things people ask me about this
Why does my shortcode print at the top of the page instead of in place? You echoed instead of returning. Shortcode handlers must return their HTML as a string, echo outputs immediately, before WordPress assembles the content.
How do I give a shortcode optional attributes? shortcode_atts(array(‘city’ => ‘Pakistan’, ‘days’ => 30), $atts) merges user input over defaults. Anything not supplied falls back cleanly, and unknown attributes are ignored.
Next
The engine rendered beautifully, and then I discovered what it was rendering, prices that were not real. The fake data bug, the worst kind for a rates site, is the next post.
