Get posts that matches both taxonomies - php

I have a movie-database, i want to know which movies actor A and B has both been featured in.
function getmoviefromactor(){
global $wp_query;
global $wpdb;
global $post;
$loop = new WP_Query(array(
'post_type' => 'movies',
'actors' => 'A', 'B',
'posts_per_page' =>-1,
));
print_r($loop);
while ( $loop->have_posts() ) : $loop->the_post();
?>
<h2 class="entry-title"><?php the_title(); ?></h2>
<?php
the_content();
endwhile;
}
The problem with this code is that Wordpress by default is searching for Actor A or B and displaying every movie they've been featured in and not just the movie(s) they've both been featured in.
Thanks,
Marten
EDIT:
I think im almost there, im stuck in a SQL-query, it works perfect if i just search for one of the actors, the problem accors when i search for both, which results in an empty array.
When i do the manual search in the SQL query i see duplicate content with different term.slugs, is there any workaround for this?
global $wpdb;
$querystr = "
SELECT *
FROM $wpdb->posts
LEFT JOIN $wpdb->term_relationships ON($wpdb->posts.ID = $wpdb->term_relationships.object_id)
LEFT JOIN $wpdb->term_taxonomy ON($wpdb->term_relationships.term_taxonomy_id = $wpdb->term_taxonomy.term_taxonomy_id)
LEFT JOIN $wpdb->terms ON($wpdb->term_taxonomy.term_id = $wpdb->terms.term_id)
WHERE $wpdb->posts.post_type = 'movies'
AND $wpdb->posts.post_status = 'publish'
AND $wpdb->term_taxonomy.taxonomy = 'actors'
AND $wpdb->terms.slug = 'A'
AND $wpdb->terms.slug = 'B'
ORDER BY $wpdb->posts.post_date DESC";
$pageposts = $wpdb->get_results($querystr, OBJECT);
print_r($pageposts);
All the best,
Marten

try this:
'actors' => array('A', 'B')

After doing some digging and experimenting, found a way to do it right. It involves posts_join and posts_where filters:
$actor1 = 'A';
$actor2 = 'B';
function join_it($join) {
$join = " INNER JOIN $wpdb->term_relationships tr1 ON($wpdb->posts.ID = tr1.object_id)
INNER JOIN $wpdb->term_taxonomy tt1 ON(tr1.term_taxonomy_id = tt1.term_taxonomy_id)
INNER JOIN $wpdb->terms t1 ON(tt1.term_id = t1.term_id)
INNER JOIN $wpdb->term_relationships tr2 ON($wpdb->posts.ID = tr2.object_id)
INNER JOIN $wpdb->term_taxonomy tt2 ON(tr2.term_taxonomy_id = tt2.term_taxonomy_id)
INNER JOIN $wpdb->terms t2 ON(tt2.term_id = t2.term_id)";
return $join;
}
function where_it($where) {
global $actor1;
global $actor2;
$where = " WHERE $wpdb->posts.post_type = 'movies'
AND tt1.taxonomy = 'actors'
AND tt2.taxonomy = 'actors'
AND t1.slug = {$actor1}
AND t2.slug = {$actor2}";
}
function getmoviefromactor(){
global $wp_query;
global $wpdb;
global $post;
add_filter('posts_join','join_it',10);
add_filter('posts_where','where_it',10);
$loop = new WP_Query();
remove_filter('posts_join','join_it',10);
remove_filter('posts_where','where_it',10);
print_r($loop);
while ( $loop->have_posts() ) : $loop->the_post();
?>
<h2 class="entry-title"><?php the_title(); ?></h2>
<?php
the_content();
endwhile;
}
The posts_where and posts_join filters adds where and join clauses respectively to the loop. I tested a similar code with my own site but if it doesn't work, please let me know.

Related

How can I combine two SQL queries using INNER JOIN?

