Merge pull request #16374 from woocommerce/fix/12273-4

Product attributes helper functions implementation
This commit is contained in:
Mike Jolley 2017-08-08 11:51:38 +01:00 committed by GitHub
commit 202498298e
4 changed files with 80 additions and 250 deletions

View File

@ -88,158 +88,71 @@ class WC_Admin_Attributes {
return $attribute; return $attribute;
} }
/**
* See if an attribute name is valid.
* @param string $attribute_name
* @return bool|WP_error result
*/
private static function valid_attribute_name( $attribute_name ) {
if ( strlen( $attribute_name ) > 28 ) {
/* translators: %s: attribute name */
return new WP_Error( 'error', sprintf( __( 'Slug "%s" is too long (28 characters max). Shorten it, please.', 'woocommerce' ), sanitize_title( $attribute_name ) ) );
} elseif ( wc_check_if_attribute_name_is_reserved( $attribute_name ) ) {
/* translators: %s: attribute name */
return new WP_Error( 'error', sprintf( __( 'Slug "%s" is not allowed because it is a reserved term. Change it, please.', 'woocommerce' ), sanitize_title( $attribute_name ) ) );
}
return true;
}
/** /**
* Add an attribute. * Add an attribute.
*
* @return bool|WP_Error * @return bool|WP_Error
*/ */
private static function process_add_attribute() { private static function process_add_attribute() {
global $wpdb;
check_admin_referer( 'woocommerce-add-new_attribute' ); check_admin_referer( 'woocommerce-add-new_attribute' );
$attribute = self::get_posted_attribute(); $attribute = self::get_posted_attribute();
$args = array(
'name' => $attribute['attribute_label'],
'slug' => $attribute['attribute_name'],
'type' => $attribute['attribute_type'],
'order_by' => $attribute['attribute_orderby'],
'has_archives' => $attribute['attribute_public'],
);
if ( empty( $attribute['attribute_name'] ) || empty( $attribute['attribute_label'] ) ) { $id = wc_create_attribute( $args );
return new WP_Error( 'error', __( 'Please, provide an attribute name and slug.', 'woocommerce' ) );
} elseif ( ( $valid_attribute_name = self::valid_attribute_name( $attribute['attribute_name'] ) ) && is_wp_error( $valid_attribute_name ) ) { if ( is_wp_error( $id ) ) {
return $valid_attribute_name; return $id;
} elseif ( taxonomy_exists( wc_attribute_taxonomy_name( $attribute['attribute_name'] ) ) ) {
/* translators: %s: attribute name */
return new WP_Error( 'error', sprintf( __( 'Slug "%s" is already in use. Change it, please.', 'woocommerce' ), sanitize_title( $attribute['attribute_name'] ) ) );
} }
$wpdb->insert( $wpdb->prefix . 'woocommerce_attribute_taxonomies', $attribute );
do_action( 'woocommerce_attribute_added', $wpdb->insert_id, $attribute );
wp_schedule_single_event( time(), 'woocommerce_flush_rewrite_rules' );
delete_transient( 'wc_attribute_taxonomies' );
return true; return true;
} }
/** /**
* Edit an attribute. * Edit an attribute.
*
* @return bool|WP_Error * @return bool|WP_Error
*/ */
private static function process_edit_attribute() { private static function process_edit_attribute() {
global $wpdb;
$attribute_id = absint( $_GET['edit'] ); $attribute_id = absint( $_GET['edit'] );
check_admin_referer( 'woocommerce-save-attribute_' . $attribute_id ); check_admin_referer( 'woocommerce-save-attribute_' . $attribute_id );
$attribute = self::get_posted_attribute(); $attribute = self::get_posted_attribute();
$args = array(
if ( empty( $attribute['attribute_name'] ) || empty( $attribute['attribute_label'] ) ) { 'name' => $attribute['attribute_label'],
return new WP_Error( 'error', __( 'Please, provide an attribute name and slug.', 'woocommerce' ) ); 'slug' => $attribute['attribute_name'],
} elseif ( ( $valid_attribute_name = self::valid_attribute_name( $attribute['attribute_name'] ) ) && is_wp_error( $valid_attribute_name ) ) { 'type' => $attribute['attribute_type'],
return $valid_attribute_name; 'order_by' => $attribute['attribute_orderby'],
} 'has_archives' => $attribute['attribute_public'],
$taxonomy_exists = taxonomy_exists( wc_attribute_taxonomy_name( $attribute['attribute_name'] ) );
$old_attribute_name = $wpdb->get_var( "SELECT attribute_name FROM {$wpdb->prefix}woocommerce_attribute_taxonomies WHERE attribute_id = $attribute_id" );
if ( $old_attribute_name != $attribute['attribute_name'] && wc_sanitize_taxonomy_name( $old_attribute_name ) != $attribute['attribute_name'] && $taxonomy_exists ) {
/* translators: %s: attribute name */
return new WP_Error( 'error', sprintf( __( 'Slug "%s" is already in use. Change it, please.', 'woocommerce' ), sanitize_title( $attribute['attribute_name'] ) ) );
}
$wpdb->update( $wpdb->prefix . 'woocommerce_attribute_taxonomies', $attribute, array( 'attribute_id' => $attribute_id ) );
do_action( 'woocommerce_attribute_updated', $attribute_id, $attribute, $old_attribute_name );
if ( $old_attribute_name != $attribute['attribute_name'] && ! empty( $old_attribute_name ) ) {
// Update taxonomies in the wp term taxonomy table
$wpdb->update(
$wpdb->term_taxonomy,
array( 'taxonomy' => wc_attribute_taxonomy_name( $attribute['attribute_name'] ) ),
array( 'taxonomy' => 'pa_' . $old_attribute_name )
); );
// Update taxonomy ordering term meta $id = wc_update_attribute( $attribute_id, $args );
if ( get_option( 'db_version' ) < 34370 ) {
$wpdb->update(
$wpdb->prefix . 'woocommerce_termmeta',
array( 'meta_key' => 'order_pa_' . sanitize_title( $attribute['attribute_name'] ) ),
array( 'meta_key' => 'order_pa_' . sanitize_title( $old_attribute_name ) )
);
} else {
$wpdb->update(
$wpdb->termmeta,
array( 'meta_key' => 'order_pa_' . sanitize_title( $attribute['attribute_name'] ) ),
array( 'meta_key' => 'order_pa_' . sanitize_title( $old_attribute_name ) )
);
}
// Update product attributes which use this taxonomy if ( is_wp_error( $id ) ) {
$old_attribute_name_length = strlen( $old_attribute_name ) + 3; return $id;
$attribute_name_length = strlen( $attribute['attribute_name'] ) + 3;
$wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->postmeta} SET meta_value = REPLACE( meta_value, %s, %s ) WHERE meta_key = '_product_attributes'",
's:' . $old_attribute_name_length . ':"pa_' . $old_attribute_name . '"',
's:' . $attribute_name_length . ':"pa_' . $attribute['attribute_name'] . '"'
) );
// Update variations which use this taxonomy
$wpdb->update(
$wpdb->postmeta,
array( 'meta_key' => 'attribute_pa_' . sanitize_title( $attribute['attribute_name'] ) ),
array( 'meta_key' => 'attribute_pa_' . sanitize_title( $old_attribute_name ) )
);
} }
echo '<div class="updated"><p>' . __( 'Attribute updated successfully', 'woocommerce' ) . '</p><p><a href="' . esc_url( admin_url( 'edit.php?post_type=product&amp;page=product_attributes' ) ) . '">' . __( 'Back to Attributes', 'woocommerce' ) . '</a></p></div>'; echo '<div class="updated"><p>' . __( 'Attribute updated successfully', 'woocommerce' ) . '</p><p><a href="' . esc_url( admin_url( 'edit.php?post_type=product&amp;page=product_attributes' ) ) . '">' . __( 'Back to Attributes', 'woocommerce' ) . '</a></p></div>';
wp_schedule_single_event( time(), 'woocommerce_flush_rewrite_rules' );
delete_transient( 'wc_attribute_taxonomies' );
return true; return true;
} }
/** /**
* Delete an attribute. * Delete an attribute.
*
* @return bool * @return bool
*/ */
private static function process_delete_attribute() { private static function process_delete_attribute() {
global $wpdb;
$attribute_id = absint( $_GET['delete'] ); $attribute_id = absint( $_GET['delete'] );
check_admin_referer( 'woocommerce-delete-attribute_' . $attribute_id ); check_admin_referer( 'woocommerce-delete-attribute_' . $attribute_id );
$attribute_name = $wpdb->get_var( "SELECT attribute_name FROM {$wpdb->prefix}woocommerce_attribute_taxonomies WHERE attribute_id = $attribute_id" ); return wc_delete_attribute( $attribute_id );
$taxonomy = wc_attribute_taxonomy_name( $attribute_name );
do_action( 'woocommerce_before_attribute_delete', $attribute_id, $attribute_name, $taxonomy );
if ( $attribute_name && $wpdb->query( "DELETE FROM {$wpdb->prefix}woocommerce_attribute_taxonomies WHERE attribute_id = $attribute_id" ) ) {
if ( taxonomy_exists( $taxonomy ) ) {
$terms = get_terms( $taxonomy, 'orderby=name&hide_empty=0' );
foreach ( $terms as $term ) {
wp_delete_term( $term->term_id, $taxonomy );
}
}
do_action( 'woocommerce_attribute_deleted', $attribute_id, $attribute_name, $taxonomy );
delete_transient( 'wc_attribute_taxonomies' );
return true;
}
return false;
} }
/** /**

View File

@ -158,15 +158,13 @@ class WC_Admin_Importers {
// Create the taxonomy // Create the taxonomy
if ( ! in_array( $attribute_name, wc_get_attribute_taxonomies() ) ) { if ( ! in_array( $attribute_name, wc_get_attribute_taxonomies() ) ) {
$attribute = array( wc_create_attribute( array(
'attribute_label' => $attribute_name, 'name' => $attribute_name,
'attribute_name' => $attribute_name, 'slug' => $attribute_name,
'attribute_type' => 'select', 'type' => 'select',
'attribute_orderby' => 'menu_order', 'order_by' => 'menu_order',
'attribute_public' => 0, 'has_archives' => false,
); ) );
$wpdb->insert( $wpdb->prefix . 'woocommerce_attribute_taxonomies', $attribute );
delete_transient( 'wc_attribute_taxonomies' );
} }
// Register the taxonomy now so that the import works! // Register the taxonomy now so that the import works!

View File

@ -240,38 +240,20 @@ class WC_REST_Product_Attributes_V1_Controller extends WC_REST_Controller {
public function create_item( $request ) { public function create_item( $request ) {
global $wpdb; global $wpdb;
$args = array( $id = wc_create_attribute( array(
'attribute_label' => $request['name'], 'name' => $request['name'],
'attribute_name' => wc_sanitize_taxonomy_name( stripslashes( $request['slug'] ) ), 'slug' => wc_sanitize_taxonomy_name( stripslashes( $request['slug'] ) ),
'attribute_type' => ! empty( $request['type'] ) ? $request['type'] : 'select', 'type' => ! empty( $request['type'] ) ? $request['type'] : 'select',
'attribute_orderby' => ! empty( $request['order_by'] ) ? $request['order_by'] : 'menu_order', 'order_by' => ! empty( $request['order_by'] ) ? $request['order_by'] : 'menu_order',
'attribute_public' => true === $request['has_archives'], 'has_archives' => true === $request['has_archives'],
); ) );
// Set the attribute slug.
if ( empty( $args['attribute_name'] ) ) {
$args['attribute_name'] = wc_sanitize_taxonomy_name( stripslashes( $args['attribute_label'] ) );
} else {
$args['attribute_name'] = preg_replace( '/^pa\_/', '', wc_sanitize_taxonomy_name( stripslashes( $args['attribute_name'] ) ) );
}
$valid_slug = $this->validate_attribute_slug( $args['attribute_name'], true );
if ( is_wp_error( $valid_slug ) ) {
return $valid_slug;
}
$insert = $wpdb->insert(
$wpdb->prefix . 'woocommerce_attribute_taxonomies',
$args,
array( '%s', '%s', '%s', '%s', '%d' )
);
// Checks for errors. // Checks for errors.
if ( is_wp_error( $insert ) ) { if ( is_wp_error( $id ) ) {
return new WP_Error( 'woocommerce_rest_cannot_create', $insert->get_error_message(), array( 'status' => 400 ) ); return new WP_Error( 'woocommerce_rest_cannot_create', $id->get_error_message(), array( 'status' => 400 ) );
} }
$attribute = $this->get_attribute( $wpdb->insert_id ); $attribute = $this->get_attribute( $id );
if ( is_wp_error( $attribute ) ) { if ( is_wp_error( $attribute ) ) {
return $attribute; return $attribute;
@ -294,10 +276,6 @@ class WC_REST_Product_Attributes_V1_Controller extends WC_REST_Controller {
$response->set_status( 201 ); $response->set_status( 201 );
$response->header( 'Location', rest_url( '/' . $this->namespace . '/' . $this->rest_base . '/' . $attribute->attribute_id ) ); $response->header( 'Location', rest_url( '/' . $this->namespace . '/' . $this->rest_base . '/' . $attribute->attribute_id ) );
// Clear transients.
$this->flush_rewrite_rules();
delete_transient( 'wc_attribute_taxonomies' );
return $response; return $response;
} }
@ -329,48 +307,17 @@ class WC_REST_Product_Attributes_V1_Controller extends WC_REST_Controller {
global $wpdb; global $wpdb;
$id = (int) $request['id']; $id = (int) $request['id'];
$format = array( '%s', '%s', '%s', '%s', '%d' ); $edited = wc_update_attribute( $id, array(
$args = array( 'name' => $request['name'],
'attribute_label' => $request['name'], 'slug' => wc_sanitize_taxonomy_name( stripslashes( $request['slug'] ) ),
'attribute_name' => wc_sanitize_taxonomy_name( stripslashes( $request['slug'] ) ), 'type' => $request['type'],
'attribute_type' => $request['type'], 'order_by' => $request['order_by'],
'attribute_orderby' => $request['order_by'], 'has_archives' => $request['has_archives'],
'attribute_public' => $request['has_archives'], ) );
);
$i = 0;
foreach ( $args as $key => $value ) {
if ( empty( $value ) && ! is_bool( $value ) ) {
unset( $args[ $key ] );
unset( $format[ $i ] );
}
$i++;
}
// Set the attribute slug.
if ( ! empty( $args['attribute_name'] ) ) {
$args['attribute_name'] = preg_replace( '/^pa\_/', '', wc_sanitize_taxonomy_name( stripslashes( $args['attribute_name'] ) ) );
$valid_slug = $this->validate_attribute_slug( $args['attribute_name'], false );
if ( is_wp_error( $valid_slug ) ) {
return $valid_slug;
}
}
if ( $args ) {
$update = $wpdb->update(
$wpdb->prefix . 'woocommerce_attribute_taxonomies',
$args,
array( 'attribute_id' => $id ),
$format,
array( '%d' )
);
// Checks for errors. // Checks for errors.
if ( false === $update ) { if ( is_wp_error( $edited ) ) {
return new WP_Error( 'woocommerce_rest_cannot_edit', __( 'Could not edit the attribute.', 'woocommerce' ), array( 'status' => 400 ) ); return new WP_Error( 'woocommerce_rest_cannot_edit', $edited->get_error_message(), array( 'status' => 400 ) );
}
} }
$attribute = $this->get_attribute( $id ); $attribute = $this->get_attribute( $id );
@ -393,10 +340,6 @@ class WC_REST_Product_Attributes_V1_Controller extends WC_REST_Controller {
$request->set_param( 'context', 'edit' ); $request->set_param( 'context', 'edit' );
$response = $this->prepare_item_for_response( $attribute, $request ); $response = $this->prepare_item_for_response( $attribute, $request );
// Clear transients.
$this->flush_rewrite_rules();
delete_transient( 'wc_attribute_taxonomies' );
return rest_ensure_response( $response ); return rest_ensure_response( $response );
} }
@ -407,8 +350,6 @@ class WC_REST_Product_Attributes_V1_Controller extends WC_REST_Controller {
* @return WP_REST_Response|WP_Error * @return WP_REST_Response|WP_Error
*/ */
public function delete_item( $request ) { public function delete_item( $request ) {
global $wpdb;
$force = isset( $request['force'] ) ? (bool) $request['force'] : false; $force = isset( $request['force'] ) ? (bool) $request['force'] : false;
// We don't support trashing for this type, error out. // We don't support trashing for this type, error out.
@ -425,25 +366,12 @@ class WC_REST_Product_Attributes_V1_Controller extends WC_REST_Controller {
$request->set_param( 'context', 'edit' ); $request->set_param( 'context', 'edit' );
$response = $this->prepare_item_for_response( $attribute, $request ); $response = $this->prepare_item_for_response( $attribute, $request );
$deleted = $wpdb->delete( $deleted = wc_delete_attribute( $attribute->attribute_id );
$wpdb->prefix . 'woocommerce_attribute_taxonomies',
array( 'attribute_id' => $attribute->attribute_id ),
array( '%d' )
);
if ( false === $deleted ) { if ( false === $deleted ) {
return new WP_Error( 'woocommerce_rest_cannot_delete', __( 'The resource cannot be deleted.', 'woocommerce' ), array( 'status' => 500 ) ); return new WP_Error( 'woocommerce_rest_cannot_delete', __( 'The resource cannot be deleted.', 'woocommerce' ), array( 'status' => 500 ) );
} }
$taxonomy = wc_attribute_taxonomy_name( $attribute->attribute_name );
if ( taxonomy_exists( $taxonomy ) ) {
$terms = get_terms( $taxonomy, 'orderby=name&hide_empty=0' );
foreach ( $terms as $term ) {
wp_delete_term( $term->term_id, $taxonomy );
}
}
/** /**
* Fires after a single attribute is deleted via the REST API. * Fires after a single attribute is deleted via the REST API.
* *
@ -453,13 +381,6 @@ class WC_REST_Product_Attributes_V1_Controller extends WC_REST_Controller {
*/ */
do_action( 'woocommerce_rest_delete_product_attribute', $attribute, $response, $request ); do_action( 'woocommerce_rest_delete_product_attribute', $attribute, $response, $request );
// Fires woocommerce_attribute_deleted hook.
do_action( 'woocommerce_attribute_deleted', $attribute->attribute_id, $attribute->attribute_name, $taxonomy );
// Clear transients.
$this->flush_rewrite_rules();
delete_transient( 'wc_attribute_taxonomies' );
return $response; return $response;
} }
@ -636,6 +557,7 @@ class WC_REST_Product_Attributes_V1_Controller extends WC_REST_Controller {
/** /**
* Validate attribute slug. * Validate attribute slug.
* *
* @deprecated 3.2.0
* @param string $slug * @param string $slug
* @param bool $new_data * @param bool $new_data
* @return bool|WP_Error * @return bool|WP_Error
@ -655,6 +577,7 @@ class WC_REST_Product_Attributes_V1_Controller extends WC_REST_Controller {
/** /**
* Schedule to flush rewrite rules. * Schedule to flush rewrite rules.
* *
* @deprecated 3.2.0
* @since 3.0.0 * @since 3.0.0
*/ */
protected function flush_rewrite_rules() { protected function flush_rewrite_rules() {

View File

@ -586,43 +586,39 @@ abstract class WC_Product_Importer implements WC_Importer_Interface {
} }
// If the attribute does not exist, create it. // If the attribute does not exist, create it.
$args = array( $attribute_id = wc_create_attribute( array(
'attribute_label' => $raw_name, 'name' => $raw_name,
'attribute_name' => $attribute_name, 'slug' => $attribute_name,
'attribute_type' => 'select', 'type' => 'select',
'attribute_orderby' => 'menu_order', 'order_by' => 'menu_order',
'attribute_public' => 0, 'has_archives' => false,
); ) );
// Validate attribute. if ( is_wp_error( $attribute_id ) ) {
if ( strlen( $attribute_name ) >= 28 ) { throw new Exception( $attribute_id->get_error_message(), 400 );
throw new Exception( sprintf( __( 'Slug "%s" is too long (28 characters max). Shorten it, please.', 'woocommerce' ), $attribute_name ), 400 );
} elseif ( wc_check_if_attribute_name_is_reserved( $attribute_name ) ) {
throw new Exception( sprintf( __( 'Slug "%s" is not allowed because it is a reserved term. Change it, please.', 'woocommerce' ), $attribute_name ), 400 );
} elseif ( taxonomy_exists( wc_attribute_taxonomy_name( $attribute_name ) ) ) {
throw new Exception( sprintf( __( 'Slug "%s" is already in use. Change it, please.', 'woocommerce' ), $attribute_name ), 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 );
}
$attribute_id = absint( $wpdb->insert_id );
// Delete transient.
delete_transient( 'wc_attribute_taxonomies' );
// Register as taxonomy while importing. // Register as taxonomy while importing.
register_taxonomy( wc_attribute_taxonomy_name( $attribute_name ), array( 'product' ), array( 'labels' => array( 'name' => $raw_name ) ) ); $taxonomy_name = wc_attribute_taxonomy_name( $attribute_name );
register_taxonomy(
$taxonomy_name,
apply_filters( 'woocommerce_taxonomy_objects_' . $taxonomy_name, array( 'product' ) ),
apply_filters( 'woocommerce_taxonomy_args_' . $taxonomy_name, array(
'labels' => array(
'name' => $raw_name,
),
'hierarchical' => true,
'show_ui' => false,
'query_var' => true,
'rewrite' => false,
) )
);
// Set product attributes global. // Set product attributes global.
$wc_product_attributes = array(); $wc_product_attributes = array();
foreach ( wc_get_attribute_taxonomies() as $tax ) { foreach ( wc_get_attribute_taxonomies() as $taxonomy ) {
$wc_product_attributes[ wc_attribute_taxonomy_name( $attribute_name ) ] = $tax; $wc_product_attributes[ wc_attribute_taxonomy_name( $taxonomy['attribute_name'] ) ] = $taxonomy;
} }
return $attribute_id; return $attribute_id;