| @@ -29,11 +29,15 @@ | ||
| 29 | 29 | * 1. ADR 0003 — values come from the FILTERED WC REST representation, never a raw |
| 30 | 30 | * projection. `prepare_object_for_response` runs WooCommerce's own |
| 31 | 31 | * `woocommerce_rest_prepare_product_object` filter; `response_to_data` resolves |
| 32 | 32 | * embedded links exactly as a real request would. |
| 33 | - * 2. Variations are serialized through the SAME products controller as products — | |
| 34 | - * `wc_get_product()` hands back a `WC_Product_Variation` and the controller | |
| 35 | - * handles it, so the two lanes cannot drift apart. | |
| 33 | + * 2. Variations are serialized through WooCommerce's own VARIATIONS controller | |
| 34 | + * (`WC_REST_Product_Variations_Controller`), products through the products | |
| 35 | + * controller — this class picks per object type so a caller cannot pick | |
| 36 | + * wrong. Hydrating a variation through the PRODUCTS controller is the #1710 | |
| 37 | + * incident (`images[]` instead of `image`: blank POS thumbnails, the | |
| 38 | + * parent's image on every order line); post-#1710 the payload species is | |
| 39 | + * the variations controller's everywhere. | |
| 36 | 40 | */ |
| 37 | 41 | final class Product_Serializer { |
| 38 | 42 | /** |
| 39 | 43 | * The WooCommerce products controller, created on first use. |
| @@ -86,10 +90,12 @@ | ||
| 86 | 90 | } |
| 87 | 91 | |
| 88 | 92 | $request = $request instanceof WP_REST_Request ? $request : $this->default_request(); |
| 89 | 93 | // Every lane that hydrates a product — changes, resolve, targeted |
| 90 | - // variations, the write ack — builds a bare `GET /` and hands it here, so | |
| 91 | - // this is the ONE place that has to carry the till's store scope into | |
| 94 | + // variations, the write ack — hands this method a request it may write to | |
| 95 | + // (a bare `GET /`, or a CLONE of the live request; never the dispatched | |
| 96 | + // request itself, because the stamps below mutate it), so this is the ONE | |
| 97 | + // place that has to carry the till's store scope into | |
| 92 | 98 | // `woocommerce_rest_prepare_product_object`. Without it the assembly line |
| 93 | 99 | // serializes the global price and the till redisplays it moments after the |
| 94 | 100 | // cashier changed the store's (pro#425). Stamping is idempotent and never |
| 95 | 101 | // overrides a scope the caller set deliberately. |
| @@ -98,10 +104,11 @@ | ||
| 98 | 104 | // dispatch, so the lane marker is the only signal a response filter has. |
| 99 | 105 | $is_variation = $object instanceof WC_Product_Variation; |
| 100 | 106 | if ( $is_variation ) { |
| 101 | 107 | // `prepare_links()` reads `$request['product_id']` to build the nested |
| 102 | - // `products/<parent>/variations/<id>` route. The lanes that hydrate here build a | |
| 103 | - // bare `GET /`, so without this the links would claim parent 0. | |
| 108 | + // `products/<parent>/variations/<id>` route. No request handed here carries | |
| 109 | + // it (bare, or cloned from the FLAT route), so without this the links | |
| 110 | + // would claim parent 0. | |
| 104 | 111 | $request->set_param( 'product_id', $object->get_parent_id() ); |
| 105 | 112 | } |
| 106 | 113 | // Store scope is carried by the request + lane marker above, both controller-agnostic, |
| 107 | 114 | // and Pro registers `bake_store_prices` on the product AND variation prepare filters — |