Finish parser and unit test
This commit is contained in:
parent
d642212205
commit
b907aa74f6
|
@ -114,18 +114,14 @@ class WC_Product_Importer extends WP_Importer {
|
|||
|
||||
$this->import_start();
|
||||
|
||||
$raw_data = $this->read_csv( $file, array( 'lines' => 3, 'parse' => true ) );
|
||||
|
||||
// TODO: Remove temporary code once mapping screen is ready.
|
||||
// Mapping screen.
|
||||
var_dump( $raw_data );
|
||||
$data = $this->read_csv( $file, array( 'parse' => true ) );
|
||||
|
||||
// Show Result
|
||||
echo '<div class="updated settings-error"><p>';
|
||||
/* translators: %s: products count */
|
||||
printf(
|
||||
__( 'Import complete - imported %s products.', 'woocommerce' ),
|
||||
'<strong>' . count( $raw_data['data'] ) . '</strong>'
|
||||
'<strong>' . count( $data ) . '</strong>'
|
||||
);
|
||||
echo '</p></div>';
|
||||
|
||||
|
@ -174,7 +170,7 @@ class WC_Product_Importer extends WP_Importer {
|
|||
* @param array $args See $default_args
|
||||
* @return array
|
||||
*/
|
||||
public function read_csv( $file, $args ) {
|
||||
public function read_csv( $file, $args = array() ) {
|
||||
|
||||
$default_args = array(
|
||||
'start_pos' => 0, // File pointer start.
|
||||
|
@ -202,7 +198,7 @@ class WC_Product_Importer extends WP_Importer {
|
|||
$data['data'][] = $row;
|
||||
$position = ftell( $handle );
|
||||
|
||||
if ( ( $args['end_pos'] > 0 && ftell( $handle ) >= $args['end_pos'] ) || 0 >= --$args[
|
||||
if ( ( $args['end_pos'] > 0 && ftell( $handle ) >= $args['end_pos'] ) || 0 === --$args[
|
||||
'lines'] ) {
|
||||
break;
|
||||
}
|
||||
|
@ -223,7 +219,7 @@ class WC_Product_Importer extends WP_Importer {
|
|||
/**
|
||||
* @param array $mapping 'raw column name' => 'mapped column name'
|
||||
*/
|
||||
private function map_headers( $data, $mapping ) {
|
||||
public function map_headers( $data, $mapping ) {
|
||||
$data['headers'] = array();
|
||||
foreach ( $data['raw_headers'] as $heading ) {
|
||||
$data['headers'] = isset( $mapping[ $heading ] ) ? $mapping[ $heading ] : $heading;
|
||||
|
@ -235,22 +231,26 @@ class WC_Product_Importer extends WP_Importer {
|
|||
* Map and format raw data to known fields.
|
||||
*
|
||||
* @param array $data
|
||||
* @return array
|
||||
*/
|
||||
private function parse_data( $data ) {
|
||||
public function parse_data( $data ) {
|
||||
|
||||
// Columns not mentioned here will get parsed with 'esc_attr'.
|
||||
// column_name => callback
|
||||
$data_formatting = array(
|
||||
'ID' => 'absint',
|
||||
'Published' => 'boolval',
|
||||
'Is featured' => 'boolval',
|
||||
//'Date sale price starts' => 'wc_format_datetime',
|
||||
//'Date sale price ends' => 'wc_format_datetime',
|
||||
'In stock?' => 'boolval',
|
||||
'Sold individually?' => 'boolval',
|
||||
'Weight' => 'absint',
|
||||
'Length' => 'absint',
|
||||
'Height' => 'absint',
|
||||
'Width' => 'absint',
|
||||
'Allow customer reviews?' => 'boolval',
|
||||
'Published' => array( $this, 'parse_bool_field' ),
|
||||
'Is featured?' => array( $this, 'parse_bool_field' ),
|
||||
'Date sale price starts' => 'strtotime',
|
||||
'Date sale price ends' => 'strtotime',
|
||||
'In stock?' => array( $this, 'parse_bool_field' ),
|
||||
'Backorders allowed?' => array( $this, 'parse_bool_field' ),
|
||||
'Sold individually?' => array( $this, 'parse_bool_field' ),
|
||||
'Weight' => array( $this, 'parse_float_field' ),
|
||||
'Length' => array( $this, 'parse_float_field' ),
|
||||
'Height' => array( $this, 'parse_float_field' ),
|
||||
'Width' => array( $this, 'parse_float_field' ),
|
||||
'Allow customer reviews?' => array( $this, 'parse_bool_field' ),
|
||||
'Purchase Note' => 'wp_kses',
|
||||
'Price' => 'wc_format_decimal',
|
||||
'Regular Price' => 'wc_format_decimal',
|
||||
|
@ -263,54 +263,131 @@ class WC_Product_Importer extends WP_Importer {
|
|||
'Download Limit' => 'absint',
|
||||
'Download Expiry Days' => 'absint',
|
||||
);
|
||||
|
||||
|
||||
$regex_match_data_formatting = array(
|
||||
'/Attribute * Value\(s\)/' => array( $this, 'parse_comma_field' ),
|
||||
'/Attribute * Visible/' => 'boolval',
|
||||
'/Download * URL/' => 'esc_url',
|
||||
);
|
||||
|
||||
|
||||
// special cases: attribute * name, attribute * value(s), attribute * default, attribute * visible
|
||||
// Download 1 Name, Download 1 URL,
|
||||
$headers = isset( $data['headers'] ) && ! empty( $data['headers'] ) ? $data['headers'] : $data['raw_headers'];
|
||||
$parse_functions = array();
|
||||
$parsed_data = array();
|
||||
|
||||
// Figure out the parse function for each column.
|
||||
foreach ( $headers as $index => $heading ) {
|
||||
|
||||
// Figure out the parse function.
|
||||
$formatting_function = 'esc_attr';
|
||||
$parse_function = 'esc_attr';
|
||||
if ( isset( $data_formatting[ $heading ] ) ) {
|
||||
$formatting_function = $data_formatting[ $heading ];
|
||||
$parse_function = $data_formatting[ $heading ];
|
||||
}
|
||||
else {
|
||||
foreach ( $regex_match_data_formatting as $regex => $callback ) {
|
||||
if ( preg_match( $regex, $heading ) ) {
|
||||
$formatting_function = $callback;
|
||||
$parse_function = $callback;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Go down the column parsing.
|
||||
foreach ( $data['data'] as &$row ) {
|
||||
$row[ $index ] = call_user_func( $formatting_function, $row[ $index ] );
|
||||
}
|
||||
$parse_functions[] = $parse_function;
|
||||
}
|
||||
|
||||
return $data;
|
||||
// Parse the data.
|
||||
foreach ( $data['data'] as $row ) {
|
||||
$item = array();
|
||||
foreach ( $row as $index => $field ) {
|
||||
$item[ $headers[ $index ] ] = call_user_func( $parse_functions[ $index ], $field );
|
||||
}
|
||||
$parsed_data[] = $item;
|
||||
}
|
||||
|
||||
return apply_filters( 'woocommerce_csv_product_parsed_data', $parsed_data, $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a comma-delineated field from a CSV.
|
||||
*
|
||||
* @param string $field
|
||||
* @return array
|
||||
*/
|
||||
public function parse_comma_field( $field ) {
|
||||
return array_map( 'esc_attr', explode( ',', $field ) );
|
||||
if ( empty( $field ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
public function parse_categories( $field ) {
|
||||
$sections = explode( ',', $field );
|
||||
return array_map( 'esc_attr', array_map( 'trim', explode( ',', $field ) ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a field that is generally '1' or '0' but can be something else.
|
||||
*
|
||||
* @param string $field
|
||||
* @return bool|string
|
||||
*/
|
||||
public function parse_bool_field( $field ) {
|
||||
if ( '0' === $field ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( '1' === $field ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Don't return explicit true or false for empty fields or values like 'notify'.
|
||||
return esc_attr( $field );
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a float value field.
|
||||
*
|
||||
* @param string $field
|
||||
* @return float|string
|
||||
*/
|
||||
public function parse_float_field( $field ) {
|
||||
if ( '' === $field ) {
|
||||
return $field;
|
||||
}
|
||||
|
||||
return floatval( $field );
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a category field from a CSV.
|
||||
* Categories are separated by commas and subcategories are "parent > subcategory".
|
||||
*
|
||||
* @param string $field
|
||||
* @return array of arrays with "parent" and "name" keys.
|
||||
*/
|
||||
public function parse_categories( $field ) {
|
||||
if ( empty( $field ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$sections = array_map( 'trim', explode( ',', $field ) );
|
||||
$categories = array();
|
||||
|
||||
foreach ( $sections as $section ) {
|
||||
|
||||
// Top level category.
|
||||
if ( false === strpos( $section, '>' ) ) {
|
||||
$categories[] = array(
|
||||
'parent' => false,
|
||||
'name' => esc_attr( $section ),
|
||||
);
|
||||
|
||||
// Subcategory.
|
||||
} else {
|
||||
$chunks = array_map( 'trim', explode( '>', $section ) );
|
||||
$categories[] = array(
|
||||
'parent' => esc_attr( reset( $chunks ) ),
|
||||
'name' => esc_attr( end( $chunks ) ),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $categories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Output header html.
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,165 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Meta
|
||||
* @package WooCommerce\Tests\Importer
|
||||
*/
|
||||
class WC_Tests_Product_Importer extends WC_Unit_Test_Case {
|
||||
|
||||
/**
|
||||
* Load up the importer classes since they aren't loaded by default.
|
||||
*/
|
||||
public function setUp() {
|
||||
require_once ABSPATH . 'wp-admin/includes/import.php';
|
||||
if ( ! class_exists( 'WP_Importer' ) ) {
|
||||
$class_wp_importer = ABSPATH . 'wp-admin/includes/class-wp-importer.php';
|
||||
if ( file_exists( $class_wp_importer ) ) {
|
||||
require $class_wp_importer;
|
||||
}
|
||||
}
|
||||
$bootstrap = WC_Unit_Tests_Bootstrap::instance();
|
||||
require_once $bootstrap->plugin_dir . '/includes/admin/importers/class-wc-product-importer.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Test parse_comma_field.
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public function test_parse_comma_field() {
|
||||
$importer = new WC_Product_Importer();
|
||||
|
||||
$field1 = 'thing 1, thing 2, thing 3';
|
||||
$field2 = 'thing 1';
|
||||
$field3 = '';
|
||||
|
||||
$expected1 = array( 'thing 1', 'thing 2', 'thing 3' );
|
||||
$expected2 = array( 'thing 1' );
|
||||
$expected3 = array();
|
||||
|
||||
$this->assertEquals( $expected1, $importer->parse_comma_field( $field1 ) );
|
||||
$this->assertEquals( $expected2, $importer->parse_comma_field( $field2 ) );
|
||||
$this->assertEquals( $expected3, $importer->parse_comma_field( $field3 ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test parse_bool_field.
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public function test_parse_bool_field() {
|
||||
$importer = new WC_Product_Importer();
|
||||
|
||||
$field1 = '1';
|
||||
$field2 = '0';
|
||||
$field3 = '';
|
||||
$field4 = 'notify';
|
||||
|
||||
$this->assertEquals( true, $importer->parse_bool_field( $field1 ) );
|
||||
$this->assertEquals( false, $importer->parse_bool_field( $field2 ) );
|
||||
$this->assertEquals( '', $importer->parse_bool_field( $field3 ) );
|
||||
$this->assertEquals( 'notify', $importer->parse_bool_field( $field4 ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test parse_float_field.
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public function test_parse_float_field() {
|
||||
$importer = new WC_Product_Importer();
|
||||
|
||||
$field1 = '12.45';
|
||||
$field2 = '5';
|
||||
$field3 = '';
|
||||
|
||||
$this->assertEquals( 12.45, $importer->parse_float_field( $field1 ) );
|
||||
$this->assertEquals( 5, $importer->parse_float_field( $field2 ) );
|
||||
$this->assertEquals( '', $importer->parse_float_field( $field3 ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test parse_categories.
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public function test_parse_categories() {
|
||||
$importer = new WC_Product_Importer();
|
||||
|
||||
$field1 = 'category1';
|
||||
$field2 = 'category1, category2, category1 > subcategory1, category1 > subcategory2';
|
||||
$field3 = '';
|
||||
|
||||
$expected1 = array(
|
||||
array(
|
||||
'parent' => false,
|
||||
'name' => 'category1'
|
||||
)
|
||||
);
|
||||
$expected2 = array(
|
||||
array(
|
||||
'parent' => false,
|
||||
'name' => 'category1'
|
||||
),
|
||||
array(
|
||||
'parent' => false,
|
||||
'name' => 'category2'
|
||||
),
|
||||
array(
|
||||
'parent' => 'category1',
|
||||
'name' => 'subcategory1'
|
||||
),
|
||||
array(
|
||||
'parent' => 'category1',
|
||||
'name' => 'subcategory2'
|
||||
)
|
||||
);
|
||||
$expected3 = array();
|
||||
|
||||
$this->assertEquals( $expected1, $importer->parse_categories( $field1 ) );
|
||||
$this->assertEquals( $expected2, $importer->parse_categories( $field2 ) );
|
||||
$this->assertEquals( $expected3, $importer->parse_categories( $field3 ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test parse_data.
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public function test_parse_data() {
|
||||
$importer = new WC_Product_Importer();
|
||||
|
||||
$data = array(
|
||||
'headers' => array( 'ID', 'Weight', 'Price', 'Categories', 'Tags', 'Extra thing', 'Is featured?', 'Download 1 URL' ),
|
||||
'data' => array(
|
||||
array( '', '12.2', '12.50', 'category1, category1 > subcategory', 'products, things, etc', 'metadata', '1', '' ),
|
||||
array( '12', '', '5', 'category2', '', '', '0', 'http://www.example.com' ),
|
||||
)
|
||||
);
|
||||
|
||||
$expected = array(
|
||||
array(
|
||||
'ID' => 0,
|
||||
'Weight' => 12.2,
|
||||
'Price' => '12.50',
|
||||
'Categories' => array(
|
||||
array( 'parent' => false, 'name' => 'category1' ),
|
||||
array( 'parent' => 'category1', 'name' => 'subcategory' ),
|
||||
),
|
||||
'Tags' => array( 'products', 'things', 'etc' ),
|
||||
'Extra thing' => 'metadata',
|
||||
'Is featured?' => true,
|
||||
'Download 1 URL' => '',
|
||||
),
|
||||
array(
|
||||
'ID' => 12,
|
||||
'Weight' => '',
|
||||
'Price' => '5',
|
||||
'Categories' => array(
|
||||
array( 'parent' => false, 'name' => 'category2' ),
|
||||
),
|
||||
'Tags' => array(),
|
||||
'Extra thing' => '',
|
||||
'Is featured?' => false,
|
||||
'Download 1 URL' => 'http://www.example.com',
|
||||
),
|
||||
);
|
||||
|
||||
$this->assertEquals( $expected, $importer->parse_data( $data ) );
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue