WordPress: pre_get_posts action combine date_query and meta_query - wordpress

In my WordPress project, I have gotten some problem.
I have many categories. The categories have a child category name New product.
Currently, I have used pre_get_posts to get the posts list in New product pages. If a post which have post_date > 2018/1/15 will be get in the category pages.
I want to add a custom checkbox keep_product in product which will keep product in New product page if post_date < 2018/1/15 when it checked.
So I think the conditions should be post_date > 2018/1/15** OR **keep_product = 1
I try to create a query, but it seem WP_Query only apply AND condition for date_query and meta_query.
function pre_get_posts_function($q){
//Code in here
$query->set('date_query', array(
array(
'after' => '2018/1/15'
);
));
$query->set('meta_query', array(
array(
'key' => 'keep_product',
'value' => '1'
)
));
}
I also try to create a posts_where action, but It ignores category condition. The product which is checked keep_after_60_days checkbox will be show in New product pages.
function pre_get_posts_function($q){
//Code in here
$query->set('date_query', array(
array(
'after' => '2018/1/15'
);
));
add_filter( 'posts_where', 'postmeta_new_releases',10,1);
}
function postmeta_new_releases( $where = '' ) {
global $wpdb;
$where .= " OR ($wpdb->postmeta.meta_key = 'keep_product' AND $wpdb->postmeta.meta_value = 1)";
return $where;
}
Could anyone help me. I get mad with it in some days.
Thanks!!

Related

How to order WooCommerce products by attribute term menu order

Let's say there's a product attribute "brand" with the following terms:
Brand A
Brand B
Brand C
Let's also say the menu order of these terms in products > attributes > size have been ordered as such:
Brand C
Brand A
Brand B
How can I sort the products on the shop page by the attribute menu order? So for example:
Brand C product
Brand A product
Brand B product
Assume each product only has a single brand attribute term.
So far I've figured the code will likely involve modifying the query using the pre_get_posts hook, but I'm having difficulty customizing the order in such a way.
Since get_terms() is returning the order we have set for our attributes we can get those ids and use them as sorting.
//Change taxonomy if you need.
$terms = get_terms( array('taxonomy' => 'pa_brand','fields' => 'ids'));
From there we can use woocommerce_product_query to modify our query.
And finaly set the order of our query to be by term_id ASC or DESC.
Here is my solution
function sort_products_by_brand( $q ) {
$terms = get_terms( array('taxonomy' => 'pa_brand','fields' => 'ids'));
$tax_query = $q->get('tax_query');
$tax_query[] = array(
'taxonomy' => 'pa_brand',
'field' => 'term_id',
'terms' => $terms,
);
$q->set( 'tax_query', $tax_query);
$q->set( 'orderby', 'term_id' );
// $q->set( 'order', 'DESC' ); // by default is ASC so uncomment if you want DESC.
}
add_action( 'woocommerce_product_query', 'sort_products_by_brand' );
Honk31's answer worked well for me. I just had to modify it a little bit.
add_action( 'pre_get_posts', 'jwd_modify_product_query' );
function jwd_modify_product_query($query) {
if ( !is_admin() && $query->is_main_query() && is_post_type_archive( 'product' ) ) {
$query->set( 'orderby', 'pa_brand' );
}
}
// https://wordpress.stackexchange.com/a/363654/94213
// Answer by honk31
add_filter('posts_clauses', 'jwd_orderby_tax_clauses', 10, 2 );
function jwd_orderby_tax_clauses($clauses, $wp_query) {
global $wpdb;
$orderby = isset($wp_query->query_vars['orderby']) ? $wp_query->query_vars['orderby'] : false;
if ($orderby && $orderby === 'pa_brand') {
$clauses['join'] .= <<<SQL
LEFT OUTER JOIN {$wpdb->term_relationships} ON {$wpdb->posts}.ID={$wpdb->term_relationships}.object_id
LEFT OUTER JOIN {$wpdb->term_taxonomy} USING (term_taxonomy_id)
LEFT OUTER JOIN {$wpdb->terms} USING (term_id)
SQL;
$clauses['where'] .= " AND (taxonomy = '{$orderby}' OR taxonomy IS NULL)";
$clauses['groupby'] = "object_id";
$clauses['orderby'] = "{$wpdb->terms}.term_order ASC";
$clauses['orderby'] .= ", {$wpdb->posts}.post_name ASC";
}
return $clauses;
}
Another step that is needed is to use the Simple Custom Post Order plugin. The plugin adds a term_order column to the terms table that the code uses to sort by. Drag and drop to change the terms order in products > attributes > brand. I actually had to move all of the terms around to get the order to sort correctly.
WooCommerce attributes do have a drag & drop functionality already, though. Perhaps it is possible to do something similar without that plugin.

Get product variation ID from variation SKU [duplicate]

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;
}

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/

Get Woocommerce product by slug

I have a function to grab products by their category and return specific data about them using their category slug like so:
$itemArgs = array(
'post_type' => 'product',
'posts_per_page' => 1000,
'product_cat' => $request['id'],
'include_children' => false
);
$loop = new WP_Query( $itemArgs );
if ( $loop->have_posts() ) : while ( $loop->have_posts() ) : $loop->the_post();
//DO STUFF
endwhile; endif;
Which works fantastically. What I need now is to get a product by its own slug. Where the one above might get every product for "Cookies" category, this one should return just the "Chocolate Chip" product.
I've tried replacing 'product_cat' with 'slug' and 'product_slug' but those don't appear to work. This seems like a fairly straightforward thing to do... there's documentation on finding a product by 48 different properties... slug is not one for some reason. I just get the entirety of the product collection returned.
If you look at the Wordpress codex post about the WP_Query You will see a part which talks about Page and Posts Parameters. There you will see you can use the name parameter.
Assuming your product slug is chocolate-chip, you can use this to retrieve your product with the post_type and name parameters:
$itemArgs = array(
'post_type' => 'product',
'name' => 'chocolate-chip'
);
$query = new WP_Query($itemArgs);
$product_obj = get_page_by_path( $slug, OBJECT, 'product' );
https://wordpress.stackexchange.com/questions/206886/get-product-details-by-url-key-in-wordpress-woocommerce

Resources