Run query inside WP hook? - mysql

I want to use the hook from below.
The hook will run when a draft is published
function mepr_clear_cached_ids($post) {
$post_type = get_post_type($post);
if($post_type && $post_type == 'memberpressproduct') {
// Run query here.
}
}
add_action('draft_to_publish', 'mepr_clear_cached_ids');
inside the hook I want to run that query to clear the database cache of the fields on the wp_postmeta table :
DELETE FROM [prefix]_postmeta WHERE
(
meta_key LIKE '_mepr_stripe_product_id_[gateway-id]%' OR
meta_key LIKE '_mepr_stripe_plan_id_[gateway-id]%' OR
meta_key LIKE '_mepr_stripe_tax_id_[gateway-id]%' OR
meta_key LIKE '_mepr_stripe_initial_payment_product_id_[gateway-id]%' OR
meta_key LIKE '_mepr_stripe_onetime_price_id_%'
)
AND post_id IN ([memberships])
[gateway-id] - it will be replaced by the gateway id. it's a letters and numbers id.
How should the correct and best syntaxis of the query from above be added inside the hook?

This is a job for $wpdb->esc_like(), $wpdb->prepare(), and $wpdb->query(), and implode().
I assume your [gateway_id] value is in a variable called $gateway_id. And I assume your [memberships] are in an array of integers called $memberships.
You probably want to do something like this to build up and then run your query.
/* a list of the meta keys */
$metaKeysToDelete = [
"_mepr_stripe_product_id_{$gateway_id}%",
"_mepr_stripe_plan_id_{$gateway_id}%",
"_mepr_stripe_tax_id_{$gateway_id}%".
"_mepr_stripe_initial_payment_product_id_{$gateway_id}%",
"_mepr_stripe_onetime_price_id_%",
];
/* construct the LIKE clauses from the meta keys, using esc_like */
$clauses = [];
foreach ( $metaKeysToDelete as $metaKey ) {
$clauses [] = "meta_key LIKE '" . $wpdb->esc_like( $metaKey ) . '"';
}
/* construct the query */
/* debug! use SELECT * in place of DELETE to make sure you have it right */
$q = "DELETE FROM $wpdb->postmeta WHERE "
. "(" . implode( ' OR ', $clauses ) . ")"
. AND post_id IN (" . implode( ',', $memberships ) . ")";
/* debug! make sure your query is correct. */ print_r( $q );
/* prepare and run the query */
$wpdb->query( $wpdb->prepare( $q ) );

Thanks for your reply
[gateway_id] value is not a variable but gateway id. it's a letters and numbers id- similar to dewhj2_3j3.
[memberships] will be a comma separate list of the memberships ids.
If I run the query directly in the database with modified/correct data for wp prefix, gataway id and memberships ids that do the work for what the query is and it clears the metadata from the wp_postmata table for the metakeys from the query.
I want to automate the process so that when membership is published from the draft to clear the cache and the metadata for the meta keys from the query
Hope all the above makes sense.

Related

SQL Syntax Error after updating to WordPress 4.8.2

