PluginProbe
WCPOS – Point of Sale (POS) plugin for WooCommerce / trunk
WCPOS – Point of Sale (POS) plugin for WooCommerce vtrunk
1.10.13 1.10.14 1.10.12 1.10.11 1.10.10 1.10.9 1.10.8 untagged-3d9b7ccddc54df87c672 1.10.7 1.10.6 1.10.5 1.10.3 1.10.4 1.10.2 1.10.1 1.10.0 1.9.17 1.9.15 1.9.16 1.9.14 1.9.13 1.9.12 1.9.11 1.9.10 1.9.9 All 158 releases
woocommerce-pos / includes / Sync / Variable_Children.php

Variable_Children.php in WCPOS – Point of Sale (POS) plugin for WooCommerce trunk, at includes/Sync/Variable_Children.php

96 lines 4.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * WCPOS sync read surface.
4 *
5 * @package WCPOS\WooCommercePOS\Sync
6 */
7
8 namespace WCPOS\WooCommercePOS\Sync;
9
10 /**
11 * Narrows a variable product's advertised child list to what the POS may actually sell.
12 *
13 * # Why
14 *
15 * `product.variations[]` is the client's ONLY discovery list for a variable product: the row
16 * expansion, the POS variations popover, the "Showing N of M" footer and the idle prefetch walk all
17 * read it and nothing else. WooCommerce fills it from `WC_Product_Variable::get_children()`, which
18 * returns every child with `post_status IN ( 'publish', 'private' )` and knows nothing about POS
19 * visibility.
20 *
21 * Every other component that answers "which children exist" already narrows the set — the
22 * variations endpoint refuses to hydrate a hidden or disabled child, {@see Variable_Price_Range}
23 * computes its range from `get_visible_children()`, and the census counts only servable rows. The
24 * parent payload was the one that did not, so a store hiding or disabling a variation got four
25 * components giving three answers:
26 *
27 * - the footer renders "Showing 2 of 3" forever, with no way to reach the third;
28 * - the popover offers a row that cannot be added;
29 * - the row expansion asks for the id, receives a shortfall, prunes it, and asks again on the next
30 * expansion — permanent churn;
31 * - the prefetch walk spends one request per walk on an id the server will never serve.
32 *
33 * Filtering here fixes all four at once, because it fixes the fact they all read.
34 *
35 * # What is filtered
36 *
37 * Two rules, both WooCommerce's own:
38 *
39 * - **POS visibility** — `online_only` children, per {@see Pos_Visibility}. Ours, and additive: it
40 * changes which records are served, never what a field means.
41 * - **Disabled children** — `post_status !== 'publish'`. WooCommerce's Enabled checkbox on the
42 * variation metabox writes `post_status = private` when unchecked, and WooCommerce honours that
43 * everywhere a customer can reach (`get_visible_children()`, `get_available_variations()`). A
44 * cashier must not be offered a variation the owner switched off.
45 *
46 * # Ordering
47 *
48 * Registered as a record augmenter at priority 10 — AFTER the revision stamper at 9. The parent's
49 * `_rxdb_revision` therefore stays a hash of the bare wc/v3 bytes, which is what the write path
50 * recomputes from a bare re-read, so narrowing this list cannot perturb optimistic concurrency.
51 * Same ordering {@see Variable_Prices} already relies on when it blanks the parent's price fields.
52 *
53 * # Why round-tripping the narrowed list is safe
54 *
55 * A client's product update layers its resident stored payload, so the narrowed array does travel
56 * back to wc/v3. `variations` is `readonly => true` in WooCommerce's product schema (v2 and v3), so
57 * WooCommerce ignores it on write and no child can be removed by omission. Anything that changes
58 * this class must re-verify that property.
59 */
60 final class Variable_Children {
61 /**
62 * Narrow `variations[]` on a serialized variable product.
63 *
64 * Non-variable products and payloads without a usable list pass through untouched. The `type`
65 * gate comes first so a page of simple products never pays for a lookup.
66 *
67 * @param mixed $payload Serialized product record.
68 * @param null|mixed $object Product object, when the lane has one loaded.
69 * @param null|mixed $request Request context.
70 */
71 public static function augment_record( $payload, $object = null, $request = null ) {
72 if ( ! \is_array( $payload ) || 'variable' !== ( $payload['type'] ?? '' ) ) {
73 return $payload;
74 }
75 if ( ! isset( $payload['variations'] ) || ! \is_array( $payload['variations'] ) ) {
76 return $payload;
77 }
78
79 $children = array_values( array_filter( array_map( 'intval', $payload['variations'] ) ) );
80 if ( array() === $children ) {
81 return $payload;
82 }
83
84 $payload['variations'] = array_values(
85 array_filter(
86 ( new Pos_Visibility() )->filter_visible_children( $children ),
87 static function ( int $id ): bool {
88 return 'publish' === get_post_status( $id );
89 }
90 )
91 );
92
93 return $payload;
94 }
95 }
96