Rentiva v4.37.0 — Vendor Public Profile Page
Every approved vendor now has a public face. v4.37.0 ships a dedicated profile page at /vendor/<slug>/ (or /bayi/<slug>/ on Turkish sites), wired up across a canonical shortcode, a Gutenberg block, and an Elementor widget — all delegating to a single renderer. Schema.org LocalBusiness JSON-LD goes out in <head> so search engines and social previews can read the vendor as a discrete entity, not as a slice of the homepage.
Why
For most of Rentiva's life, vendors lived inside the platform's own listing screens. Customers could find a specific vehicle, but they had no way to land on a vendor — to read who runs the fleet, see their reliability score and verification badge, browse their other cars, and read past reviews. Search engines had nothing to index either: no canonical URL, no schema, no breadcrumbs.
v4.37.0 closes that gap. Each vendor gets a stable URL, a structured profile, and a renderer that's reusable wherever you want to surface vendor information — a homepage block, an Elementor template, a shortcode dropped into a custom page.
i18n-aware base URL
The URL pattern follows WooCommerce's translatable /shop/, /product/ slugs:
- English site:
/vendor/<slug>/ - Turkish site:
/bayi/<slug>/
The base segment comes from _x('vendor', 'URL slug', 'mhm-rentiva'), runs through apply_filters('mhm_rentiva_vendor_profile_url_base', …) for site-owner override, then urldecode() and sanitize_title() for safety. Any locale just translates the vendor URL slug context and the rewrite rule rebuilds itself on the next flush_rewrite_rules().
When a vendor's URL slug changes (admin edits the slug field, or the auto-generator picks a different collision suffix), the previous slug is appended to a slug-history list and a 301 redirect is registered to the new canonical URL. Old links don't 404 — they redirect.
Single canonical renderer
Three render paths, one source of truth:
[rentiva_vendor_profile]shortcodemhm-rentiva/vendor-profileGutenberg blockmhm_rentiva_vendor_profileElementor widget
The block and widget delegate to the shortcode. The shortcode delegates to a partials chain (vendor-profile-hero.php, vendor-profile-vehicles.php, vendor-profile-reviews.php, vendor-profile-location.php). Whichever surface the customer arrives at — a public profile URL routed by the rewrite rule, a block embedded on a custom page, or an Elementor widget on a builder page — the HTML is identical.
This is the same Render Parity discipline Rentiva has applied across 19 blocks and 20 widgets: blocks/widgets never duplicate render logic, they delegate.
Schema.org for SEO
The <head> of a vendor profile carries a Schema.org LocalBusiness JSON-LD payload:
{
"@context": "https://schema.org",
"@type": "LocalBusiness",
"name": "Vendor display name",
"url": "https://example.com/vendor/<slug>/",
"image": "https://example.com/.../avatar.jpg"
}
Yoast SEO and Rank Math run their own canonical filter chains; v4.37.0 hooks wpseo_canonical and rank_math/frontend/canonical at PHP_INT_MAX-10 to make sure the vendor profile URL wins over the homepage canonical that those plugins default to for non-singular pages.
Hostile vendor names can't break the schema either — the JSON encoder uses JSON_HEX_TAG, so a hostile </script> injection in a display_name becomes hex-encoded instead of escaping the script element. And if url_for_user() returns an empty string (vendor has no slug yet), the schema build short-circuits and emits nothing instead of an invalid LocalBusiness with a missing URL.
Vendor admin updates
Two new fields appear under the vendor's user profile screen:
- Vendor avatar — a Media Library uploader. The chosen attachment is what
LocalBusiness.imageand the profile hero render. Falls back to a Gravatar of the user's email when unset. - Public Profile URL Slug — a text field with a live collision check. The auto-generator strips diacritics (
Yeni Satıcı→yeni-satici) and appends a numeric suffix on collision (yeni-satici-2).
Idempotent slug backfill runs once on upgrade. Existing vendors get an ASCII slug derived from their display name; the migration is gated on a version flag and never overwrites a slug an operator already set.
Verified Vendor / New Vendor badges
The profile hero shows one of two badges based on admin-configurable thresholds:
- Verified Vendor — vendor has at least N completed bookings AND a reliability score at or above the configured floor.
- New Vendor — vendor was approved within the last N days.
Both thresholds live under MHM Rentiva → Settings, default to sensible values (30 completed bookings + 80 reliability score; 90 days for new), and feed the same VendorBadgeEligibility pure-logic class used everywhere a badge is rendered.
Cache invalidation that knows what changed
Vendor profile data caches in a transient keyed by user ID + cap suffix. Seven triggers invalidate it:
- Vendor user-meta change (display name, slug, avatar, bio, badge override, etc.)
- Booking save (a new completed booking can shift reliability score)
- Comment status change (a newly approved review affects the average rating)
- Vehicle lifecycle change (a paused vehicle drops out of the vendor's vehicle list)
- Profile update hook (
profile_update)
The cache key suffix encodes the maximum vehicle and review version stamps, so the invalidator uses an SQL prefix-wildcard delete (DELETE FROM …_options WHERE option_name LIKE '_transient_mhm_rentiva_vendor_profile_<user_id>_%') instead of needing to compute the exact suffix in advance.
i18n cleanup
Phase 10 of the implementation refreshed 19 Turkish translations:
- Four newly filled (was rendering English fallback): "Public Profile URL Slug", "Member %s", "No reviews yet — be the first to leave one.", "→ %s"
- Fifteen fuzzy markers cleared: "Vendor Avatar" → "Bayi Avatarı", "Verified Vendor" → "Doğrulanmış Bayi", "New Vendor" → "Yeni Bayi", "Recent Reviews" → "Son Değerlendirmeler", "View all vehicles →" → "Tüm araçları görüntüle →", and ten more.
Browser smoke confirmed zero English fallback leak on the live profile.
Pro-gated
The whole feature lives behind Mode::canUseVendorMarketplace, the same gate that protects the existing vendor admin panel. Lite installs see no rewrite rule, no admin fields, no schema emission. Dev sites can bypass the gate locally with define('MHM_RENTIVA_DEV_PRO', true); in wp-config.php.
Stats
- PHPUnit: 904 → 982 (+78 across nine implementation phases)
- Assertions: 3162
- PHPCS: 0 errors
- i18n: 19 TR translations refreshed (4 newly filled, 15 fuzzy cleared)
- New PHP classes:
VendorProfileUrlBase,VendorProfileRewrite,VendorProfileProvider,VendorProfileSchema,VendorProfileExtension,VendorProfileCacheInvalidator,VendorSlugManager,VendorSlugMigration,VendorBadgeEligibility,VendorProfileWidget,VendorProfileBlock - Render surfaces: 1 shortcode + 1 block + 1 Elementor widget delegating to a single renderer
