Get product variation ID from variation SKU [duplicate] - woocommerce

I'm working on a separate templates page, which page gets woocommece product sku using custom field of wordpress post. i need to get product id of that sku for create woocommece object and do further things, here is my code.
global $woocommerce;
//this return sku (custom field on a wordpress post)
$sku=get_field( "product_sku" );
if($sku!=''){
//need to create object, but using sku cannot create a object,
$product = new WC_Product($sku);
echo $product->get_price_html();
}
is there way to get product id before create object, then i can pass the product id to WC_Product class constructor and create object.thank you

WooCommerce 2.3 finally adds support for this in core.
If you are using this version, you can call
wc_get_product_id_by_sku( $sku )

You can use this function (found here). Google is your friend!
function get_product_by_sku( $sku ) {
global $wpdb;
$product_id = $wpdb->get_var( $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key='_sku' AND meta_value='%s' LIMIT 1", $sku ) );
if ( $product_id ) return new WC_Product( $product_id );
return null;
}

WooCommerce product is a special post type. Because of this WP_Query might be used to find product by SKU and other parameters. This might be used as an alternative when you need to restrict your search by some other criterias.
For example you might want to specify language if you use Polylang (or other plugins) for site translations. Or you can restrict search by product type.
They do direct SQL query in the WooCommerce method get_product_id_by_sku which I think is perfectly fine in many cases. But might not work if you use translations, it will return random product but not the one in current language.
Example code to find product by SKU using WP_Query (with restriction by product type and language):
public function find( string $lang, string $sku ) {
$query = [
'lang' => $lang,
'post_type' => 'product',
'meta_query' => [
[
'key' => '_sku',
'value' => $sku,
'compare' => '='
]
],
'tax_query' => [
[
'taxonomy' => 'product_type',
'terms' => [ 'grouped' ],
'field' => 'name',
]
]
];
$posts = ( new WP_Query() )->query( $query );
return count( $posts ) > 0 ? $posts[0] : null;
}

Related

Group by wordpress posts by meta value

I have about 2000 woocommerce products on my wordpress database, each product has an ACF select field named place which value is local or regional.
My goal is to group by each products by the place meta field so i I thought that following code do the job :
class Search
{
public function __construct()
{
add_action("pre_get_posts", [$this, "filterProducts"]);
add_filter('posts_groupby', [$this, 'groupByFilter'] );
}
public function filterProducts($query){
// ... code
// This where i alter que query
// .. code
return $query;
}
public function groupByFilter($groupby)
{
global $wpdb;
return $wpdb->postmeta . ".meta_key = 'place'";
}
}
But, this not working, and output only one product while the expected result is :
-- LOCAL
product
product
-- REGIONAL
product
product
I know foreach over each product is a solution, but for performance reasons i am looking do to it directly throught main query.
NOTE : if it helps, I use timber and in the search.php file I have this piece of code :
$context = Timber::get_context();
$context["posts"] = new \Timber\PostQuery();
// print the results
dump($context['posts']);
UPDATE :
The sql results output this query :
SELECT 6288gjvs_posts.* FROM 6288gjvs_posts
WHERE 1=1
AND 6288gjvs_posts.post_type = 'product'
AND (6288gjvs_posts.post_status = 'publish'
OR 6288gjvs_posts.post_status = 'acf-disabled'
OR 6288gjvs_posts.post_status = 'complete'
OR 6288gjvs_posts.post_status = 'paid'
OR 6288gjvs_posts.post_status = 'confirmed'
OR 6288gjvs_posts.post_status = 'unpaid'
OR 6288gjvs_posts.post_status = 'pending-confirmation'
OR 6288gjvs_posts.post_status = 'cancelled'
OR 6288gjvs_posts.post_status = 'private')
GROUP BY 6288gjvs_postmeta.meta_key = 'place'
ORDER BY 6288gjvs_posts.menu_order, RAND(1348526234)
Copied it and pasted on phpmyadmin with error output :
#1054 - Champ '6288gjvs_postmeta.meta_key' not known in group statement
So i left join the post meta :
SELECT 6288gjvs_posts.post_title, 6288gjvs_posts.ID, 6288gjvs_postmeta.post_id
FROM 6288gjvs_posts
LEFT JOIN 6288gjvs_postmeta
ON 6288gjvs_posts.ID = 6288gjvs_postmeta.post_id
AND 6288gjvs_posts.post_type = 'product'
GROUP BY 6288gjvs_postmeta.meta_key = 'place'
Without success
UPDATE 2
Hello, so finally i opted with two queries, i didn't found any solution, now I have to manage two paginations in one page :(. If someone find a solution, i always follow up this post and may be useful for others.
Thank you for your helps
If you want to filter your posts by a meta key, you should definitally take a look at WP_Meta_Query.
So like you did with 'tax_query' in the 'pre_get_posts' filter, you should add another query in the main query like this:
$meta_query[] = array(
'key' => 'place',
'value' => 'your_value',
'compare' => '=',
);
$query->set('meta_query', $meta_query);
I hope you find this usefull and it solves your problem.
It is a lot easier and advisable to get posts using WP_Query instead of modifying the SQL codes that may affect other post types and queries. In your scenario where you are getting WooCommerce products, use WC_Product_Query. Below is an example that will get you products with meta_key = place
function get_xx_products()
{
$args = array(
'my_custom_var' => array(
array(
'key' => 'place',
'value' => '',
'meta_compare' => 'EXISTS'
)
),
'status' => 'publish'
);
return wc_get_products($args);
}
function hook_my_custom_var($query, $query_vars)
{
if (!empty($query_vars['my_custom_var'])) {
$query['meta_query'] = $query_vars['my_custom_var'];
}
return $query;
}
add_filter('woocommerce_product_data_store_cpt_get_products_query', 'hook_my_custom_var', 10, 2);
You can read more about it here - https://github.com/woocommerce/woocommerce/wiki/wc_get_products-and-WC_Product_Query

Custom Query Filter for Elementor Posts by relationship field (ACF)

I have a custom post type setup called 'Artists'. Each Single Artist is a page (artist profile if you wish), has a list of products that are associated with that artist via the relationship field type through Advanced Custom Fields (ACF). I need the products to be displayed within their categories on the artist page. So within Elementor I need to specify a 'Query Filter ID' to simply split the products into categories.
What I have tried so far
I am trying to display only products from a certain category in a list via a custom query as I need to generate a Query ID.
I've been trying a bunch of different ways to do this but now I'm at a loss. The latest code I have is here... what am I missing?
/** Product category 'SOFTWARE' **/
add_filter( 'software_product', 'product_category_software' );
function product_category_software( $variable ) {
$query_args = array(
'post_type' => 'product',
'tax_query' => array(
array(
'taxonomy' => 'category',
'field' => 'slug',
'terms' => 'software',
),
),
);
return $variable;
}
Answer
You need to use this
https://developers.elementor.com/custom-query-filter/#Using_the_Custom_Filter
arbitrary_name_query_id - the QUERY ID that you fill in the Elementor field,
the code to be placed within functions.php:
add_action( 'elementor/query/arbitrary_name_query_id', function( $query ) {
$meta_query = $query->get( 'meta_query' );
$meta_query[] = [
'key' => 'acf_key', // put ACF relationship field name
'value' => get_the_ID(),
'compare' => '=',
];
$query->set( 'meta_query', $meta_query );
} );
Regarding to the given code
First of all, as Wordpress Documentation on filters says:
They (i.e. filters) provide a way for functions to modify data of other
functions.
Which means that the filter function, in this case it's product_category_software, receives data that subsequntly modifies and returns it back:
add_filter( 'software_product', 'product_category_software' );
function product_category_software( $variable ) { // receive $variable
/**
* Modify $variable here
*/
return $variable; // return modified $variable
}
In your case product_category_software doesn't modify what receives, but introduces new peace of data $query_args which makes no sence, since it is not going to be returned.
Secondly, the first argument of the add_filter function should be an existent filter name, the software_product is not.
Thirdly, the correct name of the taxonomy of product categories is product_cat.
Getting products from a certain category
Aproach #1 Modfying main query with pre_get_posts
function your_arbitrary_name( $query ) { // receive
if ( ! is_admin() && is_post_type_archive( 'product' ) && $query->is_main_query() ) {
$tax_query = array(
array(
'taxonomy' => 'product_cat',
'field' => 'slug',
'terms' => array('software'),
)
);
$query->set( 'meta_query', $tax_query ); // modify
}
return $query; // and return
}
add_action( 'pre_get_posts', 'your_arbitrary_name', 20 );
Aproach #2 Use wc_get_products or WC_Product_Query
Example, use in your Controller or even in the template:
$args = array(
'category' => array( 'software' ),
);
$products = wc_get_products( $args );
Read the Docs about wc_get_products and WC_Product_Query

Get the value of a field in gravity forms and use that value as a php parameter?

I am trying to dynamically populate two dropdown fields in a Gravity Forms form. The first field dynamically populates with the terms available in a custom post type. I want the second dynamically populated field to contain the list of all post titles within the custom post type AND have those titles filtered by the term selected in the previous dropdown. Is it possible to get the value of a dropdown within Gravity Forms and pass that value as a parameter in $args to use the get_posts($args) function?
I started using the following tutorial as a guide. https://docs.gravityforms.com/dynamically-populating-drop-down-fields/
add_filter( 'gform_pre_render_3', 'populate_procedures' );
add_filter( 'gform_pre_validation_3', 'populate_procedures' );
add_filter( 'gform_pre_submission_filter_3', 'populate_procedures' );
add_filter( 'gform_admin_pre_render_3', 'populate_procedures' );
function populate_procedures( $form ) {
// Procedure Category Dropdown
foreach ( $form['fields'] as &$field ) {
The first field. The following code populates a dropdown field containing a list of all of the terms within a custom post type (procedure):
if ( $field->type != 'select' || strpos( $field->cssClass, 'populate_procedure_categories' ) === false ) {
continue;
}
$terms = get_terms( array(
'taxonomy' => 'procedure_category',
'orderby' => 'name',
'order' => 'ASC',
) );
// you can add additional parameters here to alter the posts that are retrieved
// more info: http://codex.wordpress.org/Template_Tags/get_posts
//$posts = get_posts( 'post_type=procedure&numberposts=-1&post_status=publish' );
$choices = array();
foreach ( $terms as $term ) {
$choices[] = array( 'text' => $term->name, 'value' => $term->name );
}
// update 'Select a Post' to whatever you'd like the instructive option to be
$field->placeholder = 'Select Procedure Category';
$field->choices = $choices;
The second field. The following code dynamically populates the field with all of the the post titles of the custom post type (procedure). I want to filter these results based upon the value selected above.
if ( $field->type != 'select' || strpos( $field->cssClass, 'populate_procedures' ) === false ) {
continue;
}
$args = array(
'post_status' => 'publish',
'post_type' => 'procedure',
'procedure_category' => 'cardiovascular',
);
$posts = get_posts( $args );
$choices = array();
foreach ( $posts as $post ) {
$choices[] = array( 'text' => $post->post_title, 'value' => $post->post_title );
}
// update 'Select a Post' to whatever you'd like the instructive option to be
$field->placeholder = 'Select Procedure';
$field->choices = $choices;
}
return $form;
}
The second dynamically populated field successfully pulls in the filtered list of post titles based on the $args if I explicitly listed the term (in the example above I used 'cardiovascular'). What I am wondering is if there is a way to grab the value of the previous field and use that to filter the results of the second field (without having to reload the page). Any ideas? Does Gravity Forms have a functionality like this built in?
Using this method, you would need to use multiple pages and add the second Drop Down field to the second page on the form. Then, when the user submits the first page, you can access the value of the first Drop Down from the $_POST. Gravity Forms has a helper method for doing this called rgpost(). Here's what your $args might look like:
$args = array(
'post_status' => 'publish',
'post_type' => 'procedure',
'procedure_category' => rgpost( 'input_FIELDID' ),
);
Replace FIELDID with the field ID of your first Drop Down.
With that said, if you want to accomplish this without having to touch any code, try Gravity Forms Populate Anything.
https://gravitywiz.com/documentation/gravity-forms-populate-anything/

search multiple sku's on woocommerce admin side

This sounds simple but if you have hundreds of products and need to search multiple sku's on woocommerce admin side is not available.
Lets say you need to verify 600 products, you have to:
manually add one sku to search bar
click search
get results
start again
you can't separate by , or by space, or dash.
I searched and no one have an answer or they are questions made are not answered.
How can people search multiple sku's, or products names on woocommerce admin side?
First.. you mention you have to verify many products (600), needing a multiple sku search. It looks to me you're going to do this manually?
I recommend creating a product loop in PHP where you do your verify stuff. This is probably gonna save you alot off time, and you can re-use it.
Now to the problem... Search woo products using multiple sku's.
I agree many info on the internet is kinda misleading. I've done this before, and i always used the pre_get_posts hook to change the WP (search) query before it runs.
After testing it appears the query is not being setup as a search, and the search value is empty...
So woocommerce has to do a custom search query. Luckily i quickly found mircian's post.
WooCommerce uses a Data Store for its post types ( products, orders, etc ) and the search is also done using a custom function. In this case it’s called ‘search_products’ and it does a custom query which basically returns an array of ids to be used for the results.
I modified his function to search with multiple sku's.
Insert the function in your (child) theme's functions.php.
Use '|' as a SKU delimiter. Example: '1234|1235|1236'
Tested on:
Wordpress 4.9.6
Woocommerce 3.3.5
/**
* Use multiple sku's to find WOO products in wp-admin
* NOTE: Use '|' as a sku delimiter in your search query. Example: '1234|1235|1236'
**/
function woo_multiple_sku_search( $query_vars ) {
global $typenow;
global $wpdb;
global $pagenow;
if ( 'product' === $typenow && isset( $_GET['s'] ) && 'edit.php' === $pagenow ) {
$search_term = esc_sql( sanitize_text_field( $_GET['s'] ) );
if (strpos($search_term, '|') == false) return $query_vars;
$skus = explode('|',$search_term);
$meta_query = array(
'relation' => 'OR'
);
if(is_array($skus) && $skus) {
foreach($skus as $sku) {
$meta_query[] = array(
'key' => '_sku',
'value' => $sku,
'compare' => '='
);
}
}
$args = array(
'posts_per_page' => -1,
'post_type' => 'product',
'meta_query' => $meta_query
);
$posts = get_posts( $args );
if ( ! $posts ) return $query_vars;
foreach($posts as $post){
$query_vars['post__in'][] = $post->ID;
}
}
return $query_vars;
}
add_filter( 'request', 'woo_multiple_sku_search', 20 );

Search for Custom Field with Custom Post Types

There are tutorials that explain how to limit a search to a specific category.
My question is, is there a way to configure wordpress' search to, within a custom post type, search for a custom field value.
So for example, if I search for "hello", the results would come up with posts that have a certain custom field equal to "hello". The certain post would also be a certain custom post type.
Any help is appreciated.
To filter search by custom post type use:
<?php query_posts($query_string . '&post_type=custom-post-type-name' ); ?>
before the loop.. then within the loop add a condition similar to this
<?php if ($meta_data[ 'meta-name' ] == 'hello') {
//do something
} ?>
I think here is what you are looking for:
key is the custom field.
value is the value you are looking for
and compare is the operator you want to use.
you can also use LIKE if you want.
// WP_Query arguments
$args = array (
'post_type' => 'vendors',
'post_status' => 'published',
'meta_query' => array(
array(
'key' => 'state',
'value' => 'Misissipi',
'compare' => '=',
),
),
);
// The Query
$query = new WP_Query( $args );
// The Loop
if ( $query->have_posts() ) {
while ( $query->have_posts() ) {
$query->the_post();
// do something
}
} else {
// no posts found
}
// Restore original Post Data
wp_reset_postdata();

Resources