All work
Design · Development / 2026

Rent a Car Mars

A car rental company in Prishtinë running its fleet on phone calls and a paper diary — given a public site and a staff back office that share one database, so a date booked online is unavailable everywhere the same second.

Visit rentacarmars.com ↗ Start a project
rentacarmars.com
The Rent a Car Mars home page — the headline 'Makina me qira, pa stres.' beside a photograph of a white VW Golf 8, with a 4.9 rating badge, a 'full insurance included' card, and a search bar for pickup location, drop-off location and dates.
rentacarmars.com — the fleet is rendered from the database, so the site can never advertise a car the office has retired.
Client
Rent a Car Mars · Prishtinë
Role
Design & full-stack build
Scope
Site · booking · back office
Stack
PHP · MySQL · vanilla JS
01

Overview

Mars rents cars in Prishtinë, mostly to locals and to diaspora flying in for a few weeks. The fleet was booked the way small rental firms everywhere book them: a phone call, a name in a diary, and someone's memory of whether the Tiguan is back on Friday. It works until two people ask for the same week.

A brochure website would not have helped — it would have been a fourth place the truth lives. So we built the site and the back office as one system on one database: the customer sees the same calendar the staff see, a submitted reservation takes its dates immediately, and nothing about availability is ever typed in twice.

Structure
THE CUSTOMER Fleet · calendar · reservation THE OFFICE Orders · fleet · block dates One availability query confirmed · live holds · dates blocked by staff One MySQL database — cars · bookings · staff · messages, on the client's existing shared hosting
Two faces, one truth. The public calendar, the check that runs on submit, and every admin screen all call the same query. They cannot drift, because there is nothing to keep in sync.

Every car on the site comes out of the cars table. Adding one in the back office publishes it immediately; retiring one removes it from the site while keeping its booking history intact. Even the filter pills are generated from the types the fleet actually contains — so a filter can never return an empty page.

rentacarmars.com/makinat
The Rent a Car Mars fleet page — the headline 'Gjej makinën e përsosur për çdo rast', a filter bar with Të gjitha, Hatchback, SUV and Automatik pills, and a grid of car cards for a VW Golf 8, VW T-Roc and VW Golf 8, each with its own photograph and specification chips.
Makinat — eight cars, each with its own photography, type, fuel, engine and transmission chips. The counts and the filter pills are both read from the fleet.

The pages are server-rendered, which is the point: a rental firm competing for “makina me qira Prishtinë” needs Google to see the fleet, not an empty shell waiting on JavaScript. All copy is in Albanian, because that is who is renting.

Rendered on the server

Every car is in the HTML before a line of JavaScript runs, so search engines index the fleet and the page is useful on a slow connection.

Filters built from the data

The type pills are derived from the fleet, not hardcoded in the markup. Add the first convertible and its filter appears; retire the last SUV and that filter goes.

Photography that fits the pipe

Each car ships at two sizes and the card advertises the smaller one to phones — but only once the file is verified on disk, because a srcset naming a missing image renders as broken.

This is the part that changes the business. Submitting the form does not send an email and hope — it takes the dates immediately, as a hold that lasts 30 minutes. For that half hour nobody else can book them. Staff confirm or cancel from the back office; if nobody acts, the hold expires and the dates free themselves.

01 · DATAT Pick on the calendar taken dates struck through 02 · MBAJTJA Held on submit 30 min · locked row 03 · VENDIMI Staff confirm or cancel · back office 04 · SKADIMI Or it expires no cron needed
Nothing waits on a human to stay correct. An ignored reservation does not sit in an inbox blocking a car — it releases the dates by itself, because every availability query filters expired holds out.
rentacarmars.com/rezervo?car=vw-golf-8-2023
The Rent a Car Mars reservation page for the VW Golf 8 2023 — a car card with specification chips and included extras beside a two-month availability calendar for July and August 2026, in which 21 to 25 July are shaded and struck through as already taken, with a legend for free, taken and your selection.
The customer calendar. 21–25 Korrik are struck through because someone else is holding them right now. Two months are shown at once, because a rental is a range, not a day.
THE PART THAT LOOKS OPTIONAL AND ISN'T

Two people can submit the same dates in the same instant. Both requests check availability, both see the car is free, and both insert — one car, two customers, one very bad Friday.

So the hold locks the car's row before it checks, forcing the second request to wait for the first to finish. This is not theoretical: with the lock removed, a concurrency test double-booked 2 rounds out of 8. With it, zero.

One definition of "free"

Confirmed bookings, live holds and dates blocked by staff all fall out of a single query. The customer calendar and the office calendar are literally the same code.

Public · submit check · admin

Holds that clean up after themselves

