$wpdb query not working in plugin - wordpress

I'm trying to do a reviews plugin in Wordpress, and one of the thing that my client asked me to do was that a user should be able to rate only one product every 24 horas, so I have to establish a limit between rates.
And I made this function to check if the limit is reached:
function isAllowedToRate(){
global $wpdb;
$currentDate = date('Y-m-d');
$userId = get_current_user_id();
$table_name = $wpdb->prefix .'esvn_reviews';
$isAllowed = $wpdb->get_results(
"
SELECT user_id, fecha_calificacion
FROM {$table_name}
WHERE user_id = {$userId}
AND review_date = {$currentDate}
"
);
if( count( $isAllowed > 0 ) ){
return false;
}else{
return true;
}
}
But every time I try to run it, it returns false, the error that I'm getting from Wordpress is this:
<div id='error'>
<p class='wpdberror'><strong>WordPress database error:</strong> []<br />
<code>
SELECT user_id, fecha_calificacion
FROM wp_esvn_reviews
WHERE user_id = 6
AND fecha_calificacion = 2015-10-30
</code></p>
</div>
If I take the same SQL and run it directly into the database, it works like a charm, but I keep getting this error from Wordpress and the function always returns false.

You need to wrap your date in single quotes.
$isAllowed = $wpdb->get_results(
"
SELECT user_id, fecha_calificacion
FROM {$table_name}
WHERE user_id = {$userId}
AND review_date = '{$currentDate}'
"
);
Alternatively you could use $wpdb->prepare(), which I would consider better form.
$sql = <<<SQL
SELECT user_id, fecha_calificacion
FROM {$table_name}
WHERE
user_id = %d -- this is formatted as a (d)igit
AND review_date = %s -- this is formatted as a (s)tring
SQL;
$isAllowed = $wpdb->get_results($wpdb->prepare($sql, $userId, $currentDate));

I found the error (in case someone needs the information in the future), I was treating $isAllowed as an array, and it was an object, I had to add ARRAY_A to the get_results:
$isAllowed = $wpdb->get_results(
"
SELECT user_id, fecha_calificacion
FROM {$table_name}
WHERE user_id = {$userId}
AND review_date = {$currentDate}
",ARRAY_A /* output as an associative array */
);

Related

Get meta_value from GDRating

I am trying to get meta value from GD Rating table for each post ID with following sql:
$querystr = "SELECT meta_value FROM $wpdb->gdrts_itemmeta WHERE meta_key LIKE 'stars-rating_rating'";
$ratings = $wpdb->get_results($querystr);
foreach ($ratings as $rating)
{
$rating->meta_value;
}
$ratingku = get_post_meta($post->ID, $rating, true);
But it failed. It return a word: "ARRAY".
How to get meta_value from another table (created by a plugin) for each post by using SQL or Query?
To get the rating value for the post use below query:
global $wpdb;
$querystr = "SELECT meta_value AS rating FROM ".$wpdb->prefix."gdrts_itemmeta INNER JOIN ".$wpdb->prefix."gdrts_items ON ".$wpdb->prefix."gdrts_items.item_id = ".$wpdb->prefix."gdrts_itemmeta.item_id AND ".$wpdb->prefix."gdrts_itemmeta.meta_key = 'stars-rating_rating' AND ".$wpdb->prefix."gdrts_items.id = ".$post->ID;
$result = $wpdb->get_row($querystr);
echo $result->rating;

Query with join not work

i am doing a query in my controller like this:
$aviso = $em->getRepository("FabricacionBundle:Aviso")->findBy(array("fecha" => $fecha));
$dql = "SELECT a FROM PedidosBundle:Articulo a WHERE a.aviso = :aviso";
if(isset($_GET['filterField']) && isset($_GET['filterValue'])){
$dql = "SELECT a FROM PedidosBundle:Articulo a JOIN ProductosBundle:Producto p WHERE a.aviso = :aviso";
$dql .= " AND " . $_GET['filterField'] . " LIKE '%" . $_GET['filterValue'] . "%'";
}
$query = $em->createQuery($dql)
->setParameter("aviso", $aviso[0]->getId());
//dump($query);die();
$paginator = $this->get('knp_paginator');
$articulos = $paginator->paginate(
$query,
$request->query->get('page', 1),
25
);
When i dont use the filter, this work, but when i use the filter i get the next error:
Cannot count query which selects two FROM components, cannot make distinction
Where is the problem? Thanks!
What SQL engine are you running ?
Note, that it's usually a very bad practice to insert anything directly from your $_GET variable into your SQL query, as this can lead to SQL injection.
Imagine, that someone sends \'; DROP TABLE something; -- in your $_GET['filterField'] - everything is gone.
From the top of my head, there is a lacking JOINing condition.

