Skip to content

This is part of Build log, a running series on the WordPress plugins I maintain for venue and community sites.

Both plugins had a field that started life as “just type it in”. Both of them were fine on the first site and a problem on the second. Here they are together, because the second one is funnier when you know I’d already solved it once.

First: what a room costs

A room had a free-text price. Reasonable enough — until you see what people actually type into it: £40. £40 per hour. from £40/hr. £40 (£25 for charities). POA. Contact us.

None of that can be sorted, filtered, or reliably displayed anywhere with a fixed amount of room. A room listing can’t offer “cheapest first” because there’s no number in there to sort by — there’s a sentence that happens to contain a number, sometimes, in one of several formats.

So it became three fields: a numeric amount, a unit — per hour, session, day or person — and a “contact us for pricing” checkbox for the rooms that genuinely don’t publish a rate. Three constrained inputs that can say everything the sentence could, and can also be sorted, filtered and rendered into a narrow column.

Amenities went the same way in the same release, for the same reason. Typed per room, one facility ends up spelled four ways — “Wifi”, “WiFi”, “Wi-Fi”, “wifi (free)” — and nothing can filter on it because nothing can tell those apart. They became a proper taxonomy: manage the shared list once, tick what applies per room. I didn’t build a screen for it either — WordPress’s own term manager already does that job, and using it means the feature behaves the way an admin already expects it to.

Then: what a ticket costs

A fortnight later, event cards on a client’s homepage started buckling. Price wrapping onto a second line and shoving the “Book tickets” link off the edge of the card, but only for some events.

It’s very easy to read that as a CSS bug. It was the room pricing problem wearing a CSS bug’s clothes!

Price had always been a single text field, and my own documentation described it, honestly enough, as “the price text exactly as entered — not necessarily numeric”. That sentence is the entire bug. A string can’t be sorted, compared, filtered or shortened, so every consumer could do exactly one thing with it: print the whole thing and hope it fitted. A card has a fixed amount of room. Hope is not a layout strategy.

Events Manager ticket settings and options screenshot
Events Manager ticket settings and options screenshot

The dropdown that looked right

The obvious fix is a list of ticket types, and the first draft of that list looked perfectly sensible: Adult, Child, Infant, 60+/OAP, Concessions, Carer, Under 3, Under 5, Under 12, Under 16, Under 18.

Six of those aren’t ticket types. “Under 5” is a Child with an age boundary attached, and putting both in one list creates a question nobody can answer: if an event has a Child price and an Under 5 price, what age is a Child?

The plugin also already modelled ages properly somewhere else — the restrictions box had used an over/under pair for months — so a second, incompatible way of describing ages would have left it disagreeing with itself in two different admin boxes.

So a price row became a ticket type plus an optional age qualifier: a from that’s inclusive and an under that’s exclusive. Every “Under N” collapses into two number fields, and those fields also express the thing the list never could — a range. Which mattered immediately, because a real event on the site needed exactly that:

TicketPrice
Adult£10
Child (ages 5–15)£5
Child (under 5)Free

Two Child rows with different bands — something the type-per-boundary version would have needed three separate dropdown entries to say, and still couldn’t have sorted sensibly.

Where a dropdown has to give up

Then the real source data pushed back. The full pricing for that event reads: up to two children aged 5–15 admitted free if accompanied by an adult; unaccompanied or additional children £5.

“Accompanied”. “Up to two”. “Additional”. Those are conditions, not ticket types, and no dropdown will ever hold them. So there’s one free-text conditions note per event, shown under the price table on the event’s own page and never on a listing card.

That felt like a retreat until I thought about the alternative. Constrain the names and give the conditions nowhere to live, and staff will put the conditions in the names — which is precisely the mess the whole exercise existed to fix.

The bug found on the way

Cards now show a summary the plugin derives rather than one somebody typed: empty, “Free”, “£20”, or “From £5”, plus at most one short line like “Under 5s free”. It’s bounded by construction — there is no input that makes it long.

While rewiring that, I found the theme had been falling back to $price ?: 'Free'. An event with no price entered was advertising itself as free. On the homepage. For a venue that sells tickets, that’s the wrong direction to fail in, and it had been sitting there for weeks because nothing about it looked broken.

And the card layout still needed fixing on its own merits. Structured data makes the string shorter; it doesn’t guarantee it. A flex row with no minimum-width handling was always one long value away from breaking again, so that got a proper fix rather than relying on the data staying short.

Neither field was wrong when it was written. Each was one venue with one simple case, and a string was the right size for the problem in front of me. They became wrong at exactly the moment a second venue did it differently — which is the same thing that’s true of almost everything else in this series.

If a field will be shown somewhere narrow, sorted, or filtered, it can’t be a sentence. I learned that twice in a fortnight, in two codebases, from the same mistake.