SearchAdd Request Normalization

快速結論

這段負責把 multipart / nested payload 正規化成 SearchAddInput。它不做 DB side effect。

current 入口:

SearchAddInput::fromRequest()
-> SearchAddRequestNormalizer::normalize()

新舊程式對應

Current Legacy

主要入口:

/Users/mattsu/Documents/Site/get-lancer-docker-2/get-lancer/app/Plugin/Quotes/Controller/QuoteRequestsController.php::search_add()
legacy 區段職責
raw User[...] / QuoteRequest[...] / Form[...] handling解析前端送出的 nested form payload。
checkbox / array form handling保留 multi-select、travel preference、time 類欄位語意。
FormSummary handling保留 legacy summary payload。

Current PHP 8.2

current 位置職責
Endpoint/V1/SearchAdd.php:110從 request 建立 SearchAddInput
Lib/SearchAdd/SearchAddInput.php:74-102fromRequest() 把 raw request 轉成 input,並保留 travelpref writer 所需的 raw Form 值。
Lib/SearchAdd/SearchAddRequestNormalizer.php正規化 multipart / nested payload。
Lib/SearchAdd/SearchAddInput.php:478-646拆出 QuoteRequestUserForm、nested rows 與基本型別。

行號對照

new 行號對應 legacy職責
Endpoint/V1/SearchAdd.php:110QuoteRequestsController.php:1808進入 search_add request 處理入口。
SearchAddInput.php:74-102QuoteRequestsController.php:1827-1855, 2025-2031從 request 取 payload;保留 decode 後 raw Form,再 normalize 並拆成 input 區塊。
SearchAddRequestNormalizer.php:29-75QuoteRequestsController.php:1827-1852, 2490-2510FormSummary 預設、urldecode、iOS summary flatten、移除 base64 內容。
SearchAddRequestNormalizer.php:82-99, 165-183QuoteRequestsController.php:1854-1855一般 Form 值做 rawurldecode 與 scalar-list normalization。
SearchAddInput.php:81, 115, 161-163, 545-583QuoteRequestsController.php:1854-1855另保留 decode 後、尚未攤平的 Form;只供 DB type 已確認為 travelpref 的 writer 使用。
SearchAddRequestNormalizer.php:105-118QuoteRequestsController.php:2050-2055QuoteRequest[phone_no] / User[phone] 移除非數字與 + 以外字元。
SearchAddRequestNormalizer.php:124-134QuoteRequestsController.php:2645-2646preserve_to_quote_service_id alias 成 preserve_quote_service_id
SearchAddRequestNormalizer.php:140-159QuoteRequestsController.php:2640-2642, 3041-3055landing page url decode。
SearchAddRequestNormalizer.php:211-239QuoteRequestsController.php:1941-1960country / state / city input 正規化。
SearchAddInput.php:478-565QuoteRequestsController.php:1863, 2020-2047拆出 quote category、QuoteRequest、User、Form payload。
SearchAddInput.php:589-621QuoteRequestsController.php:2978-2994, 2966-2977拆 nested QuoteRequestLandingPageFormTime rows。
SearchAddInput.php:445-473QuoteRequestsController.php:2492-2495, 2904-2931建單初值沿用 object JSON;單值/JSON array 由 form 寫表後的 updater 決定最終 DB flags。

Side Effects

無 DB side effect。

這段只產生後續流程使用的 input model。

不可改變的契約

  • 不可把 travelpref JSON 字串陣列變成逗號字串後寫入 DB。
  • request normalization 只做 Form[...] urldecode 與 scalar list normalization;{"value":"..."} / [{"value":"..."}] 的攤平要留在 form submission writer,因為那裡才知道欄位 type,才能排除 travelpref
  • FormSummary 要保留原始 legacy summary 語意。
  • QuoteRequest[match_type] 的 raw 值只代表輸入,最終落表仍要經 category 規則正規化。
  • request normalization 不應做 actor / DB 寫入。

Verification

主要測試:

tests/SearchAddInputTest.php
tests/FormSubmissionWriterTest.php
tests/SearchAddTravelPreferenceIntegrationTest.php
tests/SearchAddStep2IntegrationTest.php

2026-08-04 驗證:raw / normalized 分流 Core 2 tests / 3 assertions、Other 2 tests / 4 assertions;DB transaction 1 test / 7 assertions 通過。

待確認 / 風險

  • PHP array 不是本次 BAC-3658 的「字串 array」格式;只有 JSON string-array 走 raw travelpref 路徑,避免自行擴張 caller contract。
  • location alias 已確認到 SearchAddRequestNormalizer.php:211-239;後續若要追 state/city 建立細節,應從 resolveCountryId() / resolveStateId() / resolveCityId() 再拆第三層文件。