I have been using a hook in the Advanced Custom Fields plugin (load_field) which loads objects from a table in my database to an ACF select field. The table ('wp_new_royalsliders') is created by the RoyalSlider image slider plugin so i use the hook to populate a select field with the slider names.
This function has worked fine for a long time but recently stopped working - I think after updating core to 4.8.2:
add_filter('acf/load_field/name=media_gallery_slider', 'my_acf_royalslider_choices');
function my_acf_royalslider_choices($field){
$field['choices'] = array();
global $wpdb;
$query = $wpdb->prepare('SELECT * FROM %1$s ORDER BY ID ASC', 'wp_new_royalsliders');
$results = $wpdb->get_results($query);
if(!empty($results)) :
foreach($results as $result) :
$value = $result->id;
$label = $result->name;
$field['choices'][ $value ] = $label;
endforeach;
endif;
return $field;
}
When I turn debugging on I get an error:
WordPress database error: [You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '%1$s ORDER BY ID ASC' at line 1]
SELECT * FROM %1$s ORDER BY ID ASC
When you pass in a hardcoded string value, you don't need placeholders, you can just use
$query = $wpdb->prepare('SELECT * FROM wp_new_royalsliders ORDER BY ID ASC');
If you want to get fancy and portable, you might opt for
$query = $wpdb->prepare('SELECT * FROM ' . $wpdb->prefix . 'new_royalsliders ORDER BY ID ASC');
So it would work on other prefixes, too, but that might not be necessary if this is a very custom thing that's not going to make it into any other site.
It appears that others have noted the removal of that possibility too, see this request.
Update: as $wpdb->prepare() expects a second parameter (since its job is to escape variable inputs for use in SQL), it might complain. Solution: get rid of it and give your SQL directly to get_results:
$results = $wpdb->get_results('SELECT * FROM ' . $wpdb->prefix . 'new_royalsliders ORDER BY ID ASC');

replace mysql_real_escape_string for security purposes

I was using a WordPress Plugin That broke on update to 4.3. The error was
mysql_real_escape_string(): Access denied for user
I found out that the error was because the mysql_real_escape_string is called from within MySQL and needs to be logged in before execution. Solutions were to include a mysql_connect before the mysql_real_escape_string, which solved the problem. But it seems from many of the comments, the mysql_real_escape_string an this solution should not be used for various security reasons. But I am not sure how to change the code to PDO::quote etc as I am not really sure whats going on. The query I am trying to change is
function custom_permalinks_request($query) {
global $wpdb;
global $_CPRegisteredURL;
// First, search for a matching custom permalink, and if found, generate the corresponding
// original URL
$originalUrl = NULL;
// Get request URI, strip parameters and s
$url = parse_url(get_bloginfo('url'));
$url = isset($url['path']) ? $url['path'] : '';
$request = ltrim(substr($_SERVER['REQUEST_URI'], strlen($url)),'/');
$request = (($pos=strpos($request, '?')) ? substr($request, 0, $pos) : $request);
$request_noslash = preg_replace('#/+#','/', trim($request, '/'));
if ( !$request ) return $query;
$sql = "SELECT $wpdb->posts.ID, $wpdb->postmeta.meta_value, $wpdb->posts.post_type FROM $wpdb->posts ".
"LEFT JOIN $wpdb->postmeta ON ($wpdb->posts.ID = $wpdb->postmeta.post_id) WHERE ".
" meta_key = 'custom_permalink' AND ".
" meta_value != '' AND ".
" ( LOWER(meta_value) = LEFT(LOWER('".mysql_real_escape_string($request_noslash)."'), LENGTH(meta_value)) OR ".
" LOWER(meta_value) = LEFT(LOWER('".mysql_real_escape_string($request_noslash."/")."'), LENGTH(meta_value)) ) ".
"ORDER BY LENGTH(meta_value) DESC LIMIT 1";
$posts = $wpdb->get_results($sql);
return $query;
}
Is there an easy way to replace the mysql_real_escape_string or better way to do this? I am not sure this question is similar, but I dont see how to implement the answers.

Selecting MYSQL rows with same field names and adding a prefix

