Merge pull request #3075 from woocommerce/PCP-4160-connect-style-tab

Connect styling tab with the frontend (4160)
This commit is contained in:
Emili Castells 2025-02-07 17:16:26 +01:00 committed by GitHub
commit fa06cf936c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 338 additions and 28 deletions

View file

@ -9,8 +9,11 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Compat;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
use WooCommerce\PayPalCommerce\Compat\Assets\CompatAssets;
use WooCommerce\PayPalCommerce\Compat\Settings\SettingsMap;
use WooCommerce\PayPalCommerce\Compat\Settings\SettingsMapHelper;
use WooCommerce\PayPalCommerce\Compat\Settings\StylingSettingsMapHelper;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
return array(
@ -127,6 +130,9 @@ return array(
return array();
}
$styling_settings_map_helper = $container->get( 'compat.settings.styling_map_helper' );
assert( $styling_settings_map_helper instanceof StylingSettingsMapHelper );
return array(
new SettingsMap(
$container->get( 'settings.data.general' ),
@ -152,9 +158,29 @@ return array(
'sandbox_merchant_email' => 'merchant_email',
)
),
new SettingsMap(
$container->get( 'settings.data.styling' ),
/**
* The `StylingSettings` class stores settings as `LocationStylingDTO` objects.
* This method creates a mapping from old setting keys to the corresponding style names.
*
* Example:
* 'button_product_layout' => 'layout'
*
* This mapping will allow to retrieve the correct style value
* from a `LocationStylingDTO` object by dynamically accessing its properties.
*/
$styling_settings_map_helper->map()
),
);
},
'compat.settings.settings_map_helper' => static function( ContainerInterface $container ) : SettingsMapHelper {
return new SettingsMapHelper( $container->get( 'compat.setting.new-to-old-map' ) );
return new SettingsMapHelper(
$container->get( 'compat.setting.new-to-old-map' ),
$container->get( 'compat.settings.styling_map_helper' )
);
},
'compat.settings.styling_map_helper' => static function() : StylingSettingsMapHelper {
return new StylingSettingsMapHelper();
},
);

View file

