This is a bit confusing for me, so I will try to explain it the best I can.
I am running update but nothing is happens.
This is the query which I get:
"UPDATE users SET name = :name, surname = :surname WHERE name = :name AND surname = :surname"
I start the query like this:
$data = ['name' => 'Sasha', 'surname' => 'M'];
$user = $users->where(['name' => 'TestName', 'surname' => 'TestSurname'])->update($data);
This is the update function:
public function update($data)
{
$fields = explode(',', $this->prepareFields($data));
$values = explode(',', $this->prepareValues($data));
$i = 0;
$count = count($fields);
$query = "UPDATE {$this->_tablename} SET ";
for($i; $i < $count; $i++):
$query .= $fields[$i] . " = " . $values[$i] . ',';
endfor;
$query = rtrim($query, ',');
$query .= " WHERE " . rtrim($this->_dbWhere, ' AND ');
$this->query($query);
$this->bindData($data);
$this->_dbBind = call_user_func_array('array_merge', $this->_dbBind);
$this->bindData($this->_dbBind);
$this->execute();
return $this->lastInsertId();
}
Where function:
public function where($field, $value = null)
{
if(!is_array($field)):
$this->_dbWhere .= $field . ' = :' . $field . ' AND ';
$this->_dbBind[] = [$field => $value];
else:
foreach($field as $key => $value):
$this->_dbWhere .= $key . ' = :' . $key . ' AND ';
$this->_dbBind[] = [$key => $value];
endforeach;
endif;
return $this;
}
Bind data function:
public function bindData($data)
{
foreach ($data as $key => $value) :
$this->bind(':' . $key, $value);
endforeach;
}
public function bind($param, $value, $type = null){
if (is_null($type)) {
switch (true) {
case is_int($value):
$type = \PDO::PARAM_INT;
break;
case is_bool($value):
$type = \PDO::PARAM_BOOL;
break;
case is_null($value):
$type = \PDO::PARAM_NULL;
break;
default:
$type = \PDO::PARAM_STR;
}
}
$this->stmt->bindValue($param, $value, $type);
}
Prepare fields and prepare values:
public function prepareFields($data)
{
return $fields = implode(', ', array_keys($data));
}
public function prepareValues($data)
{
$values = implode(', :', array_keys($data));
return ':' . $values;
}
Query function:
public function query($query){
$this->stmt = $this->handler->prepare($query);
}
The crux of this is that you use the same placeholder :fieldname in the WHERE clause and in the SET portion of the statement. You do need to correct other small issues raised here, but a simple solution is to make this change in the where() function:
if(!is_array($field)):
// make up a placeholder name distinct from the one used in SET clause
$field_placeholder = ":where_".$field
$this->_dbWhere .= $field . ' = ' . $field_placeholder . ' AND ';
$this->_dbBind[] = [$field_placeholder => $value];
else:
Related
I want to override batchinsert, because I want to add ON DUPLICATE KEY UPDATE
$result = Yii::$app->db->createCommand()->batchInsert('product', ['asin', 'title', 'image', 'url', 'price'], $results)->execute();
public function batchInsert($table, $columns, $rows)
{
if (empty($rows)) {
return '';
}
$schema = $this->db->getSchema();
if (($tableSchema = $schema->getTableSchema($table)) !== null) {
$columnSchemas = $tableSchema->columns;
} else {
$columnSchemas = [];
}
$values = [];
foreach ($rows as $row) {
$vs = [];
foreach ($row as $i => $value) {
if (isset($columns[$i], $columnSchemas[$columns[$i]]) && !is_array($value)) {
$value = $columnSchemas[$columns[$i]]->dbTypecast($value);
}
if (is_string($value)) {
$value = $schema->quoteValue($value);
} elseif ($value === false) {
$value = 0;
} elseif ($value === null) {
$value = 'NULL';
}
$vs[] = $value;
}
$values[] = '(' . implode(', ', $vs) . ')';
}
$query = 'INSERT INTO ' . $schema->quoteTableName($table);
$duplicate = ' ON DUPLICATE KEY UPDATE ';
$last = end($columns);
reset($columns);
foreach ($columns as $i => $name) {
$columns[$i] = $schema->quoteColumnName($name);
$duplicate .= $schema->quoteColumnName($name) . ' = VALUES(' . $schema->quoteColumnName($name) . ')';
if ($name <> $last) {
$duplicate .= ', ';
}
}
$query .= ' (' . implode(', ', $columns) . ') ';
$query .= ' VALUES ' . implode(', ', $values);
$query .= $duplicate;
return $query;
}
This is working but now I need to override yii\db\QueryBuilder
If found this: Yii::$app->db->commandClass = new common\models\Command();
and I can override the command class, but the code in yii\db\Schema should be updated too
public function createQueryBuilder()
{
return new \common\models\QueryBuilder($this->db);
}
Or I need to do something completely different, how can I fix this?
According to this discussion about non standard sql commands of devs you can do following (not tested):
class MyQueryBuilder extends yii\db\mysql\QueryBuilder
{
public function batchInsert($table, $columns, $rows)
{
$sql = parent::batchInsert($table, $columns, $rows);
$sql .= 'ON DUPLICATE KEY UPDATE';
return $sql;
}
}
or you can do this:
$db = Yii::$app->db;
$sql = $db->queryBuilder->batchInsert($table, $fields, $rows);
$db->createCommand($sql . ' ON DUPLICATE KEY UPDATE')->execute();
<?php
namespace common\models;
use yii\db\mysql\QueryBuilder as baseQueryBuilder;
class QueryBuilder extends baseQueryBuilder
{
public function batchInsert($table, $columns, $rows)
{
$sql = parent::batchInsert($table, $columns, $rows);
$sql .= ' ON DUPLICATE KEY UPDATE ';
$schema = $this->db->getSchema();
$last = end($columns);
reset($columns);
foreach ($columns as $i => $column)
{
$columns[$i] = $schema->quoteColumnName($column);
$sql .= $schema->quoteColumnName($column) . ' = VALUES(' . $schema->quoteColumnName($column) . ')';
if ($column <> $last) {
$sql .= ', ';
}
}
return $sql;
}
}
$db = Yii::$app->db;
$queryBuilder = new \common\models\QueryBuilder(Yii::$app->db);
$query = $queryBuilder->batchInsert('product', ['asin', 'title', 'image', 'url', 'price'], $results);
$db->createCommand($query)->execute();
I am trying to build a search query based on the input from users, but I am having problems with the AND and WHERE keywords. Here is the code:
if (isset($_POST['submitBtn'])) {
$gender = $_POST['gender'];
$level = $_POST['level'];
$status = $_POST['status'];
$query = 'SELECT * FROM candidate ';
$where = array();
$criteria = array('gender' => $gender, 'level' => $level, 'status' => $status);
foreach ($criteria as $key => $value) {
if ($value !== 'all') {
$where[] = $key . ' = ' . $value;
}
}
}
The output looks like this:
Array
(
[0] => gender = masculine
[1] => level = low
[2] => status = future
)
If no option is selected, it defaults to 'all' and it is excluded from the $where[].
I need to achieve this, or anything similar:
Array
(
[0] => WHERE gender = masculine
[1] => AND level = low
[2] => AND status = future
)
The WHERE must be appended only if one or more options have been selected and the AND must be appended only if two or more options have been selected.
In the code I am using I have 9 search inputs. To keep it clear I only displayed three in the snippet. Can you please help me figure this out?
Try this:I think you need the whereClause in string not in array,here you can choose any one from two and remove the other one.
<?php
$where=array();$flag=0;// use flag to identify the where/and
$whereClause="";
$criteria = array('gender' => "masculine", 'level' => "low", 'status' => "future");
foreach ($criteria as $key => $value) {
if ($value !== 'all') {
if($flag == 0){
$where[] = " WHERE " .$key . ' = ' . $value;//if you need array
$whereClause='WHERE '.$key . ' = "' . $value.'"';//if you need string
}else{
$where[] = " AND " .$key . ' = ' . $value;
$whereClause .=' AND '.$key . ' = "' . $value.'"';
}
$flag++;
}
}
echo "<pre>";
print_r($where);
echo "</pre>";
echo $whereClause;
?>
You can do this :
$query = 'SELECT * FROM candidate ';
$where='';
$criteria = array('gender' => $gender, 'level' => $level, 'status' => $status);
foreach ($criteria as $key => $value) {
if ($value !== 'all') {
if($where=='')
$where='WHERE '.$key . ' = ' . $value;
else
$where.=' AND '.$key . ' = ' . $value;
}
}
$query.=$where; //final query
You can use simple switch statement too
<?
if (isset($_POST['submitBtn'])) {
$gender = $_POST['gender'];
$level = $_POST['level'];
$status = $_POST['status'];
$query = 'SELECT * FROM candidate ';
$where = array();
$criteria = array('gender' => $gender, 'level' => $level, 'status' => $status);
foreach ($criteria as $key => $value)
{
if ($value !== 'all')
{
switch ($key)
{
case 1:
{
$query = " WHERE " .$key . ' = ' . $value;
break;
}
case 2:
{
$query = " AND " .$key . ' = ' . $value;
break;
}
case 3:
{
$query = " AND " .$key . ' = ' . $value;
break;
}
}
$where[] = $query;
}
}
}
You need to put one incrementer ($inc) and then put the conditions as:
$inc=1;
foreach ($criteria as $key => $value) {
if ($value !== 'all') {
if($inc==1){
$where[] = 'Where '.$key . ' = ' . $value.'';
}else{
$where[] = 'AND '.$key . ' = ' . $value.'';
}
$inc++;
}
}
In My view there is one more clean way of achiving this:
if (isset($_POST['submitBtn'])) {
$gender = $_POST['gender'];
$level = $_POST['level'];
$status = $_POST['status'];
$query = 'SELECT * FROM candidate ';
$where = array("Where 1=1");
$criteria = array('gender' => $gender, 'level' => $level, 'status' => $status);
foreach ($criteria as $key => $value) {
if ($value !== 'all') {
$where[] = 'AND '.$key . ' = ' . $value.' ';
}
}
}
I am trying to create a class to save time on cleaning up my variables before sending them to the database to prevent sql injections. The basic systems is working now but i cant seem to get a where/or statement implemented. Does anyone know how to add this?
<?php
class Database {
private $db = '';
private $database = '';
function __construct($settings) {
$this->db = new mysqli('127.0.0.1', $settings['mysql_user']['username'], $settings['mysql_user']['password']);
$this->database = $settings['mysql_user']['database'];
print_r('Database Loaded!<br/>');
}
public function query($method, $database, $rows, $params, $where = array(), $or = array()) {
$count = 0;
$amount = count($rows);
$final_rows = '';
$final_data = '';
$bind_names = array();
$bind_names[0] = '';
$param_types = array(
"int" => "i",
"string" => "s",
"double" => "d",
"blob" => "b"
);
switch($method) {
case 'INSERT':
foreach ($rows as $row) {
$count = $count + 1;
$final_rows .= '`' . $row . '`' . ($count != $amount ? ', ' : '');
$final_data .= '?' . ($count != $amount ? ', ' : '');
}
$stmt = $this->db->prepare('INSERT INTO `' . $this->database . '`.`' . $database . '` (' . $final_rows . ') VALUES (' . $final_data . ')');
for ($i = 0; $i < count($params); $i++)
{
$bind_name = 'bind'.$i;
$$bind_name = $params[$i][1];
$bind_names[0] .= $param_types[$params[$i][0]];
$bind_names[] = &$$bind_name;
}
call_user_func_array( array ($stmt, 'bind_param'), $bind_names);
return $stmt->execute();
break;
case 'UPDATE':
foreach ($rows as $row) {
$count = $count + 1;
$final_rows .= '`' . $row . '`' . ($count != $amount ? ', ' : '');
$final_data .= '?' . ($count != $amount ? ', ' : '');
}
$stmt = $this->db->prepare('UPDATE `' . $this->database . '`.`' . $database . '` SET ' . $final_rows . '');
for ($i = 0; $i < count($params); $i++)
{
$bind_name = 'bind'.$i;
$$bind_name = $params[$i][1];
$bind_names[0] .= $param_types[$params[$i][0]];
$bind_names[] = &$$bind_name;
}
call_user_func_array( array ($stmt, 'bind_param'), $bind_names);
return $stmt->execute();
break;
case 'REPLACE':
foreach ($rows as $row) {
$count = $count + 1;
$final_rows .= '`' . $row . '`' . ($count != $amount ? ', ' : '');
$final_data .= '?' . ($count != $amount ? ', ' : '');
}
$stmt = $this->db->prepare('REPLACE INTO `' . $this->database . '`.`' . $database . '` (' . $final_rows . ') VALUES (' . $final_data . ')');
for ($i = 0; $i < count($params); $i++)
{
$bind_name = 'bind'.$i;
$$bind_name = $params[$i][1];
$bind_names[0] .= $param_types[$params[$i][0]];
$bind_names[] = &$$bind_name;
}
call_user_func_array( array ($stmt, 'bind_param'), $bind_names);
return $stmt->execute();
break;
}
}
}
?>
Going to make a few assumptions, but first I'll recommend you use an ORM before whipping up your own solution. Here's a good list of PHP libraries (I've linked to the database sections, which includes some very well done stand-alone ORMs https://github.com/ziadoz/awesome-php#database)
That being said I'm going to assume the $where and $or arrays are both for the WHERE construct and the items in $where are combined via AND and the $or is combined via OR.
Because you didn't describe what kind of output you were looking for I'm also assuming your $where and $or are key/value pairs which translates to "key=value AND key=value AND (key=value OR key=value)".
DISCLAIMER: This example is kind of hacky, but is the shortest/simplest way to get the example across.
$whereQuery = '';
foreach ($where as $key => $value) {
$whereQuery .= "$key = $value AND";
}
if ($or !== array()) {
$whereQuery .= '(';
foreach ($or as $key => $value) {
$whereQuery .= "$key = $value OR";
}
}
if ($whereQuery !== '') {
if (($temp = strlen($whereQuery) - strlen('AND')) >= 0 && strpos($whereQuery, 'AND', $temp) !== false) {
$whereQuery = substr($whereQuery, -4);
} else {
$whereQuery = substr($whereQuery, -3) . ')';
}
$whereQuery = "WHERE $whereQuery";
}
You can then stick the $whereQuery at the end of an UPDATE or SELECT. Even if $where and $or are empty it'll still work.
You could move the loops into functions and make it recursive if the $value was another array so you could create more complex WHERE statements.
how i can make a insert using this fuctions
I m learning php, as using this functions (mysqli abstract) but after update wont work any more.
/** insert data array */
public function insert(array $arr)
{
if ($arr)
{
$q = $this->make_insert_query($arr);
$return = $this->modifying_query($q);
$this->autoreset();
return $return;
}
else
{
$this->autoreset();
return false;
}
}
complement
/** insert query constructor */
protected function make_insert_query($data)
{
$this->get_table_info();
$this->set_field_types();
if (!is_array(reset($data)))
{
$data = array($data);
}
$keys = array();
$values = array();
$keys_set = false;
foreach ($data as $data_key => $data_item)
{
$values[$data_key] = array();
$fdata = $this->parse_field_names($data);
foreach ($fdata as $key => $val)
{
if (!$keys_set)
{
if (isset($this->field_type[$key]))
{
$keys[] = '`' . $val['table'] . '`.`' . $val['field'] . '`';
}
else
{
$keys[] = '`' . $val['field'] . '`';
}
}
$values[$data_key][] = $this->escape($val['value'], $this->is_noquotes($key), $this->field_type($key), $this->is_null($key),
$this->is_bit($key));
}
$keys_set = true;
$values[$data_key] = '(' . implode(',', $values[$data_key]) . ')';
}
$ignore = $this->ignore ? ' IGNORE' : '';
$delayed = $this->delayed ? ' DELAYED' : '';
$query = 'INSERT' . $ignore . $delayed . ' INTO `' . $this->table . '` (' . implode(',', $keys) . ') VALUES ' . implode(',',
$values);
return $query;
}
before update this class i used to insert data like this
$db = Sdba::table('users');
$data = array('name'=>'adam');
$db->insert($data);
this method of insert dont works on new class.
if i try like this i got empty columns and empty values.
thanks for any help
complete class download http://goo.gl/GK3s4E
Try using set instead of insert:
$users = Sdba::table('users');
$user['name'] = 'Alvaro';
$users->set($user);
I am trying to recreate something similar to the CodeIgniter active record. My goal is following:
When this is called:
echo $messages->select()->from()->where('user', 'foo')->where('dateadded', 'today');
it needs to create two where conductions (WHERE user = foo AND dateadded = today). But at the moment I am getting only the value of the second function called (WHERE dateadded = today). How can I do this?
This is the code I have so far:
abstract class DatabaseQuery extends Database
{
protected $_tablename;
protected $_primary_key = 'id';
protected $_dbSelect = '';
protected $_dbFrom = '';
protected $_dbJoin = '';
protected $_dbWhere = '';
function __construct()
{
parent::__construct();
}
public function __toString()
{
$query = '';
$query .= $this->_dbSelect . PHP_EOL;
$query .= $this->_dbFrom . PHP_EOL;
$query .= $this->_dbJoin . PHP_EOL;
$query .= $this->_dbWhere . PHP_EOL;
return $query;
}
public function select($select = null)
{
if($select === null):
$this->_dbSelect = "SELECT *";
else:
$this->_dbSelect = "SELECT {$select}";
endif;
return $this;
}
public function from($from = null)
{
if($from === null):
$this->_dbFrom = "FROM {$this->_tablename}";
else:
$this->_dbFrom = "FROM {$from}";
endif;
return $this;
}
public function join($table, $on)
{
if(is_string($join))
$this->_dbJoin = "JOIN {$table} ON {$on}";
return $this;
}
public function where($field, $value = null)
{
if(!is_array($field)):
$this->_dbWhere = "WHERE " . $field . ' = :' . $value;
else:
$where = 'WHERE ';
foreach($field as $key => $value):
$where .= $key . ' = :' . $key . ' AND ';
endforeach;
$this->_dbWhere = rtrim($where, ' AND ');
endif;
return $this;
}
you need some sort of a trigger to run the query at the end
$messages->select()->from()->where('user', 'foo')->where('dateadded', 'today')->get();
you can smooth things up in the trigger for example
<?php
class QueryBuilder{
private $_dbWhere;
public function where($field, $value = null)
{
if(!is_array($field)):
$this->_dbWhere .= $field . ' = :' . $value . ' AND ';
else:
foreach($field as $key => $value):
$this->_dbWhere .= $key . ' = :' . $key . ' AND ';
endforeach;
endif;
return $this;
}
// your trigger this will get the result for the class user
public function get()
{
// smooth things up (remove last AND )
$this->_dbWhere = " WHERE " . rtrim($this->_dbWhere, ' AND ');
// build the query select . from . where
// and return the result (in this case print _dbWhere)
echo $this->_dbWhere;
}
}
this will work
$db = new QueryBuilder;
$db->where('user', 'foo')->where('dateadded', 'today')->get();
However, your trying to reinvent the wheels there are tons of beautiful query builders out there take a look at Laravel's Query Builder to get some inspiration
Would that fullfill your needs ?
public function where($field, $value = null)
{
if(this->_dbWhere == ''){
$this->_dbWhere .= "WHERE ";
} else {
$this->_dbWhere .= " AND ";
}
if(!is_array($field)){
$this->_dbWhere = . $field . ' = :' . $value;
}else{
foreach($field as $key => $value):
$where .= $key . ' = :' . $key . ' AND ';
endforeach;
$this->_dbWhere = rtrim($where, ' AND ');
}
return $this;
}