$ series How We Built It · part 4 of 5
“Is there a plumber in Orting?” There are several. Until this month, the fastest way to find one was asking on Facebook.
Orting is a small town at the foot of Mount Rainier, and Rainier IT is an Orting business. Our town has about a hundred businesses: tow trucks and taco shops, accountants, a veterinary hospital, child care centers, a doughnut shop, farm stands. If you want to find them, you’re stuck with a map app that lists half of them and Facebook groups where the answer scrolls away by lunchtime.
So we built Shop Orting at shoporting.org: a free directory of every business in Orting, with hours, a map, “open now”, an events calendar and a visitor guide. Listings are free forever. There are no ads, no paid placement and nothing to buy.
This post is the tour: what it does, how it’s built, and a few pieces of code we’re proud of.

🧭 What’s on the site
The whole thing is designed around one question: what are you looking for in Orting, right now?
- The directory. 99 businesses in 11 categories, each with hours, contact info, photos and a map pin. An “Open now” filter does the time math in Pacific time.
- A map that stays readable. Nearby pins group into clusters that split apart as you zoom, so downtown doesn’t turn into a red smear.
- Search that understands people. Type “vet”, “mechanic” or “donuts” and you get the animal hospital, the auto shops and the doughnut shop.
- Events. 26 community events and counting, each with Add to Google Calendar and Apple / Outlook buttons, plus a calendar you can subscribe to once and forget.
- Topic pages. Pages like “Child Care & Preschools in Orting” or “Auto Repair in Orting” answer the exact searches locals type into Google.
- A visitor guide. Annual events, history and getting here, for the thousands of people who come for the Daffodil Parade.
- Owner accounts. Businesses claim their listing for free, then update hours, photos and descriptions themselves. Owners can also post their own events.

📱 Built for the phone in your pocket
Most people find a local business on their phone, often while they’re already in the car. Every page is designed for a phone first, then spread out for a desktop screen. There’s no app to install.

On a phone, call, directions and website links are one tap away. The map is a lightweight Leaflet and OpenStreetMap map, with no Google Maps fees. Photos load at the right size for the screen, and every image reserves its space so the page doesn’t jump around while you’re trying to tap something.
🔎 Search that knows “vet” means “animal hospital”
WordPress search is literal. If a listing says “animal hospital” and you type “vet”, you get nothing. Real people don’t search the way businesses describe themselves, so we wrote a small layer on top.
It does two things for each word you type:
- Synonyms. A plain list maps everyday words to how businesses actually describe themselves.
- Simple stemming. “Plumbers” also matches “plumbing” and “plumber”, and “donuts” also matches “doughnut”.
function so_search_synonyms() {
return [
'oil change' => [ 'auto repair', 'automotive', 'auto services', 'drivetrain' ],
'mechanic' => [ 'auto repair', 'automotive', 'auto services', 'drivetrain' ],
'vet' => [ 'animal hospital' ],
'doctor' => [ 'medical', 'family practice', 'urgent care' ],
'realtor' => [ 'real estate', 'realty' ],
'donut' => [ 'doughnut' ],
'cpa' => [ 'accounting', 'tax' ],
'haircut' => [ 'hair', 'barber' ],
// ...and more, added whenever a search comes up empty
];
}
function so_search_variants( $w ) {
$v = [ $w ];
$syn = so_search_synonyms();
foreach ( [ $w, rtrim( $w, 's' ) ] as $k ) // "donuts" → "donut" for the synonym lookup
if ( isset( $syn[ $k ] ) ) $v = array_merge( $v, $syn[ $k ] );
foreach ( [ 'ies' => 'y', 'ers' => '', 'ing' => '', 'ant' => '', 'ian' => '', 'er' => '', 'es' => '', 's' => '' ] as $suf => $rep ) {
$n = strlen( $suf );
if ( substr( $w, -$n ) === $suf && strlen( $w ) - $n >= 4 ) { $v[] = substr( $w, 0, -$n ) . $rep; break; }
}
return array_unique( $v );
}A posts_search filter then requires every word you typed to match, with each word allowed to match any of its variants. Multi-word searches stay precise, and single words get forgiving. No search plugin and no external index to keep in sync.

