From cab66fc65f504a41f59db8354500cd298b3b1ebd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacek=20Fefli=C5=84ski?= Date: Sun, 8 Feb 2026 22:11:59 +0100 Subject: [PATCH] Przygotwanie do REST --- includes/admin/pages/page-activity-form.php | 67 +------ includes/admin/pages/routes.php | 182 ++++++++++++++++++++ includes/core/crud-activity.php | 95 ++++++++++ moje-statystyki.php | 6 + 4 files changed, 287 insertions(+), 63 deletions(-) create mode 100644 includes/admin/pages/routes.php create mode 100644 includes/core/crud-activity.php diff --git a/includes/admin/pages/page-activity-form.php b/includes/admin/pages/page-activity-form.php index 006464b..df9c971 100644 --- a/includes/admin/pages/page-activity-form.php +++ b/includes/admin/pages/page-activity-form.php @@ -57,76 +57,17 @@ function mystat_handle_activity_form_submission() { return; } - $table_activities = $wpdb->prefix . 'mystat_activities'; - - // Przygotowanie danych (zamiana przecinka na kropkę w dystansie) - $distance = isset( $_POST['distance'] ) ? floatval( str_replace( ',', '.', $_POST['distance'] ) ) : 0; - - // Funkcja pomocnicza do zamiany pustych wartości na NULL, aby poprawnie zapisać je w bazie - $null_if_empty = function( $value ) { - return $value !== '' ? $value : null; - }; - - $data = array( - 'category_id' => intval( $_POST['category_id'] ), - 'date' => sanitize_text_field( $_POST['date'] ), - 'title' => sanitize_text_field( $_POST['title'] ), - 'distance' => $distance, - 'duration' => sanitize_text_field( $_POST['duration'] ), - 'calories' => intval( $_POST['calories'] ), - 'comment' => sanitize_textarea_field( $_POST['comment'] ), - 'strava_url' => $null_if_empty( esc_url_raw( $_POST['strava_url'] ) ), - 'avg_heart_rate' => $null_if_empty( intval( $_POST['avg_heart_rate'] ) ), - 'max_heart_rate' => $null_if_empty( intval( $_POST['max_heart_rate'] ) ), - 'avg_speed' => $null_if_empty( floatval( str_replace( ',', '.', $_POST['avg_speed'] ) ) ), - 'max_speed' => $null_if_empty( floatval( str_replace( ',', '.', $_POST['max_speed'] ) ) ), - 'avg_cadence' => $null_if_empty( intval( $_POST['avg_cadence'] ) ), - 'max_cadence' => $null_if_empty( intval( $_POST['max_cadence'] ) ), - 'total_elevation_gain' => $null_if_empty( intval( $_POST['total_elevation_gain'] ) ), - 'total_elevation_loss' => $null_if_empty( intval( $_POST['total_elevation_loss'] ) ), - 'min_altitude' => $null_if_empty( intval( $_POST['min_altitude'] ) ), - 'max_altitude' => $null_if_empty( intval( $_POST['max_altitude'] ) ), - 'equipment_id' => $null_if_empty( intval( $_POST['equipment_id'] ) ), - 'gpx_url' => $null_if_empty( esc_url_raw( $_POST['gpx_url'] ) ), - 'event_type_id' => $null_if_empty( intval( $_POST['event_type_id'] ) ), - ); - - // Format danych dla $wpdb->insert - $format = array( - '%d', - '%s', - '%s', - '%f', - '%s', - '%d', - '%s', // Pola podstawowe - '%s', - '%d', - '%d', - '%f', - '%f', - '%d', - '%d', // Tętno, prędkość, kadencja - '%d', - '%d', - '%d', - '%d', - '%d', - '%s', - '%d', // Wysokość, sprzęt, linki, typ wydarzenia - ); + // Use the refactored function to save data. + // We can pass $_POST directly as the function will sanitize it. + $result = mystat_save_activity_data( $_POST, $activity_id ); if ( $activity_id > 0 ) { - // UPDATE - $result = $wpdb->update( $table_activities, $data, array( 'id' => $activity_id ), $format, array( '%d' ) ); $message = 'Trening zaktualizowany pomyślnie!'; } else { - // INSERT - $result = $wpdb->insert( $table_activities, $data, $format ); $message = 'Trening dodany pomyślnie!'; } - if ( false !== $result ) { + if ( $result ) { echo '

