Get all post grouped by taxonomy - wordpress

Fairly new to wordpress, but Im trying to do what should be fairly straight forward. I want to get a list of all "departments" (a custom taxonomy). and then all the posts in those departments. A post can be in more than one department.
All the examples I have found seems to get the departments then loop through them and get the posts, but this seems like an inefficient way to query the DB.
So my end result would hopefully look like this:
[
"Department 1" => [
0 => post 1,
1 => post 2,
2 => post 3
],
"Department 2" => [
0 => post 1,
1 => post 2,
2 => post 4
],
]
Note, some posts appearing under multiple departments.
Can anyone point me in the right direction?
Thanks in advance

You use the posts_clauses filter hook and modify the SQL query based on your requirements. check below code.
function orderby_tax_grouped_clauses( $clauses, $wp_query ) {
global $wpdb;
$taxonomy = 'your_custom_taxonomy';
$clauses['join'] .= "
LEFT OUTER JOIN {$wpdb->term_relationships} AS rel2 ON {$wpdb->posts}.ID = rel2.object_id
LEFT OUTER JOIN {$wpdb->term_taxonomy} AS tax2 ON rel2.term_taxonomy_id = tax2.term_taxonomy_id
LEFT OUTER JOIN {$wpdb->terms} USING (term_id)
";
$clauses['where'] .= " AND (taxonomy = '".$taxonomy."' OR taxonomy IS NULL)";
$clauses['groupby'] = "rel2.object_id";
$clauses['orderby'] = "GROUP_CONCAT({$wpdb->terms}.name ORDER BY name ASC) "; // 'ASC' OR 'DESC';
return $clauses;
}
add_filter( 'posts_clauses', 'orderby_tax_grouped_clauses', 10, 2 );
$args = array(
'post_type' => 'your_custom_post_type', // your CPT name
'posts_per_page' => -1
);
add_filter('posts_clauses', 'orderby_tax_grouped_clauses', 10, 2 );
$post_list = new WP_Query( $args );
remove_filter('posts_clauses', 'orderby_tax_grouped_clauses', 10, 2 );
and remove filter so it's only apply to your query not all query.

Please try with this code. May is useful for you. Thanks
$list_custom_taxonomy = get_terms('your_custom_taxonomy'); // your taxonomy name
foreach($list_custom_taxonomy as $custom_single_term) {
$args = array(
'post_type' => 'your_custom_post_type', // your CPT name
'tax_query' => array(
array(
'taxonomy' => 'your_custom_taxonomy', // your taxonomy name
'field' => 'slug',
'terms' => $custom_single_term->slug,
),
),
);
$post_list = new WP_Query($args);
if($post_list->have_posts()) {
echo '<h1>'.$custom_single_term->name.'</h1>';
while($post_list->have_posts()) { $post_list->the_post();
echo '<h6>'.get_the_title().'</h6>';
}
}
wp_reset_postdata();
}

Related

Filter products by price range using WooCommerce wc_get_products

