Skip to content

Instantly share code, notes, and snippets.

@Jon007
Last active December 4, 2023 08:17
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save Jon007/ae3a112659d6670ebbd2f26d55143773 to your computer and use it in GitHub Desktop.
Save Jon007/ae3a112659d6670ebbd2f26d55143773 to your computer and use it in GitHub Desktop.
Add default Product Attributes to all WooCommerce products
/*
* Example adds certain named Product Attribute fields to the product Edit screen ready for completion.
* (Pre-requisite )
* This saves the Shop Admin having to add them manually.
*
* Why might you want to do this instead of adding Custom fields? There's plenty of nice documentation on adding custom fields
* for example: http://www.remicorson.com/mastering-woocommerce-products-custom-fields/
*
* Well a Product Attributes are a built in WooCommerce feature, using Terms which are a built in Wordpress feature.
* - no add-ons required
* - pre-existing functionality including
* - ability to link from Product to archive page of products sharing the same term
* - Term selection, hide or show Attribute, use for Product Variations
* - built-in configuration screens, display and selection mechanisms
* - highly compatible with plugin components for example to
* - enhance Product Attributes display,
* - show Variation swatches,
* - translate Attributes and terms
* - offers a level of separation between built-in fields and extension fields
* (though custom fields can be added to their own tab, and some fields make sense to add to other tabs)
*
*/
/*
* Create a default Product Attribute object for the supplied name
*
* @param string name Product Attribute taxonomy name
*
* @return WC_Product_Attribute/bool new Attribute or false if named Attribute is not found
*
*/
function acme_make_product_attribute($name)
{
global $wc_product_attributes;
if ( isset($wc_product_attributes[$name]) ){
$newattr = new WC_Product_Attribute();
$newattr->set_id(1); //any positive value is interpreted as is_taxonomy=true
$newattr->set_name($name);
$newattr->set_visible(true);
$newattr->set_variation(false);
//example of setting default value for item
if ($name=='pa_brand'){
$term = get_term_by('slug', 'acme', $name);
$newattr->set_options(array($term->term_id));
}
return $newattr;
} else {
return false;
}
}
/*
* Add default attributes to a product
*/
function acme_default_product_attributes()
{
global $product;
if (! $product) {
$product = $GLOBALS['product_object'];
}
if (! $product) {
return;
}
$attributes = $product->get_attributes();
$defaultAttributes = array(
'pa_brand',
'pa_maker',
'pa_materials',
'pa_asin',
'pa_upc',
'pa_packaging',
'pa_recommend-to',
'pa_suitable-for',
'pa_product-size',
'pa_net-weight',
);
$changed=false;
foreach ($defaultAttributes as $key){
if (! isset($attributes[$key])){
$newattr = acme_make_product_attribute($key);
if ($newattr){
$attributes[$key] = $newattr;
}
$changed = true;
}
}
if ($changed){
$product->set_attributes($attributes);
}
}
/*
* added to last hook before rendering of Product Edit screen
*/
add_action('woocommerce_product_write_panel_tabs', 'acme_default_product_attributes');
@Jon007
Copy link
Author

Jon007 commented Mar 14, 2020

Yes that’s what I use it for.
Create the attribute Country first, check the slug comes out as country, and refer to it as pa_country in the code as this example

@fkoomek
Copy link

fkoomek commented Mar 14, 2020

Ok, thank you.
So, I found out that the code stops working after activation of PayPal for WooCommerce by AngleEye. This plugin adds a tab to the product edit page so there's probably a conflict. I tried to change the priority of the hook with no luck.

@Jon007
Copy link
Author

Jon007 commented Mar 14, 2020

oh really?
I had used that plugin at some point too but turned it off after realising it was interacting and adding payload to every page..

@fkoomek
Copy link

fkoomek commented Mar 14, 2020

Yeah, I also disable the plugin where it's possible, same as with all of them :) So I disabled it on the add product page and works fine 👍

@fkoomek
Copy link

fkoomek commented May 18, 2020

Hello.
Would it be possible to add the default state of "Visible on the product page" checkbox for specific attributes? I would like to untick this checkbox default for some attributes. Thank you

@ducwp
Copy link

ducwp commented Jul 16, 2020

this is not work on WC 4.3

@fkoomek
Copy link

fkoomek commented Jul 16, 2020

It works for me.

@philmarty
Copy link

It works great for me too. With WC 4.9
Thanks a lot

@v-marinkov
Copy link

Hi, this works great - thanks! It does however show the attributes in random order for each product. Is it because some of them are empty and is there anything to be done so they are in a pre-determined order?

@fkoomek
Copy link

fkoomek commented Feb 21, 2021

Doesn't work with YOAST 15.8.

@Jon007
Copy link
Author

Jon007 commented Feb 21, 2021

@fkoomek I have this type of code in use with Yoast 15.8. Yoast free version doesn't interact with WooCommerce or custom attributes so not sure what you are expecting.

@Jon007
Copy link
Author

Jon007 commented Feb 21, 2021

@v-marinkov attribute sorting is beyond the scope of this old gist,
the attributes themselves generally appear in the order added and can be moved in the product edit screen
if you refer to the values they can be sorted on the term in Product Attributes, set to custom sorting and rearrange terms.

@fkoomek
Copy link

fkoomek commented Feb 21, 2021

@Jon007 Thank you for your reply. Yes, I use the free version of YOAST. I've just checked this on a fresh new install with only Woocommerce and YOAST installed and for me, it just stopped working with the 15.8 version. The attributes aren't created for a new product. When I roll back to 15.7. or disable the plugin it works correctly. Does it really work for you as it supposed to work with 15.8. YOAST free version?

