Skip to content

Listings ​

Real estate and directory listings: a search with filters and a map, agents, enquiries, favorites — on Houzez's and Listeo's data.

kitloom/listings · admins: kitloom/listings-filament, kitloom/listings-nova

Video lessons ​

Recorded in the Filament admin; the Nova admin works the same way.

Listings for two kinds of sites with one module: real estate — properties for sale or rent, with their agents and agencies — and directories — cafés, shops, services, places, with their hours and reviews. Visitors search with filters and a map, save what they like, and ask about a listing with a form that reaches the right person by email.

The data is where the two best-known WordPress themes keep it: properties are Houzez's (property posts with fave_property_* meta, houzez_agent and houzez_agency posts, enquiries in Houzez's CRM tables), directory listings are Listeo's (listing posts with _address, _geolocation_*, opening hours and reviews). A site that ran either theme keeps its listings, addresses, agents and leads as they are.

The admin is kitloom/listings-filament or kitloom/listings-nova. A theme draws the pages: kitloom/keystone-theme (real estate) and kitloom/waypoint-theme (a directory).

Setting it up ​

Listings settings
  1. Settings → What the site lists. Properties (real estate), Listings (a directory), or both. A kind that is off has no pages and no admin screens; its records stay in the database.
  2. Settings → Currency, Area unit, Records per page. The price is printed in the page's language: $1,250,000, 1 250 000 $. Left empty, the currency and the unit are the ones Houzez or Listeo kept — a site that moved from either shows its prices as before — else US dollars and square feet.
  3. Settings → Copy enquiries to. An office address that gets a copy of every enquiry. Without it, an enquiry nobody is named for goes to the site's admin email.
  4. Settings → Find addresses on the map. On: a listing saved with an address and no coordinates is placed on the map by OpenStreetMap's Nominatim, one address a second, each address asked once. Off: type the coordinates, or pick the point on the map in the editor.
  5. Settings → Map style. OpenFreeMap's free map by default, which needs no key. A provider of your own (MapTiler, Stadia Maps) gives you a style address with your key in it.

Then the terms: Property types (House, Apartment), Statuses (For sale, For rent), Cities and Areas, Features (Pool, Garden), Labels (Hot offer) — or a directory's Categories, Regions and Features. A city's areas, a category's sub-categories: searching for the city finds the areas too.

Properties ​

Agents and agencies
Properties

The editor has the price (with a prefix — From — and a postfix — /mo), bedrooms, bathrooms, rooms, garages, area and land with their units, the year built and a reference; the address and the point on the map; the gallery, a video and a virtual tour; Houzez's floor plans and additional details. Contact says whom the page names to ask: the property's agents, its agency, its author, or nobody.

