Codeigniter Query Builder having clause with IN statement - php

I am using CodeIgniter query builder. I want to add the having clause in there.
My code looks as follows:
(i omitted the other parts of the query):
$this->db->having($filterarray);
And i build the filterarray beforehand like this:
$filterarray = array();
if (!empty($filters['interests'])) {
$interestids = $this->interests_model->getAllIdsByDescription($filters['interests']);
$filterarray['interests IN'] = $interestids;
}
My function getAllIdsByDescription looks like this:
function getAllIdsByDescription($names){
$res = "(";
$query = $this->db->where_in('description', $names)
->select('interest_id')
->get($this->tableName);
foreach ($query->result() as $interestid) {
$res .= $interestid->interest_id;
$res .= ", ";
}
$res = substr($res, 0, -2);
$res .= ")";
return $res;
}
It translates my query to the following, hence why I have an error:
HAVING interests IN '(7)'
How do i remove the quotes around the (7) ?

You can disable the escaping.
If you are using a database that CodeIgniter escapes queries for, you
can prevent escaping content by passing an optional third argument,
and setting it to FALSE.
$this->db->having('user_id', 45); // Produces: HAVING `user_id` = 45
$this->db->having('user_id', 45, FALSE); // Produces: HAVING user_id = 45
Not sure how to implement this when just passing a single array as one argument, but you need the additional FALSE parameter.
From http://www.codeigniter.com/user_guide/database/query_builder.html

Related

How to execute prepare statement using if statement in PHP PDO?

I am fetching some data from MySQL database using PHP PDO prepared statement. I have to use if statement inside the execution of the prepared statement. See my codes below for better understanding
$query = "SELECT * FROM my_table WHERE 1=1";
if(isset($_GET['one'])){
$query .= " AND one = :one";
}
if(isset($_GET['two'])){
$query .= " AND two = :two";
}
if(isset($_GET['three'])){
$query .= " AND three = :three";
}
$result = $db->prepare($query);
$result->execute([
/* ------------------------------------
How to declare the above parameters here
as it will show error if any of the if statement is not true?
----------------------------------------*/
]);
I want to know how to declare the prepared array parameter using if statement inside the $result->execute(......]) block?
You need to create an empty $params array, and inside each if block you can push the appropriate value to it. For example:
if(isset($_GET['one'])){
$query .= " AND one = :one";
$params[':one'] = $_GET['one'];
}
Then you can simply do
$result->execute($params);
Note that you can based on what you've written, you could simplify your code with an outer foreach on a list of parameter names e.g.
$names= array('one', 'two', 'three');
$params = array();
foreach ($names as $name) {
if (isset($_GET[$name])) {
$query .= " AND $name = :$name";
$params[":$name"] = $_GET[$name];
}
}
$result->execute($params);

How do I re-write this SQL statement to accept the array variable?

I am making an API call to retrieve user_name's from my database.
My first step is to make a call to retrieve all the player_id's. They are stored in $communityPlayersIds and currently output as below:
["2","31","31","32"]
I now want to make a second call to the database to fetch the user_name's of the id's that match.
$communityPlayerNames = array();
$communityPlayerNames = $dao->getPlayerNames($communityPlayersIds);
I have looked into it and seen I should use an IN command something like this:
public function getPlayerNames($player_ids)
{
$returnValue = array();
$sql = "SELECT user_name\n"
. "FROM users\n"
. "WHERE id IN ( '".$player_ids."')";
$result = $this->conn->query($sql);
if($result != null && (mysqli_num_rows($result) >= 1)){
while($row = $result -> fetch_array(MYSQLI_ASSOC)){
if(!empty($row)){
$returnValue[] = $row;
}
}
}
return $returnValue;
}
}
However this isn't working and returns this:
[{"user_name":null}]
If I echo ($sql) I get this:
SELECT user_name FROM users WHERE id IN ( '2,31,31,32')
Which looks correct does it not?
Where am I going wrong?
You essentially want your final query to look like this
Select user_name from users where id in (2,31,31,32) -- dupe 31 does no harm
Make sure your series of IDs are inside parentheses IN ($player_ids) and not just a single quote IN '$player_ids'
You can try building the IN-part of the SQL like this:
$returnValue = array();
$in_statement = "(";
foreach($player_ids as $player_id)
{
$in_statement = $in_statement.$player_id->player_id.",";
}
rtrim($in_statement, ",");
$in_statement = $in_statement.")";
$sql = "SELECT community_players.player_id\n"
. "from community_players\n"
. "where community_players.community_id in ".$in_statement."";
So just the middle-part is new, where the $in_statement gets build. And in your SQL you have to change the "=" to "IN". You can generate the IN-part shorter, but this way you can easier see how it's done, so you can fit it to your needs.
in your code you put ' after in which shows syntax error.
You can try
Assuming your
$communityPlayersIds has below value as array:
["2","31","31","32"]
if its not an array then convert it using json_decode()
and then try below query
$sql = "SELECT community_players.player_id\n"
. "from community_players\n"
. "where community_players.community_id IN (".join(",",$communityPlayersIds).")";

creating a flexible update query with Php and pdo - problems with bindparam

