How to add NEW custom product attribute in WooCommerce programmatically?

11,682

I recently come with the same issue as you. First you need to think about the options of each attribute.

is_visible will make the attribute visible in the product page if set to 1, and only visible in the edit product page if set to 0.

is_variation is used to create variations of same product for said custom attribute. If set to 1, indicates to Woocommerce that variations exist, so it will look for posts with 'post_type' => "product_variation" that are related to original product post through attribute 'post_parent' set as original product post_id like 'post_parent' => $original_post_id

is_taxonomy is used to categorize the product based in said custom attribute. For example, think in clothes being the products to be sold, you create a custom attribute called gender to categorize the products depending on the gender.

As it seems reading your question, you just need simple custom attributes with string values visible in the product page:

["Some custom attribute"] => (string)"Something"
["Another custom attribute"] => (string)"Something else"

so the way of implementing your loop would be something like this:

[loop]

$user_id = get_current_user();
$post_id = wp_insert_post(array(
    'post_author' => $user_id,
    'post_title' => $item['termek'],
    'post_content' => $item['reszletes_leiras'],
    'post_status' => 'publish',
    'post_type' => "product",
));
wp_set_object_terms( $post_id, 'simple', 'product_type' );
update_post_meta( $post_id, '_visibility', 'visible' );
update_post_meta( $post_id, '_stock_status', 'instock');
update_post_meta( $post_id, 'total_sales', '0' );
update_post_meta( $post_id, '_downloadable', 'no' );
update_post_meta( $post_id, '_virtual', 'no' );
update_post_meta( $post_id, '_regular_price', $item['brutto_ar'] );
update_post_meta( $post_id, '_sale_price', $item['brutto_ar'] );
update_post_meta( $post_id, '_purchase_note', '' );
update_post_meta( $post_id, '_featured', 'no' );
update_post_meta( $post_id, '_weight', '' );
update_post_meta( $post_id, '_length', '' );
update_post_meta( $post_id, '_width', '' );
update_post_meta( $post_id, '_height', '' );
update_post_meta( $post_id, '_sku', $item['id'] );
update_post_meta( $post_id, '_product_attributes', array() );
update_post_meta( $post_id, '_sale_price_dates_from', '' );
update_post_meta( $post_id, '_sale_price_dates_to', '' );
update_post_meta( $post_id, '_price', '' );
update_post_meta( $post_id, '_sold_individually', '' );
update_post_meta( $post_id, '_manage_stock', 'no' );
update_post_meta( $post_id, '_backorders', 'no' );
update_post_meta( $post_id, '_stock', '' );
$i = 0;
$product_attributes = array();
foreach($item['parameterek'] as $key => $value){
    $product_attributes[sanitize_title($key)] = array (
            'name' => wc_clean($key), // set attribute name
            'value' => $value, // set attribute value
            'position' => $i,
            'is_visible' => 1,
            'is_variation' => 0,
            'is_taxonomy' => 0
        );
     $i++;
}
update_post_meta($post_id, '_product_attributes', $product_attributes);
[/loop]

In case one of your products has variations the way of implementing is, first create the base product as you already do. Then saving said custom attribute as variation.

$attr[sanitize_title($custom_attribute_name)] = array(
     'name'=> wc_clean($custom_attribute_name),
     'value'=> $string_custom_attr_values,
     'position' => $position,
     'is_visible' => 1,
     'is_variation' => 1,
     'is_taxonomy' => 0
 );

Where $string_custom_attr_values is a string of the values of variations separated by | . For example, a custom attribute of sizes for variations then $string_custom_attr_values = 'S|M|L|XL';

And then you will need a new loop inside the one you already have for $items to create the variations of said product, something like this following the previous example of variation of sizes:

