QuoteServices Search

GET /quote_services/search.json
GET /quote_services/search/tag:{tag}.json
GET /quote_services/search/page:{page}.json

快速結論

  • legacy 入口:/Users/mattsu/Documents/Site/get-lancer-php56/app/Plugin/Quotes/Controller/QuoteServicesController.php:2729-2802
  • new 入口:Endpoint/V1/QuoteServices.php:102-159,helper Endpoint/V1/QuoteServices.php:161-366
  • new routing:Lib/Common/RouterRule/Mapping.php:57-64
  • new model helper:Lib/Model/QuoteServiceTag.php:19-27
  • 搬移狀態:已補 compatibility endpoint、routing 與 PHPUnit;目前 web-app active caller 走 /quote_services/pro_list...,本 API 主要保留 legacy 相容。

流量與 caller

2026-06-17 查 legacy 正式機 ip-10-5-2-242 Apache access log,範圍含目前 log、get-lancer_access.log-20260617get-lancer_access.log.1get-lancer_access.log-202606*.gzget-lancer_access.log.[0-9]*.gz

有前台/API 200 流量:

8 GET /quote_services/search.json?keyword=a&page:1 200
3 GET /quote_services/search.json?keyword=cleaning 200
3 GET /quote_services/search.json?keyword=a 200
2 GET /quote_services/search.json?keyword=a&page:2 200
1 GET /quote_services/search/tag:%e5%86%b7%e6%b0%a3.json 200
1 GET /quote_services/search/tag:%E9%99%A4%E8%9F%B2.json 200
1 GET /quote_services/search/tag:%E7%B6%B2%E9%A0%81%E8%A8%AD%E8%A8%88.json 200
1 GET /quote_services/search/tag:%E6%B3%A5%E4%BD%9C.json 200
1 GET /quote_services/search/tag:%E6%B2%B9%E6%BC%86.json 200
1 GET /quote_services/search/tag:%E6%94%9D%E5%BD%B1.json 200
1 GET /quote_services/search/tag:%E5%AE%A4%E5%85%A7%E8%A8%AD%E8%A8%88.json 200
1 GET /quote_services/search/tag:%E5%A9%9A%E7%A6%AE.json 200
1 GET /quote_services/search/tag:%E5%86%B7%E6%B0%A3.json 200

同次查詢 quote_bids/indexquote_requests/leadsquote_services/index|view 前台/API 正常流量都沒有比這支更適合優先搬:quote_bids/index 只有 admin HTML 命中、quote_requests/leads 無命中、quote_services/index 只有 2 筆 403、quote_services/view 無命中。

web-app caller:

檔案行號行為
/Users/mattsu/Documents/Site/web-app/modules/utils/api-manager.js3463-3469getServicesWithTag(tag) 呼叫 /quote_services/search/tag:{tag}.json;目前 active caller 已註解。
/Users/mattsu/Documents/Site/web-app/modules/utils/api-manager.js2630-2647現行 active pro list 改走 /quote_services/pro_list/page_size:{pageSize}/page:{page}/.../sort:hired_count/direction:desc.json

Legacy 行為

JSON branch 只處理 named params:

legacy 行號行為
2731-2735只在 JSON request 進入;把 GET query 轉 named params,允許 tagpagetag_category_id
2736-2744tagtag_category_idpage;每頁固定 20 筆;page < 1 時改為 1
2746-2754tag 時用 QuoteServiceTag::getServiceIdsByTag($tag, $tag_category_id) 取 service ids,並加 QuoteService.is_active = 1QuoteService.is_archived = 0
2756-2758$tag_svc_ids truthy 時加 QuoteService.id = $tag_svc_ids;空陣列 / false 時不加 id filter。
2760-2769contain Attachment.amazon_s3_thumb_urlUser 基本欄位 / avatar / profile、QuoteServicePhoto.amazon_s3_thumb_urlQuoteFaqAnswer
2771-2774用相同 conditions 計算總筆數。
2776-2778讀所有 category display name list。
2780-2788查 service list,欄位限 id,user_id,address,business_name,description,quote_category_ids,is_active,is_archived,feedback_count,feedback_score_total,排序 QuoteService.id desc
2790-2799quote_category_ids 轉成 QuoteService.categories map,然後 unset quote_category_ids
2801response:{"services": result, "total_pages": ceil(count / 20), "page": page}

相容決策

  • keyword query:正式 log 有 /quote_services/search.json?keyword=... 200,但 legacy search() JSON branch 沒有讀 keyword,只允許 tag/page/tag_category_id。legacy staging 實測確認 keyword 等同 no-tag list;new 不新增文字搜尋。
  • no-tag 行為:legacy 沒有 tag 時仍回 service list;new 保留 no-tag 無 filter 行為,不補 active / archived guard。
  • tag not found:legacy $tag_svc_ids falsy 時不加 QuoteService.id filter,會回 active/non-archived service list;new 保留此行為,不改成空結果。
  • Search::index() 不作為替代:新專案既有 Endpoint/V1/Search.php response shape、排序與 fallback 不同;本次補 QuoteServices::search() 專用相容 action。

Legacy staging 實測

2026-06-17 legacy staging:

GET /quote_services/search/tag:%E5%86%B7%E6%B0%A3.json
response: 200
shape: services[0].QuoteService / Attachment / User / QuoteFaqAnswer / QuoteServicePhoto

確認 response key 與 legacy code 對齊:top-level services,每筆 service 會把 quote_category_ids 轉為 QuoteService.categories,並包含 Attachment.amazon_s3_thumb_urlUserQuoteFaqAnswerQuoteServicePhoto.amazon_s3_thumb_url

高風險分支實測:

requestcounttotal_pagespagefirst_id結論
/quote_services/search/tag:notfound-codex-test-20260617.json20117113732tag 找不到仍回 200 與 service list,不可改成空結果。
/quote_services/search.json?keyword=a&page:120345113732keyword 不做文字搜尋,行為等同 no-tag list。
/quote_services/search.json20345113732無參數回 200 與 service list。

實作時需保留上述相容行為;keyword query 不應新增文字搜尋 side effect。

New 實作

檔案行號說明
Lib/Common/RouterRule/Mapping.php57-64新增 /quote_services/search.json/quote_services/search/(.*).json 精準 route。
Endpoint/V1/QuoteServices.php102-159search() action:API key、named params、tag filter、pagination、response。
Endpoint/V1/QuoteServices.php161-366response formatter 與 nested data helper。
Lib/Model/QuoteServiceTag.php19-27補 legacy getServiceIdsByTag();只用 tag LIKE "%tag"quote_category_id,不加 public/date filter。
tests/QuoteServicesSearchTest.php全檔覆蓋 tag search shape、missing tag fallback、keyword ignored fallback。

測試

legacy staging 已測 2026-06-17:

  • /quote_services/search/tag:{tag}.json
  • /quote_services/search/tag:{missing_tag}.json
  • /quote_services/search.json?keyword=a&page:1
  • /quote_services/search.json

local container PHP 8.2:

docker exec -w /project-data cd63f9147e8d php vendor/bin/phpunit tests/QuoteServicesSearchTest.php
OK (3 tests, 23 assertions)

new staging 待切換後確認:

  • response JSON key:services,total_pages,page
  • service 欄位與 nested Attachment/User/QuoteServicePhoto/QuoteFaqAnswer
  • QuoteService.categories map 與 quote_category_ids unset
  • page size 固定 20、排序 QuoteService.id desc