PluginProbe
Booking Calendar / 11.9
Booking Calendar v11.9
11.9 11.8.4 11.8.3 11.8.2 11.8.1 11.8 11.7 11.6.1 11.6 11.5 11.4.3 11.4.2 11.4.1 11.4 11.3 11.2.1 11.2 11.1 11.0 10.15.7 10.15.6 10.1.3 10.10 10.10.1 10.10.2 All 205 releases
booking / includes / page-appointment-services / catalog / class-wpbc-appointment-services-catalog-provider.php

class-wpbc-appointment-services-catalog-provider.php in Booking Calendar 11.9, at includes/page-appointment-services/catalog/class-wpbc-appointment-services-catalog-provider.php

224 lines 8.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Appointment Services provider for the shared catalog contract.
4 *
5 * @package Booking Calendar
6 * @since 11.6.0
7 */
8
9 if ( ! defined( 'ABSPATH' ) ) {
10 exit;
11 }
12
13 /**
14 * Coordinate validated shared requests with the Service-owned repository and DTO.
15 *
16 * This adapter exists so shared catalog mechanics never need to understand
17 * Service storage, editions, ownership, availability, or action permissions.
18 */
19 final class WPBC_Appointment_Services_Catalog_Provider implements WPBC_UI_Catalog_Provider {
20
21 /** @var object Service-domain data provider. */
22 private $repository;
23
24 /** @var WPBC_Appointment_Service_Catalog_DTO Service DTO mapper. */
25 private $dto_mapper;
26
27 /** @var WPBC_Appointment_Services_Catalog_Request Service-only filters. */
28 private $service_request;
29
30 /**
31 * Construct a provider with injectable dependencies for endpoint and runtime tests.
32 *
33 * @param object|null $repository Service-domain provider.
34 * @param WPBC_Appointment_Service_Catalog_DTO|null $dto_mapper DTO mapper.
35 * @param WPBC_Appointment_Services_Catalog_Request|null $service_request Validated Service filters.
36 */
37 public function __construct( $repository = null, $dto_mapper = null, $service_request = null ) {
38 $this->repository = is_object( $repository ) ? $repository : wpbc_appointment_services_get_data_provider();
39 $this->dto_mapper = $dto_mapper instanceof WPBC_Appointment_Service_Catalog_DTO ? $dto_mapper : new WPBC_Appointment_Service_Catalog_DTO();
40 $this->service_request = $service_request instanceof WPBC_Appointment_Services_Catalog_Request ? $service_request : WPBC_Appointment_Services_Catalog_Request::create();
41 }
42
43 /**
44 * Return the catalog identifier registered by the Service configuration.
45 *
46 * @return string Stable catalog identifier.
47 */
48 public function get_catalog_id() {
49 return 'appointment_services_catalog';
50 }
51
52 /**
53 * Return one normalized Service page without rendering row HTML.
54 *
55 * @param WPBC_UI_Catalog_Request $request Validated shared request.
56 *
57 * @return WPBC_UI_Catalog_Response|WP_Error Normalized response or safe error.
58 */
59 public function get_response( $request ) {
60 if ( ! $request instanceof WPBC_UI_Catalog_Request || $this->get_catalog_id() !== $request->get_catalog_id() ) {
61 return new WP_Error( 'wpbc_appointment_services_invalid_request', __( 'The Services request is invalid.', 'booking' ) );
62 }
63 if ( ! is_object( $this->repository ) || ! method_exists( $this->repository, 'list_items' ) || ! wpbc_appointment_services_storage_is_ready() ) {
64 return wpbc_appointment_services_storage_error();
65 }
66
67 $request_values = $request->to_array();
68 $query = array(
69 'search' => $request_values['search'],
70 'status' => $this->service_request->get( 'status', 'all' ),
71 'resource_id' => $this->service_request->get( 'resource_id', 0 ),
72 'sort_by' => $request_values['sort_by'],
73 'sort_order' => $request_values['sort_order'],
74 'limit' => $request_values['items_per_page'],
75 'offset' => ( $request_values['page_number'] - 1 ) * $request_values['items_per_page'],
76 );
77 $raw_counts = method_exists( $this->repository, 'count_items' ) ? $this->repository->count_items( $query ) : null;
78 if ( is_wp_error( $raw_counts ) ) {
79 return $raw_counts;
80 }
81 if ( null === $raw_counts ) {
82 return $this->get_fallback_response( $request, $request_values, $query );
83 }
84 $counts = wpbc_appointment_services_normalize_status_counts( $raw_counts );
85 $status = $this->service_request->get( 'status', 'all' );
86 $total_items = 'all' === $status ? $counts['all'] : $counts[ $status ];
87 $total_pages = max( 1, (int) ceil( $total_items / $request_values['items_per_page'] ) );
88 if ( $request_values['page_number'] > $total_pages ) {
89 $request_values['page_number'] = $total_pages;
90 $request = WPBC_UI_Catalog_Request::create( $request->get_configuration(), $request_values );
91 if ( is_wp_error( $request ) ) {
92 return $request;
93 }
94 $query['offset'] = ( $total_pages - 1 ) * $request_values['items_per_page'];
95 }
96
97 $services = $this->repository->list_items( $query );
98 if ( is_wp_error( $services ) ) {
99 return $services;
100 }
101 $items = $this->dto_mapper->create_collection( $services );
102 if ( is_wp_error( $items ) ) {
103 return $items;
104 }
105 $provider_directory = wpbc_appointment_services_get_provider_directory();
106
107 return WPBC_UI_Catalog_Response::create(
108 $this->get_catalog_id(),
109 $request,
110 $items,
111 array(
112 'pagination' => array( 'total_items' => $total_items ),
113 'filters' => array(
114 'status' => $status,
115 'resource_id' => $this->service_request->get( 'resource_id', 0 ),
116 'status_counts' => $counts,
117 'providers' => array_values( $provider_directory ),
118 'provider_count' => count( $provider_directory ),
119 'storage_ready' => true,
120 ),
121 'capabilities' => $this->get_capabilities(),
122 )
123 );
124 }
125
126 /**
127 * Paginate a legacy replacement provider that exposes only list_items().
128 *
129 * This bounded compatibility path preserves the released Service provider
130 * filter. Native storage uses count_items() and never enters this branch.
131 *
132 * @param WPBC_UI_Catalog_Request $request Validated shared request.
133 * @param array<string,mixed> $request_values Normalized shared values.
134 * @param array<string,mixed> $query Service query values.
135 *
136 * @return WPBC_UI_Catalog_Response|WP_Error Normalized response or provider error.
137 */
138 private function get_fallback_response( $request, $request_values, $query ) {
139 $status = $this->service_request->get( 'status', 'all' );
140 $query['status'] = 'all';
141 $query['limit'] = 500;
142 $query['offset'] = 0;
143
144 $service_rows = $this->repository->list_items( $query );
145 if ( is_wp_error( $service_rows ) ) {
146 return $service_rows;
147 }
148
149 $all_services = array_map( 'wpbc_appointment_services_normalize_item', (array) $service_rows );
150 $counts = array( 'all' => count( $all_services ), 'active' => 0, 'inactive' => 0, 'archived' => 0 );
151 foreach ( $all_services as $service ) {
152 if ( isset( $counts[ $service['status'] ] ) ) {
153 ++$counts[ $service['status'] ];
154 }
155 }
156
157 $filtered_services = 'all' === $status
158 ? $all_services
159 : array_values(
160 array_filter(
161 $all_services,
162 static function ( $service ) use ( $status ) {
163 return $status === $service['status'];
164 }
165 )
166 );
167 $total_items = count( $filtered_services );
168 $total_pages = max( 1, (int) ceil( $total_items / $request_values['items_per_page'] ) );
169 $page_number = min( $request_values['page_number'], $total_pages );
170 if ( $page_number !== $request_values['page_number'] ) {
171 $request_values['page_number'] = $page_number;
172 $request = WPBC_UI_Catalog_Request::create( $request->get_configuration(), $request_values );
173 if ( is_wp_error( $request ) ) {
174 return $request;
175 }
176 }
177 $page_services = array_slice( $filtered_services, ( $page_number - 1 ) * $request_values['items_per_page'], $request_values['items_per_page'] );
178 $items = $this->dto_mapper->create_collection( $page_services );
179 if ( is_wp_error( $items ) ) {
180 return $items;
181 }
182 $provider_directory = wpbc_appointment_services_get_provider_directory();
183
184 return WPBC_UI_Catalog_Response::create(
185 $this->get_catalog_id(),
186 $request,
187 $items,
188 array(
189 'pagination' => array( 'total_items' => $total_items ),
190 'filters' => array(
191 'status' => $status,
192 'resource_id' => $this->service_request->get( 'resource_id', 0 ),
193 'status_counts' => $counts,
194 'providers' => array_values( $provider_directory ),
195 'provider_count' => count( $provider_directory ),
196 'storage_ready' => true,
197 ),
198 'capabilities' => $this->get_capabilities(),
199 )
200 );
201 }
202
203 /**
204 * Return the current user's Service action capabilities.
205 *
206 * Mutation endpoints repeat the same capability check immediately before a
207 * write. These values control presentation only and never authorize a write.
208 *
209 * @return array<string,bool> Browser-safe Service action capabilities.
210 */
211 private function get_capabilities() {
212 $can_manage_services = current_user_can( wpbc_appointment_services_get_manage_capability() );
213
214 return array(
215 'create' => $can_manage_services,
216 'edit' => $can_manage_services,
217 'duplicate' => $can_manage_services,
218 'archive' => $can_manage_services,
219 'bulk_edit' => $can_manage_services,
220 'bulk_delete' => false,
221 );
222 }
223 }
224