I'm trying to make a mysql query to select several tables and LEFT join them, however they all have same columns names 'user' etc. I want to rename all the fields in this manner . so I tried the following query
SELECT mod_backup_accounts . * AS account . * , mod_backup_subscriptions . *
FROM `mod_backup_accounts`
LEFT JOIN `mod_backup_subscriptions` ON `mod_backup_accounts`.subscription_id = `mod_backup_subscriptions`.package_id
However the mod_backup_accounts . * AS account . * makes it fail, is there a way to do this? so it would be names as account.
You cannot supply a shorthand to alias columns you must do it explicitly for each column name. In general anyway, it is typically recommended to name all columns explicitly in the SELECT list rather than using SELECT *, since it allows you to deterministically specify the column order, and protects you against accidentally pulling in a large BLOB later on if one ever gets added to the table ( or any other schema changes ).
SELECT
mod_backup_accounts.user AS account_user,
mod_backup_subscriptions.user AS subscription_user,
...
...
FROM
mod_backup_accounts
LEFT JOIN `mod_backup_subscriptions` ON `mod_backup_accounts`.subscription_id = `mod_backup_subscriptions`.package_id
I totally understand your problem about duplicated field names.
I needed that too until I coded my own function to solve it. If you are using PHP you can use it, or code yours in the language you are using for if you have this following facilities.
The trick here is that mysql_field_table() returns the table name and mysql_field_name() the field for each row in the result if it's got with mysql_num_fields() so you can mix them in a new array.
You can also modify the function to only add the "column." prefix when the field name is duplicated.
Regards,
function mysql_rows_with_columns($query) {
$result = mysql_query($query);
if (!$result) return false; // mysql_error() could be used outside
$fields = mysql_num_fields($result);
$rows = array();
while ($row = mysql_fetch_row($result)) {
$newRow = array();
for ($i=0; $i<$fields; $i++) {
$table = mysql_field_table($result, $i);
$name = mysql_field_name($result, $i);
$newRow[$table . "." . $name] = $row[$i];
}
$rows[] = $newRow;
}
mysql_free_result($result);
return $rows;
}

Using mySQL/Wordpress to get data from a metadata column

