feat: Phase 1 - Backend API meta compatibility (Level 1)

**Implemented: Backend API Enhancement for Level 1 Compatibility**

Following IMPLEMENTATION_PLAN_META_COMPAT.md Phase 1

**OrdersController.php:**
 Added get_order_meta_data() - Expose meta in API responses
 Added update_order_meta_data() - Update meta from API
 Modified show() - Include meta in response
 Modified update() - Handle meta updates
 Added filter: woonoow/order_allowed_private_meta
 Added filter: woonoow/order_updatable_meta
 Added filter: woonoow/order_api_data
 Added action: woonoow/order_updated

**ProductsController.php:**
 Added get_product_meta_data() - Expose meta in API responses
 Added update_product_meta_data() - Update meta from API
 Modified format_product_full() - Include meta in response
 Modified update_product() - Handle meta updates
 Added filter: woonoow/product_allowed_private_meta
 Added filter: woonoow/product_updatable_meta
 Added filter: woonoow/product_api_data
 Added action: woonoow/product_updated

**Meta Filtering Logic:**
- Skip internal WooCommerce meta (_wc_*)
- Skip WooNooW internal meta (_woonoow_*)
- Public meta (no underscore) - always expose
- Private meta (starts with _) - check allowed list
- Plugins can add to allowed list via filters

**Default Allowed Meta (Orders):**
- _tracking_number
- _tracking_provider
- _tracking_url
- _shipment_tracking_items
- _wc_shipment_tracking_items
- _transaction_id
- _payment_method_title

**How It Works:**
1. Plugin stores: update_post_meta($order_id, '_tracking_number', '123')
2. WooNooW API exposes: GET /orders/123 returns meta._tracking_number
3. Frontend can read/write via API
4. Plugin works WITHOUT any extra effort

**Next Steps:**
- Phase 2: Frontend components (MetaFields, useMetaFields)
- Phase 3: PHP MetaFieldsRegistry system
- Testing with popular plugins

**Status:** Backend API ready for Level 1 compatibility! 🎉
This commit is contained in:
dwindown
2025-11-20 12:22:01 +07:00
parent cb91d0841c
commit e53b8320e4
2 changed files with 206 additions and 0 deletions

View File

@@ -522,8 +522,12 @@ class OrdersController {
'notes' => $notes, 'notes' => $notes,
'coupons' => $coupon_codes, 'coupons' => $coupon_codes,
'customer_note' => $order->get_customer_note(), 'customer_note' => $order->get_customer_note(),
'meta' => self::get_order_meta_data( $order ),
]; ];
// Allow plugins to modify response (Level 1 compatibility)
$data = apply_filters( 'woonoow/order_api_data', $data, $order, $req );
return new WP_REST_Response( $data, 200 ); return new WP_REST_Response( $data, 200 );
} }
@@ -682,6 +686,11 @@ class OrdersController {
} }
} }
// Update custom meta fields (Level 1 compatibility)
if ( isset( $p['meta'] ) && is_array( $p['meta'] ) ) {
self::update_order_meta_data( $order, $p['meta'] );
}
// SOLUTION: Block WooCommerce analytics tracking (pixel.wp.com) during save // SOLUTION: Block WooCommerce analytics tracking (pixel.wp.com) during save
// This was causing 30s timeout on every order status change // This was causing 30s timeout on every order status change
add_filter( 'pre_http_request', function( $preempt, $args, $url ) { add_filter( 'pre_http_request', function( $preempt, $args, $url ) {
@@ -694,6 +703,9 @@ class OrdersController {
$order->save(); $order->save();
// Allow plugins to perform additional updates (Level 1 compatibility)
do_action( 'woonoow/order_updated', $order, $p, $req );
// Clean up // Clean up
remove_all_filters( 'pre_http_request' ); remove_all_filters( 'pre_http_request' );
@@ -2172,4 +2184,99 @@ class OrdersController {
'expiry_date' => $coupon->get_date_expires() ? $coupon->get_date_expires()->date( 'Y-m-d' ) : null, 'expiry_date' => $coupon->get_date_expires() ? $coupon->get_date_expires()->date( 'Y-m-d' ) : null,
], 200 ); ], 200 );
} }
/**
* Get order meta data for API exposure (Level 1 compatibility)
* Filters out internal meta unless explicitly allowed
*
* @param \WC_Order $order
* @return array
*/
private static function get_order_meta_data( $order ) {
$meta_data = [];
foreach ( $order->get_meta_data() as $meta ) {
$key = $meta->key;
$value = $meta->value;
// Skip internal WooCommerce meta (starts with _wc_)
if ( strpos( $key, '_wc_' ) === 0 ) {
continue;
}
// Skip WooNooW internal meta
if ( strpos( $key, '_woonoow_' ) === 0 ) {
continue;
}
// Public meta (no underscore) - always expose
if ( strpos( $key, '_' ) !== 0 ) {
$meta_data[ $key ] = $value;
continue;
}
// Private meta (starts with _) - check if allowed
$allowed_private = apply_filters( 'woonoow/order_allowed_private_meta', [
// Common shipping tracking fields
'_tracking_number',
'_tracking_provider',
'_tracking_url',
'_shipment_tracking_items',
'_wc_shipment_tracking_items',
// Payment gateway meta
'_transaction_id',
'_payment_method_title',
// Allow plugins to add their meta via filter
], $order );
if ( in_array( $key, $allowed_private, true ) ) {
$meta_data[ $key ] = $value;
}
}
return $meta_data;
}
/**
* Update order meta data from API (Level 1 compatibility)
*
* @param \WC_Order $order
* @param array $meta_updates
*/
private static function update_order_meta_data( $order, $meta_updates ) {
// Get allowed updatable meta keys
$allowed = apply_filters( 'woonoow/order_updatable_meta', [
// Common shipping tracking fields
'_tracking_number',
'_tracking_provider',
'_tracking_url',
// Allow plugins to add their meta via filter
], $order );
foreach ( $meta_updates as $key => $value ) {
// Skip internal WooCommerce meta
if ( strpos( $key, '_wc_' ) === 0 ) {
continue;
}
// Skip WooNooW internal meta
if ( strpos( $key, '_woonoow_' ) === 0 ) {
continue;
}
// Public meta (no underscore) - always allow
if ( strpos( $key, '_' ) !== 0 ) {
$order->update_meta_data( $key, $value );
continue;
}
// Private meta - check if allowed
if ( in_array( $key, $allowed, true ) ) {
$order->update_meta_data( $key, $value );
}
}
}
} }