I'm updating my mysql functions to use PDO. I've got the hang of most of it but struggling with an update function to update multiple fields in a records.
The function is in a class and I'm trying to keep it flexible to reuse with other tables etc.
Here's my function so far:
public function dbUpdateRecord($table, $values, $where)
{
$this->conn();
$fieldNames = array_keys($values);
var_dump($fieldNames);
$set="";
foreach ($fieldNames as $field) {
$set .= " $field = :$field,";
}
//strip last comma
$set = substr($set, 0, strlen($set) - 1);
$wherefields = array_keys($where);
$whereCondition="";
foreach ($wherefields as $field) {
$whereCondition .= " $field = :$field AND";
}
//strip last AND
$whereCondition = substr($whereCondition, 0, strlen($whereCondition) - 3);
$sql = "UPDATE $table SET $set WHERE $whereCondition";
var_dump($sql);
$stmt = $this->db->prepare($sql);
foreach ($values as $field => $value) {
$stmt->bindParam(':$field', $value);
}
foreach ($where as $field => $value) {
$stmt->bindParam(':$field', $value);
}
return $stmt->execute();
}
The problem is all the fields in the record are being updated by the id of the record which is contained in the $where variable.
$values contains an array of (fieldname=>value).
I think the problem lies around the bindparam and trying to make the fieldnames/placeholders dynamic
I thought I needed to use bindparam as best practice - is this correct or can I just go to execute()?
ANy help appreciated
You are lifting this log from the wrong end.
Your approach is potentially insecure yet inflexible at the same time.
What if you need a JOIN based update? What if you need OR in the WHERE (or IN)?
What you really need is a conventional query where only SET statement values have to be generated.
So, you need a helper function to produce such a statement out of data array, returning both correctly formatted SET statement and array with variables to be bound:
$fields = array("name","email");
$sql = "UPDATE users SET ".pdoSet($fields,$values,$data)." WHERE id = :id"
// now we have $values array to be passed into query
$stmt = $dbh->prepare();
$values["id"] = $_POST['id'];
$stmt->execute($values);
With this code you'll be able to make updates for the arbitrary query. And make it safe.
As a further step you will need to start using type-hinted placeholders, to make whole code like this:
$db->query("UPDATE ?n SET ?u WHERE id IN(?a)",$table,$data,$ids);
Getting back to your problem, ONe is right - you need to use bindValue instead of bindParam (as it mentioned in the tag wiki)
I believe the problem is that you are using a foreach to bind the params to the query. Why is this a problem? Because when you bind a variable, you bind a reference to that variable, so if that variable changes, the value in the query will change too. Since you are using a foreach loop, the value for all the parameters will be the latest value that the variable $value referenced to.
You can read more about this foreach behavior here and here. So basically, you have 2 options:
Use a reference to the actual value, instead of using a reference to $value (which can change its value in the next iteration)
Use an auxiliar variable that references another memory position that won't change during the loop
I came here because I was having the same problems, and YCS's solution was what I needed. For anyone else in this situation, here's the helper function I ended up using:
function commit_table($record_id, $changed_values)
{
$db = open_database();
$query = 'UPDATE table SET ';
$query_arguments = array();
$is_first = TRUE;
foreach(array_keys($changed_values) as $key)
{
if($is_first)
{
$is_first = FALSE;
}
else
{
$query .= ', ';
}
$value_var = ':' . $key;
$query .= $key;
$query .= ' = ';
$query .= $value_var;
$query_arguments[$value_var] = $changed_values[$key];
}
$query .= ' WHERE record_id = :record_id';
$query_arguments[':record_id'] = $record_id;
$stmt = $db->prepare($query);
$stmt->execute($query_arguments);
close_database($db);
}

PHP array_sum function in codeigniter

The following code is a simple select statement that should use the array_sum function to return a value. The result should be getting stored in an array and then getting added up in the SUM function:
$this->db->select('period')
->from('calcdata');
$query = $this->db->get()->result_array();
$query = array_sum($query);
echo "SUM " . $query . "\n" ;
return $query;
the result of this is "SUM 0" but it should be 147 after adding all values up in the period column.
The following code works so i don't understand why the array would be any different from this:
$a = array(2, 4, 6, 8);
echo "sum(a) = " . array_sum($a) . "\n";
I am using codeigniter to create the arrays, does anyone know what is wrong here?
Thanks
Try calling the content of the field instead, not of the whole result array:
$this->db->select('period')->from('calcdata');
$query = $this->db->get();
$period_array = array();
foreach ($query->result_array() as $row)
{
$period_array[] = intval($row['period']); //can it be float also?
}
$total = array_sum($period_array);
UPDATE:
#uzsolt is right, I almost forgot there's a dedicated function in the Active Record class, select_sum(); you might want to try that out also, something like
$this->db->select_sum('period')->get('calcdata');
Quoting from the docs:
$this->db->select_sum();
Writes a "SELECT SUM(field)" portion for your query. As with
select_max(), You can optionally include a second parameter to rename
the resulting field.
$this->db->select_sum('age'); $query = $this->db->get('members');
//> Produces: SELECT SUM(age) as age FROM members

Build SQL Select a better way ? (Oracle)

i have the following code part in one of my classes:
$l = new Location();
$result = $l->getLocIdsbyCity($city); // returns csv
$ids = explode(',', $result);
$where = 'LOC_ID = ' . $ids[0];
unset($ids[0]);
foreach ($ids as $id) {
$where .= ' OR LOC_ID = ' . $id;
}
$select->where($where);
Is there an more "elegant" way to build the select stmt? I need all records with one of the provided ids..
Assuming your csv is injection safe (contains trusted values and no user-provided input):
$l = new Location();
$result = $l->getLocIdsbyCity($city); // returns csv
$where = "LOC_ID IN ($result)";
$select->where($where);
If it's not, you should explode it, mysql_real_escape_string each value and implode back.
You can use the in operator to form a condition like:
where LOC_ID in (1,2,3,4,5)
If you are sure that $result can't contain anything harmful, you should be able to use it directly without having to split it and loop.

Categories