I have 2 SQL queries as below:
$results2 = $wpdb->get_results("SELECT * FROM `wp_posts` WHERE id IN (".implode(",", $product_ids).")");
$getprice = $wpdb->get_results("SELECT * FROM `wp_postmeta` WHERE meta_key='_regular_price' AND post_id IN (".implode(",", $product_ids).")");
I have combined them as below query:
$yeni = $wpdb->get_results("SELECT wp_posts.id, wp_posts.post_title, wp_postmeta.meta_value
FROM wp_posts
INNER JOIN wp_postmeta ON wp_posts.id = wp_postmeta.post_id
WHERE meta_key = '_regular_price'
WHERE wp_posts.id IN (".implode(",", $product_ids).")");
After combining the queries, it's giving an empty result. I can't find the issue with the updated query.
Try this sql.
I have replace this WHERE meta_key = '_regular_price'
$yeni = $wpdb->get_results("SELECT wp_posts.id, wp_posts.post_title, wp_postmeta.meta_value FROM wp_posts INNER JOIN wp_postmeta ON wp_posts.id = wp_postmeta.post_id WHERE wp_posts.id IN (".implode(",", $product_ids).") and wp_postmeta.meta_key = '_regular_price'");
I don't understand why you don't use the native Wordpress features via OOP procedure.
Your simple query, it can be translated like this:
<?php
$loop = new WP_Query(
array(
'post_type' => 'page', // post type: page, post, attachment etc..
'post__in' => $productIds, // array[]
)
);
// after that, you can use a standard wordpress loop
if($loop->have_posts()) {
while($loop->have_posts()) {
: $loop->the_post();
the_title();
print "<br>";
// access to meta key _regular_price
print get_post_meta(get_the_ID(), '_regular_price', true);
}
}
This is the right way.
Cheers
Class reference here: https://developer.wordpress.org/reference/classes/wp_query/

Total count for each order item in a loop on Woocommerce

Hi I'm learning PHP and Woocommerce at the same time!
I'm trying to get all the orders which have status processing and the total count for each and display this on a page.
Thus far I can loop through all the orders and get the name and quantity of each.
but as I don't know which product is going to be in the list, I'm not sure how I would compare the name and then add the quantity.
My current output is like this:
prod1 - v1 x 1
Prod2 - v3 x 1
prod2 - v3 x 1
prod3 - v2 x 11
prod3 - v2 x 1
What I would like is:
prod1 - v1 x 1
Prod2 - v3 x 2
prod3 - v2 x 12
The code currently is:
<?php
/*
Template Name: Print Supplier Order
*/
if (!is_user_logged_in() || !current_user_can('manage_options')) wp_die('This page is private.');
?>
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title><?php _e('Processing Orders'); ?></title>
<style>
body { background:white; color:black; width: 95%; margin: 0 auto; }
</style>
</head>
<body>
<header>
<?php if (have_posts()) : while (have_posts()) : the_post(); ?>
<h1 class="title"><?php the_title(); ?></h1>
<?php the_content(); ?>
<?php endwhile; endif; ?>
</header>
<section>
<?php
global $woocommerce;
$args = array( 'post_type' => 'shop_order', 'post_status' => 'wc-processing', 'posts_per_page' => -1 );
$loop = new WP_Query( $args );
while ( $loop->have_posts() ) : $loop->the_post();
$order_id = $loop->post->ID;
$order = new WC_Order($order_id);
$product_details = array();
$order_items = $order->get_items();
foreach( $order_items as $product ) {
echo $product['name']." x ".$product['qty'];
echo '<br>';
}
?>
<?php endwhile; ?>
</section>
</body>
</html>
Updated (Now the SQL query gives also the product name, make it even lighter)
Instead of using a WP_Query and some heavy code to get your calculations, you should better use this much more lighter and effective version code, using WPDB Class (a SQL query):
global $wpdb;
$results = $wpdb->get_results( "
SELECT DISTINCT woim2.meta_value as id, SUM(woim.meta_value) as count, woi.order_item_name as name
FROM {$wpdb->prefix}woocommerce_order_itemmeta as woim
INNER JOIN {$wpdb->prefix}woocommerce_order_items as woi ON woi.order_item_id = woim.order_item_id
INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta as woim2 ON woi.order_item_id = woim2.order_item_id
INNER JOIN {$wpdb->prefix}posts as p ON p.ID = woi.order_id
WHERE p.post_status IN ('wc-processing','wc-on-hold')
AND woim.meta_key LIKE '_qty'
AND woim2.meta_key LIKE '_product_id'
GROUP BY woim2.meta_value
" );
foreach( $results as $result ){
echo $result->name . ' (' . $result->id . ') x ' . $result->count . '<br>';
}
The product count will be based only on the processing oder status and not on product total sales.
Tested and work.
Get the product variations instead (as asked in your comment)
As asked in your comment to get the product variations that are in the order, you will replace the line:
AND woim2.meta_key LIKE '_product_id'
by the following line:
AND woim2.meta_key LIKE '_variation_id'
Get all products and variations (Excluding Variable Products)
To get all products including Product Variations but excluding Variable Products use:
global $wpdb;
$results = $wpdb->get_results( "
SELECT DISTINCT woim2.meta_value as id, SUM(woim.meta_value) as count, woi.order_item_name as name
FROM {$wpdb->prefix}woocommerce_order_itemmeta as woim
INNER JOIN {$wpdb->prefix}woocommerce_order_items as woi ON woi.order_item_id = woim.order_item_id
INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta as woim2 ON woi.order_item_id = woim2.order_item_id
INNER JOIN {$wpdb->prefix}posts as p ON p.ID = woi.order_id
WHERE p.post_status IN ('wc-processing','wc-on-hold')
AND woim.meta_key LIKE '_qty'
AND ((woim2.meta_key LIKE '_variation_id' AND woim2.meta_value > 0)
OR (woim2.meta_key LIKE '_product_id'
AND woim2.meta_value NOT IN (SELECT DISTINCT post_parent FROM {$wpdb->prefix}posts WHERE post_type LIKE 'product_variation')))
GROUP BY woim2.meta_value
" );
foreach( $results as $result ){
echo $result->name . ' (' . $result->id . ') x ' . $result->count . '<br>';
}
Tested and work.
To get them all (even product variations and variable products):
global $wpdb;
$results = $wpdb->get_results( "
SELECT DISTINCT woim2.meta_value as id, SUM(woim.meta_value) as count, woi.order_item_name as name
FROM {$wpdb->prefix}woocommerce_order_itemmeta as woim
INNER JOIN {$wpdb->prefix}woocommerce_order_items as woi ON woi.order_item_id = woim.order_item_id
INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta as woim2 ON woi.order_item_id = woim2.order_item_id
INNER JOIN {$wpdb->prefix}posts as p ON p.ID = woi.order_id
WHERE p.post_status IN ('wc-processing','wc-on-hold')
AND woim.meta_key LIKE '_qty'
AND ((woim2.meta_key LIKE '_variation_id' AND woim2.meta_value > 0)
OR woim2.meta_key LIKE '_product_id' )
GROUP BY woim2.meta_value
" );
foreach( $results as $result ){
echo $result->name . ' (' . $result->id . ') x ' . $result->count . '<br>';
}
Tested and work.
Method 1
There are a few different ways you could tackle this, but if we want to adjust your code only slightly, then we can achieve this by adding your products in the product loop to a custom array, using the product name as a key, and then working through that array.
Here's an example:
global $woocommerce;
//custom array to list products:
$products_array = array();
//start wp query
$args = array(
'post_type' => 'shop_order',
'post_status' => 'wc-processing',
'posts_per_page'=> -1
);
$loop = new WP_Query( $args );
//loop through orders
while ( $loop->have_posts() ) {
$loop->the_post();
//vars
$order_id = $loop->post->ID;
$order = new WC_Order($order_id);
$product_details = array();
$order_items = $order->get_items();
//loop through products
foreach( $order_items as $product ) {
//if product has been captured to custom array before, increment value, else add it.
if (array_key_exists($product['name'], $products_array)) {
$products_array[$product['name']] += $product['qty'];
} else {
$products_array[$product['name']] = $product['qty'];
}
}
}
//end wp_query
wp_reset_postdata();
//display list:
foreach ($products_array as $title => $quantity) {
echo $title.' x '.$quantity;
}
Method 2
Loop through all products, instead of all your orders (after a while, assuming the store is successful, there will probably be a lot more orders than products, so this method might be faster in the long run). Each product stores it's total sales as a meta value, so accessing it is quite simple:
//WP Query args
$args = array(
'post_type' => 'product',
'posts_per_page'=> -1,
'meta_key' => 'total_sales',
'orderby' => 'title',
'order' => 'ASC',
'meta_query' => array( //make sure we only grab items that have actually sold
array(
'key' => 'total_sales',
'value' => 0,
'compare' => '>'
)
)
);
$query = new WP_Query($args);
//loop through products
if($query->have_posts()) {
while($query->have_posts()) {
$query->the_post();
//use built in Wordpress functions for get_the_title() and get_post_meta()
echo get_the_title().' x '.get_post_meta(get_the_ID(), 'total_sales', true);
}
}
//end wp query
wp_reset_postdata();

List number of posts in particular category filtered by author (WP)

EDIT: I elaborated more on this topic in another thread: List categories by author ~ WITH COUNTER ~ (Wordpress) ... it might be more suitable for you.
When I'm in my Category page I want the readers to see who's posting there, and filter by Author. (Ex. Activism has 127 post, 37 by Adrian, 12 by James, etc…) I've tried a million things out there, but nothing serves my simple task! Anyone with similar experience out there?
This is what I've got:
<?php
$curauth = $wp_query->get_queried_object();
$auth_id = 1;
$cat_id = 2411;
$sql = "SELECT COUNT(*)
FROM $wpdb->posts p
JOIN $wpdb->term_relationships tr ON (p.ID = tr.object_id)
WHERE post_author = $auth_id
AND post_type = 'post'
AND post_status = 'publish'
AND post_date <= NOW()
AND tr.term_taxonomy_id = $cat_id
";
$post_count = $wpdb->get_var($sql);
echo "This Author has published $post_count posts in Activism<br />";
?>
EDITED:
OK, this code works!!! ... I'm happy, but still a bit puzzled. Why does this work, and the pne above not?
<?php
$auth_id = 1;
$cat_id = 2411;
$counter = "SELECT COUNT(*)
FROM $wpdb->posts
LEFT JOIN $wpdb->term_relationships ON($wpdb->posts.ID = $wpdb->term_relationships.object_id)
LEFT JOIN $wpdb->term_taxonomy ON($wpdb->term_relationships.term_taxonomy_id = $wpdb->term_taxonomy.term_taxonomy_id)
WHERE $wpdb->term_taxonomy.term_id = $cat_id
AND $wpdb->term_taxonomy.taxonomy = 'category'
AND $wpdb->posts.post_status = 'publish'
AND post_author = '$auth_id'
";
$user_count = $wpdb->get_var($counter);
echo "Author... has $user_count posts in Activism<br />";
?>

Wordpress - Order by author if different than "admin"

In "category.php" file I need to order my posts in this strange way:
First all the posts where author is different than "admin" (in alphabetical order by title)
Than all the post by "admin" (in the same alphabetical order)
Here is the standard code I use to do my query:
<?php global
$wp_query;
query_posts(
array_merge(
array('orderby' => 'title', 'order' => 'ASC'),
$wp_query->query
)
);
?>
Any idea about how to accomplish it without nesting two queries?
Thanks in advance!
EDIT: Following is some code that was tried, as suggested by Sepster in a previous version of his answer. But at the moment this code starts showing all the posts from 'admin' (instead of the others) until the posts with author different than 'admin' come. At that point it breaks the results and jump to the next page of results.
<?php
global $wp_query;
query_posts(
array_merge(
array('orderby' => 'title', 'order' => 'ASC'),
$wp_query->query
)
);
$adminPosts = false;
for ($i=1; $i<=2; $i++) {
while ( $wp_query->have_posts() ) {
$wp_query->the_post();
$author = get_the_author();
if ($author == 'admin' && $adminPosts == false) break;
if ($author != 'admin' && $adminPosts == true) break;
// ALL MY STUFF
} // end while
rewind_posts();
$adminPosts=true;
} // end FOR
?>
Update:
I've finally come up with a somewhat workable solution for this, but it's fair to say this really is an excercise in academics; Yes, it's do-able without executing a second loop, but really, it's pretty convoluted.
The solution is, in summary
Develop a custom SQL statement that will return the rows in the order required.
This is because in SQL, the only way to get the results in the order you need is by doing a UNION of your two subsets. To my knowledge there's no way of doing that using the "normal" WP query operations.
Execute this query, and loop over its results rather than a standard "the loop".
This is because we're getting back a recordset, rather than a WP_Query object.
Set the "Blog pages show at most X posts" setting to 1.
(on /wp-admin/options-reading.php)
A common complaint is pagination breaking when using custom queries (incidentally, the query_posts() method you're using is susceptible to this issue).
There are numerous turorials on how to do this properly, eg:
https://codex.wordpress.org/Making_Custom_Queries_using_Offset_and_Pagination
https://codex.wordpress.org/Pagination#Troubleshooting_Broken_Pagination
The first of those recommends the implementation of "Offset and Manual Pagination".
The closest I've found to an implementation of this in conjunction with a custom SQL statement is this https://wordpress.stackexchange.com/a/28717. I've borrowed heavily from this answer (and so I recommend you go over and give it an up-vote!).
However, this technique (among other more "standard" custom query approaches) suffers from a known behaviour where WP produces a 404 on the final page (if I understand correctly, because WP is still using its own query and associated max-page=posts-per-page/posts calculations to map between the page number in the URL and the delivered content).
Refer http://wordpress.org/support/topic/explanation-and-workaround-for-error-404-on-category-pagination?replies=14 for details about this issue, and a proposed solution (which unfortunately won't work for our custom SQL approach).
A known "work-around" for this issue is to reduce the number of posts-per-page to 1, as per eg http://wordpress.org/support/topic/custom-post-type-pagination-404-on-last-page
So, assuming you're happy with a global setting of 1 posts-per-page (remember you'd need to override this manually in your custom queries), here's the code:
functions.php:
...
function get_users_posts_last($userDisplayName = 'Admin', $categoryName = '') {
global $wpdb, $paged, $max_num_pages;
$paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
$post_per_page = 5;
$offset = ($paged - 1)*$post_per_page;
$sql = "
SELECT SQL_CALC_FOUND_ROWS q.* FROM
(
(
SELECT
p.*
FROM
{$wpdb->posts} p
INNER JOIN {$wpdb->users} u ON p.post_author = u.ID
LEFT JOIN {$wpdb->term_relationships} tr ON p.ID = tr.object_id
LEFT JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
LEFT JOIN {$wpdb->terms} t ON tt.term_id = t.term_id
WHERE
tt.taxonomy = 'category'
AND p.post_status = 'publish'
AND p.post_type = 'post'
AND u.display_name != '{$userDisplayName}'
" . ( $categoryName != '' ? "AND t.name = '{$categoryName}'" : "" ) . "
ORDER BY
p.post_title ASC
)
UNION
(
SELECT
p.*
FROM
{$wpdb->posts} p
INNER JOIN {$wpdb->users} u ON p.post_author = u.ID
LEFT JOIN {$wpdb->term_relationships} tr ON p.ID = tr.object_id
LEFT JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
LEFT JOIN {$wpdb->terms} t ON tt.term_id = t.term_id
WHERE
tt.taxonomy = 'category'
AND p.post_status = 'publish'
AND p.post_type = 'post'
AND u.display_name = '{$userDisplayName}'
" . ( $categoryName != '' ? "AND t.name = '{$categoryName}'" : "" ) . "
ORDER BY
p.post_title ASC
)
) q
LIMIT {$offset}, {$post_per_page};
";
$sql_result = $wpdb->get_results( $sql, OBJECT);
$sql_posts_total = $wpdb->get_var( "SELECT FOUND_ROWS();" );
$max_num_pages = ceil($sql_posts_total / $post_per_page);
return $sql_result;
}
...
category.php:
...
$postList = get_users_posts_last('admin'); // Note you can also pass a category name if necessary
if($postList) {
global $post;
foreach( $postList as $key=>$post ) {
setup_postdata($post);
// Render the post here
?>
<header class='entry-header'><h1 class='entry-title'><?php the_title(); ?></h1></header>
<div class='entry-content'><?php the_content(); ?></div>
<?php
}
// Render pagination here
?>
<div class="navigation">
<div class="previous panel"><?php previous_posts_link('« Previous page',$max_num_pages) ?></div>
<div class="next panel"><?php next_posts_link('Next page »',$max_num_pages) ?></div>
</div>
<?php
}
...
Or, just set up two separate queries ;-)

Custom Query with custom post types and category id

I am trying to grab a list of posts by a specific post type and the category id that it is assigned too.
So far from searching online i have managed to come up with
function fs_tpl_get_results($post_type, $mycat) {
global $wpdb;
$args = wp_parse_args($args,array(
'post_type' => '$post_type',
'term_id' => 22,
));
extract($args);
$sql = <<<SQL
SELECT DISTINCT
{$wpdb->terms}.*,
COUNT(*) AS post_count
FROM
{$wpdb->terms}
INNER JOIN {$wpdb->term_taxonomy} ON {$wpdb->terms}.term_id={$wpdb->term_taxonomy}.term_id
INNER JOIN {$wpdb->term_relationships} ON {$wpdb->term_taxonomy}.term_taxonomy_id={$wpdb->term_relationships}.term_taxonomy_id
INNER JOIN {$wpdb->posts} ON {$wpdb->term_relationships}.object_id={$wpdb->posts}.ID
INNER JOIN {$wpdb->term_relationships} related_relationship ON {$wpdb->posts}.ID=related_relationship.object_id
INNER JOIN {$wpdb->term_taxonomy} related_term_taxonomy ON related_relationship.term_taxonomy_id=related_term_taxonomy.term_taxonomy_id
INNER JOIN {$wpdb->terms} related_terms ON related_term_taxonomy.term_id=related_terms.term_id
WHERE 1=1
AND (related_term_taxonomy.taxonomy<>{$wpdb->term_taxonomy}.taxonomy OR related_terms.term_id<>{$wpdb->terms}.term_id)
AND {$wpdb->posts}.post_type='%s'
AND related_terms.term_id=%d
GROUP BY
{$wpdb->terms}.term_id
SQL;
$sql = $wpdb->prepare($sql,$post_type,$term_id);
$results=$wpdb->get_results($sql);
return $results;
}
But it returns an empty result, does anyone have any ideas to fix this please?
There are built in wordpress functions that can do this:
<?php
$args = array(
'category_name' => 'your_csategoty_name',
'post_type' => 'your_custom_posttype'
);
// The Query
$the_query = new WP_Query( $args );
// The Loop
while ( $the_query->have_posts() ) : $the_query->the_post();
//inside the loop
the_title();
the_content();
endwhile;
// Reset Post Data
wp_reset_postdata();
?>
http://codex.wordpress.org/Class_Reference/WP_Query#Post_.26_Page_Parameters
Here is a code:
$args = array('post_type' => 'post','meta_query' => array(array('key' => 'cat','value' => 'yes','compare' => '%')));
$cat = new WP_Query($args);
// The Loop
while ( $cat->have_posts() ) : $cat->the_post();
$P_ID = get_the_ID();
endwhile;
// Reset Post Data
wp_reset_postdata();
$post = wp_get_single_post($P_ID);
If it is not working change the page id

Categories