$string_custom_attr_values = 'S|M|L|XL';
$arr_custom_attr_values = explode("|", $string_custom_attr_values);
$total_variations = count($arr_custom_attr_values);
$variation_post_id = $post_id;
for($i = 1; $i <= $total_variations; $i++) {
    $variation_post_id += $i;
    $variation_post = array(
        'post_title' => 'Variation #' . $variation_post_id . ' of ' . $item['termek'],
        'post_name'     => 'product-' . $variation_post_id . '-variation',
        'post_status'   => 'publish',
        'post_parent'   => $post_id,
        'post_type'     => 'product_variation',
        'guid'          =>  home_url() . '/product_variation/product-' . $variation_post_id . '-variation',
        'menu_order'    =>  $i
 );

    // Insert the variation post into the database
    $variation_post_id = wp_insert_post( $variation_post );
    update_post_meta( $variation_post_id, 'attribute_'.$custom_attribute_name, $arr_custom_attr_values[$i-1]);

    /*Rest of the post_meta like in the base product*/

}//end for

Hope I have been clear enough.

Share:
11,682
Balázs Varga
Author by

Balázs Varga

I'm a freelancer web-programmer, designer, and indie-game developer from Hungary. I'm here to help others, and to get helped if I stuck somewhere in my projects. #SOreadytohelp

Updated on June 04, 2022

