feat: implement WooCommerce AJAX product filtering

This commit is contained in:
XShop
2026-09-13 20:31:20 +03:30
parent f5c283af25
commit b78e332966
15 changed files with 1543 additions and 150 deletions
+16 -17
View File
@@ -15,24 +15,22 @@ Two installable artifacts: theme (`xshop`) + plugin (`xshop-core`). Theme never
## 2. Theme – `xshop-theme`
### 2.1 File map (M3)
### 2.1 File map (M4)
```
xshop-theme/
style.css / functions.php / rtl.css / header.php / footer.php / index.php etc
inc/
setup/assets.php — tokens→base→layout→components→woo→utilities→style.css + theme.js + search.js (front-end only where data-xshop-search exists, documented)
woocommerce/setup.php — loop_start/end, toolbar, gallery remove, related 4/4, product.js conditional
assets/
css/woo.css — + search dropdown (.xshop-search, _dropdown, _results, _item, responsive fixed @640)
js/search.js — debounce 250ms, AbortController, stale seq, keyboard, a11y
template-parts/components/
ajax-search.php — reusable combobox (role=combobox, aria-expanded/controls, listbox, live status)
+ previous M2 components
woocommerce/ — 4 overrides as M2
inc/setup/assets.php — tokens→…→woo→utilities + theme.js + search.js + filter.js (front-end, DOM guard, documented)
inc/woocommerce/setup.php — loop, toolbar, gallery, related, product.js, pre_get_posts no-JS fallback
assets/css/woo.css — + search + filters (.xshop-shop, _filters, _chip, _drawer, responsive)
assets/js/search.js — debounce 250ms, AbortController, stale seq
assets/js/filter.js — URL sync, chips, debounce 300ms for price, AbortController, drawer a11y
template-parts/components/ajax-search.php + product-filters.php (sidebar+drawer, no-JS fallback)
woocommerce/archive-product.php — shop/category with filters layout + chips + AJAX grid
xshop-core/
includes/REST/Search.php — xshop/v1/search endpoint
includes/Core/Plugin.php — loads REST/Search.php
includes/Query/ProductQuery.php — shared sanitize, map_orderby, build_product_query_args, format_product, execute_query
includes/REST/Search.php — refactored to use Query
includes/REST/Products.php — xshop/v1/products (category, pa_*, price, stock, sale, rating, orderby, pagination, search)
includes/Core/Plugin.php — loads Query + both REST
```
### 2.2 Bootstrap
@@ -107,10 +105,11 @@ Tokens: `tokens.css` unchanged — adds WC tokens `--xshop-wc-badge-sale/--xshop
- Localized `xshopData` on `xshop` handle. No jQuery for theme code; Woo's jQuery remains for variations/add-to-cart.
- Build: `tools/build-release.ps1` now includes top-level folder `xshop/` wrapper (fixed in M1.1).
## 7. AJAX / REST (M3: Search)
## 7. AJAX / REST (M4)
- **Search**: `GET /wp-json/xshop/v1/search?search=&limit=` via `xshop-core/includes/REST/Search.php:1` (`register_rest_route xshop/v1/search`). Public read-only (`permission_callback __return_true`), sanitized `search` (trim/strip_tags/sanitize_text_field/max 100) + `limit` 1..20, min length 2 returns empty without DB, `WP_Query` s + `_sku` LIKE, `post_status publish`, `no_found_rows`, `fields ids` deduped to `limit` (default 6), response `{items:[{id,title,url,image,price_html,type,in_stock}], total}` + `Cache-Control: public, max-age=60`. No private data, no N+1, no draft, no customer/order, `wc_get_product` only for returned IDs.
- Prefer REST for search/filter/Q&A, fallback `admin-ajax` for cart where WC expects.
- **Search** (M3): `GET /wp-json/xshop/v1/search?search=&limit=` — public, `search` max 100 + `limit` 1..20, min 2, `WP_Query s` + `_sku LIKE`, `publish` only, `no_found_rows` `fields ids`, `{items:[id,title,url,image,price_html,type,in_stock],total}` + `Cache-Control: public, max-age=60`, shared `Query\ProductQuery`.
- **Products** (M4): `GET /wp-json/xshop/v1/products?search=&category=&pa_*=&min_price=&max_price=&stock=&on_sale=&rating=&orderby=&page=&per_page=` — public, sanitized `search` + `category` slug/ID + dynamic `pa_*` (only `taxonomy_exists`), `min/max` numeric 0..99M + `min<=max` 400, `stock all/instock/outofstock`, `on_sale 0/1` via meta OR, `rating 0..5` via `_wc_average_rating`, `orderby` allow-list `default/latest/price_asc/desc/popularity/rating/date` → `map_orderby`, `page 1..100` `per_page 1..24` default 12, shared `ProductQuery::build_product_query_args` → `WP_Query` `tax_query` + `meta_query` + `orderby` + `post__in` for sale, `execute_query` → `{items, pagination:{page,per_page,total,total_pages}}` + `Cache-Control: public, max-age=30`, no private data, no N+1.
- Prefer REST for search/filter, fallback `admin-ajax` for cart.
## 8. Security Model