How to Query Posts if Post Meta Value is multidimensional array? - wordpress

I was using the following meta Query, but I'm still getting irrelevant posts.
$current_query['meta_query'] = array(
array(
'key' => '_game_selected_platforms',
'value' => $platform->term_id,
'compare' => 'LIKE'
),
);
Whereas the meta value was multidimensional array as follows:
get_post_meta('$postID','_game_selected_platforms',false);

So here is what i did. i used the pre_get_posts filter, to further dig into that array and search if the value exist, then i used post__in
function latest_articles_of_platform($query){
$platformobject = get_query_var('get_latest_articles');
global $wpdb;
$getallposts = $wpdb->get_results( "SELECT $wpdb->posts.* FROM $wpdb->posts WHERE $wpdb->posts.post_type = 'post'", OBJECT );
if ($getallposts){
$postsin = array();
foreach ($getallposts as $getallpost) {
$platformsofposts = get_post_meta($getallpost->ID,'_game_selected_platforms',false);
if(in_array($platformsofposts->term_id, $platformsofposts ) ){
$postsin[] = $getallpost->ID;
}
}
} // if $getallposts
if (get_query_var('get_latest_articles')) {
// var_dump($getallposts);
$query->set('post__in',$postsin);
}
}
add_action( 'pre_get_posts', 'latest_articles_of_platform' );
I was searching for help with existing topic but i did not get any solution.
IMPORTANT
$current_query['get_latest_articles'] = $platform;
I have also added this line in query to identify that it is my custom query but this is only possible if you have access to front end query otherwise there are other methods to identify if it that query which you want to filter.

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

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

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/

Custom product attributes is blank even though I add it [duplicate]

