Files
WooNooW/API_ROUTES.md
Dwindi Ramadhana f397ef850f feat: Add product images support with WP Media Library integration
- 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
2025-11-26 16:18:43 +07:00

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

  1. Use plural nouns for resources (/products, /orders, /customers)
  2. Use kebab-case for multi-word resources (/pickup-locations)
  3. Use specific action names to avoid conflicts (/products/search, /orders/preview)
  4. Never create generic routes that might conflict (/products vs /products)
  5. 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/search is owned by OrdersController (NOT ProductsController)
  • This is for lightweight product search in order forms
  • ProductsController owns /products for 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/search is already used by OrdersController
  • CustomersController will own /customers for 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/validate is in OrdersController (order-specific validation)
  • CouponsController owns /coupons for 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:

  • /productsProductsController
  • /ordersOrdersController
  • /customersCustomersController (future)
  • /couponsCouponsController (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 /products is 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:

  1. Check existing routes - Review this document
  2. Choose specific names - Avoid generic routes
  3. Use sub-resources - For related data
  4. Update this document - Add new routes to registry
  5. 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, /account prefixes

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, /account prefixes

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.