관리-도구
편집 파일: kkart-attribute-functions.php
<?php /** * Kkart Attribute Functions * * @package Kkart\Functions * @version 2.1.0 */ defined( 'ABSPATH' ) || exit; /** * Gets text attributes from a string. * * @since 2.4 * @param string $raw_attributes Raw attributes. * @return array */ function kkart_get_text_attributes( $raw_attributes ) { return array_filter( array_map( 'trim', explode( KKART_DELIMITER, html_entity_decode( $raw_attributes, ENT_QUOTES, get_bloginfo( 'charset' ) ) ) ), 'kkart_get_text_attributes_filter_callback' ); } /** * See if an attribute is actually valid. * * @since 3.0.0 * @param string $value Value. * @return bool */ function kkart_get_text_attributes_filter_callback( $value ) { return '' !== $value; } /** * Implode an array of attributes using KKART_DELIMITER. * * @since 3.0.0 * @param array $attributes Attributes list. * @return string */ function kkart_implode_text_attributes( $attributes ) { return implode( ' ' . KKART_DELIMITER . ' ', $attributes ); } /** * Get attribute taxonomies. * * @return array of objects, @since 3.6.0 these are also indexed by ID. */ function kkart_get_attribute_taxonomies() { $prefix = KKART_Cache_Helper::get_cache_prefix( 'kkart-attributes' ); $cache_key = $prefix . 'attributes'; $cache_value = wp_cache_get( $cache_key, 'kkart-attributes' ); if ( false !== $cache_value ) { return $cache_value; } $raw_attribute_taxonomies = get_transient( 'kkart_attribute_taxonomies' ); if ( false === $raw_attribute_taxonomies ) { global $wpdb; $raw_attribute_taxonomies = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}kkart_attribute_taxonomies WHERE attribute_name != '' ORDER BY attribute_name ASC;" ); set_transient( 'kkart_attribute_taxonomies', $raw_attribute_taxonomies ); } /** * Filter attribute taxonomies. * * @param array $attribute_taxonomies Results of the DB query. Each taxonomy is an object. */ $raw_attribute_taxonomies = (array) array_filter( apply_filters( 'kkart_attribute_taxonomies', $raw_attribute_taxonomies ) ); // Index by ID for easer lookups. $attribute_taxonomies = array(); foreach ( $raw_attribute_taxonomies as $result ) { $attribute_taxonomies[ 'id:' . $result->attribute_id ] = $result; } wp_cache_set( $cache_key, $attribute_taxonomies, 'kkart-attributes' ); return $attribute_taxonomies; } /** * Get (cached) attribute taxonomy ID and name pairs. * * @since 3.6.0 * @return array */ function kkart_get_attribute_taxonomy_ids() { $prefix = KKART_Cache_Helper::get_cache_prefix( 'kkart-attributes' ); $cache_key = $prefix . 'ids'; $cache_value = wp_cache_get( $cache_key, 'kkart-attributes' ); if ( $cache_value ) { return $cache_value; } $taxonomy_ids = array_map( 'absint', wp_list_pluck( kkart_get_attribute_taxonomies(), 'attribute_id', 'attribute_name' ) ); wp_cache_set( $cache_key, $taxonomy_ids, 'kkart-attributes' ); return $taxonomy_ids; } /** * Get (cached) attribute taxonomy label and name pairs. * * @since 3.6.0 * @return array */ function kkart_get_attribute_taxonomy_labels() { $prefix = KKART_Cache_Helper::get_cache_prefix( 'kkart-attributes' ); $cache_key = $prefix . 'labels'; $cache_value = wp_cache_get( $cache_key, 'kkart-attributes' ); if ( $cache_value ) { return $cache_value; } $taxonomy_labels = wp_list_pluck( kkart_get_attribute_taxonomies(), 'attribute_label', 'attribute_name' ); wp_cache_set( $cache_key, $taxonomy_labels, 'kkart-attributes' ); return $taxonomy_labels; } /** * Get a product attribute name. * * @param string $attribute_name Attribute name. * @return string */ function kkart_attribute_taxonomy_name( $attribute_name ) { return $attribute_name ? 'pa_' . kkart_sanitize_taxonomy_name( $attribute_name ) : ''; } /** * Get the attribute name used when storing values in post meta. * * @since 2.6.0 * @param string $attribute_name Attribute name. * @return string */ function kkart_variation_attribute_name( $attribute_name ) { return 'attribute_' . sanitize_title( $attribute_name ); } /** * Get a product attribute name by ID. * * @since 2.4.0 * @param int $attribute_id Attribute ID. * @return string Return an empty string if attribute doesn't exist. */ function kkart_attribute_taxonomy_name_by_id( $attribute_id ) { $taxonomy_ids = kkart_get_attribute_taxonomy_ids(); $attribute_name = (string) array_search( $attribute_id, $taxonomy_ids, true ); return kkart_attribute_taxonomy_name( $attribute_name ); } /** * Get a product attribute ID by name. * * @since 2.6.0 * @param string $name Attribute name. * @return int */ function kkart_attribute_taxonomy_id_by_name( $name ) { $name = kkart_attribute_taxonomy_slug( $name ); $taxonomy_ids = kkart_get_attribute_taxonomy_ids(); return isset( $taxonomy_ids[ $name ] ) ? $taxonomy_ids[ $name ] : 0; } /** * Get a product attributes label. * * @param string $name Attribute name. * @param KKART_Product $product Product data. * @return string */ function kkart_attribute_label( $name, $product = '' ) { if ( taxonomy_is_product_attribute( $name ) ) { $slug = kkart_attribute_taxonomy_slug( $name ); $all_labels = kkart_get_attribute_taxonomy_labels(); $label = isset( $all_labels[ $slug ] ) ? $all_labels[ $slug ] : $slug; } elseif ( $product ) { if ( $product->is_type( 'variation' ) ) { $product = kkart_get_product( $product->get_parent_id() ); } $attributes = array(); if ( false !== $product ) { $attributes = $product->get_attributes(); } // Attempt to get label from product, as entered by the user. if ( $attributes && isset( $attributes[ sanitize_title( $name ) ] ) ) { $label = $attributes[ sanitize_title( $name ) ]->get_name(); } else { $label = $name; } } else { $label = $name; } return apply_filters( 'kkart_attribute_label', $label, $name, $product ); } /** * Get a product attributes orderby setting. * * @param string $name Attribute name. * @return string */ function kkart_attribute_orderby( $name ) { $name = kkart_attribute_taxonomy_slug( $name ); $id = kkart_attribute_taxonomy_id_by_name( $name ); $taxonomies = kkart_get_attribute_taxonomies(); return apply_filters( 'kkart_attribute_orderby', isset( $taxonomies[ 'id:' . $id ] ) ? $taxonomies[ 'id:' . $id ]->attribute_orderby : 'menu_order', $name ); } /** * Get an array of product attribute taxonomies. * * @return array */ function kkart_get_attribute_taxonomy_names() { $taxonomy_names = array(); $attribute_taxonomies = kkart_get_attribute_taxonomies(); if ( ! empty( $attribute_taxonomies ) ) { foreach ( $attribute_taxonomies as $tax ) { $taxonomy_names[] = kkart_attribute_taxonomy_name( $tax->attribute_name ); } } return $taxonomy_names; } /** * Get attribute types. * * @since 2.4.0 * @return array */ function kkart_get_attribute_types() { return (array) apply_filters( 'product_attributes_type_selector', array( 'select' => __( 'Select', 'kkart' ), ) ); } /** * Check if there are custom attribute types. * * @since 3.3.2 * @return bool True if there are custom types, otherwise false. */ function kkart_has_custom_attribute_types() { $types = kkart_get_attribute_types(); return 1 < count( $types ) || ! array_key_exists( 'select', $types ); } /** * Get attribute type label. * * @since 3.0.0 * @param string $type Attribute type slug. * @return string */ function kkart_get_attribute_type_label( $type ) { $types = kkart_get_attribute_types(); return isset( $types[ $type ] ) ? $types[ $type ] : __( 'Select', 'kkart' ); } /** * Check if attribute name is reserved. * https://codex.wordpress.org/Function_Reference/register_taxonomy#Reserved_Terms. * * @since 2.4.0 * @param string $attribute_name Attribute name. * @return bool */ function kkart_check_if_attribute_name_is_reserved( $attribute_name ) { // Forbidden attribute names. $reserved_terms = array( 'attachment', 'attachment_id', 'author', 'author_name', 'calendar', 'cat', 'category', 'category__and', 'category__in', 'category__not_in', 'category_name', 'comments_per_page', 'comments_popup', 'cpage', 'day', 'debug', 'error', 'exact', 'feed', 'hour', 'link_category', 'm', 'minute', 'monthnum', 'more', 'name', 'nav_menu', 'nopaging', 'offset', 'order', 'orderby', 'p', 'page', 'page_id', 'paged', 'pagename', 'pb', 'perm', 'post', 'post__in', 'post__not_in', 'post_format', 'post_mime_type', 'post_status', 'post_tag', 'post_type', 'posts', 'posts_per_archive_page', 'posts_per_page', 'preview', 'robots', 's', 'search', 'second', 'sentence', 'showposts', 'static', 'subpost', 'subpost_id', 'tag', 'tag__and', 'tag__in', 'tag__not_in', 'tag_id', 'tag_slug__and', 'tag_slug__in', 'taxonomy', 'tb', 'term', 'type', 'w', 'withcomments', 'withoutcomments', 'year', ); return in_array( $attribute_name, $reserved_terms, true ); } /** * Callback for array filter to get visible only. * * @since 3.0.0 * @param KKART_Product_Attribute $attribute Attribute data. * @return bool */ function kkart_attributes_array_filter_visible( $attribute ) { return $attribute && is_a( $attribute, 'KKART_Product_Attribute' ) && $attribute->get_visible() && ( ! $attribute->is_taxonomy() || taxonomy_exists( $attribute->get_name() ) ); } /** * Callback for array filter to get variation attributes only. * * @since 3.0.0 * @param KKART_Product_Attribute $attribute Attribute data. * @return bool */ function kkart_attributes_array_filter_variation( $attribute ) { return $attribute && is_a( $attribute, 'KKART_Product_Attribute' ) && $attribute->get_variation(); } /** * Check if an attribute is included in the attributes area of a variation name. * * @since 3.0.2 * @param string $attribute Attribute value to check for. * @param string $name Product name to check in. * @return bool */ function kkart_is_attribute_in_product_name( $attribute, $name ) { $is_in_name = stristr( $name, ' ' . $attribute . ',' ) || 0 === stripos( strrev( $name ), strrev( ' ' . $attribute ) ); return apply_filters( 'kkart_is_attribute_in_product_name', $is_in_name, $attribute, $name ); } /** * Callback for array filter to get default attributes. Will allow for '0' string values, but regard all other * class PHP FALSE equivalents normally. * * @since 3.1.0 * @param mixed $attribute Attribute being considered for exclusion from parent array. * @return bool */ function kkart_array_filter_default_attributes( $attribute ) { return is_scalar( $attribute ) && ( ! empty( $attribute ) || '0' === $attribute ); } /** * Get attribute data by ID. * * @since 3.2.0 * @param int $id Attribute ID. * @return stdClass|null */ function kkart_get_attribute( $id ) { $attributes = kkart_get_attribute_taxonomies(); if ( ! isset( $attributes[ 'id:' . $id ] ) ) { return null; } $data = $attributes[ 'id:' . $id ]; $attribute = new stdClass(); $attribute->id = (int) $data->attribute_id; $attribute->name = $data->attribute_label; $attribute->slug = kkart_attribute_taxonomy_name( $data->attribute_name ); $attribute->type = $data->attribute_type; $attribute->order_by = $data->attribute_orderby; $attribute->has_archives = (bool) $data->attribute_public; return $attribute; } /** * Create attribute. * * @since 3.2.0 * @param array $args Attribute arguments { * Array of attribute parameters. * * @type int $id Unique identifier, used to update an attribute. * @type string $name Attribute name. Always required. * @type string $slug Attribute alphanumeric identifier. * @type string $type Type of attribute. * Core by default accepts: 'select' and 'text'. * Default to 'select'. * @type string $order_by Sort order. * Accepts: 'menu_order', 'name', 'name_num' and 'id'. * Default to 'menu_order'. * @type bool $has_archives Enable or disable attribute archives. False by default. * } * @return int|WP_Error */ function kkart_create_attribute( $args ) { global $wpdb; $args = wp_unslash( $args ); $id = ! empty( $args['id'] ) ? intval( $args['id'] ) : 0; $format = array( '%s', '%s', '%s', '%s', '%d' ); // Name is required. if ( empty( $args['name'] ) ) { return new WP_Error( 'missing_attribute_name', __( 'Please, provide an attribute name.', 'kkart' ), array( 'status' => 400 ) ); } // Set the attribute slug. if ( empty( $args['slug'] ) ) { $slug = kkart_sanitize_taxonomy_name( $args['name'] ); } else { $slug = preg_replace( '/^pa\_/', '', kkart_sanitize_taxonomy_name( $args['slug'] ) ); } // Validate slug. if ( strlen( $slug ) >= 28 ) { /* translators: %s: attribute slug */ return new WP_Error( 'invalid_product_attribute_slug_too_long', sprintf( __( 'Slug "%s" is too long (28 characters max). Shorten it, please.', 'kkart' ), $slug ), array( 'status' => 400 ) ); } elseif ( kkart_check_if_attribute_name_is_reserved( $slug ) ) { /* translators: %s: attribute slug */ 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.', 'kkart' ), $slug ), array( 'status' => 400 ) ); } elseif ( ( 0 === $id && taxonomy_exists( kkart_attribute_taxonomy_name( $slug ) ) ) || ( isset( $args['old_slug'] ) && $args['old_slug'] !== $slug && taxonomy_exists( kkart_attribute_taxonomy_name( $slug ) ) ) ) { /* translators: %s: attribute slug */ return new WP_Error( 'invalid_product_attribute_slug_already_exists', sprintf( __( 'Slug "%s" is already in use. Change it, please.', 'kkart' ), $slug ), array( 'status' => 400 ) ); } // Validate type. if ( empty( $args['type'] ) || ! array_key_exists( $args['type'], kkart_get_attribute_types() ) ) { $args['type'] = 'select'; } // Validate order by. if ( empty( $args['order_by'] ) || ! in_array( $args['order_by'], array( 'menu_order', 'name', 'name_num', 'id' ), true ) ) { $args['order_by'] = 'menu_order'; } $data = array( 'attribute_label' => $args['name'], 'attribute_name' => $slug, 'attribute_type' => $args['type'], 'attribute_orderby' => $args['order_by'], 'attribute_public' => isset( $args['has_archives'] ) ? (int) $args['has_archives'] : 0, ); // Create or update. if ( 0 === $id ) { $results = $wpdb->insert( $wpdb->prefix . 'kkart_attribute_taxonomies', $data, $format ); if ( is_wp_error( $results ) ) { return new WP_Error( 'cannot_create_attribute', $results->get_error_message(), array( 'status' => 400 ) ); } $id = $wpdb->insert_id; /** * Attribute added. * * @param int $id Added attribute ID. * @param array $data Attribute data. */ do_action( 'kkart_attribute_added', $id, $data ); } else { $results = $wpdb->update( $wpdb->prefix . 'kkart_attribute_taxonomies', $data, array( 'attribute_id' => $id ), $format, array( '%d' ) ); if ( false === $results ) { return new WP_Error( 'cannot_update_attribute', __( 'Could not update the attribute.', 'kkart' ), array( 'status' => 400 ) ); } // Set old slug to check for database changes. $old_slug = ! empty( $args['old_slug'] ) ? kkart_sanitize_taxonomy_name( $args['old_slug'] ) : $slug; /** * Attribute updated. * * @param int $id Added attribute ID. * @param array $data Attribute data. * @param string $old_slug Attribute old name. */ do_action( 'kkart_attribute_updated', $id, $data, $old_slug ); if ( $old_slug !== $slug ) { // Update taxonomies in the wp term taxonomy table. $wpdb->update( $wpdb->term_taxonomy, array( 'taxonomy' => kkart_attribute_taxonomy_name( $data['attribute_name'] ) ), array( 'taxonomy' => 'pa_' . $old_slug ) ); // Update taxonomy ordering term meta. $wpdb->update( $wpdb->termmeta, array( 'meta_key' => 'order_pa_' . sanitize_title( $data['attribute_name'] ) ), // WPCS: slow query ok. array( 'meta_key' => 'order_pa_' . sanitize_title( $old_slug ) ) // WPCS: slow query ok. ); // Update product attributes which use this taxonomy. $old_taxonomy_name = 'pa_' . $old_slug; $new_taxonomy_name = 'pa_' . $data['attribute_name']; $old_attribute_key = sanitize_title( $old_taxonomy_name ); // @see KKART_Product::set_attributes(). $new_attribute_key = sanitize_title( $new_taxonomy_name ); // @see KKART_Product::set_attributes(). $metadatas = $wpdb->get_results( $wpdb->prepare( "SELECT post_id, meta_value FROM {$wpdb->postmeta} WHERE meta_key = '_product_attributes' AND meta_value LIKE %s", '%' . $wpdb->esc_like( $old_taxonomy_name ) . '%' ), ARRAY_A ); foreach ( $metadatas as $metadata ) { $product_id = $metadata['post_id']; $unserialized_data = maybe_unserialize( $metadata['meta_value'] ); if ( ! $unserialized_data || ! is_array( $unserialized_data ) || ! isset( $unserialized_data[ $old_attribute_key ] ) ) { continue; } $unserialized_data[ $new_attribute_key ] = $unserialized_data[ $old_attribute_key ]; unset( $unserialized_data[ $old_attribute_key ] ); $unserialized_data[ $new_attribute_key ]['name'] = $new_taxonomy_name; update_post_meta( $product_id, '_product_attributes', wp_slash( $unserialized_data ) ); } // Update variations which use this taxonomy. $wpdb->update( $wpdb->postmeta, array( 'meta_key' => 'attribute_pa_' . sanitize_title( $data['attribute_name'] ) ), // WPCS: slow query ok. array( 'meta_key' => 'attribute_pa_' . sanitize_title( $old_slug ) ) // WPCS: slow query ok. ); } } // Clear cache and flush rewrite rules. wp_schedule_single_event( time(), 'kkart_flush_rewrite_rules' ); delete_transient( 'kkart_attribute_taxonomies' ); KKART_Cache_Helper::invalidate_cache_group( 'kkart-attributes' ); return $id; } /** * Update an attribute. * * For available args see kkart_create_attribute(). * * @since 3.2.0 * @param int $id Attribute ID. * @param array $args Attribute arguments. * @return int|WP_Error */ function kkart_update_attribute( $id, $args ) { global $wpdb; $attribute = kkart_get_attribute( $id ); $args['id'] = $attribute ? $attribute->id : 0; if ( $args['id'] && empty( $args['name'] ) ) { $args['name'] = $attribute->name; } $args['old_slug'] = $wpdb->get_var( $wpdb->prepare( " SELECT attribute_name FROM {$wpdb->prefix}kkart_attribute_taxonomies WHERE attribute_id = %d ", $args['id'] ) ); return kkart_create_attribute( $args ); } /** * Delete attribute by ID. * * @since 3.2.0 * @param int $id Attribute ID. * @return bool */ function kkart_delete_attribute( $id ) { global $wpdb; $name = $wpdb->get_var( $wpdb->prepare( " SELECT attribute_name FROM {$wpdb->prefix}kkart_attribute_taxonomies WHERE attribute_id = %d ", $id ) ); $taxonomy = kkart_attribute_taxonomy_name( $name ); /** * Before deleting an attribute. * * @param int $id Attribute ID. * @param string $name Attribute name. * @param string $taxonomy Attribute taxonomy name. */ do_action( 'kkart_before_attribute_delete', $id, $name, $taxonomy ); if ( $name && $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}kkart_attribute_taxonomies WHERE attribute_id = %d", $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 ); } } /** * After deleting an attribute. * * @param int $id Attribute ID. * @param string $name Attribute name. * @param string $taxonomy Attribute taxonomy name. */ do_action( 'kkart_attribute_deleted', $id, $name, $taxonomy ); wp_schedule_single_event( time(), 'kkart_flush_rewrite_rules' ); delete_transient( 'kkart_attribute_taxonomies' ); KKART_Cache_Helper::invalidate_cache_group( 'kkart-attributes' ); return true; } return false; } /** * Get an unprefixed product attribute name. * * @since 3.6.0 * * @param string $attribute_name Attribute name. * @return string */ function kkart_attribute_taxonomy_slug( $attribute_name ) { $prefix = KKART_Cache_Helper::get_cache_prefix( 'kkart-attributes' ); $cache_key = $prefix . 'slug-' . $attribute_name; $cache_value = wp_cache_get( $cache_key, 'kkart-attributes' ); if ( $cache_value ) { return $cache_value; } $attribute_name = kkart_sanitize_taxonomy_name( $attribute_name ); $attribute_slug = 0 === strpos( $attribute_name, 'pa_' ) ? substr( $attribute_name, 3 ) : $attribute_name; wp_cache_set( $cache_key, $attribute_slug, 'kkart-attributes' ); return $attribute_slug; }