Insert multiple rows at a time in YII - php

I have gone through the http://www.yiiframework.com/doc/guide/1.1/en/form.table
I did not understand what this means:
$items=$this->getItemsToUpdate();
what is this $this->getItemsToUpdate() function?
I am trying to insert dynamic rows at a time. I have used jquery for creating the data but I don't know how to insert them into the database.

Yii supports to add multiple records.
Use this....
$alertStatus[] = array(
'db_field_name1' => $value1,
'db_field_name1' => $value2,
'created_on' => new CDbExpression('NOW()'),
'modified_on' => new CDbExpression('NOW()')
);
$connection = Yii::app()->db->getSchema()->getCommandBuilder();
$command = $connection->createMultipleInsertCommand('table_name', $alertStatus);
$command->execute();
$alertStatus array should contains all the fields of database

If you're not constrained to working with Active Records, you could use DAO to execute an appropriate SQL insert statement for inserting multiple rows. You can find an example of such a statement here:
Insert multiple records into MySQL with a single query
Once you have your insert statement as a string (which can be constructed dynamically using a loop in case you don't know in advance how many rows you need to insert), you can execute it like this:
$sql = 'INSERT statement goes here';
$connection = Yii::app() -> db;
$command = $connection -> createCommand($sql);
$command -> execute();

I've faced the same issue.
As far as I know Yii doesn't have default function to insert multiple rows.
So I've created one in CdbCommand class (framework/db/CDbCommand.php) :
/**
* Creates and executes an INSERT SQL statement for several rows.
* #param string $table the table that new rows will be inserted into.
* #param array $array_columns the array of column datas array(array(name=>value,...),...) to be inserted into the table.
* #return integer number of rows affected by the execution.
*/
public function insertSeveral($table, $array_columns)
{
$sql = '';
$params = array();
$i = 0;
foreach ($array_columns as $columns) {
$names = array();
$placeholders = array();
foreach ($columns as $name => $value) {
if (!$i) {
$names[] = $this->_connection->quoteColumnName($name);
}
if ($value instanceof CDbExpression) {
$placeholders[] = $value->expression;
foreach ($value->params as $n => $v)
$params[$n] = $v;
} else {
$placeholders[] = ':' . $name . $i;
$params[':' . $name . $i] = $value;
}
}
if (!$i) {
$sql = 'INSERT INTO ' . $this->_connection->quoteTableName($table)
. ' (' . implode(', ', $names) . ') VALUES ('
. implode(', ', $placeholders) . ')';
} else {
$sql .= ',(' . implode(', ', $placeholders) . ')';
}
$i++;
}
return $this->setText($sql)->execute($params);
}
Usage:
$rows = array(
array('id' => 1, 'name' => 'John'),
array('id' => 2, 'name' => 'Mark')
);
$command = Yii::app()->db->createCommand();
$command->insertSeveral('users', $rows);
Update
As Nabi K.A.Z. mentioned it is really much better not to touch source code of framework.
In my project I've actually created class MyCDbCommand (that extends CDbCommand) and I've created MyCDBConnection (that extends CDbConnection).
MyCDbCommand (extending CDbCommand):
class MyCDbCommand extends CDbCommand
{
protected $_connection;
public function __construct(CDbConnection $connection, $query = null)
{
$this->_connection = $connection;
parent::__construct($connection, $query);
}
/**
* Creates and executes an INSERT SQL statement for several rows.
* #param string $table the table that new rows will be inserted into.
* #param array $array_columns the array of column datas array(array(name=>value,...),...) to be inserted into the table.
* #return integer number of rows affected by the execution.
*/
public function insertSeveral($table, $array_columns)
{
$sql = '';
$params = array();
$i = 0;
foreach ($array_columns as $columns) {
$names = array();
$placeholders = array();
foreach ($columns as $name => $value) {
if (!$i) {
$names[] = $this->_connection->quoteColumnName($name);
}
if ($value instanceof CDbExpression) {
$placeholders[] = $value->expression;
foreach ($value->params as $n => $v) {
$params[$n] = $v;
}
} else {
$placeholders[] = ':' . $name . $i;
$params[':' . $name . $i] = $value;
}
}
if (!$i) {
$sql = 'INSERT INTO ' . $this->_connection->quoteTableName($table)
. ' (' . implode(', ', $names) . ') VALUES ('
. implode(', ', $placeholders) . ')';
} else {
$sql .= ',(' . implode(', ', $placeholders) . ')';
}
$i++;
}
return !empty($sql) ? $this->setText($sql)->execute($params) : 0;
}
}
MyCDBConnection (extending CDbConnection and using MyCDbCommand):
class MyCDbConnection extends CDbConnection
{
public function createCommand($query = null)
{
$this->setActive(true);
return new MyCDbCommand($this, $query);
}
}
Then I've changed config file (/protected/config/main.php).
I've changed CDbConnection to MyCDbConnection there:
...
'components'=>array(
...
'db' => array(
'connectionString' => 'mysql:host=localhost;dbname=dname',
'username' => 'user',
'password' => 'password',
'charset' => 'utf8',
'class' => 'MyCDbConnection', // Change default CDbConnection class to MyCDbConnection
),
...
)
...
And here you go. We did it without touching a core framework and you can use it the same way as before.

The above code from Pigalev Pavel was great.
But that need to modify core framework and this is bad!
So, I write a independent class.
Put this code in components folder under GeneralRepository.php file name.
<?php
class GeneralRepository
{
/**
* Creates and executes an INSERT SQL statement for several rows.
*
* Usage:
* $rows = array(
* array('id' => 1, 'name' => 'John'),
* array('id' => 2, 'name' => 'Mark')
* );
* GeneralRepository::insertSeveral(User::model()->tableName(), $rows);
*
* #param string $table the table that new rows will be inserted into.
* #param array $array_columns the array of column datas array(array(name=>value,...),...) to be inserted into the table.
* #return integer number of rows affected by the execution.
*/
public static function insertSeveral($table, $array_columns)
{
$connection = Yii::app()->db;
$sql = '';
$params = array();
$i = 0;
foreach ($array_columns as $columns) {
$names = array();
$placeholders = array();
foreach ($columns as $name => $value) {
if (!$i) {
$names[] = $connection->quoteColumnName($name);
}
if ($value instanceof CDbExpression) {
$placeholders[] = $value->expression;
foreach ($value->params as $n => $v)
$params[$n] = $v;
} else {
$placeholders[] = ':' . $name . $i;
$params[':' . $name . $i] = $value;
}
}
if (!$i) {
$sql = 'INSERT INTO ' . $connection->quoteTableName($table)
. ' (' . implode(', ', $names) . ') VALUES ('
. implode(', ', $placeholders) . ')';
} else {
$sql .= ',(' . implode(', ', $placeholders) . ')';
}
$i++;
}
$command = Yii::app()->db->createCommand($sql);
return $command->execute($params);
}
}
And usage anywhere:
$rows = array(
array('id' => 1, 'name' => 'John'),
array('id' => 2, 'name' => 'Mark')
);
GeneralRepository::insertSeveral(User::model()->tableName(), $rows);

Related

yii2 override batchinsert from database class

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();

Dynamically construct SQL search query?

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.' ';
}
}
}

