WordPress Posts, order by custom table results - php

I have a blog that I need to re-order based on the number of values in a completely custom table. The reason I am not using meta data is a bit complex, but this is just what I need to do.
I just need to count the number of rows in the table wp_upvotes which have a postID that matches the ID of the WordPress blog post, and order it by most "upvotes" to least. This result should include the WordPress post even if there are no values in the wp_upvotes table.
The query I am trying is this:
$post = $wpdb->get_results("
SELECT wp_posts.*, COUNT(wp_upvotes.id) AS upvotes
FROM wp_posts
LEFT JOIN wp_upvotes
ON wp_posts.ID = wp_upvotes.postID
WHERE wp_posts.post_status = 'publish'
AND wp_posts.post_type = 'post'
ORDER BY upvotes DESC, wp_posts.date DESC
LIMIT $pageNum, $numToLoad
", ARRAY_A);

If you want to ensure that there is a match between each table, you are correct to use a LEFT JOIN - an easy way to think about it is that everything on the "left" (wp_posts) will be included, and things on the "right" (wp_upvotes) will be included if they have a match, otherwise null. Just using JOIN will ensure that rows from both tables will only be shown if there is a match.
My guess is that you need to include a GROUP BY p.ID to have each upvotes value specific to a particular post.
As a note, you also have an error using wp_posts.date instead of wp_posts.post_date;
It's also a good idea to use the $wpdb-posts and $wpdb-prefix properties in case you want to use this somewhere with a database prefix that is not wp_.
If you just want to see the results of the data in wp_posts you can just run a database query with an ORDER BY and return the columns, or if you want to use the WordPress filters (on things like the_title() and the_content() you can pass the post IDs into a WP_Query with the post_id__in and orderby=post__in arguments - you would need to reference back the $upvotes value by ID however.
global $wpdb;
$sql = <<<SQL;
SELECT p.*, COUNT(v.id) as upvotes
FROM {$wpdb->posts} p
JOIN {$wpdb->prefix}upvotes v
ON p.ID = v.postID
WHERE
p.posts_status = 'publish'
AND p.post_type = 'post'
GROUP BY p.ID
ORDER BY upvotes DESC, p.post_date DESC
LIMIT $pageNum, $numToLoad
SQL;
// use ARRAY_A to access as $row['column'] instead of $row->column
$rows = $wpdb->get_results( $sql );
foreach ( $rows as $row ){
$content = $row->post_content;
$upvotes = $row->upvotes;
}

Related

Trying to get a list of unique events from wp_posts with data from wp_postmeta

I've spent about 4 hours trying to figure out why I cannot do a group by of the post_title and left join the date data from the wp_postmeta table
The code without the group by clause, results all my events but I only want to retrieve the first instance of the event, by grouping by the post_title, which should give me the only first date in the string of classes to sign up for.
What am I doing wrong here?
SELECT posts.post_title,meta.date
FROM wp_posts posts
LEFT JOIN (
SELECT meta_value AS date,post_id FROM wp_postmeta
WHERE wp_postmeta.meta_key ='_EventStartDate'
) meta
ON posts.ID = meta.post_ID
WHERE posts.post_type='Tribe_Events' AND posts.post_status = 'publish'
GROUP BY posts.post_title
Currently meta.date is not in a GROUP BY, no math is performed on this field, and it is not part of a DISTINCT list of fields
It sounds like you want the first instance of wp_postmeta.meta_value for each wp_posts.post_title
If so, try this:
Ditch the subquery and resolve your meta.meta_key filter using COALESCE
Use the MIN function on meta.meta_value
If needed, CAST meta.meta_value as a datetime
Something like this maybe...harder to know without seeing the table structure and sample data.
SELECT posts.post_title
,MIN(CAST(meta.meta_value as DATETIME)) event_start_date
FROM wp_posts posts
LEFT JOIN wp_postmeta meta
ON posts.ID = meta.post_ID
WHERE posts.post_type ='Tribe_Events'
AND posts.post_status = 'publish'
AND COALESCE(meta.meta_key, 'UNKNOWN') ='_EventStartDate'
GROUP BY posts.post_title;
This version can cope with multiple instances of '_EventStartDate' per post_title
Not sure if this is needed but it causes no harm. Just think of it as wearing a belt and suspenders.

Wordpress query for posts is pulling up revisions as results

I have the following wordpress query which is displaying the post title multiple times, I have checked and it's getting all the revisions for each post.
Here is the query:
SELECT DISTINCT post_title, ID
FROM wpblog_posts
WHERE post_title LIKE '%Kimberley%'
OR post_title LIKE '%Camping%'
AND wpblog_posts.post_type = 'post'
AND post_status = 'publish'
ORDER BY post_title DESC LIMIT 0, 6;
Anyone know why this might be happening.
Update
Removed from query string as not relevant and will make it
easier to debug.
LEFT JOIN wpblog_term_relationships rel ON rel.object_id = wpblog_posts.ID
LEFT JOIN wpblog_term_taxonomy tax ON tax.term_taxonomy_id = rel.term_taxonomy_id
LEFT JOIN wpblog_terms t ON t.term_id = tax.term_id
Cheers
Check this query if it works for you:
SELECT DISTINCT post_title, ID
FROM wpblog_posts
WHERE (post_title LIKE '%Kimberley%' OR post_title LIKE '%Camping%')
AND wpblog_posts.post_type = 'post'
AND post_status = 'publish'
ORDER BY post_title DESC LIMIT 0, 6;
The query was looking for posts LIKE '%Kimberley%' - any type of post OR LIKE '%Camping%' AND wpblog_posts.post_type = 'post' ....

Correct way to handle changeable queries in PDO?

I am currently building a news website that has features such as posting content, labeling the content into categories and voting.
I have gotten to the stage where I am making a home feed for my users, this will show the newest posts but I want to give my users the ability to sort by categories.
Here is my current query
SELECT p.*,
(SELECT Count(id)
FROM comments AS c
WHERE c.post = p.id) AS commentCount,
v.type AS vote_type,
(SELECT Count(id)
FROM votes AS vo
WHERE vo.post_id = p.id) AS totalVotes
FROM posts AS p
LEFT JOIN tagged AS t
ON p.id = t.post_id
LEFT JOIN votes AS v
ON v.post_id = p.id
WHERE (v.user_id = 1 OR v.user_id IS NULL)
AND t.tag_id = 7
ORDER BY timestamp DESC
LIMIT :limit OFFSET :offset
This query has the following parts of code that make it return posts that are only assigned (tagged) with the category id of 7 ( and without these two parts the query becomes the base query I normally use for newest posts)
LEFT JOIN tagged AS t
ON p.id = t.post_id
AND t.tag_id = 7
I was wondering if there is a way to have the base query without the category code and then if needed input the category code rather than have two querys? Is there a way to make a query 'dynamic'.
I want to implement other sorting features but feel creating a bunch of (slightly) different copies of each other would be inefficient.
Yes, people do this all the time. You can either dynamically set your parameters, such as in the first example below, or you can dynamically build your SQL statement. I would think best practice would be to: 1. Make sure it doesn't get too complicated. At the point that you have a query that is everything to everybody, look at building a query builder or multiple queries. 2. Combine dynamic SQL statements with dynamic parameters.
Prepare your statement:
SELECT p.*,
(SELECT Count(id)
FROM comments AS c
WHERE c.post = p.id) AS commentCount,
v.type AS vote_type,
(SELECT Count(id)
FROM votes AS vo
WHERE vo.post_id = p.id) AS totalVotes
FROM posts AS p
LEFT JOIN tagged AS t
ON p.id = t.post_id
LEFT JOIN votes AS v
ON v.post_id = p.id
WHERE (v.user_id = 1 OR v.user_id IS NULL)
AND t.tag_id = :tagid
ORDER BY timestamp DESC
LIMIT :limit OFFSET :offset
Now bind your element (pseudo code):
if( ! isset( $someValue ) ) $someValue = 7; // I am just giving it a default value for
// an example. You can do whatever.
pdo->bindParam( ':tagid', $someValue, parameterType such as PDO::PARAM_INT);
You can keep building like this, or you can break up your bigger SQL prepared statement. By way of example:
if( $categoryCodeNeeded === true ) // add another and statement to where portion of your sql statement
$select += " AND tag_id = 7 ";
Edit to answer the question in the comment:
The code that you provided: SELECT p.* is either assigned to a variable right now, or it is inside another piece of code that looks like this:
$statement = $dbHandler->prepare('SELECT p.* FROM posts p blah blah blah' );
If it's the second case, then you can change that line of code to read:
$statement = $dbHandler->prepare( $yourOwnSelectVariable );
Then, you can build your variable however you want.
$yourOwnSelectVariable = "SELECT p.* FROM posts p";
if( $categoryCodeNeeded )
$yourOwnSelectVariable += " WHERE tag_id = :tagid ";
$statement = $dbHandler->prepare( $yourOwnSelectVariable );
if( ! isset( $tagId ) ) $tagId = 7;
$statement->bindParam( ':tagid', $tagId, PDO::PARAM_INT);

Pull post from a specific Wordpress category

im looking to pull 3 posts from a specific Wordpress Category. At the minute I can pull 3 latest posts and display them in a flash banner using the code below.
SELECT yah_posts.*, yah_postmeta.*
FROM yah_posts
LEFT JOIN yah_postmeta ON yah_posts.ID = yah_postmeta.post_id
WHERE yah_postmeta.meta_key = 'largeimage' && yah_posts.post_status = 'publish'
ORDER BY post_date DESC LIMIT 3
I want to be able to pull 3 latest posts from a specific category instead of just 3 latest posts from every category.
I have put together this code below, but it doesn't seem to be working :(
$query = "SELECT yah_posts.*, yah_postmeta.*
FROM yah_posts
LEFT JOIN yah_postmeta ON yah_posts.ID = yah_postmeta.post_id
AND LEFT JOIN $yah_term_taxonomy ON($yah_term_relationships.term_taxonomy_id = $yah_term_taxonomy.term_taxonomy_id)
WHERE yah_postmeta.meta_key = 'largeimage' && yah_posts.post_status = 'publish'
AND $yah_term_taxonomy.term_id = '1'
AND $yah_term_taxonomy.taxonomy = 'category'
ORDER BY post_date DESC LIMIT 3";
There is actually a stack exchange site just focused on wordpress questions. Looking there and taking code from this question:
https://wordpress.stackexchange.com/questions/6533/modify-wordpress-sql-query-to-pull-from-within-a-category
You would need to add another JOIN:
LEFT JOIN $wpdb->term_taxonomy ON($wpdb->term_relationships.term_taxonomy_id = $wpdb->term_taxonomy.term_taxonomy_id)
And a couple more statements in your WHERE clause:
AND $wpdb->term_taxonomy.term_id = {term_id of the category you want}
AND $wpdb->term_taxonomy.taxonomy = 'category'
You can use wp_term_relationships in your query, take a look at wp_terms and wp_term_relationships tables in your database.

wordpress query custom fields and category

I have a query that creates a table view and then another that queries the view. The results are extremely slow.
Here is the code:
create or replace view $view_table_name as select * from wp_2_postmeta where post_id IN (
select ID FROM wp_2_posts wposts
LEFT JOIN wp_2_term_relationships ON (wposts.ID = wp_2_term_relationships.object_id)
LEFT JOIN wp_2_term_taxonomy ON (wp_2_term_relationships.term_taxonomy_id = wp_2_term_taxonomy.term_taxonomy_id)
WHERE wp_2_term_taxonomy.taxonomy = 'category'
AND wp_2_term_taxonomy.parent = $cat || wp_2_term_taxonomy.term_id = $cat
AND wposts.post_status = 'publish'
AND wposts.post_type = 'post')
The $values have been put it in for this example that queries the view table for the results.
select distinct(ID)
FROM $view_table_name wposts
LEFT JOIN wp_2_postmeta wpostmeta
ON wposts.ID = wpostmeta.post_id
WHERE post_status = 'publish'
AND ID NOT IN (SELECT post_id
FROM wp_2_postmeta
WHERE meta_key = '$var' && meta_value = '$value1')
AND ID NOT IN (SELECT post_id
FROM wp_2_postmeta
WHERE meta_key = '$var' && meta_value = '$value2')
AND ID NOT IN (SELECT post_id
FROM wp_2_postmeta
WHERE meta_key = '$var' && meta_value = '$value3')
AND postmeta.meta_key = 'pd_form'
ORDER BY CASE wpostmeta.meta_value
WHEN '$value5' THEN 1
WHEN '$value6' THEN 2
WHEN '$value7' THEN 3
WHEN '$value8' THEN 4
WHEN '$value9' THEN 5
WHEN '$value10' THEN 6
WHEN '$value11' THEN 7
WHEN '$value11' THEN 8
END;
The main problem here is a subquery in IN condition. Instead executing the subquery and then checking in the outer table for correspondences, MySQL is known to transform the query into a correlated subquery which is executed for each row in the outer table.
The usual solution is to get rid of the subquery in the IN in favour of a JOIN.
Another problem is that you use OUTER JOIN instead of inner JOIN though you do not actually need it (MySQL is usually smart enough to optimize it when it is trivial, but anyway you should express your intention more clearly).
And one more thing. Both queries seem to be dynamically generated. Beside optimizing the query itself, one should think how not to break the calling code. That may be tricky though.
Optimizing wordpress is always an interesting challenge.

Categories