From a3a3d7c24241b12c8b589adbf0d28f1a736f4305 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Mon, 7 Aug 2017 22:33:57 -0300 Subject: [PATCH 1/3] Initial implementation of product attribute helper functions --- includes/admin/class-wc-admin-attributes.php | 68 ++++++++++---------- 1 file changed, 33 insertions(+), 35 deletions(-) diff --git a/includes/admin/class-wc-admin-attributes.php b/includes/admin/class-wc-admin-attributes.php index d3c16cd0491..7323701b133 100644 --- a/includes/admin/class-wc-admin-attributes.php +++ b/includes/admin/class-wc-admin-attributes.php @@ -88,48 +88,38 @@ class WC_Admin_Attributes { 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. * @return bool|WP_Error */ private static function process_add_attribute() { - global $wpdb; check_admin_referer( 'woocommerce-add-new_attribute' ); $attribute = self::get_posted_attribute(); - if ( empty( $attribute['attribute_name'] ) || empty( $attribute['attribute_label'] ) ) { - 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 ) ) { - return $valid_attribute_name; - } 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'] ) ) ); + $args = array( + 'name' => $attribute['attribute_label'], + 'slug' => $attribute['attribute_name'], + 'type' => $attribute['attribute_type'], + 'order_by' => $attribute['attribute_orderby'], + 'has_archives' => $attribute['attribute_public'], + ); + + $id = wc_create_attribute( $args ); + + if ( is_wp_error( $id ) ) { + return $id; } - $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' ); + /** + * Attribute added. + * + * @deprecated 3.2.0 + * + * @param int $id Added attribute ID. + * @param array $attribute Attribute arguments. + */ + do_action( 'woocommerce_attribute_added', $id, $attribute ); return true; } @@ -145,10 +135,18 @@ class WC_Admin_Attributes { $attribute = self::get_posted_attribute(); - if ( empty( $attribute['attribute_name'] ) || empty( $attribute['attribute_label'] ) ) { - 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 ) ) { - return $valid_attribute_name; + $args = array( + 'name' => $attribute['attribute_label'], + 'slug' => $attribute['attribute_name'], + 'type' => $attribute['attribute_type'], + 'order_by' => $attribute['attribute_orderby'], + 'has_archives' => $attribute['attribute_public'], + ); + + $id = wc_update_attribute( $attribute_id, $args ); + + if ( is_wp_error( $id ) ) { + return $id; } $taxonomy_exists = taxonomy_exists( wc_attribute_taxonomy_name( $attribute['attribute_name'] ) ); From 70e0ab857e6b973e1f5998d9e949ddfe3600dc79 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Mon, 7 Aug 2017 21:42:56 -0300 Subject: [PATCH 2/3] Implement attributes functions to admin --- includes/admin/class-wc-admin-attributes.php | 97 ++------------------ 1 file changed, 6 insertions(+), 91 deletions(-) diff --git a/includes/admin/class-wc-admin-attributes.php b/includes/admin/class-wc-admin-attributes.php index 7323701b133..3e46f1aaa57 100644 --- a/includes/admin/class-wc-admin-attributes.php +++ b/includes/admin/class-wc-admin-attributes.php @@ -90,14 +90,14 @@ class WC_Admin_Attributes { /** * Add an attribute. + * * @return bool|WP_Error */ private static function process_add_attribute() { check_admin_referer( 'woocommerce-add-new_attribute' ); $attribute = self::get_posted_attribute(); - - $args = array( + $args = array( 'name' => $attribute['attribute_label'], 'slug' => $attribute['attribute_name'], 'type' => $attribute['attribute_type'], @@ -111,31 +111,20 @@ class WC_Admin_Attributes { return $id; } - /** - * Attribute added. - * - * @deprecated 3.2.0 - * - * @param int $id Added attribute ID. - * @param array $attribute Attribute arguments. - */ - do_action( 'woocommerce_attribute_added', $id, $attribute ); - return true; } /** * Edit an attribute. + * * @return bool|WP_Error */ private static function process_edit_attribute() { - global $wpdb; $attribute_id = absint( $_GET['edit'] ); check_admin_referer( 'woocommerce-save-attribute_' . $attribute_id ); $attribute = self::get_posted_attribute(); - - $args = array( + $args = array( 'name' => $attribute['attribute_label'], 'slug' => $attribute['attribute_name'], 'type' => $attribute['attribute_type'], @@ -149,95 +138,21 @@ class WC_Admin_Attributes { return $id; } - $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 - 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 - $old_attribute_name_length = strlen( $old_attribute_name ) + 3; - $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 '

' . __( 'Attribute updated successfully', 'woocommerce' ) . '

' . __( 'Back to Attributes', 'woocommerce' ) . '

'; - wp_schedule_single_event( time(), 'woocommerce_flush_rewrite_rules' ); - delete_transient( 'wc_attribute_taxonomies' ); - return true; } /** * Delete an attribute. + * * @return bool */ private static function process_delete_attribute() { - global $wpdb; - $attribute_id = absint( $_GET['delete'] ); - 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" ); - $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; + return wc_delete_attribute( $attribute_id ); } /** From 1a9b499be8c032d6c903e51f7a6bb4ef3eeb3a19 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Mon, 7 Aug 2017 22:14:10 -0300 Subject: [PATCH 3/3] Apply attribute helper functions to importers and REST API --- includes/admin/class-wc-admin-importers.php | 16 +-- ...-wc-rest-product-attributes-controller.php | 123 ++++-------------- .../import/abstract-wc-product-importer.php | 54 ++++---- 3 files changed, 55 insertions(+), 138 deletions(-) diff --git a/includes/admin/class-wc-admin-importers.php b/includes/admin/class-wc-admin-importers.php index 4a4afb4eb98..43d88fa4d66 100644 --- a/includes/admin/class-wc-admin-importers.php +++ b/includes/admin/class-wc-admin-importers.php @@ -158,15 +158,13 @@ class WC_Admin_Importers { // Create the taxonomy if ( ! in_array( $attribute_name, wc_get_attribute_taxonomies() ) ) { - $attribute = array( - 'attribute_label' => $attribute_name, - 'attribute_name' => $attribute_name, - 'attribute_type' => 'select', - 'attribute_orderby' => 'menu_order', - 'attribute_public' => 0, - ); - $wpdb->insert( $wpdb->prefix . 'woocommerce_attribute_taxonomies', $attribute ); - delete_transient( 'wc_attribute_taxonomies' ); + wc_create_attribute( array( + 'name' => $attribute_name, + 'slug' => $attribute_name, + 'type' => 'select', + 'order_by' => 'menu_order', + 'has_archives' => false, + ) ); } // Register the taxonomy now so that the import works! diff --git a/includes/api/v1/class-wc-rest-product-attributes-controller.php b/includes/api/v1/class-wc-rest-product-attributes-controller.php index 32fe92f5189..e4290657e1d 100644 --- a/includes/api/v1/class-wc-rest-product-attributes-controller.php +++ b/includes/api/v1/class-wc-rest-product-attributes-controller.php @@ -240,38 +240,20 @@ class WC_REST_Product_Attributes_V1_Controller extends WC_REST_Controller { public function create_item( $request ) { global $wpdb; - $args = array( - 'attribute_label' => $request['name'], - 'attribute_name' => wc_sanitize_taxonomy_name( stripslashes( $request['slug'] ) ), - 'attribute_type' => ! empty( $request['type'] ) ? $request['type'] : 'select', - 'attribute_orderby' => ! empty( $request['order_by'] ) ? $request['order_by'] : 'menu_order', - 'attribute_public' => 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' ) - ); + $id = wc_create_attribute( array( + 'name' => $request['name'], + 'slug' => wc_sanitize_taxonomy_name( stripslashes( $request['slug'] ) ), + 'type' => ! empty( $request['type'] ) ? $request['type'] : 'select', + 'order_by' => ! empty( $request['order_by'] ) ? $request['order_by'] : 'menu_order', + 'has_archives' => true === $request['has_archives'], + ) ); // Checks for errors. - if ( is_wp_error( $insert ) ) { - return new WP_Error( 'woocommerce_rest_cannot_create', $insert->get_error_message(), array( 'status' => 400 ) ); + if ( is_wp_error( $id ) ) { + 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 ) ) { return $attribute; @@ -294,10 +276,6 @@ class WC_REST_Product_Attributes_V1_Controller extends WC_REST_Controller { $response->set_status( 201 ); $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; } @@ -329,48 +307,17 @@ class WC_REST_Product_Attributes_V1_Controller extends WC_REST_Controller { global $wpdb; $id = (int) $request['id']; - $format = array( '%s', '%s', '%s', '%s', '%d' ); - $args = array( - 'attribute_label' => $request['name'], - 'attribute_name' => wc_sanitize_taxonomy_name( stripslashes( $request['slug'] ) ), - 'attribute_type' => $request['type'], - 'attribute_orderby' => $request['order_by'], - 'attribute_public' => $request['has_archives'], - ); + $edited = wc_update_attribute( $id, array( + 'name' => $request['name'], + 'slug' => wc_sanitize_taxonomy_name( stripslashes( $request['slug'] ) ), + 'type' => $request['type'], + 'order_by' => $request['order_by'], + 'has_archives' => $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. - if ( false === $update ) { - return new WP_Error( 'woocommerce_rest_cannot_edit', __( 'Could not edit the attribute.', 'woocommerce' ), array( 'status' => 400 ) ); - } + // Checks for errors. + if ( is_wp_error( $edited ) ) { + return new WP_Error( 'woocommerce_rest_cannot_edit', $edited->get_error_message(), array( 'status' => 400 ) ); } $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' ); $response = $this->prepare_item_for_response( $attribute, $request ); - // Clear transients. - $this->flush_rewrite_rules(); - delete_transient( 'wc_attribute_taxonomies' ); - 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 */ public function delete_item( $request ) { - global $wpdb; - $force = isset( $request['force'] ) ? (bool) $request['force'] : false; // 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' ); $response = $this->prepare_item_for_response( $attribute, $request ); - $deleted = $wpdb->delete( - $wpdb->prefix . 'woocommerce_attribute_taxonomies', - array( 'attribute_id' => $attribute->attribute_id ), - array( '%d' ) - ); + $deleted = wc_delete_attribute( $attribute->attribute_id ); if ( false === $deleted ) { 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. * @@ -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 ); - // 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; } @@ -636,6 +557,7 @@ class WC_REST_Product_Attributes_V1_Controller extends WC_REST_Controller { /** * Validate attribute slug. * + * @deprecated 3.2.0 * @param string $slug * @param bool $new_data * @return bool|WP_Error @@ -655,6 +577,7 @@ class WC_REST_Product_Attributes_V1_Controller extends WC_REST_Controller { /** * Schedule to flush rewrite rules. * + * @deprecated 3.2.0 * @since 3.0.0 */ protected function flush_rewrite_rules() { diff --git a/includes/import/abstract-wc-product-importer.php b/includes/import/abstract-wc-product-importer.php index 251139d29fa..e7ce0cfb359 100644 --- a/includes/import/abstract-wc-product-importer.php +++ b/includes/import/abstract-wc-product-importer.php @@ -586,43 +586,39 @@ abstract class WC_Product_Importer implements WC_Importer_Interface { } // If the attribute does not exist, create it. - $args = array( - 'attribute_label' => $raw_name, - 'attribute_name' => $attribute_name, - 'attribute_type' => 'select', - 'attribute_orderby' => 'menu_order', - 'attribute_public' => 0, - ); + $attribute_id = wc_create_attribute( array( + 'name' => $raw_name, + 'slug' => $attribute_name, + 'type' => 'select', + 'order_by' => 'menu_order', + 'has_archives' => false, + ) ); - // Validate attribute. - if ( strlen( $attribute_name ) >= 28 ) { - 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 ); + if ( is_wp_error( $attribute_id ) ) { + throw new Exception( $attribute_id->get_error_message(), 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_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. $wc_product_attributes = array(); - foreach ( wc_get_attribute_taxonomies() as $tax ) { - $wc_product_attributes[ wc_attribute_taxonomy_name( $attribute_name ) ] = $tax; + foreach ( wc_get_attribute_taxonomies() as $taxonomy ) { + $wc_product_attributes[ wc_attribute_taxonomy_name( $taxonomy['attribute_name'] ) ] = $taxonomy; } return $attribute_id;