@Jon007
Copy link
Author

Jon007 commented Feb 21, 2021

@fkoomek ok yew I see the fields are not showing up on a new draft item when Yoast 15.8 is activated

@fkoomek
Copy link

fkoomek commented Feb 21, 2021

@Jon007 Hello Jon.
I found out that it's caused by this piece of code in the file wordpress-seo\src\builders\indexable-link-builder.php from line 96:

YOAST 15.8.

/**
 * Builds the links for a post.
 *
 * @param Indexable $indexable The indexable.
 * @param string    $content   The content. Expected to be unfiltered.
 *
 * @return SEO_Links[] The created SEO links.
 */	

public function build( $indexable, $content ) {
if ( $indexable->object_type === 'post' ) {
$post = $this->post_helper->get_post( $indexable->object_id );
// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- To setup the post we need to do this explicitly.
$GLOBALS['post'] = $post;
\setup_postdata( $post );
$content = \apply_filters( 'the_content', $content );
\wp_reset_postdata();
}

YOAST 15.7.

/**
 * Builds the links for a post.
 *
 * @param Indexable $indexable The indexable.
 * @param string    $content   The content. Expected to be unfiltered.
 *
 * @return SEO_Links[] The created SEO links.
 */	

public function build( $indexable, $content ) {
if ( $indexable->object_type === 'post' ) {
$content = \apply_filters( 'the_content', $content );
}

Do you have any idea what's going on here?

@Jon007
Copy link
Author

Jon007 commented Feb 21, 2021

I don't have time to look into it right now but it looks like the problem is:
\wp_reset_postdata()
which presumably removes the attributes the code has added and reloads the post without them (on a new draft the attributes are not saved yet they are just being added as part of the render).

commenting out \wp_reset_postdata() is likely to work,

I think a different WooCommerce hook could also be used instead of woocommerce_product_write_panel_tabs,
for example save_post_product though would need to test whether that is called on a new draft.

Alternatively, every property get on a WooCommerce Product object is filterable using a filter name dynamically created in get_prop() function in abstract-wc-data.php

			if ( 'view' === $context ) {
				$value = apply_filters( $this->get_hook_prefix() . $prop, $value, $this );
			}

the filter for getting attributes should be:
woocommerce_product_get_attributes
however this also needs testing that it is called in this context. If it does then switch the code from woocommerce_product_write_panel_tabs action to woocommerce_product_get_attributes filter.

@v-marinkov
Copy link

@v-marinkov attribute sorting is beyond the scope of this old gist,
the attributes themselves generally appear in the order added and can be moved in the product edit screen
if you refer to the values they can be sorted on the term in Product Attributes, set to custom sorting and rearrange terms.

@Jon007 even if I re-order the attributes on a single product manually and update it, they still remain a random order per product. Is there a way to define a global sort order for the attributes?

@Jon007
Copy link
Author

Jon007 commented Feb 27, 2021

@fkoomek I've reported the Yoast compatibility issue on Yoast/wordpress-seo#16716

@v-marinkov, attributes have a position which is set to zero by default, so if a custom order is needed use
$newattr->set_position($value);
to set a numeric position value for new attribures

@fkoomek
Copy link

fkoomek commented Feb 27, 2021

@Jon007 Thank you very much for looking into it 🙂.

@Divepit
Copy link

Divepit commented Feb 27, 2021

Where would I add this code in order for it to execute on editing a product?

@fkoomek
Copy link

fkoomek commented Feb 27, 2021

@Divepit So you want to execute this code when you start editing product or it's just a general question where to insert the code? Code goes to functions.php file which you find in your theme directory. But it's strongly recommended to use child theme for all customizations!

@Jon007
Copy link
Author

Jon007 commented Feb 27, 2021

Um, well actually as this is admin code not front-end code it should be in a plugin rather than a theme.
For plugin development refer to: https://developer.wordpress.org/plugins/
Though actually the simplest plugin would be eg create a directory under plugins and a php file with the same name and put the code in that.

@fkoomek
Copy link

fkoomek commented Feb 27, 2021

Thanks for the clarification!

@Jon007
Copy link
Author

Jon007 commented Mar 6, 2021

@fkoomek the Yoast compatibility issue appears resolved on Yoast update 15.9.1

@JGWINBUSH
Copy link

I had to sign up just to say thank you for this, with all of my products having 15+ attributes this has saved me so much time. Thank you!

@BinTofajjal
Copy link

not working with Woocommerce Version 5.7.1

@v-marinkov
Copy link

not working with Woocommerce Version 5.7.1

Is it throwing an error or simply not ordering the attributes? Did you find a solution?

@CHAVOGIT
Copy link

CHAVOGIT commented Dec 6, 2021

How do I trigger the execution of this snippet? Is it executed automatically if I post it in the functions.php page and whenever a new product is added?

@e-orlov
Copy link

e-orlov commented Jun 1, 2023

Do you probably have an idea, how to automaticall apply a combination of attribute terms to all products in a given category? Like this: if a new product is published in a category 1, the product gets attribute "color", value "green" and attribute "size", value "big".

@planetcappe
Copy link

For those getting the error "Attempt to read property 'term_id' on bool" with PHP 8, here's the fix:
this part
if ($name=='pa_brand'){ $term = get_term_by('slug', 'acme', $name); $newattr->set_options(array($term->term_id)); }
should become
if ($name == 'pa_brand') { $term = get_term_by('slug', 'acme', $name); if ($term) { $newattr->set_options(array($term->term_id)); } }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment