woocommerce/includes/import/abstract-wc-product-importe...

612 lines
16 KiB
PHP
Raw Normal View History

2017-05-16 04:02:46 +00:00
<?php
/**
* Abstract Product importer
*
* @author Automattic
* @category Admin
* @package WooCommerce/Import
* @version 3.1.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Include dependencies.
*/
if ( ! class_exists( 'WC_Importer_Interface', false ) ) {
include_once( WC_ABSPATH . 'includes/interfaces/class-wc-importer-interface.php' );
}
/**
* WC_Product_Importer Class.
*/
abstract class WC_Product_Importer implements WC_Importer_Interface {
2017-05-16 04:43:45 +00:00
/**
* CSV file.
*
* @var string
*/
protected $file = '';
2017-05-18 16:59:40 +00:00
/**
* The file position after the last read.
*
* @var int
*/
protected $file_position = 0;
2017-05-16 04:43:45 +00:00
/**
* Importer parameters.
*
* @var array
*/
protected $params = array();
/**
* Raw keys - CSV raw headers.
*
* @var array
*/
protected $raw_keys = array();
/**
* Mapped keys - CSV headers.
*
* @var array
*/
protected $mapped_keys = array();
/**
* Raw data.
*
* @var array
*/
protected $raw_data = array();
/**
* Parsed data.
*
* @var array
*/
protected $parsed_data = array();
/**
* Get file raw headers.
*
* @return array
*/
public function get_raw_keys() {
return $this->raw_keys;
}
/**
* Get file mapped headers.
*
* @return array
*/
public function get_mapped_keys() {
return ! empty( $this->mapped_keys ) ? $this->mapped_keys : $this->raw_keys;
2017-05-16 04:43:45 +00:00
}
/**
* Get raw data.
*
* @return array
*/
public function get_raw_data() {
return $this->raw_data;
}
/**
* Get parsed data.
*
* @return array
*/
public function get_parsed_data() {
2017-05-19 21:45:58 +00:00
return apply_filters( 'woocommerce_product_importer_parsed_data', $this->parsed_data, $this->get_raw_data() );
2017-05-16 04:43:45 +00:00
}
2017-05-18 16:59:40 +00:00
/**
* Get file pointer position from the last read.
*
* @return int
*/
public function get_file_position() {
return $this->file_position;
}
/**
* Get file pointer position as a percentage of file size.
*
* @return int
*/
public function get_percent_complete() {
$size = filesize( $this->file );
if ( ! $size ) {
return 0;
}
2017-05-23 14:05:32 +00:00
return absint( min( round( ( $this->file_position / $size ) * 100 ), 100 ) );
2017-05-18 16:59:40 +00:00
}
2017-05-16 04:02:46 +00:00
/**
* Prepare a single product for create or update.
*
2017-05-26 21:42:07 +00:00
* @param array $data Item data.
2017-05-16 04:02:46 +00:00
* @return WC_Product|WP_Error
*/
2017-05-25 13:54:49 +00:00
protected function get_product_object( $data ) {
2017-05-19 00:09:25 +00:00
$id = isset( $data['id'] ) ? absint( $data['id'] ) : 0;
2017-05-16 04:02:46 +00:00
// Type is the most important part here because we need to be using the correct class and methods.
if ( isset( $data['type'] ) ) {
$types = array_keys( wc_get_product_types() );
$types[] = 'variation';
if ( ! in_array( $data['type'], $types, true ) ) {
2017-05-19 21:45:58 +00:00
return new WP_Error( 'woocommerce_product_importer_invalid_type', __( 'Invalid product type.', 'woocommerce' ), array( 'status' => 401 ) );
2017-05-16 04:02:46 +00:00
}
2017-05-19 21:45:58 +00:00
$classname = WC_Product_Factory::get_classname_from_product_type( $data['type'] );
2017-05-16 04:02:46 +00:00
if ( ! class_exists( $classname ) ) {
$classname = 'WC_Product_Simple';
}
$product = new $classname( $id );
} elseif ( isset( $data['id'] ) ) {
$product = wc_get_product( $id );
2017-05-23 14:05:32 +00:00
if ( ! $product ) {
return new WP_Error( 'woocommerce_product_csv_importer_invalid_id', sprintf( __( 'Invalid product ID %d.', 'woocommerce' ), $id ), array( 'id' => $id, 'status' => 401 ) );
}
2017-05-16 04:02:46 +00:00
} else {
2017-05-25 13:54:49 +00:00
$product = new WC_Product_Simple( $id );
2017-05-16 04:02:46 +00:00
}
2017-05-25 13:54:49 +00:00
return apply_filters( 'woocommerce_product_import_get_product_object', $product, $data );
2017-05-16 04:02:46 +00:00
}
/**
2017-05-26 14:57:17 +00:00
* Process a single item and save.
2017-05-16 04:02:46 +00:00
*
2017-05-26 14:57:17 +00:00
* @param array $data Raw CSV data.
* @return array|WC_Error
2017-05-16 04:02:46 +00:00
*/
2017-05-26 14:57:17 +00:00
protected function process_item( $data ) {
try {
// Get product ID from SKU if created during the importation.
if ( empty( $data['id'] ) && ! empty( $data['sku'] ) && ( $product_id = wc_get_product_id_by_sku( $data['sku'] ) ) ) {
$data['id'] = $product_id;
}
2017-05-26 14:57:17 +00:00
$object = $this->get_product_object( $data );
$updating = false;
2017-05-16 04:02:46 +00:00
2017-05-26 14:57:17 +00:00
if ( is_wp_error( $object ) ) {
return $object;
}
2017-05-16 04:02:46 +00:00
2017-05-26 14:57:17 +00:00
if ( $object->get_id() && 'importing' !== $object->get_status() ) {
$updating = true;
}
2017-05-16 04:02:46 +00:00
2017-05-26 18:26:55 +00:00
if ( 'external' === $object->get_type() ) {
2017-05-26 19:16:38 +00:00
unset( $data['manage_stock'], $data['stock_status'], $data['backorders'] );
2017-05-26 18:26:55 +00:00
}
2017-05-26 21:53:22 +00:00
$result = $object->set_props( array_diff_key( $data, array_flip( array( 'meta_data', 'raw_image_id', 'raw_gallery_image_ids', 'raw_attributes' ) ) ) );
2017-05-16 04:02:46 +00:00
2017-05-26 14:57:17 +00:00
if ( is_wp_error( $result ) ) {
throw new Exception( $result->get_error_message() );
}
2017-05-16 04:02:46 +00:00
2017-05-26 14:57:17 +00:00
if ( 'variation' === $object->get_type() ) {
$this->set_variation_data( $object, $data );
} else {
$this->set_product_data( $object, $data );
}
2017-05-16 04:02:46 +00:00
2017-05-26 14:57:17 +00:00
$this->set_image_data( $object, $data );
$this->set_meta_data( $object, $data );
2017-05-16 04:02:46 +00:00
2017-05-26 14:57:17 +00:00
if ( 'importing' === $object->get_status() ) {
$object->set_status( 'publish' );
}
2017-05-16 04:02:46 +00:00
2017-05-26 14:57:17 +00:00
$object = apply_filters( 'woocommerce_product_import_pre_insert_product_object', $object, $data );
$object->save();
2017-05-16 04:02:46 +00:00
2017-05-26 14:57:17 +00:00
return array(
'id' => $object->get_id(),
'updated' => $updating,
);
} catch ( Exception $e ) {
return new WP_Error( 'woocommerce_product_importer_error', $e->getMessage(), array( 'status' => $e->getCode() ) );
2017-05-16 04:02:46 +00:00
}
2017-05-26 14:57:17 +00:00
}
2017-05-16 04:02:46 +00:00
2017-05-26 14:57:17 +00:00
/**
* Convert raw image URLs to IDs and set.
*
2017-05-26 21:42:07 +00:00
* @param WC_Product $product Product instance.
* @param array $data Item data.
2017-05-26 14:57:17 +00:00
*/
protected function set_image_data( &$product, $data ) {
// Image URLs need converting to IDs before inserting.
2017-05-26 18:37:12 +00:00
if ( isset( $data['raw_image_id'] ) ) {
$product->set_image_id( $this->get_attachment_id_from_url( $data['raw_image_id'], $product->get_id() ) );
2017-05-16 04:02:46 +00:00
}
2017-05-26 14:57:17 +00:00
// Gallery image URLs need converting to IDs before inserting.
2017-05-26 18:37:12 +00:00
if ( isset( $data['raw_gallery_image_ids'] ) ) {
2017-05-26 14:57:17 +00:00
$gallery_image_ids = array();
2017-05-16 04:02:46 +00:00
2017-05-26 18:37:12 +00:00
foreach ( $data['raw_gallery_image_ids'] as $image_id ) {
2017-05-26 14:57:17 +00:00
$gallery_image_ids[] = $this->get_attachment_id_from_url( $image_id, $product->get_id() );
}
$product->set_gallery_image_ids( $gallery_image_ids );
2017-05-16 04:02:46 +00:00
}
2017-05-26 14:57:17 +00:00
}
2017-05-16 04:02:46 +00:00
2017-05-26 14:57:17 +00:00
/**
* Append meta data.
*
2017-05-26 21:42:07 +00:00
* @param WC_Product $product Product instance.
* @param array $data Item data.
2017-05-26 14:57:17 +00:00
*/
protected function set_meta_data( &$product, $data ) {
if ( isset( $data['meta_data'] ) ) {
foreach ( $data['meta_data'] as $meta ) {
$product->update_meta_data( $meta['key'], $meta['value'] );
}
2017-05-16 04:02:46 +00:00
}
2017-05-26 14:57:17 +00:00
}
2017-05-16 04:02:46 +00:00
2017-05-26 14:57:17 +00:00
/**
* Set product data.
*
* @param WC_Product $product Product instance.
2017-05-26 21:42:07 +00:00
* @param array $data Item data.
2017-05-26 14:57:17 +00:00
*
* @return WC_Product|WP_Error
* @throws Exception
*/
protected function set_product_data( &$product, $data ) {
if ( isset( $data['raw_attributes'] ) ) {
$attributes = array();
$default_attributes = array();
2017-05-16 04:02:46 +00:00
2017-05-26 14:57:17 +00:00
foreach ( $data['raw_attributes'] as $position => $attribute ) {
$attribute_id = 0;
2017-05-24 07:32:28 +00:00
// Get ID if is a global attribute.
if ( ! empty( $attribute['taxonomy'] ) ) {
$attribute_id = $this->get_attribute_taxonomy_id_by_name( $attribute['name'] );
}
2017-05-16 04:02:46 +00:00
// Set attribute visibility.
2017-05-24 08:43:15 +00:00
if ( isset( $attribute['visible'] ) ) {
$is_visible = $attribute['visible'];
2017-05-24 08:43:15 +00:00
} else {
$is_visible = 1;
2017-05-24 08:43:15 +00:00
}
// Set if is a variation attribute.
$is_variation = 0;
2017-05-16 04:02:46 +00:00
if ( $attribute_id ) {
$attribute_name = wc_attribute_taxonomy_name_by_id( $attribute_id );
2017-05-24 07:32:28 +00:00
if ( isset( $attribute['value'] ) ) {
$options = array_map( 'wc_sanitize_term_text_based', $attribute['value'] );
$options = array_filter( $options, 'strlen' );
} else {
$options = array();
}
2017-05-16 04:02:46 +00:00
// Check for default attributes and set "is_variation".
if ( ! empty( $attribute['default'] ) && in_array( $attribute['default'], $options ) ) {
$default_term = get_term_by( 'name', $attribute['default'], $attribute_name );
if ( $default_term && ! is_wp_error( $default_term ) ) {
$default = $default_term->slug;
} else {
$default = sanitize_title( $attribute['default'] );
2017-05-16 04:02:46 +00:00
}
$default_attributes[ $attribute_name ] = $default;
$is_variation = 1;
2017-05-16 04:02:46 +00:00
}
if ( ! empty( $options ) ) {
2017-05-16 04:02:46 +00:00
$attribute_object = new WC_Product_Attribute();
$attribute_object->set_id( $attribute_id );
$attribute_object->set_name( $attribute_name );
$attribute_object->set_options( $options );
2017-05-24 07:32:28 +00:00
$attribute_object->set_position( $position );
$attribute_object->set_visible( $is_visible );
$attribute_object->set_variation( $is_variation );
2017-05-16 04:02:46 +00:00
$attributes[] = $attribute_object;
}
2017-05-24 07:32:28 +00:00
} elseif ( isset( $attribute['value'] ) ) {
// Check for default attributes and set "is_variation".
if ( ! empty( $attribute['default'] ) && in_array( $attribute['default'], $attribute['value'] ) ) {
$default_attributes[ sanitize_title( $attribute['name'] ) ] = $attribute['default'];
$is_variation = 1;
2017-05-16 04:02:46 +00:00
}
2017-05-24 07:32:28 +00:00
2017-05-16 04:02:46 +00:00
$attribute_object = new WC_Product_Attribute();
2017-05-24 07:32:28 +00:00
$attribute_object->set_name( $attribute['name'] );
$attribute_object->set_options( $attribute['value'] );
2017-05-24 07:32:28 +00:00
$attribute_object->set_position( $position );
$attribute_object->set_visible( $is_visible );
$attribute_object->set_variation( $is_variation );
2017-05-16 04:02:46 +00:00
$attributes[] = $attribute_object;
}
}
2017-05-24 07:32:28 +00:00
2017-05-16 04:02:46 +00:00
$product->set_attributes( $attributes );
// Set variable default attributes.
if ( $product->is_type( 'variable' ) ) {
$product->set_default_attributes( $default_attributes );
}
2017-05-16 04:02:46 +00:00
}
}
/**
* Set variation data.
*
* @param WC_Product $variation Product instance.
2017-05-26 21:42:07 +00:00
* @param array $data Item data.
2017-05-16 04:02:46 +00:00
*
2017-05-19 00:09:25 +00:00
* @return WC_Product|WP_Error
2017-05-26 14:57:17 +00:00
* @throws Exception
2017-05-16 04:02:46 +00:00
*/
2017-05-26 14:57:17 +00:00
protected function set_variation_data( &$variation, $data ) {
$parent = false;
2017-05-19 00:09:25 +00:00
// Check if parent exist.
if ( isset( $data['parent_id'] ) ) {
$parent = wc_get_product( $data['parent_id'] );
if ( $parent ) {
$variation->set_parent_id( $parent->get_id() );
}
}
// Stop if parent does not exists.
if ( ! $parent ) {
2017-05-26 15:56:37 +00:00
return new WP_Error( 'woocommerce_product_importer_missing_variation_parent_id', __( 'Variation cannot be imported: Missing parent ID or parent does not exist yet.', 'woocommerce' ), array( 'status' => 401 ) );
2017-05-16 04:22:00 +00:00
}
2017-05-26 14:57:17 +00:00
if ( isset( $data['raw_attributes'] ) ) {
2017-05-16 04:22:00 +00:00
$attributes = array();
2017-05-26 14:57:17 +00:00
$parent_attributes = $this->get_variation_parent_attributes( $data['raw_attributes'], $parent );
2017-05-16 04:22:00 +00:00
2017-05-26 14:57:17 +00:00
foreach ( $data['raw_attributes'] as $attribute ) {
// Get ID if is a global attribute.
$attribute_id = wc_attribute_taxonomy_id_by_name( $attribute['name'] );
2017-05-16 04:22:00 +00:00
if ( $attribute_id ) {
2017-05-16 04:22:00 +00:00
$attribute_name = wc_attribute_taxonomy_name_by_id( $attribute_id );
} else {
2017-05-25 19:53:32 +00:00
$attribute_name = sanitize_title( $attribute['name'] );
2017-05-16 04:22:00 +00:00
}
if ( ! isset( $parent_attributes[ $attribute_name ] ) || ! $parent_attributes[ $attribute_name ]->get_variation() ) {
continue;
}
$attribute_key = sanitize_title( $parent_attributes[ $attribute_name ]->get_name() );
$attribute_value = isset( $attribute['value'] ) ? current( $attribute['value'] ) : '';
2017-05-16 04:22:00 +00:00
if ( $parent_attributes[ $attribute_name ]->is_taxonomy() ) {
// If dealing with a taxonomy, we need to get the slug from the name posted to the API.
$term = get_term_by( 'name', $attribute_value, $attribute_name );
if ( $term && ! is_wp_error( $term ) ) {
$attribute_value = $term->slug;
} else {
$attribute_value = sanitize_title( $attribute_value );
}
}
$attributes[ $attribute_key ] = $attribute_value;
}
$variation->set_attributes( $attributes );
}
2017-05-16 04:02:46 +00:00
}
/**
* Get variation parent attributes and set "is_variation".
*
* @param array $attributes Attributes list.
* @param WC_Product $parent Parent product data.
* @return array
*/
protected function get_variation_parent_attributes( $attributes, $parent ) {
$parent_attributes = $parent->get_attributes();
$require_save = false;
foreach ( $attributes as $attribute ) {
$attribute_id = 0;
// Get ID if is a global attribute.
if ( ! empty( $attribute['taxonomy'] ) ) {
$attribute_id = $this->get_attribute_taxonomy_id_by_name( $attribute['name'] );
}
if ( $attribute_id ) {
$attribute_name = wc_attribute_taxonomy_name_by_id( $attribute_id );
} else {
2017-05-25 19:53:32 +00:00
$attribute_name = sanitize_title( $attribute['name'] );
}
// Check if attribute handle variations.
if ( isset( $parent_attributes[ $attribute_name ] ) && ! $parent_attributes[ $attribute_name ]->get_variation() ) {
// Re-create the attribute to CRUD save and genarate again.
$parent_attributes[ $attribute_name ] = clone $parent_attributes[ $attribute_name ];
$parent_attributes[ $attribute_name ]->set_variation( 1 );
$require_save = true;
}
}
// Save variation attributes.
if ( $require_save ) {
$parent->set_attributes( array_values( $parent_attributes ) );
$parent->save();
}
return $parent_attributes;
}
2017-05-16 04:02:46 +00:00
/**
2017-05-24 06:14:54 +00:00
* Get attachment ID.
2017-05-19 00:09:25 +00:00
*
2017-05-24 06:14:54 +00:00
* @param string $url Attachment URL.
* @param int $product_id Product ID.
* @return int
2017-05-16 04:02:46 +00:00
*/
2017-05-26 14:57:17 +00:00
protected function get_attachment_id_from_url( $url, $product_id ) {
2017-05-24 06:14:54 +00:00
if ( empty( $url ) ) {
return 0;
}
2017-05-16 04:02:46 +00:00
2017-05-24 06:14:54 +00:00
$id = 0;
$upload_dir = wp_upload_dir();
$base_url = $upload_dir['baseurl'] . '/';
// Check first if attachment is on WordPress uploads directory.
if ( false !== strpos( $url, $base_url ) ) {
// Search for yyyy/mm/slug.extension
$file = str_replace( $base_url, '', $url );
$args = array(
'post_type' => 'attachment',
'post_status' => 'any',
'fields' => 'ids',
'meta_query' => array(
array(
'value' => $file,
'compare' => 'LIKE',
'key' => '_wp_attachment_metadata',
),
2017-05-24 06:26:17 +00:00
),
2017-05-24 06:14:54 +00:00
);
if ( $ids = get_posts( $args ) ) {
$id = current( $ids );
}
} else {
$args = array(
'post_type' => 'attachment',
'post_status' => 'any',
'fields' => 'ids',
'meta_query' => array(
array(
2017-05-24 06:26:17 +00:00
'value' => $url,
'key' => '_wc_attachment_source',
2017-05-24 06:14:54 +00:00
),
2017-05-24 06:26:17 +00:00
),
2017-05-24 06:14:54 +00:00
);
if ( $ids = get_posts( $args ) ) {
$id = current( $ids );
}
}
2017-05-16 04:02:46 +00:00
2017-05-24 06:14:54 +00:00
// Upload if attachment does not exists.
if ( ! $id ) {
$upload = wc_rest_upload_image_from_url( $url );
2017-05-16 04:02:46 +00:00
2017-05-24 06:14:54 +00:00
if ( is_wp_error( $upload ) ) {
throw new Exception( $upload->get_error_message(), 400 );
}
2017-05-16 04:02:46 +00:00
2017-05-24 06:14:54 +00:00
$id = wc_rest_set_uploaded_image_as_attachment( $upload, $product_id );
2017-05-16 04:02:46 +00:00
2017-05-24 06:14:54 +00:00
if ( ! wp_attachment_is_image( $id ) ) {
throw new Exception( sprintf( __( 'Not able to attach "%s".', 'woocommerce' ), $url ), 400 );
2017-05-16 04:02:46 +00:00
}
2017-05-24 06:14:54 +00:00
// Save attachment source for future reference.
update_post_meta( $id, '_wc_attachment_source', $url );
2017-05-16 04:02:46 +00:00
}
2017-05-24 06:14:54 +00:00
return $id;
2017-05-16 04:02:46 +00:00
}
/**
* Get attribute taxonomy ID by name.
* If does not exists register a new attribute.
*
* @param string $name Attribute name.
* @return int
*/
protected function get_attribute_taxonomy_id_by_name( $name ) {
global $wpdb;
// Check if exists.
if ( $attribute_id = wc_attribute_taxonomy_id_by_name( $name ) ) {
return $attribute_id;
}
// Register new attribute.
$slug = wc_sanitize_taxonomy_name( $name );
$args = array(
'attribute_label' => $name,
'attribute_name' => wc_sanitize_taxonomy_name( $name ),
'attribute_type' => 'select',
'attribute_orderby' => 'menu_order',
'attribute_public' => 0,
);
// Validate attribute.
if ( strlen( $slug ) >= 28 ) {
throw new Exception( sprintf( __( 'Slug "%s" is too long (28 characters max). Shorten it, please.', 'woocommerce' ), $slug ), 400 );
} elseif ( wc_check_if_attribute_name_is_reserved( $slug ) ) {
throw new Exception( sprintf( __( 'Slug "%s" is not allowed because it is a reserved term. Change it, please.', 'woocommerce' ), $slug ), 400 );
} elseif ( $new_data && taxonomy_exists( wc_attribute_taxonomy_name( $slug ) ) ) {
throw new Exception( sprintf( __( 'Slug "%s" is already in use. Change it, please.', 'woocommerce' ), $slug ), 400 );
}
$result = $wpdb->insert(
$wpdb->prefix . 'woocommerce_attribute_taxonomies',
$args,
array( '%s', '%s', '%s', '%s', '%d' )
);
// Pass errors.
if ( is_wp_error( $result ) ) {
throw new Exception( $result->get_error_message(), 400 );
}
// Delete transient.
delete_transient( 'wc_attribute_taxonomies' );
// Register as taxonomy while importing.
$taxonomy_data = array(
'labels' => array(
'name' => $name,
),
);
register_taxonomy( wc_attribute_taxonomy_name( $slug ), array( 'product' ), $taxonomy_data );
// Set product attributes global.
global $wc_product_attributes;
$wc_product_attributes = array();
foreach ( wc_get_attribute_taxonomies() as $tax ) {
if ( $name = wc_attribute_taxonomy_name( $tax->attribute_name ) ) {
$wc_product_attributes[ $name ] = $tax;
}
}
return $wpdb->insert_id;
}
2017-05-16 04:02:46 +00:00
}