Using mySQL/Wordpress to get data from a metadata column - mysql

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";%'

Related

Unique Profile Slug with PHP and PDO

I am using a class to generate a string name profile to slug and next use an SQL command to tell me whats the unique value to use in insert command, the problem is the command isn't working properly, sometimes it is possible to return a value which already exist...
Thats the class I am using to generate the slug: (composer require channaveer/slug)
And this the example code:
use Channaveer\Slug\Slug;
$string = "john doe";
$slug = Slug::create($string);
$profile_count_stmt = $pdo->prepare("
SELECT
COUNT(`id`) slug_count
FROM
`advogados_e_escritorios`
WHERE
`slug_perfil` LIKE :slug
");
$profile_count_stmt->execute([
":slug" => "%".$slug."%"
]);
$profile_count = $profile_count_stmt->fetchObject();
if ($profile_count && $profile_count->slug_count > 0) {
$profile_increment = $profile_count->slug_count + 1;
$slug = $slug . '-' . $profile_increment;
}
echo 'Your unique slug: '. $slug;
// Your unique slug: john-doe-5
This is the content of the table when the script run:
Do you know how can I improve the select command to prevent it to return existing slugs from DB?
Ok finally found a solution... Heres the code for who wants to generate unique profile slugs using PHP - PDO and MySQL
$string = "John Doe";
$string = mb_strtolower(preg_replace('/\s+/', '-', $string));
$slug = iconv('UTF-8', 'ASCII//TRANSLIT', $string);
$pdo = Conectar();
$sql = "
SELECT slug_perfil
FROM advogados_e_escritorios
WHERE slug_perfil
LIKE '$slug%'
";
$statement = $pdo->prepare($sql);
if($statement->execute())
{
$total_row = $statement->rowCount();
if($total_row > 0)
{
$result = $statement->fetchAll();
foreach($result as $row)
{
$data[] = $row['slug_perfil'];
}
if(in_array($slug, $data))
{
$count = 0;
while( in_array( ($slug . '-' . ++$count ), $data) );
$slug = $slug . '-' . $count;
}
}
}
echo $slug;
//john-doe-1
You should check if the slug exists or not from your database. If it already exists then you can append some random string like the following
$slug = Slug::create($string);
$slugExists = "DB query to check if the slug exists in your database then you may return the count of rows";
//If the count of rows is more than 0, then add some random string
if($slugExists) {
/** NOTE: you can use primary key - id to append after the slug, but that has to be done after you create the user record. This will help you to achieve the concurrency problem as #YourCommenSense was stating. */
$slug = $slug.time(); //time() function will return time in number of seconds
}
//DB query to insert into database
I have followed the same for my blog articles (StackCoder) too. Even LinkedIn follows the same fashion.
Following is screenshot from LinkedIn URL

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.

Mysql dynamic sql statement with PDO and parameters

I want to create a dynamic sql statement and run it using PDO. My problem is that i have some parameters and i cannot think of a way to pass the parameters.
Ex :
$query = "Select * from tbl_task where 1=1";
if (!empty($name)) $query .= " AND name = ?";
if (!empty($status)) $query .= " AND status = ?"
$db_stmt = new PDOStatement();
$db_stmt = $this->db->prepare($query);
$db_stmt->bindParam (1,$name);
$db_stmt->bindParam (2,$status);
My parameters does not get binded and i don't know how many parameters i have to bind, unless i write the same if statements but with bindParam instructions.
I tryed with mysql_real_escape_string instead bindParam to PDO but for some reason my parameters are added empty.
Any idea on how can i build a dynamic query and bind parameters to PDO ?
Edit 1 :
$arr = array();
if (!empty($name)){
$query .= " AND `name` like :NAME";
$arr['NAME'] = $name;
}
$db_stmt = new PDOStatement();
$db_stmt = $this->db->prepare($query);
$db_stmt->execute($arr);
How can i write a "like" statement ? I tried
$query .= " AND `name` like :NAME" . "%";
and is not working.
What I usually do is the following:
$query = "Select * from `tbl_task` where 1=1";
$arr = array();
if (!empty($name))
{
$query .= " AND `name` = :NAME";
$arr['NAME'] = $name;
}
if (!empty($status))
{
$query .= " AND `status` = :STATUS";
$arr['STATUS'] = $status;
}
$this->db->beginTransaction();
try
{
$tmp = $this->db->prepare($query);
$tmp->execute($arr);
$this->db->commit();
}
catch(PDOException $ex)
{
$this->db->rollBack();
$this->log->error($ex->getMessage());
}
You can't add SQL code as a parameter; only data will do. You'll have to force these bits into $query. They won't be escaped then so they shouldn't contain user-submitted data.
What I usually do is the following:
$query = "Select * from tbl_task where 1=1";
if (!empty($name)) $query .= $db->parse(" AND name = ?s", $name);
if (!empty($status)) $query .= $db->parse(" AND status = ?s",$status);
$data = $this->db->getAll($query);
the idea is in having a function to parse placeholders in arbitrary query part instead of whole query.
I don't bother with native prepared statements though. They pollute PHP scripts with heaps of useless code with not a single benefit.
To answer updated question
as you've been told, you can't bind arbitrary query part. But a literal only.
So, make your literal looks like foo% and then bind it usual way.

use a single return from a sql query

I'm using PHP to make a very specific sql query. For example sake, I have the user's ID number, but I need their name. So I do a sql query from that table with the ID number in order to return the name.
$result = mysql_query("SELECT name FROM users WHERE userID=$thisuserid",$db);
Now I want to use that. What's the most succinct way to go about making that result into a variable ths I can use?
edit:
I'm hoping that this is not the answer:
$rowCheck = mysql_num_rows($result);
if ($rowCheck > '0') {
while ($row = mysql_fetch_assoc($result)){
foreach ($row as $val){
$username = $val;
}
}
}
I have used something like this to keep it short in the past:
list($name) = mysql_fetch_row(mysql_query("SELECT name FROM users WHERE userID=$thisuserid",$db));
echo $name;
In my opinion, the best way to fetch any SQL result is through mysql_fetch_assoc(). To use it, you would do something like this:
$result = mysql_query("SELECT name FROM users WHERE userID=$thisuserid",$db);
while ($row = mysql_fetch_assoc($result)) {
echo $row['name']; // You get an array with each column returned from your query.
}
Still, MySQL extension has been replaced for MySQLi, which is acknowledged to be faster and more practical. It has both OOP and structural bindings, and takes more into account your server settings.
$result = mysql_query("SELECT name FROM users WHERE userID=$thisuserid",$db);
if (!$result) {
echo 'Could not run query: ' . mysql_error();
exit;
}
$name = mysql_fetch_row($result)[0];
You should use MySQLi as bellow:
$db = new MySQLi($host,$user,$pass,$db);
$query = $db->query('SELECT name FROM users WHERE userID='.$thisuserid);
$result = $query->fetch_object();
echo $result->name;
If you use SELECT * so you also can access via $result->{field_name}

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);