I have a PDO with a dynamically created statement, I am running into errors when I try to insert empty data into decimal fields, I believe the issue is I need input null instead of empty. Here is my code:
try {
$options = [
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
\PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
\PDO::ATTR_EMULATE_PREPARES => false,
];
$conn = new PDO("mysql:charset=utf8mb4;host=$servername;dbname=$dbname", $username, $password);
function escape_mysql_identifier($field){
return "`".str_replace("`", "``", $field)."`";
}
function prepared_insert($pdo, $table, $data) {
$keys = array_keys($data);
$keys = array_map('escape_mysql_identifier', $keys);
$fields = implode(",", $keys);
$table = escape_mysql_identifier($table);
$placeholders = str_repeat('?,', count($keys) - 1) . '?';
$sql = "INSERT INTO $table ($fields) VALUES ($placeholders)";
$pdo->prepare($sql)->execute(array_values($data));
}
prepared_insert($conn, 'products', $data);
$id = $conn->lastInsertId();
if ($id > 0) {
echo json_encode(array('response'=>'success'));
}else{
echo json_encode(array ('response'=>'error','errorMessage'=>'Row not created'));
}
}catch(PDOException $e){
echo json_encode(array ('response'=>'error','errorMessage'=>$e->getMessage()));
}
$conn = null;
How can I make this insert null instead of empty?
Change your table settings. Set it to accept null in that column:
ALTER TABLE `table` CHANGE `column` `column` VARCHAR(255) NULL DEFAULT NULL;
Related
How can I verify successful row creation (and modification) when using prepared_insert in PDO?
Here is my code:
try {
$options = [
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
\PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
\PDO::ATTR_EMULATE_PREPARES => false,
];
$conn = new PDO("mysql:charset=utf8mb4;host=$servername;dbname=$dbname", $username, $password);
function escape_mysql_identifier($field){
return "`".str_replace("`", "``", $field)."`";
}
function prepared_insert($pdo, $table, $data) {
$keys = array_keys($data);
$keys = array_map('escape_mysql_identifier', $keys);
$fields = implode(",", $keys);
$table = escape_mysql_identifier($table);
$placeholders = str_repeat('?,', count($keys) - 1) . '?';
$sql = "INSERT INTO $table ($fields) VALUES ($placeholders)";
$pdo->prepare($sql)->execute(array_values($data));
}
$data = array_filter($data);
//var_dump ($data);
prepared_insert($conn, 'products', $data);
$id = $conn->lastInsertId();
if ($id > 0) {
echo json_encode(array('response'=>'success','message'≥'Row successfully added'));
}else{
echo json_encode(array('response'=>'danger','message'≥'Row not successfully added'));
}
}catch(PDOException $e){
echo json_encode(array('response'=>'danger','message'=>$e->getMessage()));
}
$conn = null;
As you can see, right now I am doing it by using lastInsertId() but I do not think that's the correct way to do it.
Additionally, if the row was not created, how can I capture the error behind it and report it?
you not need create this condition :
if ($id > 0) {
echo json_encode(array('response'=>'success','message'≥'Row successfully added'));
}else{
echo json_encode(array('response'=>'danger','message'≥'Row not successfully added'));
}
because you using try and catch.. you can make sure it with database transaction.. try to commit and catch to rollback..
I add a lot a values with params ($queryArr) in INSERT statemnt in foreach
public function getClients()
{
helper::putToLog("\n----- getClients\n", true);
$managedCustomerService = $this->user->GetService('ManagedCustomerService', ADWORDS_VERSION);
$selector = new Selector();
$selector->fields = array("CustomerId", "Name", "CurrencyCode");
$graph = $managedCustomerService->get($selector);
if (isset($graph->entries)) {
$accounts = [];
foreach ($graph->entries as $account) {
$accounts[$account->customerId] = $account;
}
helper::putToLog('Clients found: '.count($accounts)."\n", true);
} else {
helper::putToLog('Clients not found'."\n", true);
return false;
}
$sth = $this->db->prepare('UPDATE `adwords_clients_google` set status = 2');
$sth->execute();
$sth = null;
$queryClients = "INSERT INTO `adwords_clients_google` (`client_foreign_id`, `status`, `client_name`, `client_currency`) VALUES";
foreach($accounts as $account) {
$queryArr[$account->customerId] = "(".$account->customerId.", 1, :".$account->customerId.", :".$account->customerId."_currencyCode)";
$nameArr[$account->customerId] = $account->name;
$currencyArr[$account->customerId."_currencyCode"] = $account->currencyCode;
}
$queryClients .= implode(',', $queryArr) . " ON DUPLICATE KEY UPDATE `status` = VALUES(`status`), `client_name` = VALUES(`client_name`) ";
$sth = $this->db->prepare($queryClients);
foreach ($nameArr as $key => $value) {
$sth->bindValue(":$key", str_replace("'", "\'", $value), PDO::PARAM_STR);
}
foreach ($currencyArr as $key => $value) {
$sth->bindValue(":$key", $value, PDO::PARAM_STR);
}
print_r($sth);
try {
if ($sth->execute()) {
helper::putToLog('ok queryCampaignArr, inserted rows: ' . $sth->rowCount());
} else {
helper::putToLog('not ok', true);
}
} catch (Exception $ex) {
helper::putToLog($sth->debugDumpParams(), true);
helper::putToLog("ERROR: ".$ex->getMessage(), true);
}
return true;
}
and there are 2 array of values I need to bind $nameArr and $currencyArr. I didn't get any errors but column client_currency is empty even though array $currencyArr contains all necessary values. What's wrong?
It looks like you haven't grasped the concept of prepared+paramterized statements yet.
You prepare them once and then execute them with varying parameters (one or) multiple times.
$sth = $this->db->prepare('
INSERT INTO
`adwords_clients_google`
(`client_foreign_id`, `status`, `client_name`, `client_currency`)
VALUES
(:id, 1, :name, :currency)
ON DUPLICATE KEY UPDATE
`status` = VALUES(`status`),
`client_name` = VALUES(`client_name`)
');
$sth->bindParam(':id', $id);
$sth->bindParam(':name', $name);
$sth->bindParam(':currency', $currency);
foreach($accounts as $account) {
$id = $account->customerId;
$name = $account->name;
$currency = $account->currencyCode;
$sth->execute();
}
if you haven't got any error message/log entries please make sure that the error handling mode of your PDO instance really is set to PDO::ERRMODE_EXCEPTION.
edit: to illustrate that, here's an sscce:
<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'localonly', 'localonly', array(
PDO::ATTR_EMULATE_PREPARES=>false,
PDO::MYSQL_ATTR_DIRECT_QUERY=>false,
PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION
));
setup($pdo); // creating temporary table + sample data for this example
printTable("before", $pdo);
$sth = $pdo->prepare('
INSERT INTO
`soFoo`
(`client_foreign_id`, `status`, `client_name`, `client_currency`)
VALUES
(:id, 1, :name, :currency)
ON DUPLICATE KEY UPDATE
`status` = VALUES(`status`),
`client_name` = VALUES(`client_name`)
');
$sth->bindParam(':id', $id);
$sth->bindParam(':name', $name);
$sth->bindParam(':currency', $currency);
$accounts = data();
foreach($accounts as $account) {
$id = $account->customerId;
$name = $account->name;
$currency = $account->currencyCode;
$sth->execute();
}
printTable("after", $pdo);
function data() {
return array_map(function($e) { return (object)$e; }, array(
array('customerId'=>1, 'name'=>'customerA', 'currencyCode'=>'cA'),
array('customerId'=>2, 'name'=>'customerB', 'currencyCode'=>'cB'),
));
}
function printTable($cap, $pdo) {
echo $cap, "\r\n";
foreach( $pdo->query('SELECT * FROM soFoo', PDO::FETCH_ASSOC) as $r ) {
echo join(', ', $r), "\r\n";
}
}
function setup($pdo) {
$pdo->exec('
CREATE TEMPORARY TABLE soFoo (
client_foreign_id int,
`status` int,
client_name varchar(32),
client_currency varchar(32),
unique(client_foreign_id)
)
');
$pdo->exec("INSERT INTO soFoo (client_foreign_id,status,client_name,client_currency) VALUES (1, 0, 'agent smith', 'kruegerrand')");
}
prints
before
1, 0, agent smith, kruegerrand
after
1, 1, customerA, kruegerrand
2, 1, customerB, cB
I am trying to build a class to select records from table defined on user input.
my plan is to make a class that read user input conditions as an array, because there is a lot of other forms for other tables with different number of conditions. therefore I want to make one class that works with all.
basically am trying to achieve this statement:
select * from table_name where condition1=value1 AND condition2=value2
where the number of conditions varies depends on user input.
what I have done is this class:
class SelectQuery {
private $Error;
public $conn;
public $table;
public function __construct($conn){ //establish connection
$this->conn = $conn;
}
public function SelectData($table,$cols,array $conds){
$this->table = $table;
if($cols)
{
if($conds)
{
$i=0; $cond='';
foreach ($conds as $key => $value){
if($i==0)
{
$cond .= $key. '=' .$value;
}
else
{
$cond .= ' AND ' .$key. '=' .$value;
}
$i++;
}
$sql = 'SELECT '.$cols.' FROM '.$table.' WHERE '.$cond.'';
$stmt = oci_parse($this->conn, $sql);
oci_execute($stmt);
$rows = oci_fetch_all($stmt, $data, null, null, OCI_FETCHSTATEMENT_BY_COLUMN);
if($rows)
{
return $rows['0']['0'];
}
else
{
return NULL;
}
}
else
{
$sql = 'SELECT {$cols} FROM {$table}';
$stmt = oci_parse($this->conn, $sql);
oci_execute($stmt);
$rows = oci_fetch_all($stmt, $data, null, null, OCI_FETCHSTATEMENT_BY_COLUMN);
if($rows)
{
return $rows['0']['0'];
}
else
{
return NULL;
}
}
}
}
and this is how the data is sent from form:
$manager = new OracleConnectManager();
$conn = $manager->establishConnection(); //establish connection
if( $conn ){
$table = 'MAIN';
$cols= '*';
$conds = array(
'id_number' => $_POST['id_number'],
'firstname' => $_POST['firstname'],
'secondname' => $_POST['secondname'],
'thirdname' => $_POST['thirdname'],
'familyname' => $_POST['familyname'],
'department' => $_POST['department'],
);
$sel = new SelectQuery($conn);
$dth = $sel->SelectData($table,$cols,$conds);
echo $dth;
}
when I run the code i get this error:
Warning: oci_execute(): ORA-00936: missing expression
(in the SelectQuery class, line: oci_execute($stmt); )
Warning: oci_fetch_all(): ORA-24374: define not done before fetch or execute and fetch
(in the SelectQuery class, line: $rows = oci_fetch_all($stmt, $data, null, null, OCI_FETCHSTATEMENT_BY_COLUMN); )
and there is no output.
any help would be appreciated.
thanks
There are two things:
You need to validate all the $_POST values and build $conds array for values that are set in $_POST.
Update:
i.e. if you have only one or two post values then the $conds array should be:
$conds = array(
'id_number' => $_POST['id_number'],
'firstname' => $_POST['firstname']
)
In your foreach loop, $value needs to be quoted if it is varchar type as:
foreach ($conds as $key => $value){
if($i==0)
{
$cond .= "$key='$value'";
}
else
{
$cond .= " AND $key='$value'";
}
$i++;
}
Update:
if you have int and varchar fields then do formatting when building array itself:
$conds = array(
'id_number' => $_POST['id_number'], // int type
'firstname' => "'".$_POST['firstname']."'" // string type
)
else
{
$sql = 'SELECT ' . $cols . ' FROM ' . $table;
// Other Statements
}
Hope the ELSE part for $sql should be framed like above?
I have a problem where I cannot insert anything into a MySQL database using PDO.
I get no errors but whenever I check the database if the row has been inserted, the table is empty.
I know I have a connection to the database as I am able to select but not insert.
Here is my class that entends PDO
class Database extends PDO
{
public function __construct($DB_TYPE, $DB_HOST, $DB_NAME, $DB_USER, $DB_PASS)
{
parent::__construct($DB_TYPE.':host='.$DB_HOST.';dbname='.$DB_NAME, $DB_USER, $DB_PASS);
//parent::setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTIONS);
}
/**
* select
* #param string $sql An SQL string
* #param array $array Paramters to bind
* #param constant $fetchMode A PDO Fetch mode
* #return mixed
*/
public function select($sql, $array = array(), $fetchMode = PDO::FETCH_ASSOC)
{
$sth = $this->prepare($sql);
foreach ($array as $key => $value) {
$sth->bindValue("$key", $value);
}
$sth->execute();
return $sth->fetchAll($fetchMode);
}
/**
* insert
* #param string $table A name of table to insert into
* #param string $data An associative array
*/
public function insert($table, $data)
{
/*ksort($data);
$fieldNames = implode('`, `', array_keys($data));
$fieldValues = ':' . implode(', :', array_keys($data));
$sth = $this->prepare("INSERT INTO $table (`$fieldNames`) VALUES ($fieldValues)");
foreach ($data as $key => $value) {
$sth->bindValue(":$key", $value);
}*/
$sth = $this->prepare("INSERT INTO user (`login`, `password`, `role`) VALUES (:login, :password, :role)");
$sth->bindValue(':login', 'username');
$sth->bindValue(':password', 'password');
$sth->bindValue(':role', 'owner');
$sth->execute();
/*if ($sth->errorCode() != 0) {
$arr = $sth->ErrorInfo();
throw new Exception('SQL failure:'.$arr[0].':'.$arr[1].':'.$arr[2]);
}*/
$sth->debugDumpParams();
}
Here is my table structure (kept it simple for debugging).
CREATE TABLE IF NOT EXISTS `user` (
`userid` int(11) NOT NULL AUTO_INCREMENT,
`login` varchar(25) NOT NULL,
`password` varchar(64) NOT NULL,
`role` enum('default','admin','owner') NOT NULL DEFAULT 'default',
PRIMARY KEY (`userid`)
) ENGINE=InnoDB
EDIT:
I found out where the problem lies. The problem is with the data array. If I use $data['first_name'] when calling the insert() function, it fails but if I replace all those $data[] values with hard codes values, it works so the problem lies with $data[]
I create an array of POST variables
// get all the post data from the registration form
$data = array();
$data['first_name'] = $_POST['first_name'];
$data['last_name'] = $_POST['last_name'];
$data['gender'] = $_POST['gender'];
$data['email'] = $_POST['email'];
$data['interests'] = $_POST['interests'];
$data['username'] = $_POST['username'];
$data['password'] = $_POST['password'];
$data['newsletter'] = $_POST['newsletter'];
$data['terms'] = $_POST['terms'];
$data['captcha'] = $_POST['captcha'];
This gets passed to the create function
public function create($data) {
$this->db->insert('users', array(
'first_name' => $data['first_name'], //this won't work
'first_name' => 'whatever the name is', //this will work
'last_name' => $data['last_name'],
'email' => $data['email'],
'username' => $data['username'],
'password' => $password,
'user_salt' => $user_salt,
'sex' => $data['gender'],
'interests' => $data['interests'],
'signup_date' => date('Y-m-d H:i:s'),
'verification_code' => $code
));
and this is where it gets inserted
public function insert($table, $data)
{
ksort($data);
$fieldNames = implode('`, `', array_keys($data));
$fieldValues = ':' . implode(', :', array_keys($data));
$sth = $this->prepare("INSERT INTO $table (`$fieldNames`) VALUES ($fieldValues)");
foreach ($data as $key => $value) {
$sth->bindValue(":$key", $value);
}
$sth->execute();
}
$fieldValues = implode(':,', array_keys($data));
Try this,
$fieldNames = implode(', ', array_keys($data));
$fieldValues = ':' . implode(', :', array_values($data));
$sth = $this->prepare("INSERT INTO $table ($fieldNames) VALUES ($fieldValues)");
I want to use PDO prepared statements but i find it really time consuming to type. it would be super useful if there is a function to just pass the following associative array:
array(
"title"=>$title
"userid"=>$userid
"post"=>$body
)
Keeping in mind that the keys in the array always match the rows in the SQL table. recaping everything, this should cut off the effort to type the :foo and type them again in the execute function.
I'm specifically talking about the INSERT query.
How to do that?
function pdo_insert($table, $arr=array())
{
if (!is_array($arr) || !count($arr)) return false;
// your pdo connection
$dbh = '...';
$bind = ':'.implode(',:', array_keys($arr));
$sql = 'insert into '.$table.'('.implode(',', array_keys($arr)).') '.
'values ('.$bind.')';
$stmt = $dbh->prepare($sql);
$stmt->execute(array_combine(explode(',',$bind), array_values($arr)));
if ($stmt->rowCount() > 0)
{
return true;
}
return false;
}
pdo_insert($table, array('title'=>$title, 'userid'=>$user_id, 'post'=>$body));
Slighly improved PDO Insert function that also takes security into consideration by preventing SQL Injection attacks:
// Insert an array with key-value pairs into a specified database table (MySQL).
function pdo_insert($dbh,$table,$keyvals) {
$sql = sprintf("INSERT INTO %s ( `%s` ) %sVALUES ( :%s );",
$table,
implode("`, `", array_keys($keyvals)),
PHP_EOL,
implode(", :", array_keys($keyvals))
);
$stmt = $dbh->prepare($sql);
foreach ($keyvals as $field => $value) {
$stmt->bindValue(":$field", $value, PDO::PARAM_STR);
}
$stmt->execute();
return $dbh->lastInsertId();
}
// Convert special characters to HTML safe entities.
function h($str) {
return trim(stripslashes(htmlspecialchars($str, ENT_QUOTES, 'utf-8')));
}
Example:
$dbh = new PDO($dsn);
$dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$keyvals = [
'id' => isset($_POST['id']) ? h( $_POST['id'] ) : null,
'title' => isset($_POST['title']) ? h( $_POST['title'] ) : null,
'description' => isset($_POST['description']) ? h( $_POST['description'] ) : null,
'created_at' => time(),
'created_by' => 1,
];
$last_ids[] = pdo_insert($dbh,'products',$keyvals);