- Add WP Media Library integration for product and variation images - Support images array (URLs) conversion to attachment IDs - Add images array to API responses (Admin & Customer SPA) - Implement drag-and-drop sortable images in Admin product form - Add image gallery thumbnails in Customer SPA product page - Initialize WooCommerce session for guest cart operations - Fix product variations and attributes display in Customer SPA - Add variation image field in Admin SPA Changes: - includes/Api/ProductsController.php: Handle images array, add to responses - includes/Frontend/ShopController.php: Add images array for customer SPA - includes/Frontend/CartController.php: Initialize WC session for guests - admin-spa/src/lib/wp-media.ts: Add openWPMediaGallery function - admin-spa/src/routes/Products/partials/tabs/GeneralTab.tsx: WP Media + sortable images - admin-spa/src/routes/Products/partials/tabs/VariationsTab.tsx: Add variation image field - customer-spa/src/pages/Product/index.tsx: Add gallery thumbnails display
12 KiB
12 KiB
WooNooW API Routes Standard
Namespace
All routes use: woonoow/v1
Route Naming Convention
Pattern
/{resource} # List/Create
/{resource}/{id} # Get/Update/Delete single item
/{resource}/{action} # Special actions
/{resource}/{id}/{sub} # Sub-resources
Rules
- ✅ Use plural nouns for resources (
/products,/orders,/customers) - ✅ Use kebab-case for multi-word resources (
/pickup-locations) - ✅ Use specific action names to avoid conflicts (
/products/search,/orders/preview) - ❌ Never create generic routes that might conflict (
/productsvs/products) - ❌ Never use verbs as resource names (
/get-products❌, use/products✅)
Current Routes Registry
Products Module (ProductsController.php)
GET /products # List products (admin)
GET /products/{id} # Get single product
POST /products # Create product
PUT /products/{id} # Update product
DELETE /products/{id} # Delete product
GET /products/categories # List categories
POST /products/categories # Create category
GET /products/tags # List tags
POST /products/tags # Create tag
GET /products/attributes # List attributes
Orders Module (OrdersController.php)
GET /orders # List orders
GET /orders/{id} # Get single order
POST /orders # Create order
PUT /orders/{id} # Update order
DELETE /orders/{id} # Delete order
POST /orders/preview # Preview order totals
GET /products/search # Search products for order form (⚠️ Special route)
GET /customers/search # Search customers for order form (⚠️ Special route)
⚠️ Important:
/products/searchis owned by OrdersController (NOT ProductsController)- This is for lightweight product search in order forms
- ProductsController owns
/productsfor full product management
Customers Module (CustomersController.php - Future)
GET /customers # List customers
GET /customers/{id} # Get single customer
POST /customers # Create customer
PUT /customers/{id} # Update customer
DELETE /customers/{id} # Delete customer
⚠️ Important:
/customers/searchis already used by OrdersController- CustomersController will own
/customersfor full customer management - No conflict because routes are specific
Coupons Module (CouponsController.php) ✅ IMPLEMENTED
GET /coupons # List coupons (with pagination, search, filter)
GET /coupons/{id} # Get single coupon
POST /coupons # Create coupon
PUT /coupons/{id} # Update coupon
DELETE /coupons/{id} # Delete coupon
POST /coupons/validate # Validate coupon code (OrdersController)
Implementation Details:
- List: Supports pagination (
page,per_page), search (search), filter by type (discount_type) - Create: Validates code uniqueness, requires
code,amount,discount_type - Update: Full coupon data update, code cannot be changed after creation
- Delete: Supports force delete via query param
- Validate: Handled by OrdersController for order context
Note:
/coupons/validateis in OrdersController (order-specific validation)- CouponsController owns
/couponsfor coupon CRUD management - No conflict because validate is a specific action route
Settings Module (SettingsController.php)
GET /settings # Get all settings
PUT /settings # Update settings
GET /settings/store # Get store settings
GET /settings/tax # Get tax settings
GET /settings/shipping # Get shipping settings
GET /settings/payments # Get payment settings
Analytics Module (AnalyticsController.php)
GET /analytics/overview # Dashboard overview
GET /analytics/products # Product analytics
GET /analytics/orders # Order analytics
GET /analytics/customers # Customer analytics
Conflict Prevention Rules
1. Resource Ownership
Each resource has ONE primary controller:
/products→ProductsController/orders→OrdersController/customers→CustomersController(future)/coupons→CouponsController(future)
2. Cross-Resource Operations
When one module needs data from another resource, use specific action routes:
✅ Good:
// OrdersController needs product search
register_rest_route('woonoow/v1', '/products/search', [...]);
// OrdersController needs customer search
register_rest_route('woonoow/v1', '/customers/search', [...]);
// OrdersController needs coupon validation
register_rest_route('woonoow/v1', '/orders/validate-coupon', [...]);
❌ Bad:
// OrdersController trying to own /products
register_rest_route('woonoow/v1', '/products', [...]); // CONFLICT!
// OrdersController trying to own /customers
register_rest_route('woonoow/v1', '/customers', [...]); // CONFLICT!
3. Sub-Resource Pattern
Use sub-resources for related data:
✅ Good:
// Order-specific coupons
GET /orders/{id}/coupons # List coupons applied to order
POST /orders/{id}/coupons # Apply coupon to order
DELETE /orders/{id}/coupons/{code} # Remove coupon from order
// Order-specific notes
GET /orders/{id}/notes # List order notes
POST /orders/{id}/notes # Add order note
4. Action Routes
Use descriptive action names to avoid conflicts:
✅ Good:
POST /orders/preview # Preview order totals
POST /orders/calculate-shipping # Calculate shipping
GET /products/search # Search products (lightweight)
GET /coupons/validate # Validate coupon code
❌ Bad:
POST /orders/calc # Too vague
GET /search # Too generic
GET /validate # Too generic
Registration Order
WordPress REST API uses first-registered-wins for route conflicts.
Controller Registration Order (in Routes.php):
1. SettingsController
2. ProductsController # Registers /products first
3. OrdersController # Can use /products/search (no conflict)
4. CustomersController # Will register /customers
5. CouponsController # Will register /coupons
6. AnalyticsController
⚠️ Critical:
- ProductsController MUST register before OrdersController
- This ensures
/productsis owned by ProductsController - OrdersController can safely use
/products/search(different path)
Testing for Conflicts
1. Check Route Registration
// Add to Routes.php temporarily
add_action('rest_api_init', function() {
$routes = rest_get_server()->get_routes();
error_log('WooNooW Routes: ' . print_r($routes['woonoow/v1'], true));
}, 999);
2. Test API Endpoints
# Test product list (should hit ProductsController)
curl -X GET "https://site.local/wp-json/woonoow/v1/products"
# Test product search (should hit OrdersController)
curl -X GET "https://site.local/wp-json/woonoow/v1/products/search?s=test"
# Test customer search (should hit OrdersController)
curl -X GET "https://site.local/wp-json/woonoow/v1/customers/search?s=john"
3. Frontend API Calls
// ProductsApi - Full product management
ProductsApi.list() → GET /products
ProductsApi.get(id) → GET /products/{id}
ProductsApi.create(data) → POST /products
// OrdersApi - Product search for orders
ProductsApi.search(query) → GET /products/search
// CustomersApi - Customer search for orders
CustomersApi.search(query) → GET /customers/search
Future Considerations
When Adding New Modules:
- Check existing routes - Review this document
- Choose specific names - Avoid generic routes
- Use sub-resources - For related data
- Update this document - Add new routes to registry
- Test for conflicts - Use testing methods above
Frontend Module (Customer-Facing) ✅ IMPLEMENTED
ShopController.php
GET /shop/products # List products (public)
GET /shop/products/{id} # Get single product (public)
GET /shop/categories # List categories (public)
GET /shop/search # Search products (public)
Implementation Details:
- List: Supports pagination, category filter, search, orderby
- Single: Returns detailed product info (variations, gallery, related products)
- Categories: Returns categories with images and product count
- Search: Lightweight product search (max 10 results)
CartController.php
GET /cart # Get cart contents
POST /cart/add # Add item to cart
POST /cart/update # Update cart item quantity
POST /cart/remove # Remove item from cart
POST /cart/apply-coupon # Apply coupon to cart
POST /cart/remove-coupon # Remove coupon from cart
Implementation Details:
- Uses WooCommerce cart session
- Returns full cart data (items, totals, coupons)
- Public endpoints (no auth required)
- Validates product existence before adding
AccountController.php
GET /account/orders # Get customer orders (auth required)
GET /account/orders/{id} # Get single order (auth required)
GET /account/profile # Get customer profile (auth required)
POST /account/profile # Update profile (auth required)
POST /account/password # Update password (auth required)
GET /account/addresses # Get addresses (auth required)
POST /account/addresses # Update addresses (auth required)
GET /account/downloads # Get digital downloads (auth required)
Implementation Details:
- All endpoints require
is_user_logged_in() - Order endpoints verify customer owns the order
- Profile/address updates use WC_Customer class
- Password update verifies current password
Note:
- Frontend routes are customer-facing (public or logged-in users)
- Admin routes (ProductsController, OrdersController) are admin-only
- No conflicts because frontend uses
/shop,/cart,/accountprefixes
WooCommerce Hook Bridge
Get Hooks for Context
- GET
/woonoow/v1/hooks/{context} - Purpose: Capture and return WooCommerce action hook output for compatibility with plugins
- Parameters:
context(required): 'product', 'shop', 'cart', or 'checkout'product_id(optional): Product ID for product context
- Response:
{ success: true, context: string, hooks: { hook_name: html_output } } - Example:
/woonoow/v1/hooks/product?product_id=123
Customer-Facing Frontend Routes are customer-facing (public or logged-in users)
- Admin routes (ProductsController, OrdersController) are admin-only
- No conflicts because frontend uses
/shop,/cart,/accountprefixes
Reserved Routes (Do Not Use):
/products # ProductsController (admin)
/orders # OrdersController (admin)
/customers # CustomersController (admin)
/coupons # CouponsController (admin)
/settings # SettingsController (admin)
/analytics # AnalyticsController (admin)
/shop # ShopController (customer)
/cart # CartController (customer)
/account # AccountController (customer)
Safe Action Routes:
/products/search # OrdersController (lightweight search)
/customers/search # OrdersController (lightweight search)
/orders/preview # OrdersController (order preview)
/coupons/validate # CouponsController (coupon validation)
Summary
✅ Do:
- Use plural nouns for resources
- Use specific action names
- Use sub-resources for related data
- Register controllers in correct order
- Update this document when adding routes
❌ Don't:
- Create generic routes that might conflict
- Use verbs as resource names
- Register same route in multiple controllers
- Forget to test for conflicts
Remember: First-registered-wins! Always check existing routes before adding new ones.