A corporate site for an industrial SME, built where the real job is narrow and worth doing properly: load fast, say clearly what the company does, and turn a visit into a qualified contact — without dragging in a CMS for content that changes a few times a year.
The problem & constraints
The temptation on a site like this is to reach for a content management system by reflex. But an SME with four stable pages pays for that choice twice: once in setup and hosting, and again every time the CMS needs updating for content nobody is editing. The constraint was a site that a developer can change in minutes and that has no moving parts to maintain between changes.
Approach
- One controller mapping a single action per page (home, about, services, contact) plus a dedicated POST action for the form — the routing table reads as the sitemap.
- A shared Blade layout carrying the header, footer, and asset pipeline, so a page template contains only what makes that page different.
- Server-side validation on every submitted field, with
phoneandcompanyexplicitly optional and a 2 000-character ceiling on the message body — the rules live where they can't be bypassed by disabling JavaScript. - Vite for asset bundling, keeping the CSS and JS build identical to any other modern Laravel project rather than hand-managed script tags.
Trade-offs made
Decision
Plain Blade templates over a CMS: no admin surface to secure, no plugin updates, and no database dependency for content that changes a few times a year — accepting in exchange that a developer is needed for edits, which for this client is a smaller cost than the alternative.
Decision
Validation rules in the controller rather than a FormRequest class: at one form, extracting them adds a file without adding clarity. At three forms I'd move them out — the threshold is repetition, not principle.
Results
Four pages sharing one layout, with a contact form that rejects malformed input server-side and confirms clearly on success. The delivery wiring is the piece still open: validation and the confirmation path are done, and the mail transport is stubbed pending the client's SMTP details.
What I'd do differently
Finish the mail transport first — the form currently validates and confirms without dispatching a message, which is exactly the kind of gap that looks fine in testing and loses a real enquiry in production. I'd also rate-limit the POST route: a public form with no throttle is an open invitation to spam, and Laravel gives it for free with one middleware.