PluginProbe
WCPOS – Point of Sale (POS) plugin for WooCommerce / 1.10.21
WCPOS – Point of Sale (POS) plugin for WooCommerce v1.10.21
1.10.22 1.10.21 1.10.20 1.10.19 1.10.18 1.10.17 1.10.16 1.10.15 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 All 166 releases
woocommerce-pos / includes / API / V2 / Catalog_Proxy_Controller.php

Catalog_Proxy_Controller.php in WCPOS – Point of Sale (POS) plugin for WooCommerce 1.10.21, at includes/API/V2/Catalog_Proxy_Controller.php

143 lines 5.7 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\API\V2
6 */
7
8 namespace WCPOS\WooCommercePOS\API\V2;
9
10 use WCPOS\WooCommercePOS\API\V2\Proxy\Fast_Path_Behavior;
11 use WCPOS\WooCommercePOS\API\V2\Proxy\Null_Proxy_Behavior;
12 use WCPOS\WooCommercePOS\API\V2\Proxy\Proxy_Behavior;
13 use WCPOS\WooCommercePOS\Sync\Api;
14 use WCPOS\WooCommercePOS\Sync\Collections;
15 use WCPOS\WooCommercePOS\Sync\Endpoint_Permissions;
16 use WCPOS\WooCommercePOS\Sync\Store_Scope;
17 use WP_REST_Controller;
18 use WP_REST_Request;
19 use WP_REST_Server;
20
21 // phpcs:disable Squiz.Commenting, Generic.Commenting -- Ported lab documentation is preserved verbatim.
22
23 /**
24 * Catalog proxy endpoints — route replication READS through our `{API_NAMESPACE}`
25 * namespace instead of hitting raw `wc/v3` directly (guardrail G4: replication is
26 * wrapped in our namespace so we can customize the request/response and
27 * duck-punch for replication WITHOUT editing wc/v3 or the client).
28 *
29 * Routes forward to their wc/v3 counterparts via `rest_do_request`, preserving
30 * query params except where WCPOS adapts them (such as multi-term customer search
31 * and the WCPOS-extended customer sorts),
32 * plus the underlying status + pagination headers
33 * (so the client's existing array-shaped parsing and `length < per_page`
34 * pagination keep working), then exposes a single `woocommerce_pos_sync_proxy_response`
35 * filter seam — `($data, $resource, $request)` — so replication can shape the
36 * batch in one place. Today it is a faithful pass-through; the point is the
37 * controlled seam + decoupling the client from wc/v3 routes/versioning.
38 *
39 * Per-object serialization customization (`woocommerce_pos_sync_serialized_product`
40 * etc.) stays on the per-id paths — variations/resolve/changes — where the WC
41 * object is actually loaded; this list proxy keeps wc/v3's own serialization and
42 * filters the batch as a whole.
43 */
44 class Catalog_Proxy_Controller extends WP_REST_Controller {
45 use Endpoint_Permissions;
46
47 /** Register every proxied collection route from the collection registry. */
48 public function register_routes(): void {
49 foreach ( self::resources() as $route => $meta ) {
50 $wc_route = $meta[0];
51 $resource = $meta[1];
52 $behavior_class = $meta[2];
53 register_rest_route(
54 Api::ROUTE_NAMESPACE,
55 '/' . ltrim( $route, '/' ),
56 array(
57 'methods' => WP_REST_Server::READABLE,
58 // Closure carries the target so the handler never has to re-derive
59 // it from the matched route — one handler, six wired routes.
60 'callback' => function ( WP_REST_Request $request ) use ( $wc_route, $resource, $behavior_class ) {
61 return $this->proxy( $request, $wc_route, $resource, new $behavior_class() );
62 },
63 'permission_callback' => array( $this, 'permissions_check' ),
64 )
65 );
66 }
67 }
68
69 /**
70 * Forward the request to $wc_route via rest_do_request (adapting query
71 * params where needed and preserving wc/v3 status/headers), then expose the batch through the
72 * replication seam filter. wc/v3 errors are forwarded unchanged so the
73 * client sees the same failure it would have seen hitting wc/v3 directly.
74 */
75 public function proxy( WP_REST_Request $request, string $wc_route, string $resource, ?Proxy_Behavior $behavior = null ) {
76 $behavior = $behavior ?? self::behavior_for( $resource );
77 if ( $behavior instanceof Fast_Path_Behavior ) {
78 $fast = $behavior->fast_response( $request );
79 if ( null !== $fast ) {
80 return $fast;
81 }
82 }
83 $query_params = $behavior->forwarded_params( $request->get_query_params(), $request );
84 $inner = new WP_REST_Request( WP_REST_Server::READABLE, $wc_route );
85 unset( $query_params[ Store_Scope::PARAM ] );
86 $inner->set_query_params( $query_params );
87 // The till's store scope, republished as v1's `store_id` param (pro#425).
88 // This is the greedy catalog list pull, so it is what decides whether the
89 // products grid shows each store's own price or the web store's. Stamped
90 // after set_query_params so a scope is never wiped by the forwarded set.
91 Store_Scope::stamp( $inner );
92 $forward = static function () use ( $inner ) {
93 return Store_Scope::in_v2_lane( static fn() => rest_do_request( $inner ) );
94 };
95 $response = $behavior->around( $forward );
96 if ( $response->is_error() ) {
97 return $response;
98 }
99 $data = apply_filters( 'woocommerce_pos_sync_proxy_response', $response->get_data(), $resource, $request );
100 $response->set_data( $behavior->post_process( $data ) );
101
102 return $response;
103 }
104
105 /**
106 * our namespace route => [ wc/v3 route to forward to, resource slug for
107 * the seam ] — a PROJECTION of the registry's proxy capability (#421
108 * increment 4): the route/forward/slug vocabulary now has one home
109 * (Collections), and adding a proxied collection is one registry row.
110 *
111 * Orders proxy /orders here for the browser-window / targeted / query-total
112 * reads (the client assembles each document's uuid identity from the served
113 * payload); the checkpointed greedy order lane keeps its own cursor endpoint
114 * /orders/pull in Orders_Controller.
115 */
116 private static function resources(): array {
117 $resources = array();
118 foreach ( Collections::with( 'proxy' ) as $row ) {
119 $resources[ $row['proxy']['route'] ] = array(
120 $row['proxy']['wc_route'],
121 $row['proxy']['slug'],
122 $row['proxy']['behavior'],
123 );
124 }
125
126 return $resources;
127 }
128
129 /**
130 * Resolve behavior for direct proxy() callers that did not come through route registration.
131 *
132 * @param string $resource Proxy resource slug.
133 *
134 * @return Proxy_Behavior
135 */
136 private static function behavior_for( string $resource ): Proxy_Behavior {
137 $row = Collections::by_proxy_slug( $resource );
138 $class = $row['proxy']['behavior'] ?? Null_Proxy_Behavior::class;
139
140 return new $class();
141 }
142 }
143