Refactor the model class, add the new sanitation

This commit is contained in:
Philipp Stracker 2024-10-24 16:48:44 +02:00
parent 6bbb1d83e6
commit 5f90949c7d
No known key found for this signature in database
3 changed files with 248 additions and 43 deletions

View file

@ -0,0 +1,99 @@
<?php
/**
* Abstract Data Model Base Class
*
* @package WooCommerce\PayPalCommerce\Settings\Data
*/
declare( strict_types = 1 );
namespace WooCommerce\PayPalCommerce\Settings\Data;
use RuntimeException;
/**
* Abstract class AbstractDataModel
*
* Provides a base implementation for data models that can be serialized to and from arrays,
* and provide persistence capabilities.
*/
abstract class AbstractDataModel {
/**
* Stores the model data.
*
* @var array
*/
protected array $data = array();
/**
* Option key for WordPress storage.
* Must be overridden by the child class!
*/
protected const OPTION_KEY = '';
/**
* Default values for the model.
* Child classes should override this method to define their default structure.
*
* @return array
*/
abstract protected function get_defaults() : array;
/**
* Constructor.
*
* @throws RuntimeException If the OPTION_KEY is not defined in the child class.
*/
public function __construct() {
if ( empty( static::OPTION_KEY ) ) {
throw new RuntimeException( 'OPTION_KEY must be defined in child class.' );
}
$this->data = $this->get_defaults();
$this->load();
}
/**
* Loads the model data from WordPress options.
*/
public function load() : void {
$saved_data = get_option( static::OPTION_KEY, array() );
$this->data = array_merge( $this->data, $saved_data );
}
/**
* Saves the model data to WordPress options.
*/
public function save() : void {
update_option( static::OPTION_KEY, $this->data );
}
/**
* Gets all model data as an array.
*
* @return array
*/
public function to_array() : array {
return array_merge( array(), $this->data );
}
/**
* Sets all model data from an array.
*
* @param array $data The model data.
*/
public function from_array( array $data ) : void {
foreach ( $data as $key => $value ) {
if ( ! array_key_exists( $key, $this->data ) ) {
continue;
}
$setter = "set_$key";
if ( method_exists( $this, $setter ) ) {
$this->$setter( $value );
} else {
$this->data[ $key ] = $value;
}
}
}
}

View file

