PluginProbe
Booktics – Appointment Booking Calendar for Service Businesses / 1.0.10
Booktics – Appointment Booking Calendar for Service Businesses v1.0.10
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 / base / abstracts / user-model.php

user-model.php in Booktics – Appointment Booking Calendar for Service Businesses 1.0.10, at base/abstracts/user-model.php

617 lines 15.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * User Model Abstract for managing all wp_users and wp_usermeta
4 *
5 * @package Booktics/Abstracts
6 */
7
8 namespace Booktics\Abstracts;
9
10 use JsonSerializable;
11 use WP_User_Query;
12
13 abstract class User_Model implements JsonSerializable {
14
15 /**
16 * Store specific user role
17 *
18 * @var string
19 */
20 protected $role = 'administrator';
21
22 /**
23 * Store specific meta prefix
24 *
25 * @var string
26 */
27 protected $meta_prefix = '';
28
29 /**
30 * Store user id
31 *
32 * @var integer
33 */
34 protected $id;
35
36 /**
37 * Store user data
38 *
39 * @var array
40 */
41 protected $fillable = [
42 'id' => '',
43 'first_name' => '',
44 'last_name' => '',
45 'user_email' => '',
46 'image' => '',
47 'user_login' => '',
48 'phone' => '',
49 'status' => 0,
50 'user_registered' => '',
51 ];
52
53 /**
54 * Store Filter conditions
55 *
56 * @var array
57 */
58 protected $filters = [];
59
60 /**
61 * Store error
62 *
63 * @var \WP_Error
64 */
65 protected $error;
66
67 /**
68 * User constructor
69 *
70 * @param $user
71 *
72 * return void
73 */
74 public function __construct( $user = 0 ) {
75 require_once( ABSPATH . 'wp-admin/includes/user.php' );
76
77 if ( $user instanceof self ) {
78 $this->set_id( $user->get_id() );
79 } elseif ( ! empty( $user->ID ) ) {
80 $this->set_id( $user->ID );
81 } elseif ( is_numeric( $user ) && $user > 0 ) {
82 $this->set_id( $user );
83 }
84 }
85
86 /**
87 * Get user id
88 *
89 * @return integer
90 */
91 public function get_id(): int {
92 return $this->id;
93 }
94
95 /**
96 * Set user id
97 *
98 * @param int $id
99 *
100 * @return void
101 */
102 public function set_id( int $id ) {
103 $this->id = $id;
104 }
105
106 /**
107 * Get user data
108 *
109 * @param string $name
110 *
111 * @return string|null
112 */
113 public function __get( string $name ) {
114 if ( $name === 'id' ) {
115 return $this->id;
116 }
117 if ( ! isset( $this->fillable[ $name ] ) ) {
118 return null;
119 }
120
121 $possible_function_name = 'get_' . booktics_snake_case( $name ) . '_attribute';
122 $data = $this->get_prop( $name, $this->fillable[ $name ] );
123 if ( method_exists( $this, $possible_function_name ) ) {
124 return $this->$possible_function_name( $this );
125 }
126
127 return $data;
128 }
129
130 /**
131 * Get either user data or default data
132 *
133 * @param string $prop
134 * @param string|array $default
135 *
136 * @return string|array
137 */
138 private function get_prop( $prop = '', $default = '' ) {
139 return $this->get_metadata( $prop ) ?? $default;
140 }
141
142 /**
143 * Get user metadata
144 *
145 * @param string $prop
146 *
147 * @return mixed
148 */
149 private function get_metadata( string $prop = '' ) {
150 $meta_key = $this->meta_prefix . $prop;
151
152 return get_user_meta( $this->id, $meta_key, true );
153 }
154
155 /**
156 * Set user values
157 *
158 * @param string $name
159 * @param string $value
160 *
161 * @return void
162 */
163 public function __set( string $name, string $value ) {
164 if ( strpos( $name, $this->meta_prefix ) !== false ) {
165 $name = str_replace( $this->meta_prefix, '', $name );
166 }
167
168 $this->fillable[ $name ] = $value;
169 }
170
171 /**
172 * Save or update user
173 *
174 * @param array $args
175 *
176 * @return mixed|string
177 */
178 public function save( array $args = [] ) {
179 if ( isset( $args['email'] ) ) {
180 $args['user_email'] = $args['email'];
181 unset( $args['email'] );
182 }
183
184 $user_id = $args['ID'] ?? '';
185
186 $user_data = [
187 'ID' => $user_id,
188 'role' => $this->role,
189 'password' => wp_generate_password(),
190 ];
191
192 // Add required user fields
193 foreach ( $args as $key => $value ) {
194 if ( ! isset( $this->fillable[ $key ] ) && ! in_array( $key, [
195 'email',
196 'user_email'
197 ] ) ) {
198 continue;
199 }
200 $user_data[ $key ] = $value;
201 }
202
203 $user_id = wp_insert_user( $user_data );
204 if ( is_wp_error( $user_id ) ) {
205 return $user_id;
206 }
207
208 $user = new \WP_User( $user_id );
209 $user->add_role( $this->role );
210 $this->save_metadata( $args );
211
212 if ( $user_id ) {
213 wp_update_user( $user_data );
214 } else {
215 $user_id = wp_insert_user( $user_data );
216 }
217
218 $user = get_userdata( $user_id );
219 $user->add_role( $this->role );
220 if ( ! is_wp_error( $user_id ) ) {
221 $this->set_id( $user_id );
222 $this->save_metadata( $user_data );
223 $this->retrieve_password();
224 }
225
226 return $user_id;
227 }
228
229 /**
230 * Update specific field in user or meta data
231 *
232 * @param array $data
233 *
234 * @return bool
235 */
236 public function update( array $data = [] ): bool {
237 if ( ! $this->id ) {
238 $this->error = new \WP_Error( 'no_id', __( 'User ID is required to update.', 'booktics' ) );
239
240 return false;
241 }
242
243 $user_data = [ 'ID' => $this->id ];
244 $meta_data = [];
245
246 foreach ( $data as $key => $value ) {
247 if ( in_array( $key, [ 'schedule', 'custom', 'holiday' ] ) ) {
248 continue;
249 }
250 if ( isset( $this->fillable[ $key ] ) && ! strpos( $key, $this->meta_prefix ) ) {
251 if ( in_array( $key, [ 'user_email', 'user_login' ] ) ) {
252 $user_data[ $key ] = $value;
253 }
254 $meta_data[ $key ] = $value;
255 }
256 }
257 $result = wp_update_user( $user_data );
258 if ( is_wp_error( $result ) ) {
259 $this->error = $result;
260
261 return false;
262 }
263
264 $this->save_metadata( $meta_data );
265
266 return true;
267 }
268
269 /**
270 * Store user meta data
271 *
272 * @param array $data
273 *
274 * @return void
275 */
276 public function save_metadata( array $data = [] ) {
277 foreach ( $data as $key => $value ) {
278 if ( ! isset( $this->fillable[ $key ] ) ) {
279 continue;
280 }
281 $meta_key = $this->meta_prefix . $key;
282
283 update_user_meta( $this->id, $meta_key, $value );
284 }
285 }
286
287 /**
288 * Send password reset email
289 *
290 * @return void
291 */
292 public function retrieve_password() {
293 if ( 0 === intval( $this->status ) ) {
294 retrieve_password( $this->email );
295 }
296 }
297
298 /**
299 * Delete user
300 *
301 * @return boolean
302 */
303 public function delete(): bool {
304 $user = get_userdata( $this->id );
305
306 if ( ! $user ) {
307 return false;
308 }
309
310 if ( count( $user->roles ) == 1 && in_array( $this->role, $user->roles ) ) {
311 return wp_delete_user( $this->id );
312 }
313 $user->remove_role( $this->role );
314
315 return true;
316 }
317
318 /**
319 * Append user filter condition
320 *
321 * @param string $field
322 * @param $value
323 * @param string $compare
324 *
325 * @return $this
326 */
327 public function where( string $field, $value, string $compare = '=' ): self {
328 $this->filters[] = [
329 'field' => $field,
330 'value' => $value,
331 'compare' => $compare,
332 ];
333
334 return $this;
335 }
336
337 /**
338 * Add OR conditions in where
339 *
340 * @param string $field
341 * @param $value
342 * @param string $compare
343 *
344 * @return $this
345 */
346 public function or_where( string $field, $value, string $compare = '=' ): self {
347 $this->filters[] = [
348 'field' => $field,
349 'value' => $value,
350 'compare' => $compare,
351 'relation' => 'OR',
352 ];
353
354 return $this;
355 }
356
357 /**
358 * Conditionally apply query filters
359 *
360 * @param mixed $value
361 * @param callable $callback
362 * @param callable|null $default
363 *
364 * @return $this
365 */
366 public function when( $value, callable $callback, callable $default = null ): self {
367 if ( $value ) {
368 $callback( $this, $value );
369 } elseif ( $default ) {
370 $default( $this, $value );
371 }
372
373 return $this;
374 }
375
376 /**
377 * Get all users paginated data
378 *
379 * @param array $args
380 *
381 * @return array
382 */
383 public function all( array $args = [] ): array {
384 $defaults = [
385 'role' => $this->role,
386 'number' => 20,
387 'paged' => 1,
388 'orderby' => 'ID',
389 'order' => 'DESC',
390 ];
391
392 $args = wp_parse_args( $args, $defaults );
393
394 if ( ! empty( $this->filters ) ) {
395 $meta_query = [];
396 $relation = 'AND';
397
398 foreach ( $this->filters as $filter ) {
399 if ( in_array( $filter['field'], [
400 'user_email',
401 'user_login'
402 ] ) ) {
403 $args['search'] = '*' . esc_attr( $filter['value'] ) . '*';
404 $args['search_columns'] = [ $filter['field'] ];
405 } else {
406 $meta_query[] = [
407 'key' => $this->meta_prefix . $filter['field'],
408 'value' => $filter['value'],
409 'compare' => $filter['compare'],
410 ];
411 }
412
413 if ( isset( $filter['relation'] ) && $filter['relation'] === 'OR' ) {
414 $relation = 'OR';
415 }
416 }
417 if ( ! empty( $meta_query ) ) {
418 $args['meta_query'] = [
419 'relation' => $relation,
420 ...$meta_query
421 ];
422 }
423 }
424
425 $users = new WP_User_Query( $args );
426 $items = [];
427 foreach ( $users->get_results() as $result ) {
428 $cloneObj = clone $this;
429 $userMeta = get_user_meta( $result->ID );
430
431 $items[] = $cloneObj->hydrate( $result->ID, array_merge(
432 (array) $result->data,
433 $userMeta
434 ) )->to_array();
435 }
436
437 $this->filters = [];
438
439 return [
440 'total' => $users->get_total(),
441 'per_page' => $args['number'],
442 'current_page' => $args['paged'],
443 'last_page' => ceil( $users->get_total() / $args['number'] ),
444 'items' => $items,
445 ];
446 }
447
448 /**
449 * Find user by id
450 *
451 * @param $id
452 *
453 * @return $this
454 */
455 public function find( $id ) {
456 $user = get_user_by( 'ID', $id );
457
458 if ( ! $user ) {
459 return null;
460 }
461
462 $meta = array_map( function ( $value ) {
463 return is_array( $value ) && count( $value ) === 1 ? $value[0] : $value;
464 }, get_user_meta( $user->ID ) );
465
466 $user_data = (array) $user->data;
467
468 return $this->hydrate( $id, array_merge( $user_data, $meta ) );
469 }
470
471 /**
472 * Hydrate each result with this class
473 *
474 * @param $id
475 * @param $result
476 *
477 * @return $this
478 */
479 public function hydrate( $id, $result ): self {
480 $this->set_id( $id );
481 if ( ! is_array( $result ) ) {
482 return $this;
483 }
484
485 foreach ( $result as $key => $value ) {
486 $key = str_replace( $this->meta_prefix, '', $key );
487 if ( ! isset( $this->fillable[ $key ] ) && ! in_array( $key, [
488 'user_email',
489 'email'
490 ] ) ) {
491 continue;
492 }
493 if ( is_array( $value ) ) {
494 $value = implode( ',', $value );
495 }
496
497 if ( $key === 'user_registered' ) {
498 $this->fillable['user_registered'] = ! empty( $result['user_registered'] ) ?
499 gmdate( 'j F, Y', strtotime( $result['user_registered'] ) ) : '';
500 continue;
501 }
502
503 $this->$key = empty( $value ) ? $this->fillable[ $key ] : $value;
504 }
505
506 return $this;
507 }
508
509 /**
510 * Generate username from email
511 *
512 * @param string $email
513 *
514 * @return string
515 */
516 public static function generate_username( string $email ) {
517 $username = strtok( $email, '@' );
518
519 if ( username_exists( $username ) ) {
520 $username = $username . wp_rand( 10, 100 );
521 }
522
523 return $username;
524 }
525
526 /**
527 * Convert User model object to array
528 *
529 * @return array
530 */
531 public function to_array(): array {
532 $data = [];
533
534 foreach ( $this->fillable as $key => $default ) {
535 if ( $key === 'user_email' ) {
536 $data['email'] = $this->fillable[$key];
537 continue;
538 }
539
540 $value = $this->$key ?? $this->fillable[ $key ];
541
542 // Unserialize if necessary
543 if ( is_string( $value ) && $this->is_serialized( $value ) ) {
544 $value = maybe_unserialize( $value );
545 }
546
547 // Normalize value to default type
548 if ( gettype( $value ) !== gettype( $default ) ) {
549 if ( gettype( $default ) === 'array' && is_string( $value ) && $this->is_serialized( $value ) ) {
550 $value = maybe_unserialize( $value );
551 } elseif ( gettype( $default ) === 'array' ) {
552 $value = $default;
553 }
554 }
555
556 // Cast specific keys to objects
557 if ( $key == 'schedule' ) {
558 $value = (object) $value;
559 }
560
561 // Cast specific keys to boolean
562 if ( in_array( $key, [
563 'enable_custom_schedule',
564 'enable_custom_holiday',
565 'enable_custom_availability',
566 ] ) ) {
567 $value = filter_var( $value, FILTER_VALIDATE_BOOLEAN );
568 }
569
570 if ( $key == 'user_registered' ) {
571 $value = $default;
572 }
573
574 $data[ $key ] = $value ?? $default;
575 }
576
577 $data['ID'] = $this->id;
578
579 return $data;
580 }
581
582 /**
583 * Check if a string is serialized data
584 *
585 * @param mixed $data Data to check for serialization
586 *
587 * @return bool True if data is serialized, false otherwise
588 */
589 function is_serialized( $data ) {
590 return is_string( $data ) && @unserialize( $data ) !== false;
591 }
592
593 /**
594 * Specify data which should be serialized to JSON.
595 *
596 * @return array
597 */
598 public function jsonSerialize(): array {
599 return $this->to_array();
600 }
601
602 /**
603 * Get the first user matching the current filters.
604 *
605 * @param array $args
606 * @return self|null
607 */
608 public function first(array $args = []): ?self {
609 $args['number'] = 1;
610 $args['paged'] = 1;
611 $result = $this->all($args);
612 if (!empty($result['items'][0])) {
613 return $this->hydrate($result['items'][0]['ID'], $result['items'][0]);
614 }
615 return null;
616 }
617 }