I am trying to extract data from a Wordpress database so I can get it into a new database, but having a few issues figuring things out. This is just a small sample of how some of the Wordpress tables are like.
wp_posts
id | post_type | post_status |
----------------------------------
1 | portfolio | published | ... other data
wp_postmeta
id | post_id | meta_key | meta_value |
-----------------------------------------------------------------------
1 | 1 | item_submission_title | Some title | ... other data
2 | 1 | item_technology | a:1:{i:0;s:4:"2372";} |
3 | 1 | item_description | Some description |
wp_terms
id | term_id | name |
--------------------------------
1 | 2372 | Some name | ... other data
So essentially, I have to get all id's from wp_posts where post_type is portfolio and post_status is publish.
Using these id's I then need to get the meta_value for the 3 meta_key's shown in my wp_postmeta table.
The item_technology meta_value is serialized, so I need to then unserialize it. I then need to get its id (2372) and use this to obtain further data from the wp_terms table.
There is a lot more I need to do, but achieving the above will give me the understanding of how best to do this. I have a feeling I can somehow use joins here, but not sure.
At the moment, my attempt is very ineffecient and incomplete. This is what I currently have.
$conn = Database::getInstance();
$ids = getIDs($conn);
$dataArray = array();
foreach ($ids as $row) {
$data = getData($conn, $row['id']);
$dataArray[] = $data;
}
function getIDs($conn) {
$query = "SELECT `id` FROM `wp_posts` WHERE `post_type` = \"portfolio\" and `post_status` = \"publish\"";
$sql = $conn->dbc->prepare($query);
$sql->execute();
$row = $sql->fetchAll();
return $row;
}
function getData($conn, $id) {
$query = "SELECT `meta_value` FROM `wp_postmeta` WHERE `post_id` = $id AND `meta_key` = \"item_submission_title\"";
$sql = $conn->dbc->prepare($query);
$sql->execute();
$row = $sql->fetchAll();
return $row;
}
What would be the best way to achieve what I am after?
Thanks
Considering that you have a WordPress Database, I'll assume you also have a WordPress install attached to it. If not, you should, in order to make sure you get the data you want in the manner you want without having to recreate code that deals with the multitude of WP specific "idiosyncrasies".
So the first thing you'll do is "bootstrap" WordPress so you can obtain the DB connection and all of the WordPress functionality in one east step. So let's say you create a file in the WordPress root directory...
<?php
// This includes gives us all the WordPress functionality
require_once( dirname(__FILE__) . '/wp-load.php' );
// Set parameters to gather posts
$args = array(
'posts_per_page' => -1,
'offset' => 0,
'orderby' => 'date',
'order' => 'DESC',
'post_type' => 'portfolio',
'post_status' => 'publish',
'suppress_filters' => true
);
// Retrieve matching posts
$posts_array = get_posts( $args );
// Loop through posts to get Meta values
foreach ( $posts_array as $post )
{
$item_submission_title = get_post_meta( $post->ID, 'item_submission_title', true );
$item_technology = maybe_unserialize( get_post_meta( $post->ID, 'item_technology', true ) );
$item_description = get_post_meta( $post->ID, 'item_description', true );
// Do something with this information
...
}
Of course, you might now need to get it into the other DB. WordPress can also accommodate that with a simple and powerful interface...
$otherDB = new wpdb( 'username', 'password', 'database', 'localhost' );
$table = 'other_table';
$data = array(
'item_submission_title' => $item_submission_title,
'item_technology' => $item_technology,
'item_description' => $item_description,
);
$otherDB->insert( $table, $data );
More functions of the WPDB class can be found here: https://codex.wordpress.org/Class_Reference/wpdb
Related
in my wordpress blog i have a table consist of users and their products seperated by ";" for example :
mytable
----------------------
userid | products
----------------------
1 | camera
2 | books;keyboard;computer
3 | mouse;printer
4 | scanner
now in my script the user will add a new product so i need to check if that user has more than 3 products because in this case i will do nothing and will not add this product for him but if he has less than 3 products i need to add the new product to his products seperated by ";" something like :
$userid = 3;
$newproduct = "ball"
if (pruduct field for that user has more than 2 ";" ) {
do nothing
}else{
$wpdb->update(
'mytable',
array( 'product' => concat(product, ";", $newproduct)),
array( 'userid ' => $userid ),
array( '%s' ),
array( '%d' )
);
so the result in this example would be :
mytable
----------------------
userid | products
----------------------
1 | camera
2 | books;keyboard;computer
3 | mouse;printer;ball
4 | scanner
Despite the data normalization arguments, which are incredibly valid, if for some reason you cannot correct that and must work with what exists.
Select the products column as a string and use the PHP explode() and implode() functions. You'd end up with something like this:
$current_products = explode(';', $user['products']);
// $current_products is now an array of the user's products.
if !(count($current_products) >= 3) {
$current_products[] = $new_product;
}
$user_products = implode(';', $current_products);
// Insert $user_products into your table via UPDATE
The implode and explode functions convert strings to and from arrays based on given delimiters.
In WordPress, the right way to do this is with the user_meta table. With respect, your proposed way is not a good way.
You can set these values into the user_meta table with code like this.
$meta_key = '_my_products'; /* be sure to choose a unique key prefix */
add_user_meta( $userid, $meta_key, 'football', false );
add_user_meta( $userid, $meta_key, 'basketball', false );
You can get them back with code like this
$vals = get_user_meta( $userid, $meta_key, false );
foreach ($vals as $value) {
echo $i;
}
There are other functions for updating and deleting user_meta items. They work well. Read this. https://codex.wordpress.org/Function_Reference/get_user_meta
This code is supposed to check to see if a user is logged in when posting an input, if so, it then checks to see if the ItemID and User ID already have an entry in the table, and if so, it updates that entry rather than creating a duplicate.
This is the output I need from it:
+--------+-------+----------+
| ItemID | Price | user |
+--------+-------+----------+
| 1 | 20 | 27 |
+--------+-------+----------+
This is what I'm getting:
+--------+-------+----------+
| ItemID | Price | user |
+--------+-------+----------+
| 0 | 0 | 27 |
+--------+-------+----------+
Here is the full function if needed : http://pastebin.com/W0UM68UT
if ( is_user_logged_in() ) {
$hf_user = get_current_user_id();
$hf_username = $hf_user->user_login;
global $quanid;
$inputValue = isset($_POST[$quanid]);
global $newdb;
$newdb = new wpdb( 'user', 'pass', 'db', 'localhost' );
$retval = $newdb->get_results("SELECT * FROM $table WHERE ItemID = '$quanid' AND user = '$hf_username' ");
if($retval > 0)
{
//If user exists, update
$newdb->replace($table,array(
'ItemID' => '$quanid',
'Price' => $inputValue,
'user' => $hf_username
)
);
}
else
{
global $table;
global $newdb;
$newdb->insert(
$table,
array(
'ItemID' => $quanid,
'Price' => $inputValue,
'user' => $hf_username
)
);
}
} else {
global $error;
$error = "Error: You must be logged in to submit prices";
return;
}
}
Please don't use globals...
How to avoid using PHP global objects?
Change your SELECT statement to a count for better performance:
SELECT count( 1 ) FROM $table WHERE ItemID = '$quanid' AND user = '$hf_username'
On to your question:
It appears your global $quanid; and isset($_POST[$quanid]); return unexpected values so you should see where they're set. Try using:
var_dump right below these two lines:
global $quanid;
$inputValue = isset($_POST[$quanid]);
var_dump( $quanid );
var_dump( $inputValue );
var_dump( $_POST);
die;
Is there any way to see if a post exists by a meta value?
For instance, lets say I want to see if another post has a unique meta value of "pictureID", and if so do something else.
Is there a way I could write that clause in php?
Thank you
if you dont know the post id then
you can use custom wordpress query to check post meta according to key like
global $wpdb;
$wpdb->get_results( "select * from $wpdb->postmeta where meta_key = 'pictureID' " );
And then you can get all results with post id and then get that post data.
Hope this helps ;)
You can use a standard WP_Query to return posts by meta_key using the meta_query argument and EXISTS compare type.
// query for all posts with the pictureID meta key set
$args = array(
'post_type' => 'post', // or your_custom_post_type
'meta_query' => array(
array(
'key' => 'pictureID',
'compare' => 'EXISTS',
),
),
}
// create a custom query
$my_query = new WP_Query( $args );
// loop over your query, creating a custom The Loop
if ( $my_query->have_posts() ): while ( $my_query->have_posts() ): $my_query->the_post();
// $post is now posts that have a pictureId meta value
endwhile; endif;
// reset $post
wp_reset_postdata();
If you want to quickly grab a random post_id that has this meta_key set you can go to the database directly (bypassing caching, etc).
global $wpdb;
// SQL statement to fetch the post_id using a meta_key and a published post
$sql = <<<SQL
SELECT post_id
FROM {$wpdb->postmeta} pm
JOIN {$wpdb->posts} p
ON p.ID = pm.post_id
AND post_status = 'publish'
AND post_type = 'post'
WHERE meta_key = 'pictureID'
AND meta_value != ''
AND post_id != %d
ORDER BY RAND()
LIMIT 1
SQL;
// exclude the current post by replacing %d with the current ID
$sql = $wpdb->prepare( $sql, $post->ID );
// use get_var() to return the post_id
$post_id = $wpdb->get_var( $sql );
first try to get meta value for post get_post_meta()
$postMetaValue=get_post_meta($postId,"meta_key",true);
if($postMetaValue=='pictureID')
{
//do as you want
}
I wrote this Query:
$album_id = get_the_id();
$photos = $wpdb->get_results(
"select * from wp_postmeta where post_id = '"
. $album_id
. "' AND meta_key = 'gallery_ph' order by meta_id desc"
);
Now, I want to get all thumbnails with title, name and description for this images.
I found in the DB:
meta_id | post_id | meta_key | meta_value
577 | 346 | gallery_ph | http://url to the image
and the thumbnail
meta_id | post_id | meta_key | meta_value
569 | 348 | _wp_attachment_image_alt | Jury Datiop
How are this two datas connected? I know that Jury Datiop has the url http://xxx because I can check in wp-admin. But how to write the select query? How to connect image and get title, name, description?
You don't need to query the database directly.
WordPress has a whole lot of custom functions for you to retrieve the data that you need.
Check the:
WordPress Codex : Function Reference
Specially:
get_posts
get_children
wp_get_attachment_metadata
Translation of your database query to a WordPress function:
code not tested, use the Debug to inspect the results of each operation
$album_id = get_the_id();
$photos = get_children( array(
'post_parent' => $album_id,
'post_type' => 'attachment',
'meta_key' => 'gallery_ph',
'post_mime_type' => 'image',
'order' => 'DESC',
'orderby' => 'meta_id')
);
// DEBUG
// echo '<pre>' . print_r( $meta, true ) . '</pre>';
if( $photos )
{
foreach( $photos as $img )
{
// DEBUG
// echo '<pre>' . print_r( $img, true ) . '</pre>';
$meta = wp_get_attachment_metadata( $img->ID );
// DEBUG
// echo '<pre>' . print_r( $meta, true ) . '</pre>';
}
}
I'm wanting to order Wordpress posts by the most recent comment. To the best of my knowledge this isn't possible using the WP_Query object, and would require a custom $wpdb query, which I can easily write. However, I then don't know how to setup the loop to run off this object.
Can anyone help?
Assign
select wp_posts.*, max(comment_date) as max_comment_date
from $wpdb->posts wp_posts
right join $wpdb->comments
on id = comment_post_id
group by ID
order by max_comment_date desc
limit 10
to some variable $query. You can fiddle around with the 10 or the query itself. (I'm no SQL optimization ninja.) Then your code will look something like
<?php
$results = $wpdb->get_results($query) or die('!');
foreach ($results as $result):
?>
[insert template here]
<?php endforeach ?>
This pattern is covered in more depth by the Codex.
I used a simpler, portion of a native WP in function. hope it helps and some one can continue to develop. Here is a simplified version that shows the title & excerpt of the post along with the comment content & author from the latest commented posts using get_comments.
$args = array(
'status' => 'approve',
'number' => 6,
'order' => 'DESC'
);
$comments = get_comments($args);
foreach($comments as $comment) : $count++;
$post_args = array(
'post_type' => 'post',
'p' => $comment->comment_post_ID,
'posts_per_page' => 1
);
$posts = get_posts($post_args);
foreach($posts as $post) : setup_postdata($post);
the_title();
the_excerpt();
endforeach;
echo $comment->comment_content;
echo $comment->comment_author;
endforeach;
OK guys,
A lot of great answers here, but obviously nobody's taken the time to test them.
Hao Lian gets the credit for the first best original answer, but unfortunately his code doesn't show posts without comments.
Captain Keytar is on the right track, but his code will display every single post and attachment as a separate result.
Here is a modified version of Captain Keytar but it limits the results to the type 'post'.. that has been published (to avoid getting drafts!!)
select wp_posts.*,
coalesce(
(
select max(comment_date)
from $wpdb->comments wpc
where wpc.comment_post_id = wp_posts.id
),
wp_posts.post_date
) as mcomment_date
from $wpdb->posts wp_posts
where post_type = 'post'
and post_status = 'publish'
order by mcomment_date desc
limit 10
This is an old question, but I had the same issue and found a much cleaner way to do this, so I'm posting it here in case it helps anyone.
If you use the posts_clauses filter you can then just modify the main query and still use The Loop and all the regular loop functions.
function intercept_query_clauses( $pieces ) {
global $wpdb;
$pieces['fields'] = "wp_posts.*,
(
select max(comment_date)
from " . $wpdb->comments ." wpc
where wpc.comment_post_id = wp_posts.id AND wpc.comment_approved = 1
) as mcomment_date";
$pieces['orderby'] = "mcomment_date desc";
return $pieces;
}
add_filter( 'posts_clauses', 'intercept_query_clauses', 20, 1 );
Note that I changed the sql slightly for my own purposes, but the general concept is the same.
As an addendum to Hao Lian's answer, if you use the following query:
select wp_posts.*,
coalesce(
(
select max(comment_date)
from $wpdb->comments wpc
where wpc.comment_post_id = wp_posts.id
),
wp_posts.post_date
) as mcomment_date
from $wpdb->posts wp_posts
order by mcomment_date desc
limit 10
This mixes in posts that don't have comments yet, and sorts them by post_date and max(comment_date).
Code suggested by Hao Lian works perfect except for the fact that we should add the following WHERE clause to avoid pulling POST with comment_count = 0, this situation is caused by spam comments.
The WHERE clause to add is as follows:
WHERE comment_approved = '1' AND comment_type = '' AND post_password = ''
Complete code after adding the where clause shoud look like following:
select wp_posts.*, max(comment_date) as comment_date
from wp_posts
right join wp_comments on id = comment_post_id
WHERE comment_approved = '1' AND comment_type = '' AND post_password = ''
group by ID
order by comment_date desc
limit 6
This can be done by combining WP_Comment_Query with WP_Query, like this:
// For performance, limit the number of queried comments,
// but make it be something big enough to account for "duplicate" posts.
$comments_query = new WP_Comment_Query;
$comments = $comments_query->query( array( 'number' => '100' ) );
if ( $comments ) {
foreach ( $comments as $comment ) {
// You'll want to convert the dates from string to integer so you can sort them out later
$comment_utf = strtotime($comment->comment_date);
// Build an array of post IDs with the date of the last published comment
$latest_comments[$comment->comment_post_ID] = $comment_utf;
}}
// Sort the array by date
arsort($latest_comments); foreach ($latest_comments as $key => $value) { $posts_ordered[] = $key; }
// The nice thing is that WP_Query will remove duplicates by default
$args = array ( 'posts_per_page' => '10', 'post__in' => $posts_ordered, 'orderby' => 'post__in');
$query = new WP_Query( $args );
if ( $query->have_posts() ) {
while ( $query->have_posts() ) {
$query->the_post();
// Do your stuff (add the template or whatever)
// If you want to add the comment itself, use this:
$comments = get_comments(array('number' => '1', 'post_id' => $post->ID));
foreach($comments as $comment) :
echo $comment->comment_content;
endforeach;
// That's about it
}}
wp_reset_postdata();
I'm thinking that adding in the max function will screw up your results. MySQL isn't going to pull the max from each one. It's going to pull the max from the full set. This is the query that'll get you your results:
select wp_posts.*, comment_date
from $wpdb->posts wp_posts
right join $wpdb->comments
on id = comment_post_id
group by ID
order by comment_date desc
limit 10
After that, if you want to follow WP convention, use this, and then you can use the functions that most of your templates are using (based on the loop):
$results = $wpdb->get_results($query) or die('!');
foreach ($results as $post):
setup_postdata($post);
Get 3 newest comments for custom post type 'question' regardless of approvement:
global $wpdb;
$results = $wpdb->get_results(
"
SELECT wp_posts.ID, MAX(comment_date) AS max_comment_date
FROM wp_posts
RIGHT JOIN wp_comments
ON id = comment_post_id
WHERE wp_posts.post_type = 'question'
AND wp_posts.post_status = 'publish'
GROUP BY ID
ORDER BY max_comment_date DESC
LIMIT 3
"
);
foreach ($results as $result) {
$posts_arr[] = $result->ID;
}
$args = array(
'post_type' => 'question',
'post__in' => $posts_arr,
'orderby' => 'post__in',
);
$the_query = new WP_Query( $args );
Using Lucian's BEAUTIFUL solution, I needed to alter/filter the existing WP_Query to sort posts by the latest comment. Here's the code, tested & works perfectly:
$comments_query = new WP_Comment_Query;
$comments = $comments_query->query( array( 'number' => '100' ) );
if ( $comments ) {
foreach ( $comments as $comment ) {
$comment_utf = strtotime($comment->comment_date);
$latest_comments[$comment->comment_post_ID] = $comment_utf;
}
// Sort the array by date
arsort( $latest_comments );
foreach( $latest_comments as $key => $value ) {
$posts_ordered[] = $key;
}
$query->set( 'post__in', $posts_ordered );
$query->set( 'orderby', 'post__in' );
}