Skip to content
This repository was archived by the owner on Feb 23, 2024. It is now read-only.

Commit 6d872e9

Browse files
authored
Add Formatter Classes to the Store API for Extensions to Consume (#3503)
* Create and register formatter classes * Update Schema to use formatter classes * Update tests * Currency formatter * Fix tests * Use wc_get_price_decimals() as default * Exception on non existing formatter call * Feedback * remove setter
1 parent 2a7d049 commit 6d872e9

27 files changed

+497
-77
lines changed

src/Domain/Bootstrap.php

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121
use Automattic\WooCommerce\Blocks\Domain\Services\CreateAccount;
2222
use Automattic\WooCommerce\Blocks\Domain\Services\ExtendRestApi;
2323
use Automattic\WooCommerce\Blocks\Domain\Services\Email\CustomerNewAccount;
24+
use Automattic\WooCommerce\Blocks\StoreApi\Formatters;
25+
use Automattic\WooCommerce\Blocks\StoreApi\Formatters\MoneyFormatter;
26+
use Automattic\WooCommerce\Blocks\StoreApi\Formatters\HtmlFormatter;
27+
use Automattic\WooCommerce\Blocks\StoreApi\Formatters\CurrencyFormatter;
2428

2529
/**
2630
* Takes care of bootstrapping the plugin.
@@ -193,10 +197,20 @@ function( Container $container ) {
193197
return new CreateAccount( $container->get( Package::class ) );
194198
}
195199
);
200+
$this->container->register(
201+
Formatters::class,
202+
function( Container $container ) {
203+
$formatters = new Formatters();
204+
$formatters->register( 'money', MoneyFormatter::class );
205+
$formatters->register( 'html', HtmlFormatter::class );
206+
$formatters->register( 'currency', CurrencyFormatter::class );
207+
return $formatters;
208+
}
209+
);
196210
$this->container->register(
197211
ExtendRestApi::class,
198212
function( Container $container ) {
199-
return new ExtendRestApi( $container->get( Package::class ) );
213+
return new ExtendRestApi( $container->get( Package::class ), $container->get( Formatters::class ) );
200214
}
201215
);
202216
}

src/Domain/Services/ExtendRestApi.php

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
use Automattic\WooCommerce\Blocks\Domain\Package;
55
use Automattic\WooCommerce\Blocks\StoreApi\Schemas\CartItemSchema;
66
use Automattic\WooCommerce\Blocks\StoreApi\Schemas\CartSchema;
7+
use Automattic\WooCommerce\Blocks\StoreApi\Formatters;
78
use Throwable;
89
use Exception;
910

@@ -18,13 +19,32 @@ class ExtendRestApi {
1819
*/
1920
private $package;
2021

22+
/**
23+
* Holds the formatters class instance.
24+
*
25+
* @var Formatters
26+
*/
27+
private $formatters;
28+
2129
/**
2230
* Constructor
2331
*
24-
* @param Package $package An instance of the package class.
32+
* @param Package $package An instance of the package class.
33+
* @param Formatters $formatters An instance of the formatters class.
34+
*/
35+
public function __construct( Package $package, Formatters $formatters ) {
36+
$this->package = $package;
37+
$this->formatters = $formatters;
38+
}
39+
40+
/**
41+
* Returns a formatter instance.
42+
*
43+
* @param string $name Formatter name.
44+
* @return FormatterInterface
2545
*/
26-
public function __construct( Package $package ) {
27-
$this->package = $package;
46+
public function get_formatter( $name ) {
47+
return $this->formatters->$name;
2848
}
2949

3050
/**

src/StoreApi/Formatters.php

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
namespace Automattic\WooCommerce\Blocks\StoreApi;
3+
4+
use \Exception;
5+
use Automattic\WooCommerce\Blocks\StoreApi\Formatters\DefaultFormatter;
6+
7+
/**
8+
* Formatters class.
9+
*
10+
* Allows formatter classes to be registered. Formatters are exposed to extensions via the ExtendRestApi class.
11+
*/
12+
class Formatters {
13+
/**
14+
* Holds an array of formatter class instances.
15+
*
16+
* @var array
17+
*/
18+
private $formatters = [];
19+
20+
/**
21+
* Get a new instance of a formatter class.
22+
*
23+
* @throws Exception An Exception is thrown if a non-existing formatter is used and the user is admin.
24+
*
25+
* @param string $name Name of the formatter.
26+
* @return FormatterInterface Formatter class instance.
27+
*/
28+
public function __get( $name ) {
29+
if ( ! isset( $this->formatters[ $name ] ) ) {
30+
if ( defined( 'WP_DEBUG' ) && WP_DEBUG && current_user_can( 'manage_woocommerce' ) ) {
31+
throw new Exception( $name . ' formatter does not exist' );
32+
}
33+
return new DefaultFormatter();
34+
}
35+
return $this->formatters[ $name ];
36+
}
37+
38+
/**
39+
* Register a formatter class for usage.
40+
*
41+
* @param string $name Name of the formatter.
42+
* @param string $class A formatter class name.
43+
*/
44+
public function register( $name, $class ) {
45+
$this->formatters[ $name ] = new $class();
46+
}
47+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
namespace Automattic\WooCommerce\Blocks\StoreApi\Formatters;
3+
4+
/**
5+
* Currency Formatter.
6+
*
7+
* Formats an array of monetary values by inserting currency data.
8+
*
9+
* @internal This API is used internally by Blocks--it is still in flux and may be subject to revisions.
10+
*/
11+
class CurrencyFormatter implements FormatterInterface {
12+
/**
13+
* Format a given value and return the result.
14+
*
15+
* @param array $value Value to format.
16+
* @param array $options Options that influence the formatting.
17+
* @return array
18+
*/
19+
public function format( $value, array $options = [] ) {
20+
$position = get_option( 'woocommerce_currency_pos' );
21+
$symbol = html_entity_decode( get_woocommerce_currency_symbol() );
22+
$prefix = '';
23+
$suffix = '';
24+
25+
switch ( $position ) {
26+
case 'left_space':
27+
$prefix = $symbol . ' ';
28+
break;
29+
case 'left':
30+
$prefix = $symbol;
31+
break;
32+
case 'right_space':
33+
$suffix = ' ' . $symbol;
34+
break;
35+
case 'right':
36+
$suffix = $symbol;
37+
break;
38+
}
39+
40+
return array_merge(
41+
(array) $value,
42+
[
43+
'currency_code' => get_woocommerce_currency(),
44+
'currency_symbol' => $symbol,
45+
'currency_minor_unit' => wc_get_price_decimals(),
46+
'currency_decimal_separator' => wc_get_price_decimal_separator(),
47+
'currency_thousand_separator' => wc_get_price_thousand_separator(),
48+
'currency_prefix' => $prefix,
49+
'currency_suffix' => $suffix,
50+
]
51+
);
52+
}
53+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
namespace Automattic\WooCommerce\Blocks\StoreApi\Formatters;
3+
4+
/**
5+
* Default Formatter.
6+
*
7+
* @internal This API is used internally by Blocks--it is still in flux and may be subject to revisions.
8+
*/
9+
class DefaultFormatter implements FormatterInterface {
10+
/**
11+
* Format a given value and return the result.
12+
*
13+
* @param mixed $value Value to format.
14+
* @param array $options Options that influence the formatting.
15+
* @return mixed
16+
*/
17+
public function format( $value, array $options = [] ) {
18+
return $value;
19+
}
20+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
namespace Automattic\WooCommerce\Blocks\StoreApi\Formatters;
3+
4+
/**
5+
* FormatterInterface.
6+
*
7+
* @internal This API is used internally by Blocks--it is still in flux and may be subject to revisions.
8+
*/
9+
interface FormatterInterface {
10+
/**
11+
* Format a given value and return the result.
12+
*
13+
* @param mixed $value Value to format.
14+
* @param array $options Options that influence the formatting.
15+
* @return mixed
16+
*/
17+
public function format( $value, array $options = [] );
18+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
namespace Automattic\WooCommerce\Blocks\StoreApi\Formatters;
3+
4+
/**
5+
* Html Formatter.
6+
*
7+
* Formats HTML in API responses.
8+
*
9+
* @internal This API is used internally by Blocks--it is still in flux and may be subject to revisions.
10+
*/
11+
class HtmlFormatter implements FormatterInterface {
12+
/**
13+
* Format a given value and return the result.
14+
*
15+
* The wptexturize, convert_chars, and trim functions are also used in the `the_title` filter.
16+
* The function wp_kses_post removes disallowed HTML tags.
17+
*
18+
* @param string|array $value Value to format.
19+
* @param array $options Options that influence the formatting.
20+
* @return string
21+
*/
22+
public function format( $value, array $options = [] ) {
23+
if ( is_array( $value ) ) {
24+
return array_map( [ $this, 'format' ], $value );
25+
}
26+
return is_scalar( $value ) ? wp_kses_post( trim( convert_chars( wptexturize( $value ) ) ) ) : $value;
27+
}
28+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
namespace Automattic\WooCommerce\Blocks\StoreApi\Formatters;
3+
4+
/**
5+
* Money Formatter.
6+
*
7+
* Formats monetary values using store settings.
8+
*
9+
* @internal This API is used internally by Blocks--it is still in flux and may be subject to revisions.
10+
*/
11+
class MoneyFormatter implements FormatterInterface {
12+
/**
13+
* Format a given value and return the result.
14+
*
15+
* @param mixed $value Value to format.
16+
* @param array $options Options that influence the formatting.
17+
* @return mixed
18+
*/
19+
public function format( $value, array $options = [] ) {
20+
$options = wp_parse_args(
21+
$options,
22+
[
23+
'decimals' => wc_get_price_decimals(),
24+
'rounding_mode' => PHP_ROUND_HALF_UP,
25+
]
26+
);
27+
28+
return (string) intval(
29+
round(
30+
( (float) wc_format_decimal( $value ) ) * ( 10 ** absint( $options['decimals'] ) ),
31+
0,
32+
absint( $options['rounding_mode'] )
33+
)
34+
);
35+
}
36+
}

src/StoreApi/Schemas/AbstractSchema.php

Lines changed: 12 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -232,45 +232,13 @@ protected function get_store_currency_properties() {
232232
}
233233

234234
/**
235-
* Prepares a list of store currency data to return in responses.
235+
* Adds currency data to an array of monetary values.
236236
*
237-
* @todo Core could use a more defined currency object format, making use of
238-
* constants for currency format rather than strings, and holding this type
239-
* of information instead of plugins/blocks needed to normalize things
240-
* themselves.
241-
*
242-
* @return array
237+
* @param array $values Monetary amounts.
238+
* @return array Monetary amounts with currency data appended.
243239
*/
244-
protected function get_store_currency_response() {
245-
$position = get_option( 'woocommerce_currency_pos' );
246-
$symbol = html_entity_decode( get_woocommerce_currency_symbol() );
247-
$prefix = '';
248-
$suffix = '';
249-
250-
switch ( $position ) {
251-
case 'left_space':
252-
$prefix = $symbol . ' ';
253-
break;
254-
case 'left':
255-
$prefix = $symbol;
256-
break;
257-
case 'right_space':
258-
$suffix = ' ' . $symbol;
259-
break;
260-
case 'right':
261-
$suffix = $symbol;
262-
break;
263-
}
264-
265-
return [
266-
'currency_code' => get_woocommerce_currency(),
267-
'currency_symbol' => $symbol,
268-
'currency_minor_unit' => wc_get_price_decimals(),
269-
'currency_decimal_separator' => wc_get_price_decimal_separator(),
270-
'currency_thousand_separator' => wc_get_price_thousand_separator(),
271-
'currency_prefix' => $prefix,
272-
'currency_suffix' => $suffix,
273-
];
240+
protected function prepare_currency_response( $values ) {
241+
return $this->extend->get_formatter( 'currency' )->format( $values );
274242
}
275243

276244
/**
@@ -283,28 +251,22 @@ protected function get_store_currency_response() {
283251
* @return string The new amount.
284252
*/
285253
protected function prepare_money_response( $amount, $decimals = 2, $rounding_mode = PHP_ROUND_HALF_UP ) {
286-
return (string) intval(
287-
round(
288-
( (float) wc_format_decimal( $amount ) ) * ( 10 ** $decimals ),
289-
0,
290-
absint( $rounding_mode )
291-
)
254+
return $this->extend->get_formatter( 'money' )->format(
255+
$amount,
256+
[
257+
'decimals' => $decimals,
258+
'rounding_mode' => $rounding_mode,
259+
]
292260
);
293261
}
294262

295263
/**
296264
* Prepares HTML based content, such as post titles and content, for the API response.
297265
*
298-
* The wptexturize, convert_chars, and trim functions are also used in the `the_title` filter.
299-
* The function wp_kses_post removes disallowed HTML tags.
300-
*
301266
* @param string|array $response Data to format.
302267
* @return string|array Formatted data.
303268
*/
304269
protected function prepare_html_response( $response ) {
305-
if ( is_array( $response ) ) {
306-
return array_map( [ $this, 'prepare_html_response' ], $response );
307-
}
308-
return is_scalar( $response ) ? wp_kses_post( trim( convert_chars( wptexturize( $response ) ) ) ) : $response;
270+
return $this->extend->get_formatter( 'html' )->format( $response );
309271
}
310272
}

src/StoreApi/Schemas/CartCouponSchema.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,7 @@ public function get_item_response( $coupon_code ) {
101101
return [
102102
'code' => $coupon_code,
103103
'discount_type' => $coupon->get_discount_type(),
104-
'totals' => (object) array_merge(
105-
$this->get_store_currency_response(),
104+
'totals' => (object) $this->prepare_currency_response(
106105
[
107106
'total_discount' => $this->prepare_money_response( isset( $total_discounts[ $coupon_code ] ) ? $total_discounts[ $coupon_code ] : 0, wc_get_price_decimals() ),
108107
'total_discount_tax' => $this->prepare_money_response( isset( $total_discount_taxes[ $coupon_code ] ) ? $total_discount_taxes[ $coupon_code ] : 0, wc_get_price_decimals(), PHP_ROUND_HALF_DOWN ),

0 commit comments

Comments
 (0)