$product_id, 'groups' => array(), 'rules' => array() ); if ( $product_id <= 0 ) { return $empty; } $groups = $wpdb->get_results( $wpdb->prepare( "SELECT g.id, g.slug, g.name, g.select_type, g.min_select, g.max_select, g.included_free, g.free_policy, g.description, a.id AS assignment_id, a.sort_order AS sort_order FROM {$t['assignments']} a JOIN {$t['groups']} g ON g.id = a.group_id WHERE a.product_id = %d AND a.enabled = 1 AND g.enabled = 1 ORDER BY a.sort_order ASC, g.name ASC", $product_id ), ARRAY_A ); if ( empty( $groups ) ) { return $empty; } $group_ids = array(); $out = array(); foreach ( $groups as $g ) { $gid = (int) $g['id']; $group_ids[] = $gid; $out[ $gid ] = array( 'id' => $gid, 'slug' => (string) $g['slug'], 'name' => (string) $g['name'], 'select_type' => (string) $g['select_type'], 'min_select' => (int) $g['min_select'], 'max_select' => (int) $g['max_select'], 'included_free' => (int) $g['included_free'], 'free_policy' => (string) $g['free_policy'], 'description' => (string) $g['description'], 'assignment_id' => (int) $g['assignment_id'], 'sort_order' => (int) $g['sort_order'], 'options' => array(), ); } $in = implode( ',', array_map( 'intval', $group_ids ) ); $options = $wpdb->get_results( "SELECT id, group_id, slug, label, price_delta, default_selected, exclude_from_allowance, linked_product_id, sku, availability, sort_order FROM {$t['options']} WHERE group_id IN ({$in}) AND enabled = 1 ORDER BY group_id ASC, sort_order ASC, label ASC", ARRAY_A ); $option_ids = array(); foreach ( (array) $options as $o ) { $gid = (int) $o['group_id']; if ( ! isset( $out[ $gid ] ) ) { continue; } $oid = (int) $o['id']; $option_ids[] = $oid; $out[ $gid ]['options'][ $oid ] = array( 'id' => $oid, 'group_id' => $gid, 'slug' => (string) $o['slug'], 'label' => (string) $o['label'], 'price_delta' => (float) $o['price_delta'], 'default_selected' => (int) $o['default_selected'] === 1, 'exclude_from_allowance' => (int) $o['exclude_from_allowance'] === 1, 'linked_product_id' => (int) $o['linked_product_id'], 'sku' => (string) $o['sku'], 'availability' => (string) $o['availability'], 'available' => self::effective_available( $o ), 'sort_order' => (int) $o['sort_order'], ); } $rules = array(); if ( $option_ids ) { $oin = implode( ',', array_map( 'intval', $option_ids ) ); $rows = $wpdb->get_results( "SELECT option_id, scope_assignment_id, when_group_id, when_option_id, price_delta FROM {$t['prices']} WHERE option_id IN ({$oin})", ARRAY_A ); foreach ( (array) $rows as $r ) { $oid = (int) $r['option_id']; $rules[ $oid ][] = array( 'scope_assignment_id' => (int) $r['scope_assignment_id'], 'when_group_id' => (int) $r['when_group_id'], 'when_option_id' => (int) $r['when_option_id'], 'price_delta' => (float) $r['price_delta'], ); } } return array( 'product_id' => $product_id, 'groups' => $out, 'rules' => $rules ); } public static function effective_available( array $option ) { $mode = isset( $option['availability'] ) ? (string) $option['availability'] : 'auto'; if ( 'in_stock' === $mode ) { return true; } if ( 'out_of_stock' === $mode ) { return false; } $pid = isset( $option['linked_product_id'] ) ? (int) $option['linked_product_id'] : 0; if ( $pid <= 0 && ! empty( $option['sku'] ) && function_exists( 'wc_get_product_id_by_sku' ) ) { $pid = (int) wc_get_product_id_by_sku( (string) $option['sku'] ); } if ( $pid <= 0 || ! function_exists( 'wc_get_product' ) ) { return true; } $p = wc_get_product( $pid ); return $p ? (bool) $p->is_in_stock() : true; } public static function upsert_group( array $data ) { global $wpdb; $t = self::t(); $row = array( 'name' => isset( $data['name'] ) ? (string) $data['name'] : '', 'select_type' => ( isset( $data['select_type'] ) && 'radio' === $data['select_type'] ) ? 'radio' : 'checkbox', 'min_select' => isset( $data['min_select'] ) ? max( 0, (int) $data['min_select'] ) : 0, 'max_select' => isset( $data['max_select'] ) ? max( 0, (int) $data['max_select'] ) : 0, 'included_free' => isset( $data['included_free'] ) ? max( 0, (int) $data['included_free'] ) : 0, 'free_policy' => ( isset( $data['free_policy'] ) && 'cheapest_free' === $data['free_policy'] ) ? 'cheapest_free' : 'expensive_free', 'description' => isset( $data['description'] ) ? (string) $data['description'] : '', 'enabled' => isset( $data['enabled'] ) ? (int) (bool) $data['enabled'] : 1, 'sort_order' => isset( $data['sort_order'] ) ? (int) $data['sort_order'] : 0, 'updated_at' => current_time( 'mysql', true ), ); $id = isset( $data['id'] ) ? (int) $data['id'] : 0; if ( $id > 0 ) { $wpdb->update( $t['groups'], $row, array( 'id' => $id ) ); return $id; } $row['slug'] = self::unique_slug( $t['groups'], isset( $data['slug'] ) ? $data['slug'] : $row['name'] ); $row['created_at'] = current_time( 'mysql', true ); $wpdb->insert( $t['groups'], $row ); return (int) $wpdb->insert_id; } public static function upsert_option( array $data ) { global $wpdb; $t = self::t(); $row = array( 'label' => isset( $data['label'] ) ? (string) $data['label'] : '', 'price_delta' => isset( $data['price_delta'] ) ? round( (float) $data['price_delta'], 4 ) : 0, 'default_selected' => isset( $data['default_selected'] ) ? (int) (bool) $data['default_selected'] : 0, 'exclude_from_allowance' => isset( $data['exclude_from_allowance'] ) ? (int) (bool) $data['exclude_from_allowance'] : 0, 'linked_product_id' => isset( $data['linked_product_id'] ) ? max( 0, (int) $data['linked_product_id'] ) : 0, 'sku' => isset( $data['sku'] ) ? (string) $data['sku'] : null, 'availability' => self::sanitize_availability( isset( $data['availability'] ) ? $data['availability'] : 'auto' ), 'enabled' => isset( $data['enabled'] ) ? (int) (bool) $data['enabled'] : 1, 'sort_order' => isset( $data['sort_order'] ) ? (int) $data['sort_order'] : 0, 'updated_at' => current_time( 'mysql', true ), ); $id = isset( $data['id'] ) ? (int) $data['id'] : 0; if ( $id > 0 ) { $wpdb->update( $t['options'], $row, array( 'id' => $id ) ); return $id; } $row['group_id'] = isset( $data['group_id'] ) ? (int) $data['group_id'] : 0; $row['slug'] = self::unique_slug( $t['options'], isset( $data['slug'] ) ? $data['slug'] : $row['label'], 'group_id = ' . $row['group_id'] ); $row['created_at'] = current_time( 'mysql', true ); $wpdb->insert( $t['options'], $row ); return (int) $wpdb->insert_id; } public static function assign_group( $product_id, $group_id, $sort_order = 0 ) { global $wpdb; $t = self::t(); $product_id = (int) $product_id; $group_id = (int) $group_id; $existing = (int) $wpdb->get_var( $wpdb->prepare( "SELECT id FROM {$t['assignments']} WHERE product_id = %d AND group_id = %d", $product_id, $group_id ) ); if ( $existing > 0 ) { $wpdb->update( $t['assignments'], array( 'sort_order' => (int) $sort_order, 'enabled' => 1 ), array( 'id' => $existing ) ); return $existing; } $wpdb->insert( $t['assignments'], array( 'product_id' => $product_id, 'group_id' => $group_id, 'sort_order' => (int) $sort_order, 'enabled' => 1, 'created_at' => current_time( 'mysql', true ), ) ); return (int) $wpdb->insert_id; } public static function upsert_rule( array $data ) { global $wpdb; $t = self::t(); $row = array( 'option_id' => isset( $data['option_id'] ) ? (int) $data['option_id'] : 0, 'scope_assignment_id' => isset( $data['scope_assignment_id'] ) ? (int) $data['scope_assignment_id'] : 0, 'when_group_id' => isset( $data['when_group_id'] ) ? (int) $data['when_group_id'] : 0, 'when_option_id' => isset( $data['when_option_id'] ) ? (int) $data['when_option_id'] : 0, 'price_delta' => isset( $data['price_delta'] ) ? round( (float) $data['price_delta'], 4 ) : 0, 'updated_at' => current_time( 'mysql', true ), ); $existing = (int) $wpdb->get_var( $wpdb->prepare( "SELECT id FROM {$t['prices']} WHERE option_id=%d AND scope_assignment_id=%d AND when_group_id=%d AND when_option_id=%d", $row['option_id'], $row['scope_assignment_id'], $row['when_group_id'], $row['when_option_id'] ) ); if ( $existing > 0 ) { $wpdb->update( $t['prices'], array( 'price_delta' => $row['price_delta'], 'updated_at' => $row['updated_at'] ), array( 'id' => $existing ) ); return $existing; } $row['created_at'] = current_time( 'mysql', true ); $wpdb->insert( $t['prices'], $row ); return (int) $wpdb->insert_id; } public static function delete( $which, $id ) { global $wpdb; $t = self::t(); if ( ! isset( $t[ $which ] ) ) { return; } $id = (int) $id; $wpdb->delete( $t[ $which ], array( 'id' => $id ) ); if ( 'groups' === $which ) { $opt_ids = $wpdb->get_col( $wpdb->prepare( "SELECT id FROM {$t['options']} WHERE group_id = %d", $id ) ); if ( $opt_ids ) { $oin = implode( ',', array_map( 'intval', $opt_ids ) ); $wpdb->query( "DELETE FROM {$t['prices']} WHERE option_id IN ({$oin})" ); } $wpdb->delete( $t['options'], array( 'group_id' => $id ) ); $wpdb->delete( $t['assignments'], array( 'group_id' => $id ) ); } elseif ( 'options' === $which ) { $wpdb->delete( $t['prices'], array( 'option_id' => $id ) ); } } private static function sanitize_availability( $v ) { $v = (string) $v; return in_array( $v, array( 'auto', 'in_stock', 'out_of_stock' ), true ) ? $v : 'auto'; } private static function unique_slug( $table, $source, $extra_where = '' ) { global $wpdb; $base = sanitize_title( (string) $source ); if ( '' === $base ) { $base = 'opt'; } $slug = $base; $i = 2; $cond = $extra_where ? ( ' AND ' . $extra_where ) : ''; while ( (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$table} WHERE slug = %s{$cond}", $slug ) ) > 0 ) { $slug = $base . '-' . $i; $i++; } return $slug; } }