PluginProbe
Booktics – Appointment Booking Calendar for Service Businesses / trunk
Booktics – Appointment Booking Calendar for Service Businesses vtrunk
1.0.29 1.0.28 1.0.27 1.0.26 1.0.25 1.0.24 1.0.23 1.0.22 1.0.21 1.0.20 1.0.19 trunk 1.0.0 1.0.1 1.0.10 1.0.11 1.0.12 1.0.13 1.0.14 1.0.15 1.0.16 1.0.17 1.0.18 1.0.2 1.0.3 All 31 releases
booktics / core / cart / cart-manager.php

cart-manager.php in Booktics – Appointment Booking Calendar for Service Businesses trunk, at core/cart/cart-manager.php

366 lines 10.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Booktics\Cart;
4
5 use Booktics\Abstracts\Booktics_Database;
6
7 /**
8 * Class Cart_Manager
9 * Manages shopping cart operations including adding items, updating quantities, and calculating totals
10 *
11 * @package Booktics\Cart
12 */
13 class Cart_Manager {
14 /**
15 * @var Booktics_Database The database instance
16 */
17 protected $db;
18
19 /**
20 * @var string The carts table name
21 */
22 protected $carts_table;
23
24 /**
25 * @var string The cart items table name
26 */
27 protected $cart_items_table;
28
29 /**
30 * Initialize CartManager with database connection
31 */
32 public function __construct() {
33 $this->db = new Booktics_Database();
34 $this->carts_table = booktics_get_table_name( 'booktics_carts' );
35 $this->cart_items_table = booktics_get_table_name( 'booktics_cart_items' );
36 }
37
38 /**
39 * Add item to the cart for guest users
40 *
41 * @param array $item
42 *
43 * @return array
44 */
45 public function add_to_cart_guest( array $item ): array {
46 return Cart_Session::add_item( $item );
47 }
48
49 /**
50 * Add item to the cart for both guest and registered users
51 *
52 * @param array $params Cart item parameters
53 *
54 * @return array Added cart item
55 */
56 public function add_to_cart( array $params ): array {
57 $user_id = get_current_user_id();
58 $params['uuid'] = wp_generate_uuid4();
59 if ( $user_id ) {
60 return $this->add_to_cart_user( $user_id, $params );
61 }
62
63 return $this->add_to_cart_guest( $params );
64 }
65
66 /**
67 * Add item to the cart for registered users
68 *
69 * @param int $user_id
70 * @param array $item
71 *
72 * @return array
73 */
74 public function add_to_cart_user( int $user_id, array $item ): array {
75 $carts = $this->db->select(
76 $this->carts_table, array(
77 'user_id' => $user_id,
78 )
79 );
80
81 if ( ! count( $carts ) ) {
82 $uuid = wp_generate_uuid4();
83 $this->db->insert_batch(
84 $this->carts_table, array(
85 array(
86 'uuid' => $uuid,
87 'user_id' => $user_id,
88 'total' => 0,
89 'created_at' => current_time( 'mysql' ),
90 'updated_at' => current_time( 'mysql' ),
91 ),
92 )
93 );
94
95 $carts = $this->db->select(
96 $this->carts_table, array(
97 'user_id' => $user_id,
98 )
99 );
100 }
101
102 // Check if the service_id already exists in the cart items for this cart
103 if ( count( $carts ) ) {
104 $cart = $carts[0];
105 $existing_items = $this->db->select(
106 $this->cart_items_table,
107 array(
108 'cart_id' => $cart->id,
109 'service_id' => $item['service_id'],
110 )
111 );
112 if ( ! empty( $existing_items ) ) {
113 // Delete existing items with the same service_id for this cart
114 foreach ( $existing_items as $existing_item ) {
115 $this->db->delete(
116 $this->cart_items_table,
117 array(
118 'id' => $existing_item->id,
119 )
120 );
121 }
122 }
123 }
124 $cart = count( $carts ) ? $carts[0] : array();
125 // Session cart items always carry subtotal/total keys, set to 0 when none was stored.
126 $subtotal = ! empty( $item['subtotal'] ) ? $item['subtotal'] : ( $item['price'] ?? 0 );
127 $cart_item_data = array(
128 'uuid' => $item['uuid'],
129 'cart_id' => $cart->id,
130 'team_member_id' => $item['team_member_id'],
131 'service_id' => $item['service_id'],
132 'date' => $item['date'],
133 'start_time' => $item['start_time'],
134 'end_time' => $item['end_time'],
135 'price' => $item['price'] ?? 0,
136 'subtotal' => $subtotal,
137 'total' => ! empty( $item['total'] ) ? $item['total'] : $subtotal,
138 'additional_duration_id' => booktics_sanitize_id_string( $item['additional_duration_id'] ?? '' ),
139 'created_at' => current_time( 'mysql' ),
140 'updated_at' => current_time( 'mysql' ),
141 );
142
143 $cart_item_data = apply_filters( 'booktics_pro_cart_db', $cart_item_data, $item );
144 $this->db->insert_batch( $this->cart_items_table, array( $cart_item_data ) );
145 if ( isset( $cart->id ) ) {
146 $this->update_cart_total( $cart->id );
147 }
148
149 if ( isset( $cart_item_data['extra_services'] ) ) {
150 $cart_item_data['extra_services'] = json_decode( $cart_item_data['extra_services'], true );
151 }
152
153 return $cart_item_data;
154 }
155
156 /**
157 * Synchronize session cart items to a database
158 *
159 * @param int $user_id
160 */
161 public function sync_session_to_db( int $user_id ): void {
162 $session_cart = Cart_Session::get();
163 if ( empty( $session_cart['items'] ) ) {
164 return;
165 }
166
167 foreach ( $session_cart['items'] as $item ) {
168 $this->add_to_cart_user( $user_id, $item );
169 }
170
171 Cart_Session::clear();
172 }
173
174 /**
175 * Update cart total based on items
176 *
177 * @param int $cart_id
178 */
179 protected function update_cart_total( int $cart_id ): void {
180 $items = $this->db->select(
181 $this->cart_items_table, array(
182 'cart_id' => $cart_id,
183 )
184 );
185
186 if ( empty( $items ) ) {
187 $this->db->delete( $this->carts_table, array( 'id' => $cart_id ) );
188
189 return;
190 }
191
192 $total = 0;
193 foreach ( $items as $item ) {
194 $total += (float) ( $item->total ?? $item->subtotal ?? 0 );
195 }
196
197 $this->db->update(
198 $this->carts_table,
199 array(
200 'total' => $total,
201 'updated_at' => current_time( 'mysql' ),
202 ),
203 array( 'id' => $cart_id )
204 );
205 }
206
207 /**
208 * Get a cart and its items for a user
209 *
210 * @return array
211 */
212 public function get_cart(): array {
213 $user_id = get_current_user_id();
214
215 if ( ! $user_id ) {
216 return Cart_Session::get();
217 }
218
219 $carts = $this->db->select(
220 $this->carts_table, array(
221 'user_id' => $user_id,
222 )
223 );
224 $cart = count( $carts ) ? $carts[0] : null;
225
226 if ( ! $cart ) {
227 return array( 'items' => array() );
228 }
229
230 $items = $this->db->select(
231 $this->cart_items_table, array(
232 'cart_id' => $cart->id,
233 )
234 );
235 foreach ( $items as $i => $item ) {
236 if ( isset( $item->extra_services ) ) {
237 $item->extra_services = json_decode( $item->extra_services, true );
238 }
239 }
240
241 return array_merge( (array) $cart, array( 'items' => $items ) );
242 }
243
244 /**
245 * Remove item from the cart
246 *
247 * @param string|null $id
248 */
249 public function remove_cart_item( $id = null ): void {
250 $user_id = get_current_user_id();
251 if ( ! $user_id ) {
252 if ( ! $id ) {
253 Cart_Session::clear();
254 } else {
255 Cart_Session::delete_item( $id );
256 }
257 }
258
259 $carts = $this->db->select(
260 $this->carts_table, array(
261 'user_id' => $user_id,
262 )
263 );
264
265 $cart = count( $carts ) ? $carts[0] : null;
266
267 if ( ! $cart ) {
268 return;
269 }
270
271 $conditions = array( 'cart_id' => $cart->id );
272 if ( ! empty( $id ) ) {
273 $conditions['uuid'] = $id;
274 }
275
276 $this->db->delete( $this->cart_items_table, $conditions );
277
278 $this->update_cart_total( $cart->id );
279 }
280
281 /**
282 * Edit item in the cart
283 *
284 * @param string $uuid
285 * @param array $new_data
286 *
287 * @return array Updated item data or empty array if item not found
288 */
289 public function edit_cart_item( string $uuid, array $new_data ): array {
290 if ( ! is_user_logged_in() ) {
291 $cart = Cart_Session::edit_item( $uuid, $new_data );
292 foreach ( $cart['items'] as $item ) {
293 if ( $item['uuid'] === $uuid ) {
294 return $item;
295 }
296 }
297
298 return array();
299 }
300
301 $user_id = get_current_user_id();
302 $carts = $this->db->select(
303 $this->carts_table, array(
304 'user_id' => $user_id,
305 )
306 );
307
308 $cart = count( $carts ) ? $carts[0] : null;
309
310 if ( ! $cart ) {
311 return array();
312 }
313
314 // Only allow known, editable columns from client input. This prevents
315 // arbitrary request-body keys from reaching the SQL UPDATE clause
316 // the list via the filter below.
317 $allowed_columns = apply_filters(
318 'booktics_cart_item_editable_columns',
319 array(
320 'date',
321 'start_time',
322 'end_time',
323 'service_id',
324 'team_member_id',
325 'price',
326 'subtotal',
327 'total',
328 'additional_duration_id',
329 'extra_services',
330 'group_booking',
331 'time_format',
332 'location_id',
333 )
334 );
335
336 $cart_item_data = array_intersect_key( $new_data, array_flip( $allowed_columns ) );
337 $cart_item_data['uuid'] = $uuid;
338 $cart_item_data['updated_at'] = current_time( 'mysql' );
339 $cart_item_data = apply_filters( 'booktics_pro_cart_db', $cart_item_data, $new_data );
340 $this->db->update(
341 $this->cart_items_table, $cart_item_data,
342 array(
343 'cart_id' => $cart->id,
344 'uuid' => $uuid,
345 )
346 );
347
348 $this->update_cart_total( $cart->id );
349
350 $updated_item = $this->db->select(
351 $this->cart_items_table, array(
352 'cart_id' => $cart->id,
353 'uuid' => $uuid,
354 )
355 );
356
357 foreach ( $updated_item as $i => $item ) {
358 if ( isset( $item->extra_services ) ) {
359 $item->extra_services = json_decode( $item->extra_services, true );
360 }
361 }
362
363 return count( $updated_item ) ? (array) $updated_item[0] : array();
364 }
365 }
366