'', 'first_name' => '', 'last_name' => '', 'user_email' => '', 'image' => '', 'user_login' => '', 'phone' => '', 'status' => 0, 'user_registered' => '', ]; /** * Store Filter conditions * * @var array */ protected $filters = []; /** * Store error * * @var \WP_Error */ protected $error; /** * User constructor * * @param $user * * return void */ public function __construct( $user = 0 ) { require_once( ABSPATH . 'wp-admin/includes/user.php' ); if ( $user instanceof self ) { $this->set_id( $user->get_id() ); } elseif ( ! empty( $user->ID ) ) { $this->set_id( $user->ID ); } elseif ( is_numeric( $user ) && $user > 0 ) { $this->set_id( $user ); } } /** * Get user id * * @return integer */ public function get_id(): int { return $this->id; } /** * Set user id * * @param int $id * * @return void */ public function set_id( int $id ) { $this->id = $id; } /** * Get user data * * @param string $name * * @return string|null */ public function __get( string $name ) { if ( $name === 'id' ) { return $this->id; } if ( ! isset( $this->fillable[ $name ] ) ) { return null; } $possible_function_name = 'get_' . booktics_snake_case( $name ) . '_attribute'; $data = $this->get_prop( $name, $this->fillable[ $name ] ); if ( method_exists( $this, $possible_function_name ) ) { return $this->$possible_function_name( $this ); } return $data; } /** * Get either user data or default data * * @param string $prop * @param string|array $default * * @return string|array */ private function get_prop( $prop = '', $default = '' ) { return $this->get_metadata( $prop ) ?? $default; } /** * Get user metadata * * @param string $prop * * @return mixed */ private function get_metadata( string $prop = '' ) { $meta_key = $this->meta_prefix . $prop; return get_user_meta( $this->id, $meta_key, true ); } /** * Set user values * * @param string $name * @param string $value * * @return void */ public function __set( string $name, string $value ) { if ( strpos( $name, $this->meta_prefix ) !== false ) { $name = str_replace( $this->meta_prefix, '', $name ); } $this->fillable[ $name ] = $value; } /** * Save or update user * * @param array $args * * @return mixed|string */ public function save( array $args = [] ) { if ( isset( $args['email'] ) ) { $args['user_email'] = $args['email']; unset( $args['email'] ); } $user_id = $args['ID'] ?? ''; $user_data = [ 'ID' => $user_id, 'role' => $this->role, 'password' => wp_generate_password(), ]; // Add required user fields foreach ( $args as $key => $value ) { if ( ! isset( $this->fillable[ $key ] ) && ! in_array( $key, [ 'email', 'user_email' ] ) ) { continue; } $user_data[ $key ] = $value; } $user_id = wp_insert_user( $user_data ); if ( is_wp_error( $user_id ) ) { return $user_id; } $user = new \WP_User( $user_id ); $user->add_role( $this->role ); $this->save_metadata( $args ); if ( $user_id ) { wp_update_user( $user_data ); } else { $user_id = wp_insert_user( $user_data ); } $user = get_userdata( $user_id ); $user->add_role( $this->role ); if ( ! is_wp_error( $user_id ) ) { $this->set_id( $user_id ); $this->save_metadata( $user_data ); $this->retrieve_password(); } return $user_id; } /** * Update specific field in user or meta data * * @param array $data * * @return bool */ public function update( array $data = [] ): bool { if ( ! $this->id ) { $this->error = new \WP_Error( 'no_id', __( 'User ID is required to update.', 'booktics' ) ); return false; } $user_data = [ 'ID' => $this->id ]; $meta_data = []; foreach ( $data as $key => $value ) { if ( in_array( $key, [ 'schedule', 'custom', 'holiday' ] ) ) { continue; } if ( isset( $this->fillable[ $key ] ) && ! strpos( $key, $this->meta_prefix ) ) { if ( in_array( $key, [ 'user_email', 'user_login' ] ) ) { $user_data[ $key ] = $value; } $meta_data[ $key ] = $value; } } $result = wp_update_user( $user_data ); if ( is_wp_error( $result ) ) { $this->error = $result; return false; } $this->save_metadata( $meta_data ); return true; } /** * Store user meta data * * @param array $data * * @return void */ public function save_metadata( array $data = [] ) { foreach ( $data as $key => $value ) { if ( ! isset( $this->fillable[ $key ] ) ) { continue; } $meta_key = $this->meta_prefix . $key; update_user_meta( $this->id, $meta_key, $value ); } } /** * Send password reset email * * @return void */ public function retrieve_password() { if ( 0 === intval( $this->status ) ) { retrieve_password( $this->email ); } } /** * Delete user * * @return boolean */ public function delete(): bool { $user = get_userdata( $this->id ); if ( ! $user ) { return false; } if ( count( $user->roles ) == 1 && in_array( $this->role, $user->roles ) ) { return wp_delete_user( $this->id ); } $user->remove_role( $this->role ); return true; } /** * Append user filter condition * * @param string $field * @param $value * @param string $compare * * @return $this */ public function where( string $field, $value, string $compare = '=' ): self { $this->filters[] = [ 'field' => $field, 'value' => $value, 'compare' => $compare, ]; return $this; } /** * Add OR conditions in where * * @param string $field * @param $value * @param string $compare * * @return $this */ public function or_where( string $field, $value, string $compare = '=' ): self { $this->filters[] = [ 'field' => $field, 'value' => $value, 'compare' => $compare, 'relation' => 'OR', ]; return $this; } /** * Conditionally apply query filters * * @param mixed $value * @param callable $callback * @param callable|null $default * * @return $this */ public function when( $value, callable $callback, callable $default = null ): self { if ( $value ) { $callback( $this, $value ); } elseif ( $default ) { $default( $this, $value ); } return $this; } /** * Get all users paginated data * * @param array $args * * @return array */ public function all( array $args = [] ): array { $defaults = [ 'role' => $this->role, 'number' => 20, 'paged' => 1, 'orderby' => 'ID', 'order' => 'DESC', ]; $args = wp_parse_args( $args, $defaults ); if ( ! empty( $this->filters ) ) { $meta_query = []; $relation = 'AND'; foreach ( $this->filters as $filter ) { if ( in_array( $filter['field'], [ 'user_email', 'user_login' ] ) ) { $args['search'] = '*' . esc_attr( $filter['value'] ) . '*'; $args['search_columns'] = [ $filter['field'] ]; } else { $meta_query[] = [ 'key' => $this->meta_prefix . $filter['field'], 'value' => $filter['value'], 'compare' => $filter['compare'], ]; } if ( isset( $filter['relation'] ) && $filter['relation'] === 'OR' ) { $relation = 'OR'; } } if ( ! empty( $meta_query ) ) { $args['meta_query'] = [ 'relation' => $relation, ...$meta_query ]; } } $users = new WP_User_Query( $args ); $items = []; foreach ( $users->get_results() as $result ) { $cloneObj = clone $this; $userMeta = get_user_meta( $result->ID ); $items[] = $cloneObj->hydrate( $result->ID, array_merge( (array) $result->data, $userMeta ) )->to_array(); } $this->filters = []; return [ 'total' => $users->get_total(), 'per_page' => $args['number'], 'current_page' => $args['paged'], 'last_page' => ceil( $users->get_total() / $args['number'] ), 'items' => $items, ]; } /** * Find user by id * * @param $id * * @return $this */ public function find( $id ) { $user = get_user_by( 'ID', $id ); if ( ! $user ) { return null; } $meta = array_map( function ( $value ) { return is_array( $value ) && count( $value ) === 1 ? $value[0] : $value; }, get_user_meta( $user->ID ) ); $user_data = (array) $user->data; return $this->hydrate( $id, array_merge( $user_data, $meta ) ); } /** * Hydrate each result with this class * * @param $id * @param $result * * @return $this */ public function hydrate( $id, $result ): self { $this->set_id( $id ); if ( ! is_array( $result ) ) { return $this; } foreach ( $result as $key => $value ) { $key = str_replace( $this->meta_prefix, '', $key ); if ( ! isset( $this->fillable[ $key ] ) && ! in_array( $key, [ 'user_email', 'email' ] ) ) { continue; } if ( is_array( $value ) ) { $value = implode( ',', $value ); } if ( $key === 'user_registered' ) { $this->fillable['user_registered'] = ! empty( $result['user_registered'] ) ? gmdate( 'j F, Y', strtotime( $result['user_registered'] ) ) : ''; continue; } $this->$key = empty( $value ) ? $this->fillable[ $key ] : $value; } return $this; } /** * Generate username from email * * @param string $email * * @return string */ public static function generate_username( string $email ) { $username = strtok( $email, '@' ); if ( username_exists( $username ) ) { $username = $username . wp_rand( 10, 100 ); } return $username; } /** * Convert User model object to array * * @return array */ public function to_array(): array { $data = []; foreach ( $this->fillable as $key => $default ) { if ( $key === 'user_email' ) { $data['email'] = $this->fillable[$key]; continue; } $value = $this->$key ?? $this->fillable[ $key ]; // Unserialize if necessary if ( is_string( $value ) && $this->is_serialized( $value ) ) { $value = maybe_unserialize( $value ); } // Normalize value to default type if ( gettype( $value ) !== gettype( $default ) ) { if ( gettype( $default ) === 'array' && is_string( $value ) && $this->is_serialized( $value ) ) { $value = maybe_unserialize( $value ); } elseif ( gettype( $default ) === 'array' ) { $value = $default; } } // Cast specific keys to objects if ( $key == 'schedule' ) { $value = (object) $value; } // Cast specific keys to boolean if ( in_array( $key, [ 'enable_custom_schedule', 'enable_custom_holiday', 'enable_custom_availability', ] ) ) { $value = filter_var( $value, FILTER_VALIDATE_BOOLEAN ); } if ( $key == 'user_registered' ) { $value = $default; } $data[ $key ] = $value ?? $default; } $data['ID'] = $this->id; return $data; } /** * Check if a string is serialized data * * @param mixed $data Data to check for serialization * * @return bool True if data is serialized, false otherwise */ function is_serialized( $data ) { return is_string( $data ) && @unserialize( $data ) !== false; } /** * Specify data which should be serialized to JSON. * * @return array */ public function jsonSerialize(): array { return $this->to_array(); } /** * Get the first user matching the current filters. * * @param array $args * @return self|null */ public function first(array $args = []): ?self { $args['number'] = 1; $args['paged'] = 1; $result = $this->all($args); if (!empty($result['items'][0])) { return $this->hydrate($result['items'][0]['ID'], $result['items'][0]); } return null; } }