Insert data on mysql using mysqli function

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);

PHP PDO - Update with bindValue (set and where parametes are same)

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:

php mysql query condition oop

I was wondering how I could make a general function for SELECT mysql queries from my SelectQuery object in PHP. SelectQuery extends Query, which means it inherits the database connection, a realescape method (which is mysqli_real_escape_string()), and a query method which executes the query. Besides that, it also gets a protected variable called _sql, which is the SQL the query() method passes to the database. And it also gets a protected variable called _table, which contains the (escaped) name of the table it's working on.
My code:
public function select($columns = array('*'), $known = null, $limit = null, $offset = null, $orderby = null, $asc = true) {
if (!is_array($columns)) {
new Error('Parameter is not an array.');
return;
}
$select = '';
foreach($columns as $column) {
$select .= (($select != '')?', ':'') . '`' . $this->realescape($column) . '`';
}
$conditions = '';
if (is_array($known)) {
foreach($known as $column => $value) {
$conditions .= (($conditions != '')?' AND ':'WHERE ') . '`' . $this->realescape($column) . '` = ' . ((is_string($value))?'\'':'') . $this->realescape($value) . ((is_string($value))?'\'':'');
}
}
$domain = '';
if ($limit !== null) {
$domain = 'LIMIT ' . $this->realescape($limit);
if ($offset !== null) {
$domain .= ' OFFSET = ' . $this->realescape($offset);
}
}
$order = '';
if ($orderby !== null) {
$order = 'ORDER BY `' . $this->realescape($orderby) . '` ' . (($asc)?'ASC':'DESC');
}
$this->_sql = 'SELECT ' . $select . ' FROM `' . $this->_table . '`';
if ($conditions != '') {
$this->_sql .= ' ' . $conditions;
}
if ($domain != '') {
$this->_sql .= ' ' . $domain;
}
if ($order != '') {
$this->_sql .= ' ' . $order;
}
return $this->query();
}
The $known variable might be set, if set it should be an array which contains all the 'known' elements of the rows we are selecting.
My question: How can I make this so that conditions such as
age < 18, or date > 5120740154 are easily made?
Also, if you think the way I'm making this work is wrong, please say so.
Thanks in advance
Dynamically build WHERE conditions that can use any operators
Create a $where array that holds your where conditions.
$where = array (
'age <' => 18,
'date >' => 5120740154
);
You can build that into your SQL query using a function like this:
private function buildWhereConditions($where) {
$conditions = 'WHERE 1=1';
/*1=1 is just so you can easily append ANDs */
foreach ($where as $key => $value) {
$conditions .= " AND $key $value ";
}
return $conditions;
}
This will return the where conditions to be appended to your query:
WHERE 1=1 AND age < 18 AND date > 5120740154

Categories