How to display WordPress WooCommerce custom attribute in templates/functions? - php

Would put this in WP Stack Exchange, but often they say since it has PHP it should be in SO, so never quite sure where is best. Can move if more appropriate.
To display a custom woocommerce product attribute called, “Fabrics” for example I’ve read you could do the following.
$fabric_values = get_the_terms( $product->id, ‘pa_fabrics’);
foreach ( $fabric_values as $fabric_value ) {
echo $fabric_value->name;
}
However, is there a shorter way since we would be using a lot of attributes throughout php templates.
For instance is there a way to simply do, “echo get_the_terms( $product->id, ‘pa_fabrics’);”
Or is there a single function one could add to their site, so that would then be able to echo any product attribute which a single very short line like above like you can when use “Advanced Custom Fields” with non WooCommerce sites?
UPDATE
Found this thread on SO that offers a way to create a single short code to relatively easily get the data. While that's certainly an option, would like to see if there is any cleaner built in ways such as:
echo get_the_terms( $product->id, 'pa_fabrics');
or
echo $product->get_attributes('pa_fabrics');
The last option seems the cleanest and most ideal but results in an error: "Fatal error: Uncaught Error: Call to a member function get_attributes() on null in (my functions.php file where the code was added).

The answer to your question is it depends. Consider for a moment how flexible you need this to be.
Let's start by looking at what is wrong with the 2 suggested examples.
1) echo get_the_terms( . . .
When using a function it's important to know the return type. get_the_terms() will return an array when successful. You need to do something with that array in order to display it.
https://developer.wordpress.org/reference/functions/get_the_terms/
2) echo $product->get_attributes(...
You're heading down the right path :) The error you're seeing tells you that $product isn't what you're expecting it to be. get_attributes() is a method of the WC_Product class. You need to have an instance of that class in order to use it.
One way of getting hold of the product would be to use wc_get_product().
$product = wc_get_product();
Now the second problem you have is with the method itself. get_attributes(), like get_the_terms(), will return an array. It's then your responsibility to display that data.
Instead I believe you're looking for get_attribute(). This method takes an attribute name as its only argument and returns a string of attribute values.
Example:
// Get a product instance. I could pass in an ID here.
// I'm leaving empty to get the current product.
$product = wc_get_product();
// Output fabrics in a list separated by commas.
echo $product->get_attribute( 'pa_fabrics' );
// Now that I have $product, I could output other attributes as well.
echo $product->get_attribute( 'pa_colors' );

Related

How to properly create a WooCommerce Variation using the helper class WC_Product_Variation

Before anything, I'd like to say that I have thoroughly searched all over internet to find as much help as I could into solving this particular issue.
I have tried many different ways of creating a variation, in particular this one which also didn't seem to bind the label to the field next to the variation ID on my product page. The outcome was the same as the method I've written with the helper class. I still have it commented somewhere so if you happen to know a solution for this specific way of creating a variation, hit me up!
Some context: I have a csv file with a lot of information on my products. I wrote a script to import them into WooCommerce programmatically using the WC_Product_Simple helper class and a stripped down version of the original csv (with only two items), and everything worked perfectly for the two products. Item #2 is flour, so I figured I'd try creating variations for it instead of creating multiple simple products (for instance Flour (100Gr), Flour (500Gr) and Flour (1Kg)).
I was able to create an attribute for my WC_Product_Variable, set its variation field to true, create three variations (100Gr, 500Gr and 1Kg) and save them. When I look at my Products page in WooCommerce, I can see the three variations under my variable product.
The issue I'm having is that I can't bind a particuliar field to one variation, no matter how hard I try. I have no idea how this field is called, tho I've noticed that setting it reads/writes the post_excerpt column in my {$wpdb->prefix}posts table.
Here is what is displayed:
And here is what I want to achieve programmatically:
Here's the code:
/**
* At this point, the product is correctly created and available through $wc_product.
* Its id is stored in $product_id.
* I also have an array with information from my product called $product.
* This is where the information is stored to create said product.
* You don't have to worry about the creation of the product itself.
*/
// I have stored as a boolean whether my product had variations or not.
if ($variable) {
// Weight => Label
$weights = array(
'100' => '100Gr',
'500' => '500Gr',
'1000' => '1Kg'
);
$attribute = new WC_Product_Attribute();
$attribute->set_id(0);
$attribute->set_name('Weight');
$attribute->set_options(
$weights
);
$attribute->set_position(0);
$attribute->set_visible(true);
$attribute->set_variation(true);
$wc_product->set_attributes(array($attribute));
$wc_product->save();
// At this point my attribute is created. There is no save method for WC_Product_Attribute.
// I'm 100% sure that everything up to this point works as expected, I've left it here
// for clarity and for better understanding what the code does.
foreach ($weights as $weight=>$label) {
// Each variation will be named after the product sku followed by its label.
// For instance: sku5436-100Gr where sku5436 is the variable product.
$variation_sku = "{$product["sku"]}-{$label}";
// Will uncomment once this works!
// These two lines make sure that each variation is unique through their sku.
// $var_id = wc_get_product_id_by_sku($variation_sku);
// $variation = $var_id?new WC_Product_Variation($var_id):new WC_Product_Variation();
/**
* I create my variation,
* I set its parent id,
* I set its sku,
* I calculate its price with a simple weight/price formula,
* I set its price
*
* And here's the part I'm not too sure about
* I thought "set_attributes" would select between 100Gr, 500Gr and 1Kg
* but it looks like it doesn't do anything. I could just remove that line
* and nothing would change.
*/
$variation = new WC_Product_Variation();
$variation->set_parent_id($product_id);
$variation->set_sku($variation_sku);
$variation_price = ($weight / 1000) * $product["kg_price"];
$variation->set_regular_price($variation_price);
// This is the line I thought would bind the label to the field, but it doesn't
$variation->set_attributes(array("Weight" => $label));
$id_var = $variation->save();
// I tried manually setting the post excerpt. The update works, but since
// it wasn't set through the helper class, I'm assuming some cache
// thing isn't correctly binding the label to the field which I still don't know the name
// global $wpdb;
// $wpdb->update($wpdb->prefix . "posts", array("post_excerpt" => $label), array("ID" => $id_var));
}
}
In short: I'd like to bind the fields next to the post ID (see screenshots) to the correct variation. My code does almost everything except that.
I would also really appreciate if someone could tell me the name of this field. The closest thing I've found is that it is bound to the post_excerpt.

Magento 2/Wordpress/Fishpig related posts

I am currently trying to retrieve a list of related posts on the post article page (post single). I have created a new function within /Block/Post/ListPost.php
public function getRelatedPosts()
{
$posts = $this->getPosts();
die($this->getCategoryId());
return $this->_postCollection;
}
However when I try and output getCategoryId, I get nothing. I'm also unsure how I apply a category filter to the post collection.
Can anyone advise here?
I'm not sure where you have got the getCategoryId method from but this is not part of the ListPost block class so will not work. You cannot just invent methods like this.
You should check the block class for what methods are available. An easy way to do that without even loading the file is to add the following PHP to the class:
echo sprintf('<pre>%s</pre>', print_r(get_class_methods($this)));
exit;
You don't specify in what way the posts should be related but I'm guessing you want to get posts from the same category. One option to do this would be to load the primary category of the post and then get a post collection based on this. If you look in the Post class file, you will see the getParentTerm($taxonomy) method.
if ($category = $post->getParentTerm('category')) {
$relatedPosts = $category->getPostCollection();
// Remove the current post from the collection
$relatedPosts->addFieldToFilter('ID', array('neq' => $post->getId()));
}
You should always look at the class file's you're working with. That is the beauty of open source. You can literally see what methods are available for each object and you can even see how they work.

Trouble passing arguments to WordPress pre_get_posts

I need your help! I am having trouble passing arguments to a function using pre_get_posts.
Purpose of passing arguments:
I have a custom taxonony-$taxonomy.php page which I use to list posts related to a particular category within the specified taxonomy. On this page I also retrieve & setup tags assigned to each post from a custom non hierarchical taxonomy and list them as links on the side. I have another taxonomy-$taxonomy.php (for the custom non hierarchical taxonomy mentioned above) setup so when a user chooses a link he/she clicks on a tag it will direct them to this taxonomy page. This is all working fine.
However, here's where I'm having issues. The posts listed on the non hierarchical taxonomy page are those associate to a particular tag but from multiple categories. What I’m trying to achieve is to list posts associates to the tag chosen and from the category previously being viewed. So for example: Lets say the user clicks on category ‘Accounting’, then chooses the tag ‘Creative Services’. All posts listed should not only be associated with ‘Creative Services’ must also be assigned to only the ‘Accounting’ category. This is where I’ve been trying to pass arguments to the function used by pre_get_posts.
How have I tried passing arguments?:
1- Setup globals: This way no arguments have to be past via the function being invoked. It did not work because the timing is off. The globals are not yet set when the action is called. Below is my code with example post_type and taxonomy. Notice $category is the global variable which hold the category within that taxonomy.
// Alter main query on doc_tag taxonomy templates
function only_query_doc_tag_posts( $query ) {
global $category;
if ( is_tax(array('doc_tag')) && !is_admin() ) {
if($query->is_main_query()) {
$query->set( 'post_type', 'post_type' );
$query->set( 'taxonomy', 'taxonomy_array' );
$query->set( 'taxonomy_name', $category ); //global
}
}
}
add_action( 'pre_get_posts', 'only_query_doc_tag_posts' );
Results:
When I view the main query it shows all the changes except the one using the global. If I manually insert the value into the function, instead of using the a global variable then it works. However, I'd like to be able to change this on the fly. Just so you're aware I do get the posts related to the tag but not those only associated to the category indicated by the global (when using the global).
2- do_action:
// Alter main query on doc_tag taxonomy templates
function only_query_doc_tag_posts( $query,
$post_types, //array
$taxonomies, //array
$category //global var
) {
global $category;
if ( is_tax(array('doc_tag')) && !is_admin() ) {
if($query->is_main_query()) {
$query->set( 'post_type', $post_types );
$query->set( 'taxonomy', $taxonomies );
$query->set( 'taxonomy_name', $category ); //global
}
}
}
add_action( 'pre_get_posts', 'only_query_doc_tag_posts', 10, 4 );
Then on the custom non hierarchical taxonomy page template I add the following:
do_action( 'pre_get_post', $post_types, $taxonomies, $category );
I have a feeling my second approach is not technically but I could be wrong, which is why I'm posting it here in hopes that someone can provide some direction. If I missed information that would help you help me please let me know.
Thank you in advance for helping me with this.
FYI: I've read these posts but did not get find a solution. Maybe it's there and I missed it? Please let me know.
Wordpress pre_get_posts and passing arguments
WordPress pre_get_posts not working
wp_query not filtering tax_query correctly in pre_get_posts
Passing arguments with add_action and do_action throwing error 'First argument is expected to be a valid callback'
Wordpress, filter posts if custom query variables are present. (pre_get_posts, add_vars)
Wordpress pre_get_posts category filter removes custom menu items
passing argument using add_action in wordpress!
I think I understand what you try to do.
Long in short - no, you can not do (do_action) by yourself, it is called by wp query parts.
If you try to pass arguments, you need look into [$query->query_vars].
I think a proper solution, for you need, is write a proper url rewrite rules, it can auto pick the taxonomy, and put it into url list, then values can auto show in [$query->query_vars] .
Which is [add_filter('rewrite_rules_array', 'set_url_rule');]
You may also need install plugin [rewrite-rules-inspector] to help you inspect the rewrite status.
Anyway, it could take you an afternoon to find the logical behind it, but then, it all makes sense.
If you just looking for a quick solution, you can inject some code into query_vars, you just need to :
add_filter('query_vars', 'insert_query_vars');
function insert_query_vars( $vars ){
array_push($vars, 'c_type');
array_push($vars, 'c_tax');
array_push($vars, 'c_term');
return $vars;
}

Replace WooCommerce Class

WooCommerce has a class "get_shipping_to_display()" in /wp-content/plugins/woocommerce/classes/class-wc-order.php, that keeps outputting $36 in my invoices (using the Print Invoice & Packing List plugin) as the value for shipping... regardless of what the shipping out is actually.
I've narrowed it down to that class, as using "get_shipping()" displays the actual shipping price.
I don't want to modify WooCommerce core files (so as not to cause problems with updates later on), so how do I go about replacing that class with my own class in functions.php so I can try and narrow down what is causing the issue... and what would that look like?
The last line in the function get_shipping_to_display() is:
return apply_filters( 'woocommerce_order_shipping_to_display', $shipping, $this );
That means that if you create a filter on woocommerce_order_shipping_to_display you can ultimately output whatever you want (though technically contents of that function will still run first). The $shipping variable contains the text output you are seeing now and $this will have all the properties of the class, including the order $id, $status, etc which you could use to do whatever lookups/calculations you want.

Magento ->getSku() or getData(’sku’) returns empty string

I have Magento 1.3.2 and I have weird issue:
When I am in list.phtml, and I try to fetch the SKU by using getSku() or getData('sku') I get empty string. getName() does work. However, when I do that from other pages, it works well.
I var_dump-ed it and no SKU is shown.
What can cause this?
I'm surprised nobody has given you the easiest and most proper answer yet:
Go to your admin, Catalog >> Attributes >> Manage Attributes. Then edit the 'sku' attribute. Change the "Used in Product Listing" from 'No' to 'Yes'. You will then have access to it from the product object in list.phtml with ->getSku()
The other option is to re-load the product object in the list.phtml using the ID of the product you already have. The code reads something a little like:
$sku = Mage::getModel('catalog/product')->load($_product->getId())->getSku();
Note that $_product is what you are getting in your collection already, and note that getSku is case sensitive (as are all Magento attributes getter/setters).
#Prattski's solution is preferable as you don't really want to be messing with loading/manipulating the objects, but it sounds as though your collection is a little messed up. SKU is one of the core fields that exists in the base catalog_product_entity table, so would be unusual not to be loaded.
Probably sku is not added to the list of attributes when a collection is retrieved. I assume you are talking a bout the file /template/catalog/product/list.phtml. If so, then you need to extend the corresponding code file (/app/code/core/Mage/Catalog/Block/Product/List.php).
I think your best bet is to overload the getLoadedProductCollection() method to:
public function getLoadedProductCollection()
{
return $this->_getProductCollection()->addAttributeToSelect('sku');
}
This might not work, I have not been able to test it, as in my store the sku and all other attributes are accessible in the list.phtml template file.
Try this:
<?php
$current_product = Mage::registry('current_product');
if($current_product) {
$sku = $current_product->getSku();
// output sku
echo $sku;
}
?>
I had same problem too but tried $_product['sku']
it works for me
$_product["sku"]; enough to get the product sku.

Categories