I am using wordpress metadata to store dynamic data.
I've ran into a wall and need some help,I have once piece of meta data that basically is a multidimensional array.
The issue I have is how the metadata is stored I'm having a hard time retrieving what i need.
At the bottom I have included how wordpress stores the data (it's one column)
This is how the first entry looks like before it's stored
shopper_wp_id:3
shopper_status_time:2011-11-29 17:24:49
shopper_status_comment:COMMENTS
shopper_status:declined
shopper_answers:["TEST1","TEST2","TEST3","TEST4","TEST5","TEST6"]
shopper_preapproval_status:maybe
What i need to be able to do is find all the entries where the shopper_wp_id = 3 and the shopper_status = declined
If those 2 values where next to each other but they are separated by a datetime that is ever changing. Any ideas?
a:4:{i:0;a:6:{s:13:"shopper_wp_id";s:1:"3";s:19:"shopper_status_time";s:19:"2011-11-29 17:24:49";s:22:"shopper_status_comment";s:8:"COMMENTS";s:14:"shopper_status";s:8:"declined";s:15:"shopper_answers";a:6:{i:0;s:5:"TEST1";i:1;s:5:"TEST2";i:2;s:5:"TEST3";i:3;s:5:"TEST4";i:4;s:5:"TEST5";i:5;s:5:"TEST6";}s:26:"shopper_preapproval_status";s:5:"maybe";}i:1;a:7:{s:13:"shopper_wp_id";s:4:"2063";s:19:"shopper_status_time";s:19:"2011-11-30 16:37:52";s:22:"shopper_status_comment";s:17:"sgdfsgfgdfdfgfhsh";s:14:"shopper_status";s:4:"paid";s:15:"shopper_answers";a:6:{i:0;s:10:"sdfsadfdfs";i:1;s:11:"dfgdsfgsdfg";i:2;s:10:"sdfgfdsfdg";i:3;s:9:"dgsdfgdfg";i:4;s:10:"sgdfsgfdgd";i:5;s:10:"sdfgfgfgds";}s:26:"shopper_preapproval_status";s:3:"yes";s:13:"blog_post_url";s:17:"http://google.com";}i:2;a:6:{s:13:"shopper_wp_id";s:4:"2916";s:19:"shopper_status_time";s:19:"2011-11-29 20:13:13";s:22:"shopper_status_comment";s:7:"dfbdfdf";s:14:"shopper_status";s:8:"declined";s:15:"shopper_answers";a:6:{i:0;s:15:"cvczxvzxcvzxcbz";i:1;s:11:"zcvfxzbxbxb";i:2;s:8:"zvzxcbzb";i:3;s:10:"zfdbfdbdfb";i:4;s:11:"zdfbdfbfbdf";i:5;s:6:"bfdfdh";}s:26:"shopper_preapproval_status";s:2:"no";}i:3;a:6:{s:13:"shopper_wp_id";s:4:"1614";s:19:"shopper_status_time";s:19:"2011-11-29 20:16:06";s:22:"shopper_status_comment";s:15:"sfdhfsdhsdfhdsh";s:14:"shopper_status";s:8:"declined";s:15:"shopper_answers";a:6:{i:0;s:8:"sdfsdfsd";i:1;s:15:"zvzfbdfbsdfbdbd";i:2;s:15:"dfgdsfhsfdsfhdf";i:3;s:17:"xfbfghfghnfsgnfgn";i:4;s:17:"dsfgshfdshsfdghsg";i:5;s:12:"sdffsdhsdfhh";}s:26:"shopper_preapproval_status";s:2:"no";}}
The string is serialized, so you need to unserialize it, then you can more easily get the information you need using the resulting structure (in this case, an associative array). For example
$data = 'a:4:{i:0;a:6:{s:13:"shopper_wp_id";s:1:"3";s:19:"shopper_status_time";s:19:"2011-11-29 17:24:49";s:22:"shopper_status_comment";s:8:"COMMENTS";s:14:"shopper_status";s:8:"declined";s:15:"shopper_answers";a:6:{i:0;s:5:"TEST1";i:1;s:5:"TEST2";i:2;s:5:"TEST3";i:3;s:5:"TEST4";i:4;s:5:"TEST5";i:5;s:5:"TEST6";}s:26:"shopper_preapproval_status";s:5:"maybe";}i:1;a:7:{s:13:"shopper_wp_id";s:4:"2063";s:19:"shopper_status_time";s:19:"2011-11-30 16:37:52";s:22:"shopper_status_comment";s:17:"sgdfsgfgdfdfgfhsh";s:14:"shopper_status";s:4:"paid";s:15:"shopper_answers";a:6:{i:0;s:10:"sdfsadfdfs";i:1;s:11:"dfgdsfgsdfg";i:2;s:10:"sdfgfdsfdg";i:3;s:9:"dgsdfgdfg";i:4;s:10:"sgdfsgfdgd";i:5;s:10:"sdfgfgfgds";}s:26:"shopper_preapproval_status";s:3:"yes";s:13:"blog_post_url";s:17:"http://google.com";}i:2;a:6:{s:13:"shopper_wp_id";s:4:"2916";s:19:"shopper_status_time";s:19:"2011-11-29 20:13:13";s:22:"shopper_status_comment";s:7:"dfbdfdf";s:14:"shopper_status";s:8:"declined";s:15:"shopper_answers";a:6:{i:0;s:15:"cvczxvzxcvzxcbz";i:1;s:11:"zcvfxzbxbxb";i:2;s:8:"zvzxcbzb";i:3;s:10:"zfdbfdbdfb";i:4;s:11:"zdfbdfbfbdf";i:5;s:6:"bfdfdh";}s:26:"shopper_preapproval_status";s:2:"no";}i:3;a:6:{s:13:"shopper_wp_id";s:4:"1614";s:19:"shopper_status_time";s:19:"2011-11-29 20:16:06";s:22:"shopper_status_comment";s:15:"sfdhfsdhsdfhdsh";s:14:"shopper_status";s:8:"declined";s:15:"shopper_answers";a:6:{i:0;s:8:"sdfsdfsd";i:1;s:15:"zvzfbdfbsdfbdbd";i:2;s:15:"dfgdsfhsfdsfhdf";i:3;s:17:"xfbfghfghnfsgnfgn";i:4;s:17:"dsfgshfdshsfdghsg";i:5;s:12:"sdffsdhsdfhh";}s:26:"shopper_preapproval_status";s:2:"no";}}';
$data = unserialize($data);
foreach ($data as $shopper){
if(isset($shopper['shopper_wp_id']) && $shopper['shopper_wp_id'] == 3){
//do something
}
}
Thanks everyone but i figured it out, not sure it's the best but it seems to be pretty quick..
I ended up using a combination of mySQL's LOCATE and SUBSTRING with a HAVING kicker.
Below is my function and then below that is the SQL that it creates.
function db_get_users_shops_by_shopper_status($shopper_wp_id, $limit = 0, $shopper_status) {
global $wpdb;
if (!is_array($shopper_status)) {
$shopper_status = array($shopper_status);
}
$sql = "
SELECT wp_posts . *,
SUBSTRING(meta_value,
LOCATE('{s:13:\"shopper_wp_id\";s:" . strlen($shopper_wp_id) . ":\"" . $shopper_wp_id . "\";', meta_value),
LOCATE('s:15:\"shopper_answers\";', meta_value))
AS shopper_entity
FROM wp_posts, wp_postmeta
WHERE post_type='shoppertunity'
AND wp_posts.ID = wp_postmeta.post_id AND meta_key = 'shoppers'
AND meta_value LIKE '%s:13:\"shopper_wp_id\";s:" . strlen($shopper_wp_id) . ":\"" . $shopper_wp_id . "\";%'
HAVING shopper_entity LIKE ";
for ($i = 0; $i < sizeof($shopper_status); ++$i) {
if ($i > 0) {
$sql .= " OR shopper_entity LIKE ";
}
$sql .= " '%s:14:\"shopper_status\";s:" . strlen($shopper_status[$i]) . ":\"" . $shopper_status[$i] . "\";%'";
}
if ($limit != 0) {
$sql .= "LIMIT " . $limit;
}
//echo($sql ."<br/><br/>");
$results = $wpdb -> get_results($sql);
return $results;
}
SQL Results
SELECT
`wp_posts`.*,
SUBSTRING(`meta_value`,
LOCATE('{s:13:"shopper_wp_id";s:4:"2063";',
`meta_value`),
LOCATE('s:15:"shopper_answers";', `meta_value`)) AS `shopper_entity`
FROM
wp_posts,
wp_postmeta
WHERE
post_type = 'shoppertunity' AND wp_posts.ID = wp_postmeta.post_id AND meta_key = 'shoppers' AND meta_value LIKE '%s:13:"shopper_wp_id";s:1:"3";%'
HAVING shopper_entity LIKE '%s:14:"shopper_status";s:9:"completed";%' OR shopper_entity LIKE '%s:14:"shopper_status";s:4:"paid";%'

Wordpress Shortcode that query some value from MySql

I've a MySql table where I put some value: id, name of opportunity, category of opportunity, commission etc etc. Now I need to create (automatically) a shortcode that call these value win an array, so for example if i write [opportunity id="1"] wordpress display banner of the opportunity in the database that have id=1.
This is my code
function opportunity_banner_shortcode($atts) {
extract(shortcode_atts(array("id" => ''), $atts));
global $table_prefix, $wpdb, $user_level;
$table_name = $table_prefix . "opportunities";
$finds = $wpdb->get_results("SELECT * FROM {$table_name}", ARRAY_A);
if(sizeof($finds)){
foreach($finds as $find)
return "<a href='" . $find["opp_link"].
"'><img src='" . $find["opp_banner_preview"]."'></a> ";
}
}
add_shortcode('opportunity', 'opportunity_banner_shortcode');
Thanks to all
Maybe the query should be
$finds = $wpdb->get_results("SELECT * FROM {$table_name} WHERE id={$id}",
ARRAY_A);