Real-estate CMS
A CMS for a property consultancy: listings, pages and a blog, written in a block editor and published with proper SEO. The demo runs as the fictional “Best Real Estate Consultant In Your City™”.
The problem
Small real-estate businesses publish a lot of near-identical content: listings, area guides, blog posts. Off-the-shelf site builders make that slow to write and poor for search, and none of them let you feed content in from your own scripts.
The brief was a CMS the business could run day to day, that ranks well, and that accepts drafts produced elsewhere, including drafts written with AI.
What I built
- 22 domain models behind the listings, pages and blog.
- An Editor.js authoring flow, so content is stored as structured blocks and rendered consistently on every public page.
- SEO meta and JSON-LD schema across the public pages.
- An import API: blog posts, pages and listings drafted by AI are pushed in through scripts and land as drafts for a human to review.
- Zero-downtime deploys in production using release directories and an atomic symlink switch.
Key decisions
- Inertia instead of a separate SPA and API
- The admin is a Vue app, but routing, validation and auth stay in Laravel. One codebase, one deploy, no duplicated form rules, and the import API is the only public JSON surface.
- Structured blocks, not HTML
- Editor.js stores content as JSON blocks. That keeps rendering predictable, lets the import API validate content before it is saved, and means AI-drafted posts cannot inject arbitrary markup.
- Octane on FrankenPHP
- Keeping the application booted between requests makes public pages fast without a page cache that has to be invalidated every time an editor saves.
- Drafts first for anything imported
- Nothing pushed through the API goes live on its own. Scripts can create and update drafts; a person publishes them.
Stack
Laravel 13, Inertia, Vue, Octane/FrankenPHP, Postgres
Results
- 231 automated tests.
- 22 domain models, running in production.
- Deploys switch releases atomically, with no downtime.
Screenshots
What I would do differently
I would write the import API contract first, as a schema, and generate the validation from it, instead of letting it grow alongside the scripts that call it.
I would add visual regression tests for the public templates earlier. Unit and feature tests catch logic errors, not a broken layout.