View File

@@ -371,8 +371,16 @@ class ProductsController {
$product->set_gallery_image_ids($data['gallery_image_ids']); $product->set_gallery_image_ids($data['gallery_image_ids']);
} }
// Update custom meta fields (Level 1 compatibility)
if (isset($data['meta']) && is_array($data['meta'])) {
self::update_product_meta_data($product, $data['meta']);
}
$product->save(); $product->save();
// Allow plugins to perform additional updates (Level 1 compatibility)
do_action('woonoow/product_updated', $product, $data, $request);
// Handle variations for variable products // Handle variations for variable products
if ($product->is_type('variable')) { if ($product->is_type('variable')) {
if (isset($data['attributes'])) { if (isset($data['attributes'])) {
@@ -565,6 +573,12 @@ class ProductsController {
$data['variations'] = self::get_product_variations($product); $data['variations'] = self::get_product_variations($product);
} }
// Expose meta data (Level 1 compatibility)
$data['meta'] = self::get_product_meta_data($product);
// Allow plugins to modify response (Level 1 compatibility)
$data = apply_filters('woonoow/product_api_data', $data, $product);
return $data; return $data;
} }
@@ -697,4 +711,89 @@ class ProductsController {
} }
} }
} }
/**
* Get product meta data for API exposure (Level 1 compatibility)
* Filters out internal meta unless explicitly allowed
*
* @param \WC_Product $product
* @return array
*/
private static function get_product_meta_data($product) {
$meta_data = [];
foreach ($product->get_meta_data() as $meta) {
$key = $meta->key;
$value = $meta->value;
// Skip internal WooCommerce meta (starts with _wc_)
if (strpos($key, '_wc_') === 0) {
continue;
}
// Skip WooNooW internal meta
if (strpos($key, '_woonoow_') === 0) {
continue;
}
// Public meta (no underscore) - always expose
if (strpos($key, '_') !== 0) {
$meta_data[$key] = $value;
continue;
}
// Private meta (starts with _) - check if allowed
$allowed_private = apply_filters('woonoow/product_allowed_private_meta', [
// Common custom fields
'_custom_field',
// Allow plugins to add their meta via filter
], $product);
if (in_array($key, $allowed_private, true)) {
$meta_data[$key] = $value;
}
}
return $meta_data;
}
/**
* Update product meta data from API (Level 1 compatibility)
*
* @param \WC_Product $product
* @param array $meta_updates
*/
private static function update_product_meta_data($product, $meta_updates) {
// Get allowed updatable meta keys
$allowed = apply_filters('woonoow/product_updatable_meta', [
// Common custom fields
'_custom_field',
// Allow plugins to add their meta via filter
], $product);
foreach ($meta_updates as $key => $value) {
// Skip internal WooCommerce meta
if (strpos($key, '_wc_') === 0) {
continue;
}
// Skip WooNooW internal meta
if (strpos($key, '_woonoow_') === 0) {
continue;
}
// Public meta (no underscore) - always allow
if (strpos($key, '_') !== 0) {
$product->update_meta_data($key, $value);
continue;
}
// Private meta - check if allowed
if (in_array($key, $allowed, true)) {
$product->update_meta_data($key, $value);
}
}
}
} }