📅 Events you can put in your own calendar
A town calendar is only useful if people actually see it. So every event page has Add to Google Calendar and Apple / Outlook (.ics) buttons, and the whole calendar is a live feed you can subscribe to. Subscribe once, and new Orting events show up in your phone’s calendar on their own.


Calendar files are an old, picky format. Lines longer than 75 bytes must be “folded” onto continuation lines, and a multi-byte character like an emoji must never be split in half. Here’s the folding function that keeps Outlook and Apple Calendar happy:
/** Fold to 75-octet lines as RFC 5545 requires. */
function so_ics_fold( $line ) {
$out = ''; $cur = '';
foreach ( preg_split( '//u', $line, -1, PREG_SPLIT_NO_EMPTY ) as $ch ) {
if ( strlen( $cur . $ch ) > 74 ) { $out .= $cur . "\r\n "; $cur = ''; }
$cur .= $ch;
}
return $out . $cur . "\r\n";
}There’s also a Subscribe page with RSS feeds for new events, new businesses and new listings in each category. People who just want to know what’s new can follow along in a feed reader, or have a free service email them, without us ever collecting an email address.

🤖 Keeping events fresh without a full-time editor
The hard part of a community calendar is keeping it current. Events are scattered across the library’s calendar, event sites and flyers. So once a week, a small Python job collects new Orting events from those sources, removes duplicates, fills in the full description and saves each one as a draft.
[Unit]
Description=Run the Shop Orting event harvest every Monday morning
[Timer]
OnCalendar=Mon *-*-* 07:00:00 America/Los_Angeles
RandomizedDelaySec=10min
Persistent=true
[Install]
WantedBy=timers.targetNothing goes live automatically. The job posts a summary to our team chat with a link to each draft, and a person reviews and approves each one. Automation does the tedious collecting, and a human makes the call. That’s how we like to run everything at Rainier IT.
🧸 Topic pages, and privacy for home businesses
Some of the most useful pages are the narrowest. “Child Care & Preschools in Orting” lists the licensed centers and preschools we verified against the state’s child care licensing records.

Some of those providers are family child care homes, which means the “business address” is someone’s house. Those listings, and any other home-based business, say “Serves Orting and nearby” instead of a street address. They get no map pin, and a phone number appears only if the owner publishes it. Being helpful should never mean putting a family’s home address on the internet.
🏪 Listings that owners control
Every listing starts from public information. Then the owner can claim it for free in about two minutes: correct the hours, add photos and a logo, and get a Verified by owner badge. After that, it’s theirs to keep current.

Owners sign in to a stripped-down account that goes straight to their own listing. It can only edit their listing and their events, and can’t touch anyone else’s page.
🛠️ Under the hood
| Layer | What we used |
|---|---|
| CMS | WordPress, with a hand-coded theme and no page builder |
| Data | Custom post types for businesses and events, plus a category taxonomy |
| Map | Leaflet + OpenStreetMap, with marker clustering |
| SEO | Structured data on every page (LocalBusiness, Event, ChildCare and more), XML sitemaps, canonical URLs, IndexNow pings |
| Hosting | Self-hosted on our own infrastructure, behind Cloudflare |
| Automation | A weekly systemd timer for the event harvest, with team-chat notifications for approvals |
A hand-coded theme sounds like more work than a page builder, and on day one it is. But it means pages that load fast on a phone over rural LTE, structured data written for each type of business, and no plugin stack to babysit. For a site meant to serve a town for years, that trade is easy.
🌄 Why we did it
Rainier IT is an Orting business. When a neighbor’s shop gets found, the whole town does a little better, and a directory that is free, honest and current is one small way to help with that. No ads, no paid placement, no “featured” listings for sale.
If you run an Orting business: find yours on shoporting.org and claim it for free. If you’re not listed yet, add your business.
If you want something like this for your town, your organization or your own business: this is the kind of work we do. See our web design services, or get in touch and tell us what you’re trying to build.
Protected by reCAPTCHA. Google's Privacy Policy and Terms apply.