I'm getting a bunch of paginated variable products using wc_get_products(). And I would like to only get products in a given price-range. The problem is that there are a lot of products, so only 20 products are fetched at a time.
According to the documentation, there are no meta_query()-option when using wc_get_products().
So how do I get this?
Here is my current query:
$products_query = [
'category' => [ get_queried_object()->slug ],
'limit' => 20,
'paginate' => true,
'paged' => (get_query_var('paged')) ? get_query_var('paged') : 1,
];
$products = wc_get_products( $products_query );
Now remember, that it's something that needs to be implemented before the products are queried from the database.
Let's say that I have 100 products total. Since the products are paginated, then if I have ordered the products after _price (ASC), then page 1 should return the 20 cheapest products. And page 3 should return the products 40-60 (after they have been sorted).
Solution attempts
Attempt 1 - Try using a meta_query anyways
I figured that wc_get_products was inspired and built on top of WP_Query, so I tried this:
$products_query = [
'category' => [ get_queried_object()->slug ],
'limit' => 20,
'paginate' => true,
'paged' => (get_query_var('paged')) ? get_query_var('paged') : 1,
'meta_query' => [
'relation' => 'AND',
[
'key' => '_price',
'value' => 100,
'compare' => '<=',
'type' => 'NUMERIC'
]
]
];
$products = wc_get_products( $products_query );
It just ignores the meta_query-part and returns a result as if it wasn't there.
Attempt 2 - Using WP_Query
Everywhere I look, there are arrows pointing towards wc_get_products and away from wp_query.
So I haven't pursued this.
Attempt 3 - Raw SQL
One solution would be to make a raw SQL-query. But since the products are variable products, then the SQL-query would be quite a few joins, since it should first find all products - and then find all variations. And then sort the products after the lowest variation-price (if the price-sort is ascending) - and after the highest variation-price (if the price-sort is descending). It's obviously possible, but I was hoping to find a more WordPress-like solution. And I'm not good at SQL.
Attempt 4 - Using a plugin
I look around a bit - and sort'n'filter-plugins for WooCommerce are monsters, since they slab scripts and styles left, right and center. So this seems like a poor solution.
Attempt 5 - Using WooCommerce API
I can see that min_price and max_price are mentioned under List all products. So this might actually work.
Variable products are something complicated to filter by priceā€¦ So the following is not perfect, but show you a way to enable a price range in WC_Product_Query.
So the following function will enable a custom price range query on WC_Product_Query:
add_filter( 'woocommerce_product_data_store_cpt_get_products_query', 'handle_price_range_query_var', 10, 2 );
function handle_price_range_query_var( $query, $query_vars ) {
if ( ! empty( $query_vars['price_range'] ) ) {
$price_range = explode( '|', esc_attr($query_vars['price_range']) );
if ( is_array($price_range) && count($price_range) == 2 ) {
$query['meta_query']['relation'] = 'AND';
$query['meta_query'][] = array(
'key' => '_price',
'value' => reset($price_range), // From price value
'compare' => '>=',
'type' => 'NUMERIC'
);
$query['meta_query'][] = array(
'key' => '_price',
'value' => end($price_range), // To price value
'compare' => '<=',
'type' => 'NUMERIC'
);
$query['orderby'] = 'meta_value_num'; // sort by price
$query['order'] = 'ASC'; // In ascending order
}
}
return $query;
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
EXAMPLE USAGE
Handle float numbers.
The 1st price is separated from the 2nd one by a pipe |
Here we query products from $10.25 to $50 (price range):
$products = wc_get_products( [
'limit' => 20,
'status' => 'publish',
'price_range' => '10.25|50', // The first price is separated from the 2nd one with a pipe
] );
echo '<ul>';
foreach( $products as $product ) {
echo '<li>'.$product->get_name().' '.$product->get_price_html().'</li>';
}
echo '</ul>';
To filter only variable products, you can add the following line to the query:
'type' => 'variable',
Documentation: wc_get_products and WC_Product_Query
function handle_custom_query($query, $query_vars)
{
if (!empty($query_vars['meta_query'])) {
foreach ($query_vars['meta_query'] as $q) {
$query['meta_query'][] = $q;
}
}
return $query;
}
add_filter('woocommerce_product_data_store_cpt_get_products_query', 'handle_custom_query', 10, 3);
usage:
$query = new \WC_Product_Query(
array(
// ...standard queries
// https://github.com/woocommerce/woocommerce/wiki/wc_get_products-and-WC_Product_Query
'meta_query' => array(
// ...array of meta queries
),
)
);
$products = $query->get_products();

Get category and terms from wp_query result in WordPress

I'm trying to make a counter of category and terms from a WP_QUERY where I'm search for specific word.
But I'm not be able to understand if is possible, I've read the documentation and some posts here on Stack Overflow, but I didn't find any solution.
My code to find something is simple:
$args['post_type'] = $post_type;
$args['post_status'] = 'publish';
$args['posts_per_page'] = 14;
$args['paged'] = 1;
$args['s'] = 'something to search';
$query = new WP_Query($args);
Now I need to count how many posts belongs from 'category 1', or how many belongs from 'term x'.
Could I get this information using a simple select?
this is a solution - please customize it
$args = [
'post_type' => 'post',
'posts_per_page' => -1
];
$results = new wp_query( $args );
$terms = [];
while( $results->have_posts() ){
$results->the_post();
$get_terms = get_the_terms( get_the_ID(), 'category' );
foreach( $get_terms as $term ) {
$terms[] = $term->term_id;
}
}
$repeated_terms = array_count_values($terms);
print_r($repeated_terms);
//then get term's information from term's ids and show counts
Try this:
Extracted and adapted from: https://developer.wordpress.org/reference/classes/wp_query/
$args = array(
'post_type' => 'post',
'tax_query' => array(
array(
'taxonomy' => 'category', // get 'posts' only within this taxonomy (category)
'field' => 'slug', // get 'posts' by the slug field (can also be 'name')
'terms' => 'category1', //get 'posts' terms by the slug field named: 'category1' (or termX)
),
),
's' => 'ferrari' // search on matching posts the keyword 'ferrari'
);
$query = new WP_Query( $args );
I've realised that Wordpress haven't a good solution without lopping inside looping, so I did a select count, like this:
SELECT wp_terms.name, wp_terms.slug, wp_terms.term_id, Count(wp_terms.term_id) as count FROM wp_posts
INNER JOIN wp_term_taxonomy ON wp_term_taxonomy.taxonomy = 'category'
INNER JOIN wp_term_relationships ON wp_term_relationships.object_id = wp_posts.ID AND wp_term_relationships.term_taxonomy_id = wp_term_taxonomy.term_taxonomy_id
INNER JOIN wp_terms ON wp_terms.term_id = wp_term_taxonomy.term_id
INNER JOIN wp_postmeta ON meta_key = 'controledata' and wp_postmeta.post_id = wp_posts.ID
WHERE post_type = 'eventos' AND post_status = 'publish' AND count > 0 AND wp_terms.slug LIKE '%some_term%'
GROUP BY wp_terms.name, wp_terms.slug, wp_terms.term_id
Order by wp_terms.name asc
So, it's not overload the server, but I'm need two select to mount this page.
Thanks for every answer.

wordpress post in category serial number

How can I get a serial number of current post in category?
For ex. I have a category Cars with 4 posts in it. When I open some post I want to see navigation like this: Post 3 of 4 [<<] [>>]
Most straightforward way is querying the posts in the category, like this:
// WP_Query arguments
$args = array (
'category_name' => 'cars',
'posts_per_page' => '-1',
'order' => 'DESC',
'orderby' => 'date',
);
// The Query
$query = new WP_Query( $args );
Then you can get the number of posts with
// $query->found_posts gives the number of posts the query has found
// with the parameters you set
echo $query->found_posts;
And you can count up the post you display:
$count = 0;
foreach ( $query->posts as $count_post ) {
$count++;
// assuming you are inside The Loop
if ( get_the_ID() == $count_post->ID ) {
break;
}
}
// now you can get the "serial number" of the post
echo $count;
This might not be the most "WP way" of doing it, but it should work. :)

