/** * Descomplicar® Crescimento Digital * https://descomplicar.pt */ * @link https://descomplicar.pt * @since 1.0.0 */ namespace Care_API\Models; if ( ! defined( 'ABSPATH' ) ) { exit; } /** * Class Clinic * * Model for handling clinic data and operations * * @since 1.0.0 */ class Clinic { /** * Database table name * * @var string */ private static $table_name = 'kc_clinics'; /** * Clinic ID * * @var int */ public $id; /** * Clinic properties * * @var array */ private $data = array(); /** * Required fields for clinic creation * * @var array */ private static $required_fields = array( 'name', 'email', 'telephone_no', 'address', 'city', 'state', 'country', 'postal_code' ); /** * Constructor * * @param int|array $clinic_id_or_data Clinic ID or data array * @since 1.0.0 */ public function __construct( $clinic_id_or_data = null ) { if ( is_numeric( $clinic_id_or_data ) ) { $this->id = (int) $clinic_id_or_data; $this->load_data(); } elseif ( is_array( $clinic_id_or_data ) ) { $this->data = $clinic_id_or_data; $this->id = isset( $this->data['id'] ) ? (int) $this->data['id'] : null; } } /** * Load clinic data from database * * @return bool True on success, false on failure * @since 1.0.0 */ private function load_data() { if ( ! $this->id ) { return false; } global $wpdb; $table = $wpdb->prefix . self::$table_name; $clinic_data = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$table} WHERE id = %d", $this->id ), ARRAY_A ); if ( $clinic_data ) { $this->data = $clinic_data; return true; } return false; } /** * Create a new clinic * * @param array $clinic_data Clinic data * @return int|WP_Error Clinic ID on success, WP_Error on failure * @since 1.0.0 */ public static function create( $clinic_data ) { // Validate required fields $validation = self::validate_clinic_data( $clinic_data ); if ( is_wp_error( $validation ) ) { return $validation; } global $wpdb; $table = $wpdb->prefix . self::$table_name; // Prepare data for insertion $insert_data = array( 'name' => sanitize_text_field( $clinic_data['name'] ), 'email' => sanitize_email( $clinic_data['email'] ), 'telephone_no' => sanitize_text_field( $clinic_data['telephone_no'] ), 'specialties' => isset( $clinic_data['specialties'] ) ? wp_json_encode( $clinic_data['specialties'] ) : '', 'address' => sanitize_textarea_field( $clinic_data['address'] ), 'city' => sanitize_text_field( $clinic_data['city'] ), 'state' => sanitize_text_field( $clinic_data['state'] ), 'country' => sanitize_text_field( $clinic_data['country'] ), 'postal_code' => sanitize_text_field( $clinic_data['postal_code'] ), 'status' => isset( $clinic_data['status'] ) ? (int) $clinic_data['status'] : 1, 'clinic_admin_id' => isset( $clinic_data['clinic_admin_id'] ) ? (int) $clinic_data['clinic_admin_id'] : null, 'clinic_logo' => isset( $clinic_data['clinic_logo'] ) ? (int) $clinic_data['clinic_logo'] : null, 'profile_image' => isset( $clinic_data['profile_image'] ) ? (int) $clinic_data['profile_image'] : null, 'extra' => isset( $clinic_data['extra'] ) ? wp_json_encode( $clinic_data['extra'] ) : '', 'created_at' => current_time( 'mysql' ) ); $insert_data = array_map( array( self::class, 'prepare_for_db' ), $insert_data ); $result = $wpdb->insert( $table, $insert_data ); if ( $result === false ) { return new \WP_Error( 'clinic_creation_failed', 'Failed to create clinic: ' . $wpdb->last_error, array( 'status' => 500 ) ); } return $wpdb->insert_id; } /** * Update clinic data * * @param int $clinic_id Clinic ID * @param array $clinic_data Updated clinic data * @return bool|WP_Error True on success, WP_Error on failure * @since 1.0.0 */ public static function update( $clinic_id, $clinic_data ) { if ( ! self::exists( $clinic_id ) ) { return new \WP_Error( 'clinic_not_found', 'Clinic not found', array( 'status' => 404 ) ); } global $wpdb; $table = $wpdb->prefix . self::$table_name; // Prepare update data $update_data = array(); $allowed_fields = array( 'name', 'email', 'telephone_no', 'specialties', 'address', 'city', 'state', 'country', 'postal_code', 'status', 'clinic_admin_id', 'clinic_logo', 'profile_image', 'extra' ); foreach ( $allowed_fields as $field ) { if ( isset( $clinic_data[ $field ] ) ) { $value = $clinic_data[ $field ]; switch ( $field ) { case 'email': $update_data[ $field ] = sanitize_email( $value ); break; case 'specialties': case 'extra': $update_data[ $field ] = is_array( $value ) ? wp_json_encode( $value ) : $value; break; case 'status': case 'clinic_admin_id': case 'clinic_logo': case 'profile_image': $update_data[ $field ] = (int) $value; break; case 'address': $update_data[ $field ] = sanitize_textarea_field( $value ); break; default: $update_data[ $field ] = sanitize_text_field( $value ); } } } if ( empty( $update_data ) ) { return new \WP_Error( 'no_data_to_update', 'No valid data provided for update', array( 'status' => 400 ) ); } $update_data = array_map( array( self::class, 'prepare_for_db' ), $update_data ); $result = $wpdb->update( $table, $update_data, array( 'id' => $clinic_id ), null, array( '%d' ) ); if ( $result === false ) { return new \WP_Error( 'clinic_update_failed', 'Failed to update clinic: ' . $wpdb->last_error, array( 'status' => 500 ) ); } return true; } /** * Delete a clinic * * @param int $clinic_id Clinic ID * @return bool|WP_Error True on success, WP_Error on failure * @since 1.0.0 */ public static function delete( $clinic_id ) { if ( ! self::exists( $clinic_id ) ) { return new \WP_Error( 'clinic_not_found', 'Clinic not found', array( 'status' => 404 ) ); } // Check for dependencies if ( self::has_dependencies( $clinic_id ) ) { return new \WP_Error( 'clinic_has_dependencies', 'Cannot delete clinic with associated appointments or patients', array( 'status' => 409 ) ); } global $wpdb; $table = $wpdb->prefix . self::$table_name; $result = $wpdb->delete( $table, array( 'id' => $clinic_id ), array( '%d' ) ); if ( $result === false ) { return new \WP_Error( 'clinic_deletion_failed', 'Failed to delete clinic: ' . $wpdb->last_error, array( 'status' => 500 ) ); } return true; } /** * Get clinic by ID * * @param int $clinic_id Clinic ID * @return array|null Clinic data or null if not found * @since 1.0.0 */ public static function get_by_id( $clinic_id ) { global $wpdb; $table = $wpdb->prefix . self::$table_name; $clinic = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$table} WHERE id = %d", $clinic_id ), ARRAY_A ); if ( $clinic ) { return self::format_clinic_data( $clinic ); } return null; } /** * Get all clinics with optional filtering * * @param array $args Query arguments * @return array Array of clinic data * @since 1.0.0 */ public static function get_all( $args = array() ) { global $wpdb; $table = $wpdb->prefix . self::$table_name; $defaults = array( 'status' => null, 'search' => '', 'limit' => 50, 'offset' => 0, 'orderby' => 'name', 'order' => 'ASC' ); $args = wp_parse_args( $args, $defaults ); $where_clauses = array( '1=1' ); $where_values = array(); // Status filter if ( ! is_null( $args['status'] ) ) { $where_clauses[] = 'status = %d'; $where_values[] = $args['status']; } // Search filter if ( ! empty( $args['search'] ) ) { $where_clauses[] = '(name LIKE %s OR email LIKE %s OR city LIKE %s)'; $search_term = '%' . $wpdb->esc_like( $args['search'] ) . '%'; $where_values[] = $search_term; $where_values[] = $search_term; $where_values[] = $search_term; } $where_sql = implode( ' AND ', $where_clauses ); // Build query $query = "SELECT * FROM {$table} WHERE {$where_sql}"; $query .= sprintf( ' ORDER BY %s %s', sanitize_sql_orderby( $args['orderby'] ), sanitize_sql_orderby( $args['order'] ) ); $query .= $wpdb->prepare( ' LIMIT %d OFFSET %d', $args['limit'], $args['offset'] ); if ( ! empty( $where_values ) ) { $query = $wpdb->prepare( $query, $where_values ); } $clinics = $wpdb->get_results( $query, ARRAY_A ); return array_map( array( self::class, 'format_clinic_data' ), $clinics ); } /** * Get total count of clinics * * @param array $args Filter arguments * @return int Total count * @since 1.0.0 */ public static function get_count( $args = array() ) { global $wpdb; $table = $wpdb->prefix . self::$table_name; $where_clauses = array( '1=1' ); $where_values = array(); if ( ! is_null( $args['status'] ?? null ) ) { $where_clauses[] = 'status = %d'; $where_values[] = $args['status']; } if ( ! empty( $args['search'] ?? '' ) ) { $where_clauses[] = '(name LIKE %s OR email LIKE %s OR city LIKE %s)'; $search_term = '%' . $wpdb->esc_like( $args['search'] ) . '%'; $where_values[] = $search_term; $where_values[] = $search_term; $where_values[] = $search_term; } $where_sql = implode( ' AND ', $where_clauses ); $query = "SELECT COUNT(*) FROM {$table} WHERE {$where_sql}"; if ( ! empty( $where_values ) ) { $query = $wpdb->prepare( $query, $where_values ); } return (int) $wpdb->get_var( $query ); } /** * Check if clinic exists * * @param int $clinic_id Clinic ID * @return bool True if exists, false otherwise * @since 1.0.0 */ public static function exists( $clinic_id ) { global $wpdb; $table = $wpdb->prefix . self::$table_name; $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$table} WHERE id = %d", $clinic_id ) ); return (int) $count > 0; } /** * Check if clinic has dependencies (appointments, patients, etc.) * * @param int $clinic_id Clinic ID * @return bool True if has dependencies, false otherwise * @since 1.0.0 */ private static function has_dependencies( $clinic_id ) { global $wpdb; // Check appointments $appointments_count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->prefix}kc_appointments WHERE clinic_id = %d", $clinic_id ) ); if ( (int) $appointments_count > 0 ) { return true; } // Check patient mappings $patients_count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->prefix}kc_patient_clinic_mappings WHERE clinic_id = %d", $clinic_id ) ); if ( (int) $patients_count > 0 ) { return true; } // Check doctor mappings $doctors_count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->prefix}kc_doctor_clinic_mappings WHERE clinic_id = %d", $clinic_id ) ); return (int) $doctors_count > 0; } /** * Validate clinic data * * @param array $clinic_data Clinic data to validate * @return bool|WP_Error True if valid, WP_Error if invalid * @since 1.0.0 */ private static function validate_clinic_data( $clinic_data ) { $errors = array(); // Check required fields foreach ( self::$required_fields as $field ) { if ( empty( $clinic_data[ $field ] ) ) { $errors[] = "Field '{$field}' is required"; } } // Validate email format if ( ! empty( $clinic_data['email'] ) && ! is_email( $clinic_data['email'] ) ) { $errors[] = 'Invalid email format'; } // Check for duplicate email if ( ! empty( $clinic_data['email'] ) ) { global $wpdb; $table = $wpdb->prefix . self::$table_name; $existing_clinic = $wpdb->get_var( $wpdb->prepare( "SELECT id FROM {$table} WHERE email = %s", $clinic_data['email'] ) ); if ( $existing_clinic ) { $errors[] = 'A clinic with this email already exists'; } } if ( ! empty( $errors ) ) { return new \WP_Error( 'clinic_validation_failed', 'Clinic validation failed', array( 'status' => 400, 'errors' => $errors ) ); } return true; } /** * Format clinic data for API response * * @param array $clinic_data Raw clinic data * @return array Formatted clinic data * @since 1.0.0 */ private static function format_clinic_data( $clinic_data ) { if ( ! $clinic_data ) { return null; } // Parse JSON fields if ( ! empty( $clinic_data['specialties'] ) ) { $clinic_data['specialties'] = json_decode( $clinic_data['specialties'], true ) ?: array(); } if ( ! empty( $clinic_data['extra'] ) ) { $clinic_data['extra'] = json_decode( $clinic_data['extra'], true ) ?: array(); } // Cast numeric fields $numeric_fields = array( 'id', 'status', 'clinic_admin_id', 'clinic_logo', 'profile_image' ); foreach ( $numeric_fields as $field ) { if ( isset( $clinic_data[ $field ] ) ) { $clinic_data[ $field ] = (int) $clinic_data[ $field ]; } } return $clinic_data; } /** * Prepare data for database insertion/update * * @param mixed $value Data value * @return mixed Prepared value * @since 1.0.0 */ private static function prepare_for_db( $value ) { if ( is_null( $value ) ) { return null; } if ( is_array( $value ) ) { return wp_json_encode( $value ); } return $value; } /** * Get clinic statistics * * @param int $clinic_id Clinic ID * @return array Clinic statistics * @since 1.0.0 */ public static function get_statistics( $clinic_id ) { global $wpdb; $stats = array( 'total_appointments' => 0, 'total_patients' => 0, 'total_doctors' => 0, 'revenue_this_month' => 0, 'appointments_today' => 0 ); // Total appointments $stats['total_appointments'] = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->prefix}kc_appointments WHERE clinic_id = %d", $clinic_id ) ); // Total patients $stats['total_patients'] = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->prefix}kc_patient_clinic_mappings WHERE clinic_id = %d", $clinic_id ) ); // Total doctors $stats['total_doctors'] = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->prefix}kc_doctor_clinic_mappings WHERE clinic_id = %d", $clinic_id ) ); // Revenue this month $stats['revenue_this_month'] = (float) $wpdb->get_var( $wpdb->prepare( "SELECT SUM(CAST(actual_amount AS DECIMAL(10,2))) FROM {$wpdb->prefix}kc_bills WHERE clinic_id = %d AND MONTH(created_at) = MONTH(CURRENT_DATE()) AND YEAR(created_at) = YEAR(CURRENT_DATE()) AND payment_status = 'paid'", $clinic_id ) ) ?: 0; // Appointments today $stats['appointments_today'] = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->prefix}kc_appointments WHERE clinic_id = %d AND appointment_start_date = CURDATE()", $clinic_id ) ); return $stats; } }