Comments

  • Balázs Varga
    Balázs Varga about 2 years

    I have a pretty big database of products on a webpage. This webpage was written in PHP, and now I need to migrate its database to a Wordpress (WooCommerce) site. I already figured out how to add the products. I made a php file (outside wordpress) with this header, so I can use all of the wordpress functions:

    include('../wp/wp-load.php');
    require_once('../wp/wp-admin/includes/media.php');
    require_once('../wp/wp-admin/includes/file.php');
    require_once('../wp/wp-admin/includes/image.php');
    

    First I get all products from the old database to a php array $items, and then loop through this array using a foreach loop. Inside the loop I have this code:

    $user_id = get_current_user();
    $post_id = wp_insert_post(array(
        'post_author' => $user_id,
        'post_title' => $item['termek'],
        'post_content' => $item['reszletes_leiras'],
        'post_status' => 'publish',
        'post_type' => "product",
    ));
    wp_set_object_terms( $post_id, 'simple', 'product_type' );
    update_post_meta( $post_id, '_visibility', 'visible' );
    update_post_meta( $post_id, '_stock_status', 'instock');
    update_post_meta( $post_id, 'total_sales', '0' );
    update_post_meta( $post_id, '_downloadable', 'no' );
    update_post_meta( $post_id, '_virtual', 'no' );
    update_post_meta( $post_id, '_regular_price', $item['brutto_ar'] );
    update_post_meta( $post_id, '_sale_price', $item['brutto_ar'] );
    update_post_meta( $post_id, '_purchase_note', '' );
    update_post_meta( $post_id, '_featured', 'no' );
    update_post_meta( $post_id, '_weight', '' );
    update_post_meta( $post_id, '_length', '' );
    update_post_meta( $post_id, '_width', '' );
    update_post_meta( $post_id, '_height', '' );
    update_post_meta( $post_id, '_sku', $item['id'] );
    update_post_meta( $post_id, '_product_attributes', array() );
    update_post_meta( $post_id, '_sale_price_dates_from', '' );
    update_post_meta( $post_id, '_sale_price_dates_to', '' );
    update_post_meta( $post_id, '_price', '' );
    update_post_meta( $post_id, '_sold_individually', '' );
    update_post_meta( $post_id, '_manage_stock', 'no' );
    update_post_meta( $post_id, '_backorders', 'no' );
    update_post_meta( $post_id, '_stock', '' );
    

    My problem is that I also have an array in $item['parameterek'], which looks kile this:

    ["Some custom attribute"] => (string)"Something"
    ["Another custom attribute"] => (string)"Something else"
    

    Now I need to add these fields to the products as custom attributes. The array key is the name, and the value is... the value, of course. I tried to do it this way, but this code seems to do nothing:

    foreach(array_keys($item['parameterek']) as $key) {
        register_attribute($key);
    }
    wproduct_set_attributes($post_id, $item['parameterek']);
    
    function register_attribute($name){
        $paname = 'pa_' . htmlspecialchars(stripslashes($name));
        $permalinks = get_option('woocommerce_permalinks');
        $taxonomy_data = array(
                            'hierarchical'          => false,
                            'update_count_callback' => '_update_post_term_count',
                            'labels'                => array(
                                    'name'              => $name,
                                    'singular_name'     => $name,
                                    'search_items'      => sprintf( __( 'Search %s', 'woocommerce' ), $label ),
                                    'all_items'         => sprintf( __( 'All %s', 'woocommerce' ), $label ),
                                    'parent_item'       => sprintf( __( 'Parent %s', 'woocommerce' ), $label ),
                                    'parent_item_colon' => sprintf( __( 'Parent %s:', 'woocommerce' ), $label ),
                                    'edit_item'         => sprintf( __( 'Edit %s', 'woocommerce' ), $label ),
                                    'update_item'       => sprintf( __( 'Update %s', 'woocommerce' ), $label ),
                                    'add_new_item'      => sprintf( __( 'Add New %s', 'woocommerce' ), $label ),
                                    'new_item_name'     => sprintf( __( 'New %s', 'woocommerce' ), $label )
                                ),
                            'show_ui'           => false,
                            'query_var'         => true,
                            'rewrite'           => array(
                                'slug'         => empty( $permalinks['attribute_base'] ) ? '' : trailingslashit( $permalinks['attribute_base'] ) . sanitize_title( $name ),
                                'with_front'   => false,
                                'hierarchical' => true
                            ),
                            'sort'              => false,
                            'public'            => true,
                            'show_in_nav_menus' => false,
                            'capabilities'      => array(
                                'manage_terms' => 'manage_product_terms',
                                'edit_terms'   => 'edit_product_terms',
                                'delete_terms' => 'delete_product_terms',
                                'assign_terms' => 'assign_product_terms',
                            )
                        );
        register_taxonomy($paname, array('product'), $taxonomy_data);
    }
    
    function wcproduct_set_attributes($post_id, $attributes) {
        $i = 0;
        // Loop through the attributes array
        foreach ($attributes as $name => $value) {
            $product_attributes[$i] = array (
                'name' => 'pa_' . htmlspecialchars(stripslashes($name)), // set attribute name
                'value' => $value, // set attribute value
                'position' => 1,
                'is_visible' => 1,
                'is_variation' => 0,
                'is_taxonomy' => 1
            );
    
            $i++;
        }
        update_post_meta($post_id, '_product_attributes', $product_attributes);
    }
    

    So my question is: What is the easiest way to add custom product attributes from an array using PHP?

  • Balázs Varga
    Balázs Varga over 7 years
    Thanks for your answer, it works, but I have a little problem with that. wc_clean seems to return empty string if there is an accented chararcter in the name. If I remove wc_clean from the code, it doesn't include anything. Any suggestions? (Now it looks like this on the product page: s21.postimg.org/akc7vsllz/image.png )
  • Dez
    Dez over 7 years
    wc_clean uses sanitize_text_field. You can check here the function: woocommerce.wp-a2z.org/oik_api/sanitize_text_field Debug to see if you are not applying any stripslash or character encoding function that prevents wc_clean to work right.
  • Balázs Varga
    Balázs Varga over 7 years
    Finally I can get it to work using wc_clean(htmlentities($key, 0, "ISO8859-1")); Now it works perfectly, thanks again for your fast answer! :) ( Results: s10.postimg.org/v9ft0g14p/image.png )
  • asdru
    asdru over 7 years
    cool, but do you know how to add a new attribute entry in the attributes list (let say the edit.php?post_type=product&page=product_attributes page) so it can be added to a product with 'is_taxonomy' => 1 and wp_set_object_terms( $post_id, 'my value', 'pa_my-attribute', true); ?
  • Bilal Halayqa
    Bilal Halayqa about 7 years
    @asdru , did you find the answer of your question ?
  • asdru
    asdru almost 6 years
    @BilalHalayqa sorry for the late response. I found a way and I wrote it in a plugin, I don't know if it is still valid (it was for woocommerce 2) but it is in the public MdtFeatureList::saveInWoocommerce() methid in this file github.com/antcolag/mdt/blob/master/ProductAttributeList.php bye