Agents have their photo, position, email, phones, WhatsApp and website, and the agency they work for. An agent linked to a site user (Houzez's user field) sees the enquiries sent to them on the site's account page. Agencies have their logo and contacts; an agency's page lists its agents and every property of theirs.

Directory listings ​

Directory listings

A listing has its address, its point on the map, a price range (from 15 to 40), its phone, email, website and social links, its opening hours by weekday — a day open twice, a night past midnight — and whether it is verified or featured. Its owner is its author: enquiries go to the listing's own email, else to the owner's. Visitors leave reviews with one to five stars; the average is kept on the listing as Listeo keeps it. A review follows Settings → Discussion as a comment does: with kitloom/comments installed, all of its rules (an account when asked for, held and disallowed words, links, a first comment held, the same review twice, too fast); without it, the account, the held and disallowed words and Comment must be manually approved.

A listing page is its archive — /property/, /listing/ — or a term's — /city/lisbon/, /listing-category/cafes/. The visitor narrows it in the address, and the address can be shared:

ParameterWhat it does
type, status, locationterm slugs, any of them: type=house,apartment
featuresall of them: features=pool,garden
min_price, max_pricea listing's price range meets the one asked for
beds, bathsat least
min_size, max_sizein the site's unit
featured=1featured only
near=38.71,-9.14&radius=5within 5 km, nearest first
bounds=south,west,north,eastwhat the map shows
swords in the title or the address
orderbynewest (the default), price, price-desc, featured; size for properties, rating (top rated) for a directory

Next to the results the filters show what is left to choose, with counts: three houses, two apartments — a category counts its sub-categories' listings too, as choosing it finds them. The map shows every listing found, not only the page's — up to a thousand; past that it asks to zoom in.

Enquiries ​

Enquiries

The form on a listing's page sends a name, email, phone, message and what it is about (Book a viewing, I want to rent…). Each person it goes to gets their own enquiry, under their own lead of that address — as each Houzez agent has a CRM of their own — and an email whose Reply answers the visitor. The same address asking again is the same lead: what it gives fills only what the lead lacks — a form never overwrites the name or phone the agent has.

Listings → Enquiries in the admin lists them all, with their status: new, read, answered, closed, and a private note. An agent with an account answers theirs on the site's account page; Manage every enquiry (manage_enquiries, on the roles screen) sees everyone's.

Personal data: WordPress's Export Personal Data gives an address's leads and enquiries, and what its account saved; Erase Personal Data deletes them.

Not read: Listeo's private messages between visitors and owners (listeo_core_conversations, listeo_core_messages) stay in the database untouched; the module's enquiries start afresh in Houzez's CRM tables.

For the visitor ​

  • Saved on the account page: the listings they marked with the heart, newest first — on a site that moved, what they saved with Houzez or bookmarked with Listeo. Every card knows whether its viewer saved it.

The agents' and agencies' archives (/agent/, /agency/) list them in the admin's order — the order field, then by name.

  • Enquiries on the account page of whoever is asked.

For the theme ​

WhatWhere
A listing page's results, filters, facets, price range, map stylethe page part listingSearch (Site\ListingSearchPart documents the keys); on any other page — the front page's search form — the first kind's form without results
A property's or listing's pagelistingDetails: the card, gallery, facts, features, floor plans, location, contacts (agents, agency or owner), hours and open now, reviews, the enquiry form, the favorite, similar listings
An agent's or agency's pageagentProfile: the card, the agency's agents, their listings, a form to ask them
A listing's review starsreviewStars: comment id => stars, for the comments the theme draws
The map's pointsGET /wp-json/listings/v1/markers?kind=property&… with the page's own parameters — each with its title, link, price, type, place, rating and a small picture for a pop-up
An enquiryPOST /wp-json/listings/v1/enquiries — listing, name, email, phone, message, type, agent, the protection's fields
A reviewPOST /wp-json/listings/v1/listings/{id}/reviews — review, rating, author, email
The heartPOST / DELETE /wp-json/listings/v1/favorites/{id}
An enquiry's statusPOST /wp-json/listings/v1/enquiries/{id}/status — status, note

The map is MapLibre GL with the style from map.style; load it only on the pages that draw a map. Templates: archive-property, single-property, taxonomy-property_city, single-houzez_agent, archive-listing, single-listing… as WordPress's hierarchy names them.

Where things are kept ​

WhatWhere
Properties, agents, agencies, listingsposts of the types property, houzez_agent, houzez_agency, listing, their meta under Houzez's and Listeo's keys
A key with many values (a property's gallery, its agents)a meta row per value, as WordPress keeps it
A listing's gallery and hoursListeo's shapes: _gallery a map of attachment id => address; _monday_opening_hour and _monday_closing_hour one time each, or lists side by side for a day open twice
The search indexlisting_index: price, rooms, area, coordinates, featured — made again whenever a listing is saved, once the request is over, whoever wrote its meta; rebuilt from the meta with php artisan listings:reindex (after an import that wrote the meta past the writers)
Enquirieshouzez_crm_leads and houzez_crm_enquiries, Houzez's columns; created when missing, left in place when the module is removed
Saved listingsthe user meta houzez_favorites (JSON). Until the module writes it for a user, Houzez's option houzez_favorites-{user id} and Listeo's user meta listeo_core-bookmarked-posts are read; the first change copies them over
Reviewscomments on the listing, the stars in listeo-rating, the average in listeo-avg-rating
Settingsoptions kitloom_listings_*; the currency and area unit fall back to Houzez's houzez_options (default_multi_currency, currency_symbol, measurement_unit, area_prefix_default) and Listeo's listeo_currency
Configurationconfig/kitloom-listings.php: the addresses' bases, the throttle, the map's credit line, the geocoder

What the application overrides ​

  • The addresses: kitloom-listings.slugs — a site whose Houzez said /homes/ sets 'property' => 'homes'.
  • The geocoder: bind Kitloom\Listings\Geo\Geocoder to a provider of your own (Google, Mapbox, your own Nominatim at geocoder.url).
  • The models: listing_property, listing_listing, listing_agent, listing_agency, listing_lead, listing_enquiry in the model registry.
  • What is on: kitloom-listings.routes.register, notifications.register, account.register, ai.register, privacy.register, filament.register, nova.register.

AI agents (kitloom/ai): listings_search, listings_get, listings_save (as the caller, who needs edit_properties or edit_listings — the post writer checks the rest), listings_enquiries, listings_enquiry_status, and the kinds, currency and enquiries in describe_site.

Tests ​

From the application that installs the package: php vendor/bin/phpunit --testsuite Listings. A theme's test creates listings with Kitloom\Listings\Testing\CreatesListings.

kitloom — modules and themes for Laravel.
Pricing · Services · License · Refunds · Privacy · Community · [email protected]