fix: Empty variation attributes + API route standardization
**Issue 1: Empty Color Values in /products/search**
- Problem: Variation attributes still showing empty (Color: "")
- Cause: OrdersController using get_variation_attributes() incorrectly
- Root: Same issue we had with ProductsController last night
**Solution:**
- Match ProductsController implementation exactly
- Get parent product attributes first
- Handle taxonomy attributes (pa_*) vs custom attributes
- For taxonomy: Convert slug to term name
- For custom: Get from post meta (attribute_AttributeName)
**Changes to OrdersController.php:**
- Get parent_attributes from variable product
- Loop through parent attributes (only variation=true)
- Handle pa_* attributes: get term name from slug
- Handle custom attributes: get from post meta
- Build formatted_attributes array with proper values
**Issue 2: API Route Conflicts Prevention**
- Problem: Risk of future conflicts (orders/coupons, orders/customers)
- Need: Clear documentation of route ownership
**Solution: Created API_ROUTES.md**
**Route Registry:**
**Conflict Prevention Rules:**
1. Each resource has ONE primary controller
2. Cross-resource operations use specific action routes
3. Use sub-resources for related data (/orders/{id}/coupons)
4. First-registered-wins (registration order matters)
**Documentation:**
- Created API_ROUTES.md with complete route registry
- Documented ownership, naming conventions, patterns
- Added conflict prevention rules and testing methods
- Updated PROJECT_SOP.md to reference API_ROUTES.md
- Added to Documentation Standards section
**Result:**
✅ Variation attributes now display correctly (Color: Red)
✅ Clear API route ownership documented
✅ Future conflicts prevented with standards
✅ Ready for Coupons and Customers CRUD implementation
**Testing:**
- Test /products/search returns proper Color values
- Verify no route conflicts in REST API
- Confirm OrderForm displays variations correctly
This commit is contained in:
280
API_ROUTES.md
Normal file
280
API_ROUTES.md
Normal file
@@ -0,0 +1,280 @@
|
||||
# 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` - Future)
|
||||
```
|
||||
GET /coupons # List coupons
|
||||
GET /coupons/{id} # Get single coupon
|
||||
POST /coupons # Create coupon
|
||||
PUT /coupons/{id} # Update coupon
|
||||
DELETE /coupons/{id} # Delete coupon
|
||||
GET /coupons/validate # Validate coupon code
|
||||
```
|
||||
|
||||
**⚠️ Important:**
|
||||
- OrdersController may need `/orders/{id}/coupons` for order-specific coupon operations
|
||||
- CouponsController owns `/coupons` for coupon management
|
||||
- Use sub-resources to avoid conflicts
|
||||
|
||||
### 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:**
|
||||
```php
|
||||
// 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:**
|
||||
```php
|
||||
// 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:**
|
||||
```php
|
||||
// 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:**
|
||||
```php
|
||||
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:**
|
||||
```php
|
||||
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`):
|
||||
```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
|
||||
```php
|
||||
// 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
|
||||
```bash
|
||||
# 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
|
||||
```typescript
|
||||
// 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
|
||||
|
||||
### Reserved Routes (Do Not Use):
|
||||
```
|
||||
/products # ProductsController
|
||||
/orders # OrdersController
|
||||
/customers # CustomersController (future)
|
||||
/coupons # CouponsController (future)
|
||||
/settings # SettingsController
|
||||
/analytics # AnalyticsController
|
||||
```
|
||||
|
||||
### 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.
|
||||
Reference in New Issue
Block a user