# PhpStorm Agent Prompt: Rebuild Hotel Discovery Search as a Reliable Public Search Contract
Copy everything below into the IDE agent as one task.
---
You are fixing the production-critical public hotel discovery route:
`GET /locations?type=rentals&category=hotel`
Read these project references before editing:
- `HOTEL_DISCOVERY_SEARCH_AUDIT_2026-08-28.md`
- `C:\Users\Svet PC\Downloads\zapazime_2026-08-28_22-55-21.sql` — schema reference only. Do **not** import, alter, expose, or commit this dump.
- `app/Http/Controllers/WebController.php`
- `app/Models/Venue.php`, `Location.php`, `Place.php`, `Category.php`,
`VenueObject.php`, `RentalUnit.php`, `RatePlan.php`, `Stay.php`, and booking
models relevant to date availability.
- `resources/views/web/locations.blade.php`
- `resources/views/web/partials/nav.blade.php`
- `app/Providers/AppServiceProvider.php`
## Goal
Make public hotel discovery dependable from category navigation through results,
location/place/venue detail and booking handoff. This must be a coherent search
system, not a collection of independent text filters. Use one validated request
contract and one shared public-search scope for results, category counters, map,
cards, detail eligibility and booking revalidation.
## Non-negotiable safety rules
- Preserve unrelated working-tree changes.
- Use PHP 8.3+ only; do not weaken Composer platform checks.
- Do not use `withoutGlobalScopes()` for public discovery unless you replace the
removed tenant constraints with an explicit, reviewed platform-publication
scope. Never cross company/workspace boundaries accidentally.
- Do not return inactive, unpublished, unverified, expired-certificate,
suspended, rejected, private, deleted, or tenant-inaccessible inventory.
- Do not fabricate price, rating, availability, count, amenities or review data.
- Do not hide a failure with a broad catch, silent fallback or text matching that
bypasses an inactive category/type.
- Do not submit real bookings/payments or modify production data. Use factories,
seeders and sandbox/non-chargeable test methods only.
## Confirmed defects to fix
1. `WebController::locations()` receives `category=hotel`, but
`buildVenueSearchQuery()` only receives the booking type and never applies
the selected category.
2. Deactivated categories/types are bypassable: a missing/inactive `Category`
currently falls back to venue text matching, and if a booking type has no
active categories, the booking-type filter is omitted altogether.
3. Search only checks venue `is_active`; it does not consistently use the
model's public-display policy or require active parent place/location and
bookable active room/unit inventory.
4. The Blade filter panel emits flat keys such as `ss`, `min_price`,
`property_type`, `group_adults`, `amenities[]`, while the controller reads a
different nested `q.*` schema. Most controls are ineffective.
5. Hotel date/guest/room controls do not query availability.
6. Amenity filtering queries columns on `venue_facilities`, but the actual
facility details are reached through the `facility()` relationship.
7. Rating/review-count filters are unimplemented; sorting has duplicate IDs, no
reliable change handler and incompatible values.
8. Navigation count, category-card count, location count and map/result count
use different queries. The category card can display a fabricated fallback
price or a conflicting availability number.
9. Category matching is hard-coded/keyword-based (`LIKE` on text) instead of a
stable category identifier. The public category manager is missing a complete
super-admin on/off control with auditability.
## Required design
### 1. Canonical query contract and validation
Create a dedicated request/DTO/value object for public discovery. Choose one
URL schema and make server, Blade and JavaScript use it consistently. Prefer
readable top-level GET parameters, for example:
`type`, `category`, `destination`, `checkin`, `checkout`, `adults`,
`children`, `children_ages[]`, `rooms`, `min_price`, `max_price`,
`currency`, `property_type`, `amenities[]`, `min_rating`, `min_reviews`,
`sort`, `page`.
For hotels, use **check-in/check-out** terminology; do not reuse vehicle
pickup/drop-off semantics. Preserve compatible legacy parameters temporarily
only through an explicit normalizer and redirect/canonical URL strategy.
Validate and normalize:
- exact allowed booking types and active canonical category;
- category belongs to type;
- dates are valid and checkout is after checkin;
- sensible guest/room limits and non-negative prices;
- known sort values and known facilities/property types;
- pagination limits and deterministic default ordering.
An unknown or disabled category/type must be a localized safe 404 or a clear
empty state according to the established product convention—never a broadened
result set. Document the choice and test it.
### 2. One public inventory scope
Implement an explicit reusable query scope/service, e.g. `PublicDiscoveryQuery`
or appropriately named Eloquent scopes. Every public-search consumer must use
it: navigation/card counters, `/locations`, map/list/grid, location/place/venue
detail eligibility and booking final revalidation.
It must require, according to actual schema/model semantics:
- venue is active and soft-delete-safe;
- venue passes public visibility requirements (verified, certificate valid and
not suspended/rejected/high-risk); reuse/fix `publiclyDisplayable()` rather
than duplicating partial conditions;
- active eligible parent place and location;
- public company/workspace publication constraints, without data leakage;
- for hotel availability/counts, at least one active, eligible, bookable room or
rental unit. Honor room block, maintenance and housekeeping readiness where
they are part of the booking model.
Do not broaden `NULL booking_type` as a permanent compatibility rule. Migrate
legacy records to a canonical type/category mapping, then make the public scope
strict. Provide a reviewed, idempotent migration/command/seed repair plan.
### 3. Canonical categories and super-admin publication control
Make `categories` the single source of truth for public category definition:
stable slug, booking type, name/translation, sort order and `is_active`.
- Add an authenticated, authorization-protected super-admin management surface
(use the existing admin convention) to activate/deactivate categories and,
if appropriate, booking types.
- Add confirmation, audit-log entry, clear active/inactive state and cache
invalidation.
- Deactivation must immediately remove the category from navigation, APIs,
direct URLs, counters, maps, results, details and booking handoff.
- Replace keyword `LIKE` category matching with stable mapping/foreign-key or
a clearly defined transitional mapping. Do not match category by venue name or
description in the final public path.
### 4. Complete hotel criteria
Implement the retained controls end-to-end:
- destination across country/city/location/place, with safe autocomplete;
- check-in/check-out availability using the existing stays/bookings/room
assignments/room blocks and unit model; avoid N+1 overlap checks;
- adults, children (including ages if schema/product supports it), rooms and
occupancy at the room/unit level;
- real rate-plan/unit pricing in the requested/display currency, including a
clear tax/fee policy; do not use a venue `price_per_hour` proxy for hotels;
- valid hotel property types and active facilities via `venueFacilities.facility`;
- approved/public review aggregates only, with minimum-rating and review-count
filters;
- explicit, implemented sorts: recommended, total price ascending/descending,
rating, newest and distance only when user coordinates are actually available.
If a control cannot be made truthful now, remove it from the public UI rather
than leaving a decorative filter.
### 5. Counters and presentation
Define the metric on every UI count and use batched queries from the shared
scope. Examples: “properties”, “room types” and “available rooms for selected
dates” are different metrics and must never be interchanged.
- Navigation category count and hotel category-card count must use the same
documented metric as the current result set when no dates are supplied.
- With dates/guests, display an explicitly date-available result total.
- Use paginator `total()` where the UI says total results, not `count()` for the
current page.
- Remove hard-coded availability/price fallback copy. Hide unavailable metrics
or show a truthful localized empty state.
- Make map, grid and list read the identical filtered result IDs.
### 6. Front-end behavior and accessibility
Refactor `resources/views/web/locations.blade.php` so there is one form and one
source of filter state. Eliminate duplicate `id="sort-select"` values. Use a
normal GET submission that works without JavaScript; JavaScript may enhance it
but must emit the canonical server contract. Preserve search criteria through:
category → locations → location → place → venue detail → booking review.
Use localized labels, accessible input labels, validation errors, URL history,
clear/reset behavior and meaningful empty/no-availability states. Keep mobile
layout responsive and do not reintroduce horizontal overflow.
## Tests required
Add focused feature/model/browser tests using deterministic factories/fixtures.
At minimum cover:
1. published active verified hotel with eligible unit appears;
2. non-hotel rental never appears under `category=hotel`;
3. disabled/unknown category and disabled booking type cannot be reached by URL;
4. inactive, soft-deleted, unverified, expired-certificate, suspended venue;
5. inactive parent place/location; inactive/blocked/dirty/maintenance room;
6. destination, valid/invalid dates, guest/room capacity, price, amenity,
property type, rating, review count and each sort option;
7. booking overlap, room block and concurrent availability revalidation;
8. counter equality across navigation, category card, paginator total and map;
9. pagination preserves criteria; grid/list/map show the same IDs;
10. direct venue/location deep links and booking handoff obey the same scope;
11. super-admin category on/off authorization, audit logging and cache refresh;
12. non-JavaScript GET form behavior plus browser tests for the visible controls.
Run the focused suite on PHP 8.3+ and report exact commands/results. If the
current local runtime is not PHP 8.3+, configure/use the project-supported
runtime; do not bypass platform checks.
## Deliverable
At completion provide:
1. root-cause summary and the final canonical query contract;
2. changed files and migrations/data-repair steps;
3. a precise rollout order, cache/queue commands and rollback plan;
4. tests run and results;
5. any remaining product decisions that require owner approval (for example,
canonical count semantics, tax display policy, or availability rules).