The products in my clients website require certain attributes which I have added via Products -> Attributes in the Wordpress administration. In this import script I'm coding I need to use the function update_post_meta($post_id, $meta_key, $meta_value) to import the proper attributes and values.
Currently I have the function like so:
update_post_meta( $post_id, '_product_attributes', array());
However I'm not sure how to properly pass along the attributes and their values?
Right so it took me a while to figure it out myself but I finally managed to do this by writing the following function:
// #param int $post_id - The id of the post that you are setting the attributes for
// #param array[] $attributes - This needs to be an array containing ALL your attributes so it can insert them in one go
function wcproduct_set_attributes($post_id, $attributes) {
$i = 0;
// Loop through the attributes array
foreach ($attributes as $name => $value) {
$product_attributes[$i] = array (
'name' => htmlspecialchars( stripslashes( $name ) ), // set attribute name
'value' => $value, // set attribute value
'position' => 1,
'is_visible' => 1,
'is_variation' => 1,
'is_taxonomy' => 0
);
$i++;
}
// Now update the post with its new attributes
update_post_meta($post_id, '_product_attributes', $product_attributes);
}
// Example on using this function
// The attribute parameter that you pass along must contain all attributes for your product in one go
// so that the wcproduct_set_attributes function can insert them into the correct meta field.
$my_product_attributes = array('hdd_size' => $product->hdd_size, 'ram_size' => $product->ram_size);
// After inserting post
wcproduct_set_attributes($post_id, $my_product_attributes);
// Woohay done!
I hope this function will help other people if they need to import multiple attributes pro-grammatically in WooCommerce!
I tried Daniel's answer, and it didn't work for me. It might be that the Wordpress/Woocommerce code has changed since, or perhaps I didn't quite understand how to do it, but either way that code did nothing for me. After a lot of work using it as a base, however, I came up with this snippet of code and put it on my theme's functions.php:
function wcproduct_set_attributes($id) {
$material = get_the_terms( $id, 'pa_material');
$material = $material[0]->name;
// Now update the post with its new attributes
update_post_meta($id, '_material', $material);
}
// After inserting post
add_action( 'save_post_product', 'wcproduct_set_attributes', 10);
With this, I can take what I set as "material" on my WooCommerce install as a custom attribute and add it to the formal meta as _material. This in turn allows me to use another snippet of code so the WooCommerce search function extends to meta fields, meaning I can search for a material in the WooCommerce search field and have all items with that material appear.
I hope this is useful to somebody.
#Daniels's answer works, won't decide on right or wrong, however if you want to add the values as a taxonomy term under attributes you have to adapt the code as below (set is_taxonomy = 1). Otherwise Woocommerce sees it as custom meta field(?). It still adds the value under attributes. This will only work for strings. For values that are arrays the code has to be adapted.
Additionally it uses the wp_set_object_terms that #Anand suggests as well. I was using that, because all the documentation I could find led to believe that had to be used. However if one only uses the wp_set_object_terms then I couldn't see the attributes in the edit product screen. Using the information from both answers and reading on the subject resulted in the solution.
You will need to tweak the code for things such as product variations.
/*
* Save Woocommerce custom attributes
*/
function save_wc_custom_attributes($post_id, $custom_attributes) {
$i = 0;
// Loop through the attributes array
foreach ($custom_attributes as $name => $value) {
// Relate post to a custom attribute, add term if it does not exist
wp_set_object_terms($post_id, $value, $name, true);
// Create product attributes array
$product_attributes[$i] = array(
'name' => $name, // set attribute name
'value' => $value, // set attribute value
'is_visible' => 1,
'is_variation' => 0,
'is_taxonomy' => 1
);
$i++;
}
// Now update the post with its new attributes
update_post_meta($post_id, '_product_attributes', $product_attributes);
}
Then call the function:
$custom_attributes = array('pa_name_1' => $value_1, 'pa_name_2' => $value_2, 'pa_name_3' => $value_3);
save_wc_custom_attributes($post_id, $custom_attributes);
Thank you for posting the code Daniel & Anand. It helped me a great deal.
Don't know if this is the "correct" way to do this... But I needed a function to add ACF repeater fields with a date value as a attribute on post save, so this was the function I came up with:
add_action( 'save_post', 'ed_save_post_function', 10, 3 );
function ed_save_post_function( $post_ID, $post, $update ) {
//print_r($post);
if($post->post_type == 'product')
{
$dates = get_field('course_dates', $post->ID);
//print_r($dates);
if($dates)
{
$date_arr = array();
$val = '';
$i = 0;
foreach($dates as $d)
{
if($i > 0)
{
$val .= ' | '.date('d-m-Y', strtotime($d['date']));
}
else{
$val .= date('d-m-Y', strtotime($d['date']));
}
$i++;
}
$entry = array(
'course-dates' => array(
'name' => 'Course Dates',
'value' => $val,
'position' => '0',
'is_visible' => 1,
'is_variation' => 1,
'is_taxonomy' => 0
)
);
update_post_meta($post->ID, '_product_attributes', $entry);
}
}
}
Hope this helps someone.

wordpress $query->set by meta key value

I am trying to modify all queries of a post type "products" to only show if the product/post has a certain meta_key of "wholesale_price" which the meta_value needs to be greater than 0.
Here is what i have:
add_action( 'pre_get_posts', 'rc_modify_query_get_design_projects' );
function rc_modify_query_get_design_projects( $query ) {
if($query->is_main_query() && $query->query_vars['post_type'] == 'product' ) {
$query->set('meta_key', 'wholesale_price');
$query->set('meta_compare', '>');
$query->set('meta_value', '0');
}
}
For some reason this still returns everything. Is this the right way of doing things?
The problem is the method by which you are setting your meta_query. You should read up on the WP_Query functionality, because meta queries are unfortunately not that simple.
You need to do something more like so:
add_action( 'pre_get_posts', 'rc_modify_query_get_design_projects' );
function rc_modify_query_get_design_projects( $query ) {
// Do not add meta arguments in admin pages
if (is_admin()) {
return;
}
if($query->is_main_query() && $query->query_vars['post_type'] == 'product' ) {
//Get original meta query
$meta_query = (array)$query->get('meta_query');
// Add your criteria
$meta_query[] = array(
'key' => 'wholesale_price',
'value' => 0,
'compare' => '>',
);
// Set the meta query to the complete, altered query
$query->set('meta_query',$meta_query);
}
And no need to return anything in this function.

Resources