How to get the post ID associated with a Woocommerce Subscription

I am desperately trying to programmatically get the post ID associated with a WooCommerce Subscription object. I am starting from the user id, and trying to request the database using the get_posts function. A call using the get_users_subscriptions works but the objects returned do not include their ID, only the associated order_id or product_id.
$subscriptions = WC_Subscriptions_Manager::get_users_subscriptions(
$current_user->ID
);
$subscription_posts = get_posts( array(
'orderby' => 'date',
'order' => 'ASC',
'numberposts' => 1,
'meta_key' => '_customer_user',
'meta_value' => $current_user->ID,
'post_type' => 'shop_subscription'
) );
The get_post request is sadly returning an empty array. Do you see something wrong in my request ?
Thanks in advance,
I did not find an easy solution so I went with a SQL request based on the order_id contained in the subscription object I get using the call of:
$subscriptions = WC_Subscriptions_Manager::get_users_subscriptions(
$current_user->ID
);
The $wpdb call looks like the following:
$subscription_id = $wpdb->get_var(
"SELECT ID
FROM $wpdb->posts
WHERE `post_type`='shop_subscription'
AND `post_parent`=$first_order->ID
ORDER BY `post_date` ASC
LIMIT 1;"
);
If it helps someone, you're welcome !
I've achieved the same thing with the following code, if you are using the wp rest APIs, it will also add a property to the response:
add_action(
'rest_api_init',
function() {
// add _subscription_id to wc-order response
register_rest_field( 'shop_order', '_subscription_id', [
'get_callback' => function ($order) {
$subscriptionIds = wcs_get_subscriptions_for_order( $order['id'] );
foreach( $subscriptionIds as $subscriptionId => $subscription )
if($subscription->order->id == $order['id']) break;
foreach( $order['meta_data'] as $meta )
if ($meta->key === '_subscription_renewal') {
$metaSubscriptionId = $meta->value; break;
}
return $subscriptionId ?: (int) $metaSubscriptionId;
},
'update_callback' => null,
'schema' => null,
]);
}
);

how to send title and permalink through JSON using get_posts

This is what i print out.
$args = array(
'category' => 'Animation',
'numberposts' => 8
);
$posts_array = get_posts( $args );
echo json_encode($posts_array);
this is the result: (there are 8 identical JSON lists like this, just showing 1 for convenience.)
{"ID":554,"post_author":"1","post_date":"2012-12-28 19:17:43","post_date_gmt":"2012-12-28 19:17:43","post_content":"The race to space is on. As nations compete, we follow the progress of a single chimpanzee that's been recruited into the Space Program. His results might prove influential for the better of all mankind. ...will he be up for the challenge","post_title":"Alpha","post_excerpt":"","post_status":"publish","comment_status":"open","ping_status":"open","post_password":"","post_name":"alpha","to_ping":"","pinged":"","post_modified":"2012-12-28 19:17:43","post_modified_gmt":"2012-12-28 19:17:43","post_content_filtered":"","post_parent":0,"guid":"http://localhost/Wordpress%203.4.2/?p=554","menu_order":0,"post_type":"post","post_mime_type":"","comment_count":"0","filter":"raw"
but i just want to send the id and the post_content. i keep on getting null values and cant figure out why.
Just filter by the fields you need: (id and the post_content or title and permalink)
$args = array(
'category' => 'Animation',
'numberposts' => 8
);
$posts_array = get_posts($args);
$send_array = array();
foreach ($posts_array as $key => $value)
{
$send_array[$key]["ID"] = $value->ID;
$send_array[$key]["post_content"] = $value->post_content;
}
echo json_encode($send_array);
exit;

Resources