Sin descripción

wc-attribute-functions.php 21KB

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