This question already has answers here:
PHP PDO Insert Using Loop
(2 answers)
Closed 5 months ago.
foreach($parentinfojson as $value) {
if (!empty($parentinfojson )) {
$stmt2 = $dbh -> prepare("INSERT INTO parentinfo (last_name,first_name,status) VALUES (:lastname,:firstname,:status)");
$stmt2_ = $stmt2 -> execute(array(':firstname' => $value['firstname'], ':lastname' => $value['lastname'], ':status' => $status));
} else {
$stmt2_ = $stmt2 -> execute();
}
}
if ($stmt2_ && $stmt3_ && $stmt1_ && $stmt_ && $stmt5_ && $stmt4_) {
echo json_encode(array(
'error' => false,
'message' => "Added"
));
}
This is my execute in Inserting new data in the table. When i tested the adding of empty data(parentinfojson is empty) i get error that Notice: Undefined variable: stmt2_. What i did is i added an else statement and i initialize the variable still i get error. I tried to echo something in the else statement as well but i get error. Now I run out of idea on how to initialize the variable when the json is empty so that i dont get the error undefined variable
You just defined $stmt2 inside the loop, if $parentinfojson is empty it'll certainly get undefined. Why not define/initialize it.
// initialize up top
$stmt_ = $stmt1_ = $stmt2_ = $stmt3_ = $stmt4_ = $stmt5_ = false;
$stmt2 = $dbh->prepare("INSERT INTO parentinfo (last_name,first_name,status) VALUES (:lastname,:firstname,:status)");
foreach($parentinfojson as $value) {
$stmt2_ = $stmt2->execute(array(
':firstname' => $value['firstname'],
':lastname' => $value['lastname'],
':status' => $status
));
}
if ($stmt2_ && $stmt3_ && $stmt1_ && $stmt_ && $stmt5_ && $stmt4_) {
echo json_encode(array(
'error' => false,
'message' => "Added"
));
}
Sidenote: Another way would be to build the query dynamically, including the placeholders and the values. So that in turn, you don't have to loop each batches of insert but instead, creating the SQL batch insert then binding all of the values into one single insert invocation:
$stmt_ = $stmt1_ = $stmt2_ = $stmt3_ = $stmt4_ = $stmt5_ = false;
if(!empty($parentinfojson)) {
$base_query = 'INSERT INTO parentinfo (last_name, first_name, status) VALUES ';
$placeholders = implode(',', array_map(function($batch){
return '(' . implode(',', array_fill(0, count($batch), '?')) . ')';
}, $parentinfojson));
$base_query .= $placeholders;
$parentinfojson = call_user_func_array('array_merge', array_map('array_values', $parentinfojson));
$stmt2 = $dbh->prepare($base_query);
$stmt2_-> $stmt2->execute($parentinfojson);
}
There might be a chance that the json variable you are receiving is not empty, so you should also add a check for valid json, this is the function to check a valid json
function isJson($string) {
json_decode($string);
return (json_last_error() == JSON_ERROR_NONE);
}
Related
I'm trying to build a script where I need to read a txt file and execute some process with the lines on the file. For example, I need to check if the ID exists, if the information has updated, if yes, then update the current table, if no, then insert a new row on another temporary table to be manually checked later.
These files may contain more than 20,30 thousand lines.
When I just read the file and print some dummie content from the lines, it takes up to 40-50ms. However, when I need to connect to the database to do all those verifications, it stops before the end due to the timeout.
This is what I'm doing so far:
$handle = fopen($path, "r") or die("Couldn't get handle");
if ($handle) {
while (!feof($handle)) {
$buffer = fgets($handle, 4096);
$segment = explode('|', $buffer);
if ( strlen($segment[0]) > 6 ) {
$param = [':code' => intval($segment[0])];
$codeObj = Sql::exec("SELECT value FROM product WHERE code = :code", $param);
if ( !$codeObj ) {
$param = [
':code' => $segment[0],
':name' => $segment[1],
':value' => $segment[2],
];
Sql::exec("INSERT INTO product_tmp (code, name, value) VALUES (:code, :name, :value)", $param);
} else {
if ( $codeObj->value !== $segment[2] ) {
$param = [
':code' => $segment[0],
':value' => $segment[2],
];
Sql::exec("UPDATE product SET value = :value WHERE code = :code", $param);
}
}
}
}
fclose($handle);
}
And this is my Sql Class to connect with PDO and execute the query:
public static function exec($sql, $param = null) {
try {
$conn = new PDO('mysql:charset=utf8mb4;host= '....'); // I've just deleted the information to connect to the database (password, user, etc.)
$q = $conn->prepare($sql);
if ( isset($param) ) {
foreach ($param as $key => $value) {
$$key = $value;
$q->bindParam($key, $$key);
}
}
$q->execute();
$response = $q->fetchAll();
if ( count($response) ) return $response;
return false;
} catch(PDOException $e) {
return 'ERROR: ' . $e->getMessage();
}
}
As you can see, each query I do through Sql::exec(), is openning a new connection. I don't know if this may be the cause of such a delay on the process, because when I don't do any Sql query, the script run within ms.
Or what other part of the code may be causing this problem?
First of all, make your function like this,
to avoid multiple connects and also o get rid of useless code.
public static function getPDO() {
if (!static::$conn) {
static::$conn = new PDO('mysql:charset=utf8mb4;host= ....');
}
return static::$conn;
}
public static function exec($sql, $param = null) {
$q = static::getPDO()->prepare($sql);
$q->execute($param);
return $q;
}
then create unique index for the code field
then use a single INSERT ... ON DUPLICATE KEY UPDATE query instead of your thrree queries
you may also want to wrap your inserts in a transaction, it may speed up the inserts up to 70 times.
I want to insert my notifications static , but I'm checking the variable type is not empty, but at the moment insert says Message: Illegal string offset 'notificationCount' and Undefined index: type. I'm trying to make my array dynamically, but it doesn't seem to work.
public function addNotification($message, $product_id, $type = ''){
$types = array('new' => 0, 'pending' => 1, 'low stock' => 3);
if (isset($types[$type]) === false) {
throw new \InvalidArgumentException('Value for third parameter must be one of new, pending, or low stock.');
}
$type = $types[$type];
$time = time();
$query = "SELECT COUNT(*) AS notificationCount FROM storelte_notifications WHERE product_id = ? AND type = ? ";
$previousNotification = $this->db->query($query, array($product_id, $type));
if ($previousNotification[0]['notificationCount'] == 0) {
$sql = "INSERT INTO storelte_notifications (message,type,product_id,created_at) VALUES(?, ?, ?, ?)";
$this->db->query($sql, array($message, $type, $product_id, $time));
try {
if ($this->db->query($sql)) {
return true;
}else{
return false;
}
} catch (Exception $e) {
}
}else{
return true;
}
}
controller
public function add(){
$this->notification->addNotification('low stock',4228,'type');
}
$sql_prev_notification is a string that you've created here:
$sql_prev_notification = "SELECT COUNT(*) AS notificationCount FROM storelte_notifications WHERE product_id = ? AND type = ? ";
You use it to execute a query with:
$this->db->query($sql_prev_notification, array($product_id, $type));
but you haven't assigned the returned results of the query to anything.
$sql_prev_notification is still a string, so when you do this:
if ($sql_prev_notification[0]['notificationCount'] == 0) {
$sql_prev_notification[0] is referring to the first letter in the string (S), which obviously isn't an array, hence the
Illegal string offset 'notificationCount'
You probably want something more like:
$sql = "SELECT COUNT(*) AS notificationCount FROM storelte_notifications WHERE product_id = ? AND type = ? ";
$sql_prev_notification = $this->db->query($sql, array($product_id, $type));
if ($sql_prev_notification[0]['notificationCount'] == 0) {
although you should also be checking if your query actually returns anything before referring to a specific item from its results.
Don't Panic's answer (https://stackoverflow.com/a/43529550/4132627) is the right one to fix the illegal string offset, go with that.
For the error Undefined index: type, it would appear that you are passing the string "type" as the value for the variable $type. It then uses that value as a key for the $types array, but the $types array doesn't have an index "type" - its indexes are "new", "pending", and "low stock".
To fix this, you have to pass either "new", "pending", or "low stock" as the third parameter to your addNotification function:
$this->notification->addNotification('low stock',4228,'new');
//or
$this->notification->addNotification('low stock',4228,'pending');
//of
$this->notification->addNotification('low stock',4228,'low stock');
You should also check that the key passed is valid, otherwise you will continue to get this notice. In fact, passing the wrong value probably causes your code to function irregularly, in which case throwing an exception is probably a good idea:
if (isset($types[$type]) === false) {
throw new \InvalidArgumentException('Value for third parameter must be one of new, pending, or low stock.');
}
$type = $types[$type];
You can try this:
public function addNotification( $message, $product_id, $type = '' ){
$types = array( 'new' => 0, 'pending' => 1, 'low stock' => 3);
if (isset($types[$type]) === false) {
throw new \InvalidArgumentException('Value for third parameter must be one of new, pending, or low stock.');
}
$type = $types[$type];
$time = time();
$this->db->select_sum('notificationCount');
$this->db->where(['product_id' => $product_id, 'type' => $type]);
$query = $this->db->get();
$previousNotification = $query->row_array()
if ( $previousNotification['notificationCount'] == 0 ) {
$this->db->trans_start();
$this->db->insert(storelte_notifications, ['message' => $message, 'type' => $type, 'product_id' => $product_id, 'created_at' => $time]);
$this->db->trans_complete();
return $this->db->trans_status();
} else {
return true;
}
}
This question already has an answer here:
Adding multiple files with PDO
(1 answer)
Closed 7 years ago.
I have made a script to upload multiple files using a form:
<form action="upload_image.php" id="form_img" method="POST" enctype="multipart/form-data">
<div align="center">
<div class="fileUpload btn btn-primary">
<span>Carica immagini nella galleria</span>
<input type="file" name="immagini[]" multiple="multiple" id="file_img" class="upload"/>
<script>
document.getElementById("file_img").onchange = function() {
document.getElementById("form_img").submit();
};
</script>
</div>
</div>
</form>
The javascript code is supposed to submit the form when user have chosen a file and here is the php I am using to process the upload:
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
session_start();
$where = dirname(__FILE__);
include($where . "/config/db.php");
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
foreach ($_FILES as $file) {
$nome_file_temporaneo = $file["tmp_name"];
$nome_file_vero = $file["name"];
$tipo_file = $file["type"];
$not_profilo = '1';
for($i=0;$i<sizeof($tipo_file);$i++) {
$dati_file = file_get_contents($nome_file_temporaneo[$i]);
$query = "INSERT INTO ".$_SESSION['id']." (immagine,type,profilo) values (?,?,?)";
$stmt = $dbh->prepare($query);
$stmt->bindParam(1, $dati_file, PDO::PARAM_LOB);
$stmt->bindParam(2, $tipo_file[$i],PDO::PARAM_STR);
$stmt->bindParam(3, $not_profilo, PDO::PARAM_STR);
$stmt->execute();
}
}
header("location: profile_set.php");
?>
This gives me an error:
Fatal error: in C:\xampp\htdocs\tp\upload_image.php on line 24
Line 24 is the line that contains: $stmt->execute()
Any help would be appreciated.
Try binding using an array inserted into the ->execute(array()). If you want to make sure that values are what they should be, just do some validation in the foreach() loop. One last thing, you say your form does multiple uploading but you have only one input and you have it upload as soon as the input changes, so that is a tad confusing:
// I am just saving your connection to a function just to clean it up a bit
function connection()
{
include(__DIR__."/config/db.php");
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $dbh;
}
// I like to reogranize my $_FILES array so each file is in it's own array
function organize($array = false)
{
if(!is_array($array) || empty($array))
return $array;
foreach($array['name'] as $key => $value) {
if($array['error'][$key] != 0) {
$files[$key] = false;
continue;
}
$files[$key] = array(
"name" => $array['name'][$key],
"tmp_name" => $array['tmp_name'][$key],
"type" => $array['type'][$key],
"error" => $array['error'][$key],
"size" => $array['size'][$key]
);
}
return $files;
}
// This will return an array of bind values and statement values
function CompileUpload($use_name = 'immagini')
{
// If empty, do nothing
if(empty($_FILES[$use_name]))
return false;
//Reorganize array
$FILES = organize($_FILES[$use_name]);
$return = false;
foreach ($FILES as $i => $file) {
if($file["error"] !== 0)
continue;
// I would suggest just saving the name and location of
// the file(s) instead of saving them to the database.
$temp = $file["tmp_name"];
$name = $file["name"];
$type = $file["type"];
$data = file_get_contents($temp);
// Create a bind array
$bind[":".$i."name"] = $name;
$bind[":".$i."type"] = $type;
$bind[":".$i."data"] = $data;
// Create the append values for the sql statement
$bCols[$i][] = ":".$i."name";
$bCols[$i][] = ":".$i."type";
$bCols[$i][] = ":".$i."data";
// Implode and save to a master row array
$iCols[] = "(".implode(",",$bCols[$i]).")";
}
// If there is no bind array (errors in file array)
// just return false
if(empty($bind))
return false;
// assign bind
$return['bind'] = $bind;
// Implode rows
$return['cols'] = implode(",",$iCols);
// return the final data array
return $return;
}
To use:
// Make sure to include the above functions here....
// Get the uploads
$uploads = CompileUpload();
// If there are uploads and the user is logged in
if(!empty($uploads) && !empty($_SESSION['id'])) {
// Is this really correct? Do you have a table for each user?
// Compile your statement
$statement = "INSERT into `".$_SESSION['id']."` (`immagine`,`type`,`profilo`) VALUES ".$uploads['cols'];
// Get connection and prepare
// You may need to do $con = connection(); $con->prepare...etc.
// but this should work
$query = connection()->prepare($statement);
// Execute with bind values
$query->execute($uploads['bind']);
}
The sql statement would look something like this:
INSERT into `whatever` (`immagine`,`type`,`profilo`) VALUES (:0name,:0type,:0data)
Multiple uploads would be:
INSERT into `whatever` (`immagine`,`type`,`profilo`) VALUES (:0name,:0type,:0data),(:1name,:1type,:1data)
<?php
include_once '../includes/db_connect.php';
fetch_evt_values($conn, 7475, 2, 16);
function fetch_evt_values($conn, $p_frm_id, $p_evt_id, $p_usr_id) {
$p_rec_id = 0;
$l_rslt_msg = '';
$l_result = array(
'data' => array(),
'msg' => '0000'
);
$sql = 'BEGIN PHPEVT.EV_MOD.FETCH_EVT_VALUES(';
//$sql .= ':c_load_id,';
$sql .= ':c_frm_id,';
$sql .= ':c_evt_id,';
$sql .= ':c_rec_id,';
$sql .= ':c_usr_id,';
$sql .= ':c_rslt';
$sql .= '); END;';
if ($stmt = oci_parse($conn,$sql)) {
$l_results = oci_new_cursor($conn);
//oci_bind_by_name($stmt,':c_load_id',$p_load_id);
oci_bind_by_name($stmt,':c_frm_id',$p_frm_id);
oci_bind_by_name($stmt,':c_evt_id',$p_evt_id);
oci_bind_by_name($stmt,':c_rec_id',$p_rec_id);
oci_bind_by_name($stmt,':c_usr_id',$p_usr_id);
oci_bind_by_name($stmt,':c_rslt',$l_results,-1,OCI_B_CURSOR);
if(oci_execute($stmt)){ //Execute the prepared query.
oci_execute($l_results);
while($r = oci_fetch_array($l_results,OCI_ASSOC)) {
$l_evt_values = explode('|', $r['EVENT_VALUES']);
foreach($l_evt_values as $l_evt_value) {
list($l_ID, $l_value) = explode('#', $l_evt_value);
$l_values[] = array('ID' => $l_ID, 'VALUE' => $l_value);
}
$l_result['data'][] = array(
'LOAD_ID' => $r['LOAD_ID'],
'REC_ID' => $r['REC_ID'],
'TRAIT' => $l_values,
'G_MSG' => $r['G_MSG']
);
$l_rslt_msg = $r['G_MSG'];
}
} else {
//echo 'cannot get user';
$l_rslt_msg = '0005'; //PHP_MEMBER.FETCH_USER return error code
}
} else {
//echo 'connect fail';
$l_rslt_msg = '0006'; //Could not connect to database.
}
oci_close($conn);
echo json_encode($l_result);
}
?>
So on a webpage, when a user requests an event, a database call is made using this code to retrieve some values in the format :
"62#20000|65#15710|66#6|67#6|68#0|69#0|".
The PHP then breaks it apart by |, splits the ID#Value, puts everything into an array, then returns it as a JSON which is then parsed into a table. The latter works perfectly fine. But when this tries to fetch more than about 600 records or so, I get a 500 Internal Server Error, and I've figured it's something in this PHP that's handling the call.
I'm not convinced it's the database entirely, as a call for 3500 records with no further processing other than the JSON being returned is generally done in 5s or less.
Why would this code be failing at 500+ records? I've tried AJAX timeout of 0.
I'm want to retrieve all posts with a certain listId from the database, but only retrieves the last one. Below is the code, what am I doing wrong?
from listModel.php:
public function GetListElements($listId) {
$query = "SELECT le.listElemId, le.listElemName, le.listElemOrderPlace, led.listElemDesc
FROM listElement AS le
INNER JOIN listElemDesc as led
ON le.listElemId = led.listElemId
WHERE le.listId=?";
$stmt = $this->m_db->Prepare($query);
$stmt->bind_param("i", $listId);
$listElements = $this->m_db->GetListElements($stmt);
return $listElements;
}
from database.php:
public function GetListElements(\mysqli_stmt $stmt) {
if ($stmt === FALSE) {
throw new \Exception($this->mysqli->error);
}
//execute the statement
if ($stmt->execute() == FALSE) {
throw new \Exception($this->mysqli->error);
}
//Bind the $ret parameter so when we call fetch it gets its value
if ($stmt->bind_result($listElemId, $listElemName, $listElemOrderPlace, $listElemDesc) == FALSE) {
throw new \Exception($this->mysqli->error);
}
// Hämtar ids och användarnamn och lägger i arrayen.
while ($stmt->fetch()) {
$listElements = array('listElemId' => $listElemId,
'listElemName' => $listElemName,
'listElemOrderPlace' => $listElemOrderPlace,
'listElemDesc' => $listElemDesc);
}
$stmt->Close();
return $listElements; // contains only the last post in the table
}
The tables
listElement: listElemId, listElemName, listId, listElemDescId, listElemOrderPlace
listElemDesc: listElemDescId, listElemId, listElemDesc
You are overwriting $listElements variable on each iteration, to fix it you can use an array variable:
$listElements = array();
while ($stmt->fetch()) {
$listElements[] = array('listElemId' => $listElemId, //notice brackets: []
'listElemName' => $listElemName,
'listElemOrderPlace' => $listElemOrderPlace,
'listElemDesc' => $listElemDesc);