Expiry is a filter on every read, not a scheduled job. There is no cron to forget, and nothing rots if the host kills background tasks.

30 minutes · no cron

A row lock, not a race

The car's row is locked before availability is checked, so two simultaneous submits are serialised. The second one is told the dates went, rather than quietly taking them too.

SELECT … FOR UPDATE

Holding costs the visitor nothing

Which is exactly why one script could block the whole fleet. Each address is capped at three live holds and six submissions an hour — over the cap the enquiry still reaches the office, it just reserves nothing.

3 holds · 6 submits / hour

Kosovo time, not server time

The host runs UTC. The database session is pinned to Prishtinë time on every connection — without it a 30-minute hold expires two hours before it was created.

Europe/Belgrade

The enquiry survives the email

Shared hosting drops mail silently. Every reservation and contact message is written to the database first, so a failed send loses the notification, never the customer.

Stored, then sent

Staff log in at /admin and land on Porositë — orders, holds first, because those are the ones on a clock. The list refreshes every five seconds and counts each hold down live, so a reservation that arrived while the page was open appears without anyone reloading anything.

rentacarmars.com/admin
The Rent a Car Mars back office orders screen — a LIVE indicator, a count of 2 pending out of 4 total, then two pending reservations for an Audi Q2 and a VW Golf 8 each showing a countdown of the time left on the hold, the dates, the customer's name, phone and email, their message, and Konfirmo and Anulo buttons, above two already-confirmed reservations.
Porositë. Two holds counting down, each with the customer's phone number one tap away — because confirming a rental in Prishtinë still ends with a phone call.

The calendar in the back office is the customer's calendar — the same widget, the same query, the same shading. That is deliberate: “is the T-Roc free on the 24th?” has one answer in this business, and two screens that could render it differently would eventually do so.

01

Sessions that expire, logins that throttle

Staff sessions carry CSRF tokens on every state change, and repeated failed logins are rate limited per account and address.

02

Confirming can fail, and says so

A hold can expire while staff are reading it. Confirm re-checks the dates and refuses with a plain explanation rather than writing a booking over someone else's.

03

Nothing is deleted

Cars are hidden, not removed, and cancelled bookings keep their record and who decided it — so the history survives a change of mind.

04

Credentials live outside the site

The database config sits one level above the web root. If PHP ever breaks on a shared host, the file cannot be served as plain text.

02

Engineering

The whole thing is PHP, MySQL and vanilla JavaScript with no build step — and that is a design decision, not a shortcut. The client is on HostGator shared hosting: Apache, PHP, MySQL, no Node, no container, no deploy pipeline they would have to pay someone to maintain.

A framework here would have bought abstractions and cost them the ability to hand the site to anyone else. Instead: files upload, schema imports, site runs. The header and footer live in one file, every colour and radius is a custom property in one :root block, and the entire front end is one stylesheet and one script.

ONE SUBMIT, IN A TRANSACTION Lock the car row the second submit waits here Re-check availability the same query the calendar drew Write the hold status = hold · expires in 30 min The office email + the live orders screen Everyone else those dates now render struck through
The submit is the booking. By the time the confirmation email leaves, the dates are already gone from every other visitor's calendar — the office is being notified, not asked for permission.

Most of this traffic is a phone — someone comparing cars on the way to the airport. The booking calendar reflows to a single month, the fleet stacks, and the filter pills wrap instead of scrolling off the edge. The back office is responsive too, which matters more than it sounds: it means a hold can be confirmed from behind the counter, or from the car park, without finding a desk.

The Rent a Car Mars home page on a phone — the headline stacks above the hero car photograph and the search card.
Ballina
The fleet page on a phone — the filter pills wrap onto two rows above a stacked search card.
Makinat
The reservation page on a phone — the car card sits above a single-month availability calendar.
Rezervo
The back office orders screen on a phone — pending reservations with their countdowns and full-width Konfirmo and Anulo buttons.
Admin
Tech stack
PHP 8 MySQL · InnoDB PDO Vanilla JS CSS custom properties Archivo Space Mono Apache · .htaccess Shared hosting No build step
1 query
Defines availability for every screen
30 min
Holds that release themselves, no cron
5 sec
Back office refresh, counting down live
0 deps
Nothing to install, on the host they had
Outcome

Mars went from a fleet tracked in someone's head to a fleet tracked in one place, visible to the customer and the office at the same time. The site sells the cars; the platform behind it makes sure it can only sell the ones that are actually free.

1 query
Availability has a single definition — the customer calendar, the submit check and every admin screen all ask it.
0
Double bookings under a concurrency test that produced 2 in 8 rounds before the row lock went in.
No cron
Holds expire as a condition of every read, so nothing rots if a scheduled job never runs.
More work