I am completely stumped. Here is my php (CodeIgniter) code:
function mod()
{
$uid = $this->session->userdata('uid');
$pid = $this->input->post('pid');
if ($this->_verify($uid,$pid))
{
$name = $this->input->post('name');
$price = $this->input->post('price');
$curr = $this->input->post('curr');
$url = $this->input->post('url');
$query = $this->db->query("UPDATE items SET
name=".$this->db->escape($name).",
price=".$this->db->escape($price).",
currency=".$this->db->escape($curr),",
url=".$this->db->escape($url)."
WHERE pid=".$this->db->escape($pid)." LIMIT 1");
}
header('location: '.$this->session->userdata('current'));
}
The purpose of this code is to modify the properties (name, price, currency, url) of a row in the 'items' table (priary key is pid). However, for some reason, allowing this function to run once modifies the name, price, currency and url of ALL entries in the table, regardless of their pid and of the LIMIT 1 thing I tacked on the end of the query. It's as if the last line of the query is being completely ignored.
As if this wasn't strange enough, I replaced "$query = $this->db->query(" with an "echo" to see the SQL query being run, and it outputs a query much like I would expect:
UPDATE items
SET name = 'newname',
price = 'newprice',
currency = 'newcurrency',
url = 'newurl'
WHERE pid = '10'
LIMIT 1
Copy-pasting this into a MySQL window acts exactly as I want: it modifies the row with the selected pid.
What is going on here???
Now I feel stupid: all it took was seeing my code in a different font. My code has
currency=".$this->db->escape($curr),",
instead of
currency=".$this->db->escape($curr).",
The echoing made it work just fine because apparently you can give echo more than one string, comma separated, and it concatenates them
cries I spent hours on this
I know you answered your own question, but let me just add this to the pile: You're not leveraging CodeIgniter AT ALL in this sort of query - which if you used CI as it's intended, you wouldn't have had that typo. Your query should look like this (among other things):
$query = $this->db->update('items',
array('name' => $this->input->post('name'),
'price' => $this->input->post('price'),
'curr' => $this->input->post('curr')),
array('id' => $this->input->post('id')),
1);
By assembling the query string by hand, you're undoing what CI does for you. Only when you're using some complex JOIN statement should you be writing your own SQL in CI, and even then, you want to use the sprintf PHP function to make sure you're not introducing typos.
Related
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');
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();
My problem seems like a rather small one to me, yet, I cannot figure out a proper solution.
The Setup: I have a table 'city_locations' with the columns 'country', 'city', 'longitude', 'latitude'. The countrys are given by 2-Letter ISO codes. I want them to be full names.
For this, I have imported the table 'countrycodes', containing only the columns 'name' and 'code'.
$namechange = $con->prepare("UPDATE city_locations SET country=? WHERE country=?");
$list = $con->prepare("SELECT name, code FROM countrycodes");
$list->execute();
$list->bind_result($name, $code);
$namechange->bind_param('ss', $name, $code);
while ($list->fetch()){
while ($namechange->execute()) {}
echo "$code is now $name <br>";
}
I succesfully retrieve all pairs in the (outer) while loop.
$namechange->execute(); however doesn't do anything - I tried it with and without the while loop, tried using LIMIT 0, 10000 in the query (though I'm not entirely sure I understand LIMIT right). With and without the while loop, the statement doesn't do anything. With LIMIT 0, 10000 the statement cannot be preapred properly (gives an error).
I also tried to bind the params new in every step of the while loop - didn't seem to do anything either.
When running the same command from my web interface, it works fine. However, in that case, I have to type all 200+ codes manually. Seems like a bit much work.
Thanks a lot for your help,
Kjeld.
EDIT: $con is of type mysqli.
$namechange = $con->prepare("UPDATE city_locations SET country=? WHERE country=?");
$list = $con->prepare("SELECT name, code FROM countrycodes");
$list->execute();
$list->bind_result($name, $code);
while ($list->fetch()){
$namechange->bind_param('ss', $name, $code);
$namechange->execute();
echo "$code is now $name <br>";
}
it will probably solve your problem.
I have been tasked to update the price list on our website. Currently, we have to do this one item at a time inside the Admin Panel.
However, we have over 3000 items, and tiered pricing for each item (up to 5 tiers)
Problem is, in the sell_prices table, the prices are structured like so:
10.50:9.50:8.50;7.50;6.50 in one cell.
I am attempting to write a script that will update each sell_price by 10%
UPDATE inv_items
SET sell_prices = sell_prices * 1.10
WHERE id = xxxxxx
I have also tried:
UPDATE inv_items
SET sell_prices = sell_prices * 1.10; sell_prices = sell_prices * 1.10
WHERE id = xxxxxx
But naturally received an error.
This works great but only updates one record, and leaves the rest blank.
Currently, I am working through PhpMyAdmin but I will write a new Price Increase script once I can figure this out.
I have backed up the database.
If I understand you correctly then you have 5? prices in one field, colon separated?
That is a really bizarre way of doing it. There may be a nifty way of doing it with mySQL parsing, but from PHP you're going to need to pull the values out, explode them into an array, apply the price increase to each element, implode it back with the colons and write it back to the database. It's as clunky as all get-out but faster than doing it by hand. Just.
Going forward if you can you really need to look at refactoring that; that's just going to keep biting you.
You'll need to do something like:
select sell_prices from inv_items
(get the values into php)
(split the values by delimiter ':')
(update each value)
(rebuild the line of values with ':' in between)
update inv_items set sell_prices = (value string you just created)
EDIT with mysqli function as suggested:
$qry = 'SELECT id, sell_prices FROM `tablename`';
if($result = $mysqli->query($qry)) {
while ($row = $result->fetch_assoc()) {
$temp = explode(';', $row['sell_prices']);
foreach ($temp as &$price) {
$price = (float)$price*1.1;
}
$prices[$row['id']] = implode(';', $temp);
}
foreach ($prices as $id => $pricesStr) {
$stmt = $mysqli->prepare("UPDATE `tablename` SET sell_prices = ? WHERE id = ?");
$stmt->bind_param('si', $pricesStr, $id);
$stmt->execute();
$stmt->close();
}
}
Please note that I wrote this on the fly without testing, i may overlooked something :)
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);