wc-attribute-functions.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654
  1. <?php
  2. /**
  3. * WooCommerce Attribute Functions
  4. *
  5. * @package WooCommerce/Functions
  6. * @version 2.1.0
  7. */
  8. defined( 'ABSPATH' ) || exit;
  9. /**
  10. * Gets text attributes from a string.
  11. *
  12. * @since 2.4
  13. * @param string $raw_attributes Raw attributes.
  14. * @return array
  15. */
  16. function wc_get_text_attributes( $raw_attributes ) {
  17. return array_filter( array_map( 'trim', explode( WC_DELIMITER, html_entity_decode( $raw_attributes, ENT_QUOTES, get_bloginfo( 'charset' ) ) ) ), 'wc_get_text_attributes_filter_callback' );
  18. }
  19. /**
  20. * See if an attribute is actually valid.
  21. *
  22. * @since 3.0.0
  23. * @param string $value Value.
  24. * @return bool
  25. */
  26. function wc_get_text_attributes_filter_callback( $value ) {
  27. return '' !== $value;
  28. }
  29. /**
  30. * Implode an array of attributes using WC_DELIMITER.
  31. *
  32. * @since 3.0.0
  33. * @param array $attributes Attributes list.
  34. * @return string
  35. */
  36. function wc_implode_text_attributes( $attributes ) {
  37. return implode( ' ' . WC_DELIMITER . ' ', $attributes );
  38. }
  39. /**
  40. * Get attribute taxonomies.
  41. *
  42. * @return array of objects
  43. */
  44. function wc_get_attribute_taxonomies() {
  45. $attribute_taxonomies = get_transient( 'wc_attribute_taxonomies' );
  46. if ( false === $attribute_taxonomies ) {
  47. global $wpdb;
  48. $attribute_taxonomies = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}woocommerce_attribute_taxonomies WHERE attribute_name != '' ORDER BY attribute_name ASC;" );
  49. set_transient( 'wc_attribute_taxonomies', $attribute_taxonomies );
  50. }
  51. return (array) array_filter( apply_filters( 'woocommerce_attribute_taxonomies', $attribute_taxonomies ) );
  52. }
  53. /**
  54. * Get a product attribute name.
  55. *
  56. * @param string $attribute_name Attribute name.
  57. * @return string
  58. */
  59. function wc_attribute_taxonomy_name( $attribute_name ) {
  60. return $attribute_name ? 'pa_' . wc_sanitize_taxonomy_name( $attribute_name ) : '';
  61. }
  62. /**
  63. * Get the attribute name used when storing values in post meta.
  64. *
  65. * @since 2.6.0
  66. * @param string $attribute_name Attribute name.
  67. * @return string
  68. */
  69. function wc_variation_attribute_name( $attribute_name ) {
  70. return 'attribute_' . sanitize_title( $attribute_name );
  71. }
  72. /**
  73. * Get a product attribute name by ID.
  74. *
  75. * @since 2.4.0
  76. * @param int $attribute_id Attribute ID.
  77. * @return string Return an empty string if attribute doesn't exist.
  78. */
  79. function wc_attribute_taxonomy_name_by_id( $attribute_id ) {
  80. global $wpdb;
  81. $attribute_name = $wpdb->get_var(
  82. $wpdb->prepare(
  83. "
  84. SELECT attribute_name
  85. FROM {$wpdb->prefix}woocommerce_attribute_taxonomies
  86. WHERE attribute_id = %d
  87. ", $attribute_id
  88. )
  89. );
  90. if ( $attribute_name && ! is_wp_error( $attribute_name ) ) {
  91. return wc_attribute_taxonomy_name( $attribute_name );
  92. }
  93. return '';
  94. }
  95. /**
  96. * Get a product attribute ID by name.
  97. *
  98. * @since 2.6.0
  99. * @param string $name Attribute name.
  100. * @return int
  101. */
  102. function wc_attribute_taxonomy_id_by_name( $name ) {
  103. $name = str_replace( 'pa_', '', wc_sanitize_taxonomy_name( $name ) );
  104. $taxonomies = wp_list_pluck( wc_get_attribute_taxonomies(), 'attribute_id', 'attribute_name' );
  105. return isset( $taxonomies[ $name ] ) ? (int) $taxonomies[ $name ] : 0;
  106. }
  107. /**
  108. * Get a product attributes label.
  109. *
  110. * @param string $name Attribute name.
  111. * @param WC_Product $product Product data.
  112. * @return string
  113. */
  114. function wc_attribute_label( $name, $product = '' ) {
  115. if ( taxonomy_is_product_attribute( $name ) ) {
  116. $name = wc_sanitize_taxonomy_name( str_replace( 'pa_', '', $name ) );
  117. $all_labels = wp_list_pluck( wc_get_attribute_taxonomies(), 'attribute_label', 'attribute_name' );
  118. $label = isset( $all_labels[ $name ] ) ? $all_labels[ $name ] : $name;
  119. } elseif ( $product ) {
  120. if ( $product->is_type( 'variation' ) ) {
  121. $product = wc_get_product( $product->get_parent_id() );
  122. }
  123. $attributes = array();
  124. if ( false !== $product ) {
  125. $attributes = $product->get_attributes();
  126. }
  127. // Attempt to get label from product, as entered by the user.
  128. if ( $attributes && isset( $attributes[ sanitize_title( $name ) ] ) ) {
  129. $label = $attributes[ sanitize_title( $name ) ]->get_name();
  130. } else {
  131. $label = $name;
  132. }
  133. } else {
  134. $label = $name;
  135. }
  136. return apply_filters( 'woocommerce_attribute_label', $label, $name, $product );
  137. }
  138. /**
  139. * Get a product attributes orderby setting.
  140. *
  141. * @param string $name Attribute name.
  142. * @return string
  143. */
  144. function wc_attribute_orderby( $name ) {
  145. global $wc_product_attributes, $wpdb;
  146. $name = str_replace( 'pa_', '', sanitize_title( $name ) );
  147. if ( isset( $wc_product_attributes[ 'pa_' . $name ] ) ) {
  148. $orderby = $wc_product_attributes[ 'pa_' . $name ]->attribute_orderby;
  149. } else {
  150. $orderby = $wpdb->get_var( $wpdb->prepare( "SELECT attribute_orderby FROM {$wpdb->prefix}woocommerce_attribute_taxonomies WHERE attribute_name = %s;", $name ) );
  151. }
  152. return apply_filters( 'woocommerce_attribute_orderby', $orderby, $name );
  153. }
  154. /**
  155. * Get an array of product attribute taxonomies.
  156. *
  157. * @return array
  158. */
  159. function wc_get_attribute_taxonomy_names() {
  160. $taxonomy_names = array();
  161. $attribute_taxonomies = wc_get_attribute_taxonomies();
  162. if ( ! empty( $attribute_taxonomies ) ) {
  163. foreach ( $attribute_taxonomies as $tax ) {
  164. $taxonomy_names[] = wc_attribute_taxonomy_name( $tax->attribute_name );
  165. }
  166. }
  167. return $taxonomy_names;
  168. }
  169. /**
  170. * Get attribute types.
  171. *
  172. * @since 2.4.0
  173. * @return array
  174. */
  175. function wc_get_attribute_types() {
  176. return (array) apply_filters(
  177. 'product_attributes_type_selector', array(
  178. 'select' => __( 'Select', 'woocommerce' ),
  179. )
  180. );
  181. }
  182. /**
  183. * Check if there are custom attribute types.
  184. *
  185. * @since 3.3.2
  186. * @return bool True if there are custom types, otherwise false.
  187. */
  188. function wc_has_custom_attribute_types() {
  189. $types = wc_get_attribute_types();
  190. return 1 < count( $types ) || ! array_key_exists( 'select', $types );
  191. }
  192. /**
  193. * Get attribute type label.
  194. *
  195. * @since 3.0.0
  196. * @param string $type Attribute type slug.
  197. * @return string
  198. */
  199. function wc_get_attribute_type_label( $type ) {
  200. $types = wc_get_attribute_types();
  201. return isset( $types[ $type ] ) ? $types[ $type ] : __( 'Select', 'woocommerce' );
  202. }
  203. /**
  204. * Check if attribute name is reserved.
  205. * https://codex.wordpress.org/Function_Reference/register_taxonomy#Reserved_Terms.
  206. *
  207. * @since 2.4.0
  208. * @param string $attribute_name Attribute name.
  209. * @return bool
  210. */
  211. function wc_check_if_attribute_name_is_reserved( $attribute_name ) {
  212. // Forbidden attribute names.
  213. $reserved_terms = array(
  214. 'attachment',
  215. 'attachment_id',
  216. 'author',
  217. 'author_name',
  218. 'calendar',
  219. 'cat',
  220. 'category',
  221. 'category__and',
  222. 'category__in',
  223. 'category__not_in',
  224. 'category_name',
  225. 'comments_per_page',
  226. 'comments_popup',
  227. 'cpage',
  228. 'day',
  229. 'debug',
  230. 'error',
  231. 'exact',
  232. 'feed',
  233. 'hour',
  234. 'link_category',
  235. 'm',
  236. 'minute',
  237. 'monthnum',
  238. 'more',
  239. 'name',
  240. 'nav_menu',
  241. 'nopaging',
  242. 'offset',
  243. 'order',
  244. 'orderby',
  245. 'p',
  246. 'page',
  247. 'page_id',
  248. 'paged',
  249. 'pagename',
  250. 'pb',
  251. 'perm',
  252. 'post',
  253. 'post__in',
  254. 'post__not_in',
  255. 'post_format',
  256. 'post_mime_type',
  257. 'post_status',
  258. 'post_tag',
  259. 'post_type',
  260. 'posts',
  261. 'posts_per_archive_page',
  262. 'posts_per_page',
  263. 'preview',
  264. 'robots',
  265. 's',
  266. 'search',
  267. 'second',
  268. 'sentence',
  269. 'showposts',
  270. 'static',
  271. 'subpost',
  272. 'subpost_id',
  273. 'tag',
  274. 'tag__and',
  275. 'tag__in',
  276. 'tag__not_in',
  277. 'tag_id',
  278. 'tag_slug__and',
  279. 'tag_slug__in',
  280. 'taxonomy',
  281. 'tb',
  282. 'term',
  283. 'type',
  284. 'w',
  285. 'withcomments',
  286. 'withoutcomments',
  287. 'year',
  288. );
  289. return in_array( $attribute_name, $reserved_terms, true );
  290. }
  291. /**
  292. * Callback for array filter to get visible only.
  293. *
  294. * @since 3.0.0
  295. * @param WC_Product_Attribute $attribute Attribute data.
  296. * @return bool
  297. */
  298. function wc_attributes_array_filter_visible( $attribute ) {
  299. return $attribute && is_a( $attribute, 'WC_Product_Attribute' ) && $attribute->get_visible() && ( ! $attribute->is_taxonomy() || taxonomy_exists( $attribute->get_name() ) );
  300. }
  301. /**
  302. * Callback for array filter to get variation attributes only.
  303. *
  304. * @since 3.0.0
  305. * @param WC_Product_Attribute $attribute Attribute data.
  306. * @return bool
  307. */
  308. function wc_attributes_array_filter_variation( $attribute ) {
  309. return $attribute && is_a( $attribute, 'WC_Product_Attribute' ) && $attribute->get_variation();
  310. }
  311. /**
  312. * Check if an attribute is included in the attributes area of a variation name.
  313. *
  314. * @since 3.0.2
  315. * @param string $attribute Attribute value to check for.
  316. * @param string $name Product name to check in.
  317. * @return bool
  318. */
  319. function wc_is_attribute_in_product_name( $attribute, $name ) {
  320. $is_in_name = stristr( $name, ' ' . $attribute . ',' ) || 0 === stripos( strrev( $name ), strrev( ' ' . $attribute ) );
  321. return apply_filters( 'woocommerce_is_attribute_in_product_name', $is_in_name, $attribute, $name );
  322. }
  323. /**
  324. * Callback for array filter to get default attributes. Will allow for '0' string values, but regard all other
  325. * class PHP FALSE equivalents normally.
  326. *
  327. * @since 3.1.0
  328. * @param mixed $attribute Attribute being considered for exclusion from parent array.
  329. * @return bool
  330. */
  331. function wc_array_filter_default_attributes( $attribute ) {
  332. return ( ! empty( $attribute ) || '0' === $attribute );
  333. }
  334. /**
  335. * Get attribute data by ID.
  336. *
  337. * @since 3.2.0
  338. * @param int $id Attribute ID.
  339. * @return stdClass|null
  340. */
  341. function wc_get_attribute( $id ) {
  342. global $wpdb;
  343. $data = $wpdb->get_row(
  344. $wpdb->prepare(
  345. "
  346. SELECT *
  347. FROM {$wpdb->prefix}woocommerce_attribute_taxonomies
  348. WHERE attribute_id = %d
  349. ", $id
  350. )
  351. );
  352. if ( is_wp_error( $data ) || is_null( $data ) ) {
  353. return null;
  354. }
  355. $attribute = new stdClass();
  356. $attribute->id = (int) $data->attribute_id;
  357. $attribute->name = $data->attribute_label;
  358. $attribute->slug = wc_attribute_taxonomy_name( $data->attribute_name );
  359. $attribute->type = $data->attribute_type;
  360. $attribute->order_by = $data->attribute_orderby;
  361. $attribute->has_archives = (bool) $data->attribute_public;
  362. return $attribute;
  363. }
  364. /**
  365. * Create attribute.
  366. *
  367. * @since 3.2.0
  368. * @param array $args Attribute arguments {
  369. * Array of attribute parameters.
  370. *
  371. * @type int $id Unique identifier, used to update an attribute.
  372. * @type string $name Attribute name. Always required.
  373. * @type string $slug Attribute alphanumeric identifier.
  374. * @type string $type Type of attribute.
  375. * Core by default accepts: 'select' and 'text'.
  376. * Default to 'select'.
  377. * @type string $order_by Sort order.
  378. * Accepts: 'menu_order', 'name', 'name_num' and 'id'.
  379. * Default to 'menu_order'.
  380. * @type bool $has_archives Enable or disable attribute archives. False by default.
  381. * }
  382. * @return int|WP_Error
  383. */
  384. function wc_create_attribute( $args ) {
  385. global $wpdb;
  386. $args = wp_unslash( $args );
  387. $id = ! empty( $args['id'] ) ? intval( $args['id'] ) : 0;
  388. $format = array( '%s', '%s', '%s', '%s', '%d' );
  389. // Name is required.
  390. if ( empty( $args['name'] ) ) {
  391. return new WP_Error( 'missing_attribute_name', __( 'Please, provide an attribute name.', 'woocommerce' ), array( 'status' => 400 ) );
  392. }
  393. // Set the attribute slug.
  394. if ( empty( $args['slug'] ) ) {
  395. $slug = wc_sanitize_taxonomy_name( $args['name'] );
  396. } else {
  397. $slug = preg_replace( '/^pa\_/', '', wc_sanitize_taxonomy_name( $args['slug'] ) );
  398. }
  399. // Validate slug.
  400. if ( strlen( $slug ) >= 28 ) {
  401. /* translators: %s: attribute slug */
  402. return new WP_Error( 'invalid_product_attribute_slug_too_long', sprintf( __( 'Slug "%s" is too long (28 characters max). Shorten it, please.', 'woocommerce' ), $slug ), array( 'status' => 400 ) );
  403. } elseif ( wc_check_if_attribute_name_is_reserved( $slug ) ) {
  404. /* translators: %s: attribute slug */
  405. return new WP_Error( 'invalid_product_attribute_slug_reserved_name', sprintf( __( 'Slug "%s" is not allowed because it is a reserved term. Change it, please.', 'woocommerce' ), $slug ), array( 'status' => 400 ) );
  406. } elseif ( ( 0 === $id && taxonomy_exists( wc_attribute_taxonomy_name( $slug ) ) ) || ( isset( $args['old_slug'] ) && $args['old_slug'] !== $slug && taxonomy_exists( wc_attribute_taxonomy_name( $slug ) ) ) ) {
  407. /* translators: %s: attribute slug */
  408. return new WP_Error( 'invalid_product_attribute_slug_already_exists', sprintf( __( 'Slug "%s" is already in use. Change it, please.', 'woocommerce' ), $slug ), array( 'status' => 400 ) );
  409. }
  410. // Validate type.
  411. if ( empty( $args['type'] ) || ! array_key_exists( $args['type'], wc_get_attribute_types() ) ) {
  412. $args['type'] = 'select';
  413. }
  414. // Validate order by.
  415. if ( empty( $args['order_by'] ) || ! in_array( $args['order_by'], array( 'menu_order', 'name', 'name_num', 'id' ), true ) ) {
  416. $args['order_by'] = 'menu_order';
  417. }
  418. $data = array(
  419. 'attribute_label' => $args['name'],
  420. 'attribute_name' => $slug,
  421. 'attribute_type' => $args['type'],
  422. 'attribute_orderby' => $args['order_by'],
  423. 'attribute_public' => isset( $args['has_archives'] ) ? (int) $args['has_archives'] : 0,
  424. );
  425. // Create or update.
  426. if ( 0 === $id ) {
  427. $results = $wpdb->insert(
  428. $wpdb->prefix . 'woocommerce_attribute_taxonomies',
  429. $data,
  430. $format
  431. );
  432. if ( is_wp_error( $results ) ) {
  433. return new WP_Error( 'cannot_create_attribute', $results->get_error_message(), array( 'status' => 400 ) );
  434. }
  435. $id = $wpdb->insert_id;
  436. /**
  437. * Attribute added.
  438. *
  439. * @param int $id Added attribute ID.
  440. * @param array $data Attribute data.
  441. */
  442. do_action( 'woocommerce_attribute_added', $id, $data );
  443. } else {
  444. $results = $wpdb->update(
  445. $wpdb->prefix . 'woocommerce_attribute_taxonomies',
  446. $data,
  447. array( 'attribute_id' => $id ),
  448. $format,
  449. array( '%d' )
  450. );
  451. if ( false === $results ) {
  452. return new WP_Error( 'cannot_update_attribute', __( 'Could not update the attribute.', 'woocommerce' ), array( 'status' => 400 ) );
  453. }
  454. // Set old slug to check for database changes.
  455. $old_slug = ! empty( $args['old_slug'] ) ? wc_sanitize_taxonomy_name( $args['old_slug'] ) : $slug;
  456. /**
  457. * Attribute updated.
  458. *
  459. * @param int $id Added attribute ID.
  460. * @param array $data Attribute data.
  461. * @param string $old_slug Attribute old name.
  462. */
  463. do_action( 'woocommerce_attribute_updated', $id, $data, $old_slug );
  464. if ( $old_slug !== $slug ) {
  465. // Update taxonomies in the wp term taxonomy table.
  466. $wpdb->update(
  467. $wpdb->term_taxonomy,
  468. array( 'taxonomy' => wc_attribute_taxonomy_name( $data['attribute_name'] ) ),
  469. array( 'taxonomy' => 'pa_' . $old_slug )
  470. );
  471. // Update taxonomy ordering term meta.
  472. $table_name = get_option( 'db_version' ) < 34370 ? $wpdb->prefix . 'woocommerce_termmeta' : $wpdb->termmeta;
  473. $wpdb->update(
  474. $table_name,
  475. array( 'meta_key' => 'order_pa_' . sanitize_title( $data['attribute_name'] ) ), // WPCS: slow query ok.
  476. array( 'meta_key' => 'order_pa_' . sanitize_title( $old_slug ) ) // WPCS: slow query ok.
  477. );
  478. // Update product attributes which use this taxonomy.
  479. $old_attribute_name_length = strlen( $old_slug ) + 3;
  480. $attribute_name_length = strlen( $data['attribute_name'] ) + 3;
  481. $wpdb->query(
  482. $wpdb->prepare(
  483. "UPDATE {$wpdb->postmeta} SET meta_value = REPLACE( meta_value, %s, %s ) WHERE meta_key = '_product_attributes'",
  484. 's:' . $old_attribute_name_length . ':"pa_' . $old_slug . '"',
  485. 's:' . $attribute_name_length . ':"pa_' . $data['attribute_name'] . '"'
  486. )
  487. );
  488. // Update variations which use this taxonomy.
  489. $wpdb->update(
  490. $wpdb->postmeta,
  491. array( 'meta_key' => 'attribute_pa_' . sanitize_title( $data['attribute_name'] ) ), // WPCS: slow query ok.
  492. array( 'meta_key' => 'attribute_pa_' . sanitize_title( $old_slug ) ) // WPCS: slow query ok.
  493. );
  494. }
  495. }
  496. // Clear cache and flush rewrite rules.
  497. wp_schedule_single_event( time(), 'woocommerce_flush_rewrite_rules' );
  498. delete_transient( 'wc_attribute_taxonomies' );
  499. return $id;
  500. }
  501. /**
  502. * Update an attribute.
  503. *
  504. * For available args see wc_create_attribute().
  505. *
  506. * @since 3.2.0
  507. * @param int $id Attribute ID.
  508. * @param array $args Attribute arguments.
  509. * @return int|WP_Error
  510. */
  511. function wc_update_attribute( $id, $args ) {
  512. global $wpdb;
  513. $attribute = wc_get_attribute( $id );
  514. $args['id'] = $attribute ? $attribute->id : 0;
  515. if ( $args['id'] && empty( $args['name'] ) ) {
  516. $args['name'] = $attribute->name;
  517. }
  518. $args['old_slug'] = $wpdb->get_var(
  519. $wpdb->prepare(
  520. "
  521. SELECT attribute_name
  522. FROM {$wpdb->prefix}woocommerce_attribute_taxonomies
  523. WHERE attribute_id = %d
  524. ", $args['id']
  525. )
  526. );
  527. return wc_create_attribute( $args );
  528. }
  529. /**
  530. * Delete attribute by ID.
  531. *
  532. * @since 3.2.0
  533. * @param int $id Attribute ID.
  534. * @return bool
  535. */
  536. function wc_delete_attribute( $id ) {
  537. global $wpdb;
  538. $name = $wpdb->get_var(
  539. $wpdb->prepare(
  540. "
  541. SELECT attribute_name
  542. FROM {$wpdb->prefix}woocommerce_attribute_taxonomies
  543. WHERE attribute_id = %d
  544. ", $id
  545. )
  546. );
  547. $taxonomy = wc_attribute_taxonomy_name( $name );
  548. /**
  549. * Before deleting an attribute.
  550. *
  551. * @param int $id Attribute ID.
  552. * @param string $name Attribute name.
  553. * @param string $taxonomy Attribute taxonomy name.
  554. */
  555. do_action( 'woocommerce_before_attribute_delete', $id, $name, $taxonomy );
  556. if ( $name && $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_attribute_taxonomies WHERE attribute_id = %d", $id ) ) ) {
  557. if ( taxonomy_exists( $taxonomy ) ) {
  558. $terms = get_terms( $taxonomy, 'orderby=name&hide_empty=0' );
  559. foreach ( $terms as $term ) {
  560. wp_delete_term( $term->term_id, $taxonomy );
  561. }
  562. }
  563. /**
  564. * After deleting an attribute.
  565. *
  566. * @param int $id Attribute ID.
  567. * @param string $name Attribute name.
  568. * @param string $taxonomy Attribute taxonomy name.
  569. */
  570. do_action( 'woocommerce_attribute_deleted', $id, $name, $taxonomy );
  571. wp_schedule_single_event( time(), 'woocommerce_flush_rewrite_rules' );
  572. delete_transient( 'wc_attribute_taxonomies' );
  573. return true;
  574. }
  575. return false;
  576. }