How to prevent `out of stock` items to show as a filter option in the sidebar (layered nav widget ) in WooCommerce?

I have 'hide out of stock' checked in the settings, however when using the layered navigation widget to filter results by shoe size, it is returning products which have that size listed as an attribute, but the size is out of stock. Is there a fix to this?
WordPress version 3.9.1, WooCommerce version 2.1.7
http://www.foten.se
He means not showing the products in the filter results. If a user filters by size M, he wants to see available size M products in the results, not All products that used to have a size M but dont anymore...
This is something woocommerce has not solved in years! and something that anyone opening a woocommerce store should know. I am sure most of them wont use woocommerce because of this issue alone.
I solve it by running the following script every night
$dir = dirname(__FILE__);
include_once($dir . '/../../../../wp-load.php');
$currentCount = 0;
$pageSize = 10;
global $wpdb;
$run = true;
while ($run) {
// select all variation of products with stock == 0
$sql = "SELECT * FROM `wpmf_postmeta` as pm
LEFT JOIN wpmf_posts as p on p.ID = pm.post_id
WHERE
meta_key = '_stock' and meta_value = 0
and p.post_parent <> 0 LIMIT $currentCount," . ($currentCount + $pageSize);
// var_dump($sql);die;
$res = $wpdb->get_results($sql, ARRAY_A);
if (!$res) { //|| $currentCount > 20
$run = false;
break;
}
foreach ($res as $r) {
$post_parent = $r['post_parent'];
$post_excerpt = $r['post_excerpt'];
$size = preg_replace('/[^0-9.]/', '', $post_excerpt);
// echo($post_parent . ", $size" . '<br>');
if ($size && $size != '') {
// find the term ID and delete it from parent product
$res_term = $wpdb->get_results("SELECT * FROM `wpmf_term_relationships` as tr
LEFT JOIN wpmf_terms as t on t.term_id = tr.term_taxonomy_id
where `object_id` = $post_parent and name = $size", ARRAY_A);
// var_dump($terms_rel);
if ($res_term) {
$query = "
DELETE FROM wpmf_term_relationships
WHERE term_taxonomy_id = " . $res_term[0]['term_id'] . "
AND object_id = " . $post_parent . "
";
$res_query = $wpdb->query(
$query);
echo($post_parent . ", $size, $res_query" . '<br>');
}
}
$currentCount++;
}
}
wp_cache_flush();
echo 'done! ' . $currentCount;
The problem this script fixes is: the sidebar filters the product by attribute but the stock is manage by product variation (child post).
The DB doesn't have a way to link the 2 fields, therefore it is not possible to create a query that filters attributes that has a matching variation with stock == 0.
Therefore this script solve the problem by deleting products attributes that have stock == 0
here is a opposite script to set an attribute for a product with stock > 0 and a missing attr:
$dir = dirname(__FILE__);
include_once($dir . '/../../../../wp-load.php');
$currentCount = 0;
$pageSize = 10;
global $wpdb;
$run = true;
while ($run) {
// select all varaition of a prod with stock > 0
$sql = "SELECT * FROM `wpmf_postmeta` as pm
LEFT JOIN wpmf_posts as p on p.ID = pm.post_id
WHERE
meta_key = '_stock' and meta_value <> 0
and p.post_parent <> 0 LIMIT $currentCount," . ($currentCount + $pageSize);
// var_dump($sql);die;
$res = $wpdb->get_results($sql, ARRAY_A);
if (!$res) { //|| $currentCount > 20
$run = false;
break;
}
foreach ($res as $r) {
$post_parent = $r['post_parent'];
$post_excerpt = $r['post_excerpt'];
$size = preg_replace('/[^0-9.]/', '', $post_excerpt);
if ($size && $size != '') {
// find the relevant term
$res_parent = $wpdb->get_results("SELECT * FROM `wpmf_term_relationships` as tr
LEFT JOIN wpmf_terms as t on t.term_id = tr.term_taxonomy_id
where `object_id` = $post_parent and name = $size", ARRAY_A);
// var_dump($terms_rel);
// if term is missing, create it
if (!$res_parent) {
wp_set_object_terms($post_parent, $size, 'pa_size', true);
echo($post_parent . ", $size" . '<br>');
}
$currentCount++;
}
}
}
wp_cache_flush();
echo 'done! ' . $currentCount;
NOTE:
this is not a proper solution to the problem - a solution should be at the design level, i.e. finding a way to link the 2 fields with an SQL query, this solution is a temp by-pass until a real solution is available
I didn't fully tested this code, I will update the answer if necessary in the future
I'm not fully familiar with the DB structure of wordpress or woocommerce, and the result of this code might change depending on different plugins, using this code is at your own risk
In WooCommerce, the default option is that, it shows all products that are in stock and out of stock on search results and the product category pages. This is a WooCommerce default functionality
Link regarding this:
https://github.com/woothemes/woocommerce/issues/5840
Also, not showing these products on the contrary is a bad idea, as it is not as per the best SEO practices.

Delete posts with custom field date older than current date via cronjob

I want to make a cron job witch deletes all posts older than the date in a custom field of the post. I got following function within my functions.php My custom field name is bewerbungs_frist.
function foobar_truncate_posts(){
global $wpdb;
$currenttime = new DateTime();
$currenttime_string = $currenttime->format('Ymd');
# Set your threshold of max posts and post_type name
$post_type = 'job';
# Query post type
$query = "
SELECT ID FROM $wpdb->posts
WHERE post_type = '$post_type'
AND post_status = 'publish'
ORDER BY post_modified DESC
";
$results = $wpdb->get_results($query);
# Check if there are any results
if(count($results)){
foreach($results as $post){
$customfield = get_field('bewerbungs_frist', $post->ID);
$customfield_object = new DateTime($customfield);
$customfield_string = $customfield_object->format('Ymd');
if ( $customfield_string < $currenttime_string ) {
echo "The value of the custom date field is in the past";
echo $customfield_string;
$purge = wp_delete_post($post->ID);
}
}
}
}
foobar_truncate_posts();
I use a plugin to handle my cronjobs. The Hock name is: foobar_truncate_posts and Arguments is []
The cronjob works but it does not delete those post with the date of the customfield older than todays date. The two variables are the same.
$currenttime_string 20130820
$customfield_string 20130820
there's a typo in your code, you're missing an 's' at the end of $result.
This:
foreach($result as $post){
Should be this:
foreach($results as $post){
I just tried it out. Once you make that fix the wp_delete_post() works great.
EDIT
I'm really unclear on what you're trying to do. You want to check if the custom field is set to some time in the past? What purpose does the continue serve? Also, I'm guessing you're using Advanced Custom Fields (ACF). Using the jquery Date Picker, you can format your date to Ymd by default so you don't have to convert it to a DateTime object.
At any rate, this function should explain how to properly set and compare time values, you should be able to take it from there:
function foobar_truncate_posts(){
global $wpdb;
$currenttime = new DateTime();
$currenttime_string = $currenttime->format('Ymd');
# Set your threshold of max posts and post_type name
$post_type = 'post_type_job';
# Query post type
$query = "
SELECT ID FROM $wpdb->posts
WHERE post_type = '$post_type'
AND post_status = 'publish'
ORDER BY post_modified DESC
";
$results = $wpdb->get_results($query);
# Check if there are any results
if(count($results)){
foreach($results as $post){
$customfield = get_field('bewerbungs_frist', $post->ID);
$customfield_object = new DateTime($customfield);
$customfield_string = $customfield_object->format('Ymd');
if ( $customfield_string < $currenttime_string ) {
echo "The value of the custom date field is in the past";
$purge = wp_delete_post($post->ID);
}
}
}
}
foobar_truncate_posts();

Symfony2: Doctrine subquery in the WHERE clause including a LIMIT

I'm trying to convert this SQL into either DQL or whatever the query builder variant would look like.
select *
from project_release r
where (select s.title as status_name
from release_status_log l
left join release_status s
on l.release_status_id = s.id
where l.release_id = r.id
order by l.created_at desc
limit 1
) not in ('Complete', 'Closed')
;
From inside the repository class for the Release entity, I've tried this
return $this->getEntityManager()->createQuery("
select r.*
from MyBundle:Release r
where (select s.title
from MyBundle:ReleaseStatusLog l
join l.status s
where l.release = r
order by l.createdAt desc
limit 1
) IN ('Complete','Closed')
order by r.release_date ASC
limit 10
")->getArrayResult();
Which gives the error
[Syntax Error] line 0, col 265: Error: Expected
Doctrine\ORM\Query\Lexer::T_CLOSE_PARENTHESIS, got 'limit'
Which is referring to the limit 1 in the subquery.
So then I tried this
return $this
->createQueryBuilder('r')
->select('r.*')
->where("(select s.title
from MyBundle:ReleaseStatusLog l
join l.status s
where l.release = r
order by l.created_at desc
limit 1
) $inClause ('Complete', 'Closed')
")
->setMaxResults( $limit )
->orderBy('release_date', 'ASC')
->getQuery()
->getArrayResult()
;
Which gives the same error. How can I execute a subquery limited to 1 row per row in the parent query?
Symfony 2.0.15
Doctrine 2.1.7
PHP 5.3.3
MySQL 5.1.52
I have a solution for this now. I ended up falling back on the native query system with the result set mapping from the entity in questions.
It's not a great solution, but it works and until I see another solution, it's the only option with this type of WHERE clause.
Here's what my finder method looks like now
/**
* Finds Releases by their current status
*
* #param array $statuses White-list of status names
* #param boolean $blackList Treat $statuses as a black-list
* #param integer $limit Limit the number of results returned
* #param string $order Sort order, ASC or DESC
*
* #throws \InvalidArgumentException
*
* #return array <Release>
*/
public function findByCurrentStatus( array $statuses, $blackList=false, $limit=null, $order='ASC' )
{
if ( empty( $statuses ) )
{
throw new \InvalidArgumentException( "Must provide at least one status" );
}
$inClause = $blackList ? 'not in' : 'in';
$rsm = new ResultSetMappingBuilder($this->getEntityManager());
$rsm->addRootEntityFromClassMetadata('MyBundle:Release', 'r');
$SQL = "
select *
from project_release r
where (select s.title as status_name
from release_status_log l
left join release_status s
on l.release_status_id = s.id
where l.release_id = r.id
order by l.created_at desc
limit 1
) $inClause ('" . implode( "','", $statuses ) . "')
order by r.release_date $order
";
if ( $limit )
{
$SQL .= " limit $limit";
}
return $this
->getEntityManager()
->createNativeQuery( $SQL, $rsm )
->getResult()
;
}
I kind of loathed going back to building a query as a string, but oh well. Oh, and for you eagle-eyes, $statuses does not come from user data, so no SQL injection vulnerabilities here ;)
In addition to your Native SQL solution, you can create two queries using DQL within a single repository method.
Some adjustment may be required but you could try this:
public function findCompletedReleases()
{
$em = $this->getEntityManager();
$dqlSubQuery = <<<SQL
SELECT
s.title status_name
FROM
Acme\MyBundle\Entity\ReleaseStatus s,
Acme\MyBundle\Entity\ReleaseStatusLog l,
Acme\MyBundle\Entity\Release r
WHERE
l.release = r.id AND
l.status = s.id
ORDER BY l.createdAt DESC
SQL;
$statusName = $em->createQuery($dqlSubQuery)
->setMaxResults(1)
->getSingleScalarResult();
$dql = <<<SQL
SELECT
r
FROM
Acme\MyBundle\Entity\Release r
WHERE
:status_name IN ('Complete','Closed')
ORDER BY r.release_date ASC
SQL;
$q = $em->createQuery($dql)
->setParameters(array('status_name' => $statusName))
->setMaxResults(10);
return $q->getArrayResult();
}

Resources