' . esc_html( $message ) . '

'; } else { echo '

Wystąpił błąd podczas zapisu do bazy.

'; diff --git a/includes/admin/pages/routes.php b/includes/admin/pages/routes.php new file mode 100644 index 0000000..b752225 --- /dev/null +++ b/includes/admin/pages/routes.php @@ -0,0 +1,182 @@ + WP_REST_Server::READABLE, + 'callback' => 'mystat_get_activities_api', + 'permission_callback' => 'mystat_api_permissions_check', + 'args' => array( + 'page' => array( + 'validate_callback' => 'is_numeric', + ), + 'per_page' => array( + 'validate_callback' => 'is_numeric', + ), + ), + ), + array( + 'methods' => WP_REST_Server::CREATABLE, + 'callback' => 'mystat_create_activity_api', + 'permission_callback' => 'mystat_api_permissions_check', + ), + ) + ); + + // Route for a single activity + register_rest_route( + $namespace, + '/activities/(?P[\d]+)', + array( + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => 'mystat_get_activity_api', + 'permission_callback' => 'mystat_api_permissions_check', + ), + array( + 'methods' => WP_REST_Server::EDITABLE, + 'callback' => 'mystat_update_activity_api', + 'permission_callback' => 'mystat_api_permissions_check', + ), + array( + 'methods' => WP_REST_Server::DELETABLE, + 'callback' => 'mystat_delete_activity_api', + 'permission_callback' => 'mystat_api_permissions_check', + ), + ) + ); +} + +/** + * Permission check for API endpoints. + * + * @return bool + */ +function mystat_api_permissions_check() { + return current_user_can( 'manage_options' ); +} + +/** + * Get a collection of activities. + * + * @param WP_REST_Request $request Full details about the request. + * @return WP_REST_Response|WP_Error + */ +function mystat_get_activities_api( WP_REST_Request $request ) { + global $wpdb; + $table_activities = $wpdb->prefix . 'mystat_activities'; + + $per_page = $request->get_param( 'per_page' ) ? (int) $request->get_param( 'per_page' ) : 20; + $page = $request->get_param( 'page' ) ? (int) $request->get_param( 'page' ) : 1; + $offset = ( $page - 1 ) * $per_page; + + $sql = $wpdb->prepare( + "SELECT a.*, c.name as category_name, et.name as event_type_name, eq.name as equipment_name + FROM $table_activities a + LEFT JOIN {$wpdb->prefix}mystat_categories c ON a.category_id = c.id + LEFT JOIN {$wpdb->prefix}mystat_event_types et ON a.event_type_id = et.id + LEFT JOIN {$wpdb->prefix}mystat_equipment eq ON a.equipment_id = eq.id + ORDER BY a.date DESC, a.id DESC + LIMIT %d OFFSET %d", + $per_page, + $offset + ); + + $results = $wpdb->get_results( $sql ); + + return new WP_REST_Response( $results, 200 ); +} + +/** + * Get a single activity. + * + * @param WP_REST_Request $request Full details about the request. + * @return WP_REST_Response|WP_Error + */ +function mystat_get_activity_api( WP_REST_Request $request ) { + global $wpdb; + $id = (int) $request['id']; + + $sql = $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}mystat_activities WHERE id = %d", $id ); + $activity = $wpdb->get_row( $sql ); + + if ( ! $activity ) { + return new WP_Error( 'not_found', 'Activity not found', array( 'status' => 404 ) ); + } + + return new WP_REST_Response( $activity, 200 ); +} + +/** + * Create a new activity. + * + * @param WP_REST_Request $request Full details about the request. + * @return WP_REST_Response|WP_Error + */ +function mystat_create_activity_api( WP_REST_Request $request ) { + $params = $request->get_json_params(); + $activity_id = mystat_save_activity_data( $params ); + + if ( ! $activity_id ) { + return new WP_Error( 'cant-create', 'Error creating activity', array( 'status' => 500 ) ); + } + + $response = mystat_get_activity_api( new WP_REST_Request( 'GET', "/mystat/v1/activities/{$activity_id}" ) ); + $response->set_status( 201 ); // 201 Created + return $response; +} + +/** + * Update an existing activity. + * + * @param WP_REST_Request $request Full details about the request. + * @return WP_REST_Response|WP_Error + */ +function mystat_update_activity_api( WP_REST_Request $request ) { + $id = (int) $request['id']; + $params = $request->get_json_params(); + $activity_id = mystat_save_activity_data( $params, $id ); + + if ( ! $activity_id ) { + return new WP_Error( 'cant-update', 'Error updating activity', array( 'status' => 500 ) ); + } + + return mystat_get_activity_api( $request ); +} + +/** + * Delete an activity. + * + * @param WP_REST_Request $request Full details about the request. + * @return WP_REST_Response|WP_Error + */ +function mystat_delete_activity_api( WP_REST_Request $request ) { + global $wpdb; + $id = (int) $request['id']; + + $result = $wpdb->delete( $wpdb->prefix . 'mystat_activities', array( 'id' => $id ), array( '%d' ) ); + + if ( ! $result ) { + return new WP_Error( 'cant-delete', 'Error deleting activity', array( 'status' => 500 ) ); + } + + return new WP_REST_Response( array( 'message' => 'Activity deleted successfully.' ), 200 ); +} \ No newline at end of file diff --git a/includes/core/crud-activity.php b/includes/core/crud-activity.php new file mode 100644 index 0000000..2f25ef2 --- /dev/null +++ b/includes/core/crud-activity.php @@ -0,0 +1,95 @@ +prefix . 'mystat_activities'; + + // Helper to convert empty strings to NULL for the database. + $null_if_empty = function( $value ) { + // Also check for 0 as we don't want to nullify it for numeric fields. + return ( '' !== $value && ! is_null( $value ) ) ? $value : null; + }; + + // Sanitize and prepare data. + $prepared_data = array( + 'category_id' => isset( $data['category_id'] ) ? intval( $data['category_id'] ) : null, + 'date' => isset( $data['date'] ) ? sanitize_text_field( $data['date'] ) : current_time( 'Y-m-d' ), + 'title' => isset( $data['title'] ) ? sanitize_text_field( $data['title'] ) : '', + 'distance' => isset( $data['distance'] ) ? floatval( str_replace( ',', '.', $data['distance'] ) ) : 0, + 'duration' => isset( $data['duration'] ) ? sanitize_text_field( $data['duration'] ) : '00:00:00', + 'calories' => isset( $data['calories'] ) ? intval( $data['calories'] ) : null, + 'comment' => isset( $data['comment'] ) ? sanitize_textarea_field( $data['comment'] ) : null, + 'strava_url' => isset( $data['strava_url'] ) ? $null_if_empty( esc_url_raw( $data['strava_url'] ) ) : null, + 'avg_heart_rate' => isset( $data['avg_heart_rate'] ) ? $null_if_empty( intval( $data['avg_heart_rate'] ) ) : null, + 'max_heart_rate' => isset( $data['max_heart_rate'] ) ? $null_if_empty( intval( $data['max_heart_rate'] ) ) : null, + 'avg_speed' => isset( $data['avg_speed'] ) ? $null_if_empty( floatval( str_replace( ',', '.', $data['avg_speed'] ) ) ) : null, + 'max_speed' => isset( $data['max_speed'] ) ? $null_if_empty( floatval( str_replace( ',', '.', $data['max_speed'] ) ) ) : null, + 'avg_cadence' => isset( $data['avg_cadence'] ) ? $null_if_empty( intval( $data['avg_cadence'] ) ) : null, + 'max_cadence' => isset( $data['max_cadence'] ) ? $null_if_empty( intval( $data['max_cadence'] ) ) : null, + 'total_elevation_gain' => isset( $data['total_elevation_gain'] ) ? $null_if_empty( intval( $data['total_elevation_gain'] ) ) : null, + 'total_elevation_loss' => isset( $data['total_elevation_loss'] ) ? $null_if_empty( intval( $data['total_elevation_loss'] ) ) : null, + 'min_altitude' => isset( $data['min_altitude'] ) ? $null_if_empty( intval( $data['min_altitude'] ) ) : null, + 'max_altitude' => isset( $data['max_altitude'] ) ? $null_if_empty( intval( $data['max_altitude'] ) ) : null, + 'equipment_id' => isset( $data['equipment_id'] ) ? $null_if_empty( intval( $data['equipment_id'] ) ) : null, + 'gpx_url' => isset( $data['gpx_url'] ) ? $null_if_empty( esc_url_raw( $data['gpx_url'] ) ) : null, + 'event_type_id' => isset( $data['event_type_id'] ) ? $null_if_empty( intval( $data['event_type_id'] ) ) : null, + ); + + // Data formats for $wpdb. + $format = array( + '%d', // category_id + '%s', // date + '%s', // title + '%f', // distance + '%s', // duration + '%d', // calories + '%s', // comment + '%s', // strava_url + '%d', // avg_heart_rate + '%d', // max_heart_rate + '%f', // avg_speed + '%f', // max_speed + '%d', // avg_cadence + '%d', // max_cadence + '%d', // total_elevation_gain + '%d', // total_elevation_loss + '%d', // min_altitude + '%d', // max_altitude + '%d', // equipment_id + '%s', // gpx_url + '%d', // event_type_id + ); + + if ( $activity_id > 0 ) { + // UPDATE + $result = $wpdb->update( $table_activities, $prepared_data, array( 'id' => $activity_id ), $format, array( '%d' ) ); + if ( false !== $result ) { + return $activity_id; + } + } else { + // INSERT + $result = $wpdb->insert( $table_activities, $prepared_data, $format ); + if ( $result ) { + return $wpdb->insert_id; + } + } + + return false; +} \ No newline at end of file diff --git a/moje-statystyki.php b/moje-statystyki.php index c8b7135..0f4e124 100644 --- a/moje-statystyki.php +++ b/moje-statystyki.php @@ -17,6 +17,7 @@ define( 'MYSTAT_PLUGIN_URL', plugin_dir_url( __FILE__ ) ); // --- 1. PLIKI RDZENNE I AKTYWACJA --- require_once MYSTAT_PLUGIN_DIR . 'includes/activation.php'; require_once MYSTAT_PLUGIN_DIR . 'includes/core/gpx-parser.php'; +require_once MYSTAT_PLUGIN_DIR . 'includes/core/crud-activity.php'; require_once MYSTAT_PLUGIN_DIR . 'includes/core/gpx-upload.php'; register_activation_hook( __FILE__, 'mystat_activate' ); @@ -42,6 +43,11 @@ add_action( 'admin_menu', 'mystat_add_admin_menu' ); add_action( 'admin_init', 'mystat_admin_init_setup' ); add_action( 'admin_enqueue_scripts', 'mystat_enqueue_admin_styles' ); +// --- 3. REST API --- +require_once MYSTAT_PLUGIN_DIR . 'includes/api/routes.php'; +add_action( 'rest_api_init', 'mystat_register_rest_routes' ); + + // --- 3. SHORTCODE DO WYŚWIETLANIA NA FRONCIE --- require_once MYSTAT_PLUGIN_DIR . 'includes/frontend/assets.php'; require_once MYSTAT_PLUGIN_DIR . 'includes/frontend/shortcodes.php';