@ -1,6 +1,6 @@
<?php
/**
* Settings container class
* Onboarding Profile class
*
* @package WooCommerce\PayPalCommerce\Settings\Data
*/
@ -10,33 +10,125 @@ declare( strict_types = 1 );
namespace WooCommerce\PayPalCommerce\Settings\Data;
/**
* Class OnboardingProfile
*
* This class serves as a container for managing the onboarding profile details
* within the WooCommerce PayPal Commerce plugin. It provides methods to retrieve
* and save the onboarding profile data using WordPress options.
*/
class OnboardingProfile {
class OnboardingProfile extends AbstractDataModel {
/**
* Options key where profile details are stored.
* Option key where profile details are stored.
*
* @var string
*/
private const KEY = 'woocommerce-ppcp-data-onboarding';
protected const OPTION_KEY = 'woocommerce-ppcp-data-onboarding';
/**
* Returns the current onboarding profile details.
* Get default values for the model.
*
* @return array
*/
public function get_data() : array {
return get_option( self::KEY, array() );
protected function get_defaults() : array {
return array(
'step' => 0,
'use_sandbox' => false,
'use_manual_connection' => false,
'client_id' => '',
'client_secret' => '',
);
}
// -----
/**
* Gets the 'step' setting.
*
* @return int
*/
public function get_step() : int {
return (int) $this->data['step'];
}
/**
* Saves the onboarding profile details.
* Sets the 'step' setting.
*
* @param array $data The profile details to save.
* @param int $step Whether to use sandbox mode.
*/
public function save_data( array $data ) : void {
update_option( self::KEY, $data );
public function set_step( int $step ) : void {
$this->data['step'] = $step;
}
/**
* Gets the 'use sandbox' setting.
*
* @return bool
*/
public function get_use_sandbox() : bool {
return (bool) $this->data['use_sandbox'];
}
/**
* Sets the 'use sandbox' setting.
*
* @param bool $use_sandbox Whether to use sandbox mode.
*/
public function set_use_sandbox( bool $use_sandbox ) : void {
$this->data['use_sandbox'] = $use_sandbox;
}
/**
* Gets the 'use manual connection' setting.
*
* @return bool
*/
public function get_use_manual_connection() : bool {
return (bool) $this->data['use_manual_connection'];
}
/**
* Sets the 'use manual connection' setting.
*
* @param bool $use_manual_connection Whether to use manual connection.
*/
public function set_use_manual_connection( bool $use_manual_connection ) : void {
$this->data['use_manual_connection'] = $use_manual_connection;
}
/**
* Gets the client ID.
*
* @return string
*/
public function get_client_id() : string {
return $this->data['client_id'];
}
/**
* Sets the client ID.
*
* @param string $client_id The client ID.
*/
public function set_client_id( string $client_id ) : void {
$this->data['client_id'] = sanitize_text_field( $client_id );
}
/**
* Gets the client secret.
*
* @return string
*/
public function get_client_secret() : string {
return $this->data['client_secret'];
}
/**
* Sets the client secret.
*
* @param string $client_secret The client secret.
*/
public function set_client_secret( string $client_secret ) : void {
$this->data['client_secret'] = sanitize_text_field( $client_secret );
}
}

View file

@ -17,7 +17,8 @@ use WooCommerce\PayPalCommerce\Settings\Data\OnboardingProfile;
/**
* REST controller for the onboarding module.
*
* Responsible for persisting and loading the state of the onboarding wizard.
* This API acts as the intermediary between the "external world" and our
* internal data model.
*/
class OnboardingRestEndpoint extends RestEndpoint {
/**
@ -32,7 +33,35 @@ class OnboardingRestEndpoint extends RestEndpoint {
*
* @var OnboardingProfile
*/
protected $profile;
protected OnboardingProfile $profile;
/**
* Field mapping for request to profile transformation.
*
* @var array
*/
private array $field_map = array(
'step' => array(
'js_name' => 'step',
'sanitize' => 'to_number',
),
'use_sandbox' => array(
'js_name' => 'useSandbox',
'sanitize' => 'to_boolean',
),
'use_manual_connection' => array(
'js_name' => 'useManualConnection',
'sanitize' => 'to_boolean',
),
'client_id' => array(
'js_name' => 'clientId',
'sanitize' => 'sanitize_text_field',
),
'client_secret' => array(
'js_name' => 'clientSecret',
'sanitize' => 'sanitize_text_field',
),
);
/**
* Constructor.
@ -73,50 +102,35 @@ class OnboardingRestEndpoint extends RestEndpoint {
}
/**
* Returns an object with all details of the current onboarding wizard
* progress.
* Returns all details of the current onboarding wizard progress.
*
* @return WP_REST_Response The current state of the onboarding wizard.
*/
public function get_details() : WP_REST_Response {
$details = $this->profile->get_data();
$js_data = $this->sanitize_for_javascript(
$this->profile->to_array(),
$this->field_map
);
return rest_ensure_response( $details );
return rest_ensure_response( $js_data );
}
/**
* Receives an object with onboarding details and persists it in the DB.
* Updates onboarding details based on the request.
*
* @param WP_REST_Request $request Full data about the request.
*
* @return WP_REST_Response The current state of the onboarding wizard.
* @return WP_REST_Response The updated state of the onboarding wizard.
*/
public function update_details( WP_REST_Request $request ) : WP_REST_Response {
$details = $this->profile->get_data();
$wp_data = $this->sanitize_for_wordpress(
$request->get_params(),
$this->field_map
);
$get_param = fn( $key ) => wc_clean( wp_unslash( $request->get_param( $key ) ) );
$this->profile->from_array( $wp_data );
$this->profile->save();
$raw_step = $get_param( 'step' );
$raw_completed = $get_param( 'completed' );
$raw_use_sandbox = $get_param( 'useSandbox' );
$raw_use_manual_connection = $get_param( 'useManualConnection' );
if ( is_numeric( $raw_step ) ) {
$details['step'] = intval( $raw_step );
}
if ( null !== $raw_completed ) {
$details['completed'] = (bool) $raw_completed;
}
if ( null !== $raw_use_sandbox ) {
$details['useSandbox'] = (bool) $raw_use_sandbox;
}
if ( null !== $raw_use_manual_connection ) {
$details['useManualConnection'] = (bool) $raw_use_manual_connection;
}
$this->profile->save_data( $details );
return rest_ensure_response( $details );
return $this->get_details();
}
}