PluginProbe
Booktics – Appointment Booking Calendar for Service Businesses / 1.0.2
Booktics – Appointment Booking Calendar for Service Businesses v1.0.2
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.2, at base/abstracts/user-model.php

601 lines 14.6 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
314 $user->remove_role( $this->role );
315
316 return true;
317 }
318
319 /**
320 * Append user filter condition
321 *
322 * @param string $field
323 * @param $value
324 * @param string $compare
325 *
326 * @return $this
327 */
328 public function where( string $field, $value, string $compare = '=' ): self {
329 $this->filters[] = [
330 'field' => $field,
331 'value' => $value,
332 'compare' => $compare,
333 ];
334
335 return $this;
336 }
337
338 /**
339 * Add OR conditions in where
340 *
341 * @param string $field
342 * @param $value
343 * @param string $compare
344 *
345 * @return $this
346 */
347 public function or_where( string $field, $value, string $compare = '=' ): self {
348 $this->filters[] = [
349 'field' => $field,
350 'value' => $value,
351 'compare' => $compare,
352 'relation' => 'OR',
353 ];
354
355 return $this;
356 }
357
358 /**
359 * Conditionally apply query filters
360 *
361 * @param mixed $value
362 * @param callable $callback
363 * @param callable|null $default
364 *
365 * @return $this
366 */
367 public function when( $value, callable $callback, callable $default = null ): self {
368 if ( $value ) {
369 $callback( $this, $value );
370 } elseif ( $default ) {
371 $default( $this, $value );
372 }
373
374 return $this;
375 }
376
377 /**
378 * Get all users paginated data
379 *
380 * @param array $args
381 *
382 * @return array
383 */
384 public function all( array $args = [] ): array {
385 $defaults = [
386 'role' => $this->role,
387 'number' => 20,
388 'paged' => 1,
389 'orderby' => 'ID',
390 'order' => 'DESC',
391 ];
392
393 $args = wp_parse_args( $args, $defaults );
394
395 if ( ! empty( $this->filters ) ) {
396 $meta_query = [];
397 $relation = 'AND';
398
399 foreach ( $this->filters as $filter ) {
400 if ( in_array( $filter['field'], [
401 'user_email',
402 'user_login'
403 ] ) ) {
404 $args['search'] = '*' . esc_attr( $filter['value'] ) . '*';
405 $args['search_columns'] = [ $filter['field'] ];
406 } else {
407 $meta_query[] = [
408 'key' => $this->meta_prefix . $filter['field'],
409 'value' => $filter['value'],
410 'compare' => $filter['compare'],
411 ];
412 }
413
414 if ( isset( $filter['relation'] ) && $filter['relation'] === 'OR' ) {
415 $relation = 'OR';
416 }
417 }
418 if ( ! empty( $meta_query ) ) {
419 $args['meta_query'] = [
420 'relation' => $relation,
421 ...$meta_query
422 ];
423 }
424 }
425
426 $users = new WP_User_Query( $args );
427 $items = [];
428 foreach ( $users->get_results() as $result ) {
429 $cloneObj = clone $this;
430 $userMeta = get_user_meta( $result->ID );
431
432 $items[] = $cloneObj->hydrate( $result->ID, array_merge(
433 (array) $result->data,
434 $userMeta
435 ) )->to_array();
436 }
437
438 $this->filters = [];
439
440 return [
441 'total' => $users->get_total(),
442 'per_page' => $args['number'],
443 'current_page' => $args['paged'],
444 'last_page' => ceil( $users->get_total() / $args['number'] ),
445 'items' => $items,
446 ];
447 }
448
449 /**
450 * Find user by id
451 *
452 * @param $id
453 *
454 * @return $this
455 */
456 public function find( $id ) {
457 $user = get_user_by( 'ID', $id );
458
459 if ( ! $user ) {
460 return null;
461 }
462
463 $meta = array_map( function ( $value ) {
464 return is_array( $value ) && count( $value ) === 1 ? $value[0] : $value;
465 }, get_user_meta( $user->ID ) );
466
467 $user_data = (array) $user->data;
468
469 return $this->hydrate( $id, array_merge( $user_data, $meta ) );
470 }
471
472 /**
473 * Hydrate each result with this class
474 *
475 * @param $id
476 * @param $result
477 *
478 * @return $this
479 */
480 public function hydrate( $id, $result ): self {
481 $this->set_id( $id );
482 if ( ! is_array( $result ) ) {
483 return $this;
484 }
485
486 foreach ( $result as $key => $value ) {
487 $key = str_replace( $this->meta_prefix, '', $key );
488 if ( ! isset( $this->fillable[ $key ] ) && ! in_array( $key, [
489 'user_email',
490 'email'
491 ] ) ) {
492 continue;
493 }
494 if ( is_array( $value ) ) {
495 $value = implode( ',', $value );
496 }
497
498 if ( $key === 'user_registered' ) {
499 $this->fillable['user_registered'] = ! empty( $result['user_registered'] ) ?
500 gmdate( 'j F, Y', strtotime( $result['user_registered'] ) ) : '';
501 continue;
502 }
503
504 $this->$key = empty( $value ) ? $this->fillable[ $key ] : $value;
505 }
506
507 return $this;
508 }
509
510 /**
511 * Generate username from email
512 *
513 * @param string $email
514 *
515 * @return string
516 */
517 public static function generate_username( string $email ) {
518 $username = strtok( $email, '@' );
519
520 if ( username_exists( $username ) ) {
521 $username = $username . wp_rand( 10, 100 );
522 }
523
524 return $username;
525 }
526
527 /**
528 * Convert User model object to array
529 *
530 * @return array
531 */
532 public function to_array(): array {
533 $data = [];
534
535 foreach ( $this->fillable as $key => $default ) {
536 if ( $key === 'user_email' ) {
537 $data['email'] = $this->fillable[$key];
538 continue;
539 }
540
541 $value = $this->$key ?? $this->fillable[ $key ];
542
543 // Unserialize if necessary
544 if ( is_string( $value ) && $this->is_serialized( $value ) ) {
545 $value = maybe_unserialize( $value );
546 }
547
548 // Normalize value to default type
549 if ( gettype( $value ) !== gettype( $default ) ) {
550 if ( gettype( $default ) === 'array' && is_string( $value ) && $this->is_serialized( $value ) ) {
551 $value = maybe_unserialize( $value );
552 } elseif ( gettype( $default ) === 'array' ) {
553 $value = $default;
554 }
555 }
556
557 // Cast specific keys to objects
558 if ( $key == 'schedule' ) {
559 $value = (object) $value;
560 }
561
562 // Cast specific keys to boolean
563 if ( in_array( $key, [
564 'enable_custom_schedule',
565 'enable_custom_holiday'
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 }