Subtract a number from a SQL Query in code-igniter framework - php

I am wondering how I can subtract 1 from a field in my database
i tried the following but it just set it to "-1", did not actually subtract it.
// update listing status to sold
this->db->update("listings", array("cases" => "cases" - 1,

Your code inserts -1 in the database because in PHP, "cases" - 1 evaluates to -1.
Try echo ("any_string"-1);. Read String conversion to numbers for details.
In CodeIgniter, you cannot use the the format of the function $this->db->update you have used directly to implement this query since the update function escapes all values passed to it.
However, you can use the following code to implement the same.
//Passing false as third parameter turns off escaping
$this->db->set('cases', 'cases - 1', false);
$this->db->where('id', $id); //Your where condition
$this->db->update('listings');
Alternatively, you can write the query manually and use the query function.
$sql = "UPDATE `listings` SET `cases` = `cases` - 1 WHERE [your where condition]";
$this->db->query($sql);

$this->db->set('cases','`cases`-1',FALSE);
$this->db->where('id', $id);/*whatever condition you want*/
$this->db->update('yourtablename');
Heres a link from codeigniter userguide:
https://codeigniter.com/user_guide/database/query_builder.html

Related

CodeIgniter UPDATE works on first, but not successive AJAX calls

This is the weirdest problem. My update query works consistently if I write it as a query string. Here's my model function:
public function approveListing($params)
{
//This always works.
$sql = "UPDATE `assets` set approved = ".$params['approved']." WHERE as_id = ".$params['as_id']."";
$this->db->query($sql);
// and I use this select query to detect the actual updated value change.
$this->db->select('approved');
$this->db->where('as_id', $params['as_id']);
$query = $this->db->get('assets');
foreach($query->result() as $row)
{
$params['approved'] = $row->approved;
}
return $params;
}
...and the output will look something like this:
as_id = 260
approved = 1 (or 0, if the input param is 0)
But if I use the query builder method, rather than a sql string, it works exactly once:
public function approveListing($params)
{
// This only works on the first ajax call. After that, no update occurs.
$this->db->set('approved', $params['approved']); // this will be a value of 1 or 0
$this->db->where('as_id', $params['as_id']);
$this->db->update('assets');
$params['updated'] = $this->db->affected_rows();
// and I use this select query to detect the actual updated value change.
$this->db->select('approved');
$this->db->where('as_id', $params['as_id']);
$query = $this->db->get('assets');
foreach($query->result() as $row)
{
$params['approved'] = $row->approved;
}
return $params;
}
...and the output will look something like this:
as_id = 260
approved = 1
updated = 0 <!- notice this is the affected_rows() value. :( ->
$params['approved'] is either a 1 or a 0. The field approved in table assets is a BIT(1)
The function is being called from a controller function, which itself is being called from an ajax call, which sends the changes of a set of radio button clicks (either '1' or '0').
In the case of the query builder update, I am also capturing the affected_rows. The first time I do the query, the affected_rows() = 1. Every time thereafter, the affected_rows = 0, and by checking the record in PHPMyAdmin, I can see the value just doesn't want to change.
Well, I really dislike answering my own questions, but since I did find an answer, and since the question (though rare) is not "too local", but is, in fact, something other coders are going to run into if they try to update a MySQL data type BIT (why we don't see a lot of questions about data type BIT is because it's one of the newest MySQL or MariaDB data types), here is what's going on.
CodeIgniter query builder wraps the value with single quotes, like so:
UPDATE `assets` set approved = '1' WHERE as_id = 260
MySQL doesn't like that. You could either just hand write your query, like this:
$sql = "UPDATE `assets` set approved = ".$params['approved']." WHERE as_id = ".$params['as_id']."";
$this->db->query($sql);
...But that's not a good solution, it's a copout. The query builder should work.
What you have to do is to declare the value as an INT, and the way you do it is like this:
$this->db->set('approved', (int) $params['approved']);
$this->db->where('as_id', $params['as_id']);
$this->db->update('assets');

PHP wpdb get_var query returning zero

I have the following code, which is adding some data to a database via server side API. The field order_number should be created via $OrderNumberNext, which is calculated by a count(*)+1 as order_num variable.
However, checking the database after this is done shows that this only ever calculates as 0 (zero). Should I be using a different function call for this?
function addChartToSet($chartId, $setId){
$chartWithId = $this->db->get_var($this->db->prepare("select id from chords where chord_id=%s",$chartId));
$setWithId = $this->db->get_var($this->db->prepare("select id from sets where set_id=%s",$setId));
$orderNumberNext = $this->db->get_var($this->db->prepare("select count(*)+1 as `order_num` from `chords_inside_sets` where `set_id`=%s",$setId));
$this->db->query($this->db->prepare("update sets set `last_updated_time`=NOW() where `set_id`=%s",$setId));
$this->db->query($this->db->prepare("insert into `chords_inside_sets` set `chord_id`=%s , `set_id`=%s, `order_number`= %s",$chartWithId,$setWithId,$orderNumberNext));
return array("last_updated_time"=> $this->getMaxUpdatedTimeForSet($setId), "query"=> $this->db->last_query);
}
You are adding 1 inside the query that fetches the data. It cannot work. Get your count first through the SELECT query, then add one on the next line, like this:
$orderNumberNext = $this->db->get_var($this->db->prepare("select count(*) as `order_num` from `chords_inside_sets` where `set_id`=%s",$setId));
$orderNumberNext++;

SQL query result to string

I'm using SQL in Yii framework.
I need to show the person's latest active week (it's number and date).So I wrote following code:
public function latestWeek()
{
$datalogin=//the login is working fine
$sql ="SELECT w.number,MAX(w.start_date)
FROM tbl_person_week t, tbl_week w
WHERE t.person_id=$this->id AND t.week_id=w.id";
$query = mysqli_query($datalogin, $sql);
return $query;
}
Now , I checked this query on the server and it works fine (almost) but first thing: I need to convert it into string , because yii's CgridView can't read it , and I couldn't find a working solution for this.
Second: on the server , it gave me the max date indeed , but not it's correct number , but the first number available. How can I fix this as well?
Queries like that should never be used in objective framework. If yu want to execute your own query, you should do it this way:
$sql = "your sql code";
$array = Yii::app()->db->createCommand($sql)->queryAll();
As result you will get multidimensional array with selected columns and rows
If you want to use it in grid view, you should do it this way:
$count = Yii::app()->db->createCommand($sql)->queryScalar();
$dataProvider = new CSqlDataProvider($sql, array('totalItemCount'=>$count));
$this->widget('zii.widgets.grid.CGridView', array(
'id'=>'grid-id',
'dataProvider'=> $dataProvider,
));
You can also use connection other than Yii::app()->db. Check CDbConnection class in docs.
edit: if you wanna use queries like mysql_fetch_assoc, check out also queryRow() method instead of queryAll()
Use Mysql_fetch _array
public function latestWeek()
{
$datalogin=//the login is working fine
$sql ="SELECT w.number,MAX(w.start_date)
FROM tbl_person_week t, tbl_week w
WHERE t.person_id=$this->id AND t.week_id=w.id";
$query = mysqli_query($datalogin, $sql);
while($row = mysqli_fetch_array($query)){
echo $row;
}
}
Assuming from your qu. that you want the week number and start date as one string, you have to concatenate the two columns in the sql.
You also need to specify that the week number is from the row with the maximum start date, which isn't as simple as you might first think.
I don't like injecting the person_id straight into SQL, it isn't awful in this case but is a bad habit to get into security-wise. There are binding methods available in the framework and I agree with Arek, that you should lean on the yii framework as much as possible.
To get the scalar string value, if you are insisting on using your own SQL.. I suggest the following:
$sql='
SELECT CONCAT('Week ',tw.number,' starting ',tw.start_date)
FROM tbl_week tw
JOIN (
SELECT MAX(twi.start_date) max_start_date
FROM tbl_week twi
JOIN tbl_person_week tpwi
ON tpwi.week_id = twi.id
AND tpwi.person_id = :person_id
) i
ON tw.start_date = i.max_start_date;
';
$command=Yii::app()->db->createCommand($sql);
$command->bindParam(":person_id", $this->id);
return $command->queryScalar();

PHP - PDO not taking imploded array as question mark parameter for IN clause in SELECT query

I have a problem with a question mark parameter in a prepared statement using PDO. My Query class looks like this (for now, I'm still adding functionality like data limits, custom parameters filtering and automatic detection of supported statements for the driver being used):
// SQL query
class Query {
public $attributes;
// constructor for this object
public function __construct() {
if ($arguments = func_get_args()) {
$tmp = explode(" ", current($arguments));
if (in_array(mb_strtoupper(current($tmp)), ["ALTER", "DELETE", "DROP", "INSERT", "SELECT", "TRUNCATE", "UPDATE"], true)) {
// classify the query type
$this->attributes["type"] = mb_strtoupper(current($tmp));
// get the query string
$this->attributes["query"] = current($arguments);
// get the query parameters
if (sizeof($arguments) > 1) {
$this->attributes["parameters"] = array_map(function ($input) { return (is_array($input) ? implode(",", $input) : $input); }, array_slice($arguments, 1, sizeof($arguments)));
}
return $this;
}
}
}
}
This is the code fragment which executes the query:
$parameters = (!empty($this->attributes["queries"][$query]->attributes["parameters"]) ? $this->attributes["queries"][$query]->attributes["parameters"] : null);
if ($query = $this->attributes["link"]->prepare($this->attributes["queries"][$query]->attributes["query"], [\PDO::ATTR_CURSOR => \PDO::CURSOR_FWDONLY])) {
if ($query->execute((!empty($parameters) ? $parameters : null))) {
return $query->fetchAll(\PDO::FETCH_ASSOC);
}
}
And this is how I call it in my test code:
$c1->addQuery("lists/product-range", "SELECT * FROM `oc_product` WHERE `product_id` IN (?);", [28, 29, 30, 46, 47]);
if ($products = $c1->execute("test2")) {
foreach ($products as $product) {
print_r($product);
}
}
The problem I have is I just see the first product (it's a test against a vanilla OpenCart installation) with id 28. As you can see in my code, if the passed parameter is an array, it gets automatically detected by the lambda I have in place in the Query class constructor, so it gets rendered as a string like 28,29,30,46,47.
Is there a missing parameter in PDO setup I'm missing? Or maybe there's some bug or platform limit in what I'm doing? I know there's some limitations on what PDO can do in regards to arrays, and that's why I pre-implode all arrays for them to be passed like a simple string.
There's some procedures I've seen here in SO which, basically, composes the query string like WHERE product_id IN ({$marks}), where $marks is being dynamically generated using a procedure like str_repeat("?", sizeof($parameters)) but that's not what I'm looking for (I could resort to that in case there's no known alternative, but it doesn't look like a very elegant solution).
My development environment is composed of: Windows 7 x64, PHP 5.4.13 (x86, thread-safe), Apache 2.4.4 (x86) and MySQL 5.6.10 x64.
Any hint would be greatly appreciated :)
A ? placeholder can only substitute for a single literal. If you want an IN clause to accept an arbitrary number of values, you must prepare a new query for each possible length of your array.
E.g., if you want to select ids in array [1, 2], you need a query that looks like SELECT * FROM tbl WHERE id IN (?,?). If you then pass in a three-item array, you need to prepare a query like SELECT * FROM tbl WHERE id IN (?,?,?), and so on.
In other words, you cannot know with certainty what query you want to build/create until the moment you have the data you want to bind to the prepared statement.
This is not a PDO limitation, it is fundamental to how prepared queries work in SQL databases. Think about it--what datatype would the ? be in SQL-land if you said IN ? but had ? stand in for something non-scalar?
Some databases have array-types (such as PostgreSQL). Maybe they can interpret IN <array-type> the same way as IN (?,?,...) and this would work. But PDO has no way of sending or receiving array-type data (there is no PDO::PARAM_ARRAY), and since this is an uncommon and esoteric feature it's unlikely PDO ever will.
Note there is an extra layer of brokenness here. A normal database, when faced with the condition int_id = '1,2,3,4' would not match anything since '1,2,3,4' cannot be coerced to an integer. MySQL, however, will convert this to the integer 1! This is why your query:
$pstmt = $db->prepare('SELECT * FROM `oc_product` WHERE `product_id` IN (?)');
$pstmt->execute(array('28,29,30,46,47'));
Will match product_id = 28. Behold the insanity:
mysql> SELECT CAST('28,29,30,46,47' AS SIGNED INTEGER);
+------------------------------------------+
| CAST('28,29,30,46,47' AS SIGNED INTEGER) |
+------------------------------------------+
| 28 |
+------------------------------------------+
1 rows in set (0.02 sec)
Lambda detects an array and creates coma delimited string from it, and passed argument is treated as string, so the query looks like:
SELECT * FROM tbl WHERE id IN('1,2,3,4')
'1,2,3,4' is one string value for SQL.
If you are expecting only numerical values, you can omit adding them as parameters and simply put them in the query:
$a = [28, 29, 30, 46, 47];
$s = "SELECT * FROM tbl WHERE id IN(".implode(',', array_map('intval', $a)).")";
For different data types, you have to add as many parameter placeholders as you need, and bind every parameter separately.

active record in codeigniter automatically adds quotes around where clause values

I've tried reading other posts on stackoverflow and also checked the active record documentation for ci, but i can't seem to find the answer to my question
I have the following logic in my model:
$query = $this->db->get_where('categories', array('parent_id' => $category_id));
the sql this generates as per the last_query() method is:
SELECT * FROM (categories) WHERE parent_id = '8'
I need to remove the quotes around the number 8. How would I do that?
I've tried using the select statement and passing false as the second parm. So for example:
$this->db->select('*', false);
$this->db->from('categories');
$this->db->where('parent_id=',$category_id);
But that didn't really change much. Any suggestions?
Thank you
By default, CodeIgniter tries to predict the data type in your comparison, and use the appropriate SQL syntax accordingly. If your query is using single quotes, it might indicate that $category_id is being treated as a string rather than an integer. What happens if you try:
$this->db->select('*');
$this->db->from('categories');
$this->db->where('parent_id', (int) $category_id);
Alternatively, you can construct your own WHERE statement manually:
$this->db->where('parent_id = ' . (int) $category_id);
For MIN and MAX query I used null and false keyword to remove the quotes.
$this->db->where("$value > min_column",null,false);
$this->db->where("$value < max_column",null,false);
The idea of the methods is to auto escape to protect against SQL injections, if for some reason you don't want to you can send a raw query like this :
$q = "select * from categories where parent_id = $category_id";
$this->db->query($q)->result();
Which i find much easier. However i think you can send an extra false paremeter to disable it, something like :
$query = $this->db->get_where('categories', array('parent_id' => $category_id),false);
FYI, if you want to send raw queries and escape them(for more complex queries) you can use :
$category_id = $this->db->escape($category_id);

Categories