@ -1,18 +1,18 @@
<?php
/**
* A map of new to old settings.
* A map of old to new settings.
*
* @package WooCommerce\PayPalCommerce\Compat
* @package WooCommerce\PayPalCommerce\Compat\Settings
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Compat;
namespace WooCommerce\PayPalCommerce\Compat\Settings;
use WooCommerce\PayPalCommerce\Settings\Data\AbstractDataModel;
/**
* A map of new to old settings.
* A map of old to new settings.
*
* @psalm-type newSettingsKey = string
* @psalm-type oldSettingsKey = string
@ -26,9 +26,9 @@ class SettingsMap {
private AbstractDataModel $model;
/**
* The map of the new setting key to the old setting keys.
* The map of the old setting key to the new setting keys.
*
* @var array<newSettingsKey, oldSettingsKey>
* @var array<oldSettingsKey, newSettingsKey>
*/
private array $map;
@ -36,7 +36,7 @@ class SettingsMap {
* The constructor.
*
* @param AbstractDataModel $model The new settings model.
* @param array<newSettingsKey, oldSettingsKey> $map The map of the new setting key to the old setting keys.
* @param array<oldSettingsKey, newSettingsKey> $map The map of the old setting key to the new setting keys.
*/
public function __construct( AbstractDataModel $model, array $map ) {
$this->model = $model;
@ -53,9 +53,9 @@ class SettingsMap {
}
/**
* The map of the new setting key to the old setting keys.
* The map of the old setting key to the new setting keys.
*
* @return array<newSettingsKey, oldSettingsKey>
* @return array<oldSettingsKey, newSettingsKey>
*/
public function get_map(): array {
return $this->map;

View file

@ -2,14 +2,15 @@
/**
* A helper for mapping the new/old settings.
*
* @package WooCommerce\PayPalCommerce\Compat
* @package WooCommerce\PayPalCommerce\Compat\Settings
*/
declare( strict_types = 1 );
namespace WooCommerce\PayPalCommerce\Compat;
namespace WooCommerce\PayPalCommerce\Compat\Settings;
use RuntimeException;
use WooCommerce\PayPalCommerce\Settings\Data\StylingSettings;
/**
* A helper class to manage the transition between legacy and new settings.
@ -41,15 +42,24 @@ class SettingsMapHelper {
*/
protected array $model_cache = array();
/**
* A helper for mapping the old/new styling settings.
*
* @var StylingSettingsMapHelper
*/
protected StylingSettingsMapHelper $styling_settings_map_helper;
/**
* Constructor.
*
* @param SettingsMap[] $settings_map A list of settings maps containing key definitions.
* @param SettingsMap[] $settings_map A list of settings maps containing key definitions.
* @param StylingSettingsMapHelper $styling_settings_map_helper A helper for mapping the old/new styling settings.
* @throws RuntimeException When an old key has multiple mappings.
*/
public function __construct( array $settings_map ) {
public function __construct( array $settings_map, StylingSettingsMapHelper $styling_settings_map_helper ) {
$this->validate_settings_map( $settings_map );
$this->settings_map = $settings_map;
$this->settings_map = $settings_map;
$this->styling_settings_map_helper = $styling_settings_map_helper;
}
/**
@ -80,15 +90,18 @@ class SettingsMapHelper {
*/
public function mapped_value( string $old_key ) {
$this->ensure_map_initialized();
if ( ! isset( $this->key_to_model[ $old_key ] ) ) {
return null;
}
$mapping = $this->key_to_model[ $old_key ];
$model_id = spl_object_id( $mapping['model'] );
$mapping = $this->key_to_model[ $old_key ];
$model = $mapping['model'] ?? false;
return $this->get_cached_model_value( $model_id, $mapping['new_key'], $mapping['model'] );
if ( ! $model ) {
return null;
}
return $this->get_cached_model_value( spl_object_id( $model ), $old_key, $mapping['new_key'], $mapping['model'] );
}
/**
@ -108,17 +121,23 @@ class SettingsMapHelper {
* Retrieves a cached model value or caches it if not already cached.
*
* @param int $model_id The unique identifier for the model object.
* @param string $old_key The key in the old settings structure.
* @param string $new_key The key in the new settings structure.
* @param object $model The model object.
*
* @return mixed|null The value of the key in the model, or null if not found.
*/
protected function get_cached_model_value( int $model_id, string $new_key, object $model ) {
protected function get_cached_model_value( int $model_id, string $old_key, string $new_key, object $model ) {
if ( ! isset( $this->model_cache[ $model_id ] ) ) {
$this->model_cache[ $model_id ] = $model->to_array();
}
return $this->model_cache[ $model_id ][ $new_key ] ?? null;
switch ( true ) {
case $model instanceof StylingSettings:
return $this->styling_settings_map_helper->mapped_value( $old_key, $this->model_cache[ $model_id ] );
default:
return $this->model_cache[ $model_id ][ $new_key ] ?? null;
}
}
/**

View file

@ -0,0 +1,265 @@
<?php
/**
* A helper for mapping the old/new styling settings.
*
* @package WooCommerce\PayPalCommerce\Compat\Settings
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Compat\Settings;
use RuntimeException;
use WooCommerce\PayPalCommerce\Button\Helper\ContextTrait;
use WooCommerce\PayPalCommerce\Settings\DTO\LocationStylingDTO;
/**
* A map of old to new styling settings.
*
* @psalm-import-type newSettingsKey from SettingsMap
* @psalm-import-type oldSettingsKey from SettingsMap
*/
class StylingSettingsMapHelper {
use ContextTrait;
/**
* Maps old setting keys to new setting style names.
*
* The `StylingSettings` class stores settings as `LocationStylingDTO` objects.
* This method creates a mapping from old setting keys to the corresponding style names.
*
* Example:
* 'button_product_layout' => 'layout'
*
* This mapping will allow to retrieve the correct style value
* from a `LocationStylingDTO` object by dynamically accessing its properties.
*
* @psalm-return array<oldSettingsKey, newSettingsKey>
*/
public function map(): array {
$mapped_settings = array(
'smart_button_locations' => '',
'pay_later_button_locations' => '',
'disable_funding' => '',
'googlepay_button_enabled' => '',
'applepay_button_enabled' => '',
);
foreach ( $this->locations_map() as $old_location_name => $new_location_name ) {
foreach ( $this->styles() as $style ) {
$old_styling_key = $this->get_old_styling_setting_key( $old_location_name, $style );
$mapped_settings[ $old_styling_key ] = $style;
}
}
return $mapped_settings;
}
/**
* Retrieves the value of a mapped key from the new settings.
*
* @param string $old_key The key from the legacy settings.
* @param LocationStylingDTO[] $styling_models The list of location styling models.
*
* @return mixed The value of the mapped setting, (null if not found).
*/
public function mapped_value( string $old_key, array $styling_models ) {
switch ( $old_key ) {
case 'smart_button_locations':
return $this->mapped_smart_button_locations_value( $styling_models );
case 'pay_later_button_locations':
return $this->mapped_pay_later_button_locations_value( $styling_models );
case 'disable_funding':
return $this->mapped_disabled_funding_value( $styling_models );
case 'googlepay_button_enabled':
return $this->mapped_google_pay_or_apple_pay_enabled_value( $styling_models, 'googlepay' );
case 'applepay_button_enabled':
return $this->mapped_google_pay_or_apple_pay_enabled_value( $styling_models, 'applepay' );
default:
foreach ( $this->locations_map() as $old_location_name => $new_location_name ) {
foreach ( $this->styles() as $style ) {
if ( $old_key !== $this->get_old_styling_setting_key( $old_location_name, $style ) ) {
continue;
}
$location_settings = $styling_models[ $new_location_name ] ?? false;
if ( ! $location_settings instanceof LocationStylingDTO ) {
continue;
}
return $location_settings->$style ?? null;
}
}
return null;
}
}
/**
* Returns a mapping of old button location names to new settings location names.
*
* @return string[] The mapping of old location names to new location names.
*/
protected function locations_map(): array {
return array(
'product' => 'product',
'cart' => 'cart',
'cart-block' => 'cart',
'checkout' => 'classic_checkout',
'mini-cart' => 'mini_cart',
'checkout-block-express' => 'express_checkout',
);
}
/**
* Returns a mapping of current context to new button location names.
*
* @return string[] The mapping of current context to new button location names.
*/
protected function current_context_to_new_button_location_map(): array {
return array(
'product' => 'product',
'cart' => 'cart',
'cart-block' => 'cart',
'checkout' => 'classic_checkout',
'pay-now' => 'classic_checkout',
'mini-cart' => 'mini_cart',
'checkout-block' => 'express_checkout',
);
}
/**
* Returns the available style names.
*
* @return string[] The list of available style names.
*/
protected function styles(): array {
return array(
'enabled',
'methods',
'shape',
'label',
'color',
'layout',
'tagline',
);
}
/**
* Returns the old styling setting key name based on provided location and style names.
*
* @param string $location The location name.
* @param string $style The style name.
* @return string The old styling setting key name.
*/
protected function get_old_styling_setting_key( string $location, string $style ): string {
$location_setting_name_part = $location === 'checkout' ? '' : "_{$location}";
return "button{$location_setting_name_part}_{$style}";
}
/**
* Retrieves the mapped smart button locations from the new settings.
*
* @param LocationStylingDTO[] $styling_models The list of location styling models.
* @return string[] The list of enabled smart button locations.
*/
protected function mapped_smart_button_locations_value( array $styling_models ): array {
$enabled_locations = array();
$locations = array_flip( $this->locations_map() );
foreach ( $styling_models as $model ) {
if ( ! $model->enabled ) {
continue;
}
$enabled_locations[] = $locations[ $model->location ] ?? '';
if ( $model->location === 'cart' ) {
$enabled_locations[] = 'cart';
}
}
return $enabled_locations;
}
/**
* Retrieves the mapped pay later button locations from the new settings.
*
* @param LocationStylingDTO[] $styling_models The list of location styling models.
* @return string[] The list of locations where the Pay Later button is enabled.
*/
protected function mapped_pay_later_button_locations_value( array $styling_models ): array {
$enabled_locations = array();
$locations = array_flip( $this->locations_map() );
foreach ( $styling_models as $model ) {
if ( ! $model->enabled || ! in_array( 'paylater', $model->methods, true ) ) {
continue;
}
$enabled_locations[] = $locations[ $model->location ] ?? '';
if ( $model->location === 'cart' ) {
$enabled_locations[] = 'cart';
}
}
return $enabled_locations;
}
/**
* Retrieves the mapped disabled funding value from the new settings.
*
* @param LocationStylingDTO[] $styling_models The list of location styling models.
* @return array|null The list of disabled funding, or null if none are disabled.
*/
protected function mapped_disabled_funding_value( array $styling_models ): ?array {
$disabled_funding = array();
$locations_to_context_map = $this->current_context_to_new_button_location_map();
foreach ( $styling_models as $model ) {
if ( $model->location !== $locations_to_context_map[ $this->context() ] || in_array( 'venmo', $model->methods, true ) ) {
continue;
}
$disabled_funding[] = 'venmo';
return $disabled_funding;
}
return null;
}
/**
* Retrieves the mapped enabled/disabled Google Pay or Apple Pay value from the new settings.
*
* @param LocationStylingDTO[] $styling_models The list of location styling models.
* @param 'googlepay'|'applepay' $button_name The button name ('googlepay' or 'applepay').
* @return int The enabled (1) or disabled (0) state.
* @throws RuntimeException If an invalid button name is provided.
*/
protected function mapped_google_pay_or_apple_pay_enabled_value( array $styling_models, string $button_name ): ?int {
if ( $button_name !== 'googlepay' && $button_name !== 'applepay' ) {
throw new RuntimeException( 'Wrong button name is provided. Either "googlepay" or "applepay" can be used' );
}
$locations_to_context_map = $this->current_context_to_new_button_location_map();
foreach ( $styling_models as $model ) {
if ( ! $model->enabled
|| $model->location !== $locations_to_context_map[ $this->context() ]
|| ! in_array( $button_name, $model->methods, true )
) {
continue;
}
return 1;
}
return 0;
}
}

View file

@ -9,9 +9,9 @@ declare( strict_types = 1 );
namespace WooCommerce\PayPalCommerce\WcGateway\Settings;
use WooCommerce\PayPalCommerce\Compat\SettingsMapHelper;
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
use WooCommerce\PayPalCommerce\Compat\Settings\SettingsMapHelper;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
/**
* Class Settings

View file

@ -2,11 +2,11 @@
namespace WooCommerce\PayPalCommerce\Tests\E2e;
use WooCommerce\PayPalCommerce\Compat\SettingsMap;
use WooCommerce\PayPalCommerce\Compat\SettingsMapHelper;
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
use WooCommerce\PayPalCommerce\Compat\Settings\SettingsMap;
use WooCommerce\PayPalCommerce\Compat\Settings\SettingsMapHelper;
use WooCommerce\PayPalCommerce\Settings\Data\AbstractDataModel;
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
class SettingsTest extends TestCase {
private Settings $settings;