How could this code inserted 0 values to my database?
<?php
$params = [];
$table = "log";
$values = [
"text" => "missing socket file named ../../app/shop/socket/sectionheaderNavigationView.php",
"level" => 2,
"created_at" => 1672806059253,
"created_by" => 0
];
$queryColumn = "";
$queryValues = "";
$n = 0;
foreach($values as $x => $y) {
$queryColumn .= "__" . $x;
$queryValues .= ":" . $x;
$params[":" . $x] = $y;
$n++;
if($n < count($values)) {
$queryColumn .= ", ";
$queryValues .= ", ";
}
}
$query = "INSERT INTO $table($queryColumn) VALUES($queryValues)";
$db = new PDO("mysql:host=127.0.0.1;dbname=db_shop", "root", "");
$stmt = $db->prepare($query);
if(count($params) > 0) {
foreach($params as $x => $y) {
switch(true) {
case is_int($y): $type = PDO::PARAM_INT; break;
case is_string($y): $type = PDO::PARAM_STR; break;
case is_bool($y): $type = PDO::PARAM_BOOL; break;
default: $type = PDO::PARAM_NULL; break;
}
var_dump($x . " " . $y . " " . $type, $query);
if(!$stmt->bindParam($x, $y, $type)) {
break;
}
}
}
try {
$stmt->execute();
} catch(PDOException $e) {
echo "Exception: " . $e;
}
?>
The above code resulted
MariaDB [db_shop]> select * from log;
+-------+-----------+--------+---------+--------------+--------------+
| __key | __message | __text | __level | __created_at | __created_by |
+-------+-----------+--------+---------+--------------+--------------+
| 1 | | 0 | 0 | 0 | 0 |
| 2 | | 0 | 0 | 0 | 0 |
Isn't if even its null when checking $params shouldn't it be empty not 0 instead?
MariaDB [db_shop]> desc log;
+--------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+-------------+------+-----+---------+----------------+
| __key | int(11) | NO | PRI | NULL | auto_increment |
| __text | varchar(82) | NO | | NULL | |
| __level | int(11) | NO | | NULL | |
| __created_at | char(16) | NO | | NULL | |
| __created_by | int(11) | NO | | NULL | |
+--------------+-------------+------+-----+---------+----------------+
How could i properly handle this error, there also no exception.
Here the var_dump inside the looping checking for $params.
string(87) ":text missing socket file named ../../app/shop/socket/sectionheaderNavigationView.php 2"
string(108) "INSERT INTO log(__text, __level, __created_at, __created_by) VALUES(:text, :level, :created_at, :created_by)"
string(10) ":level 2 1"
string(108) "INSERT INTO log(__text, __level, __created_at, __created_by) VALUES(:text, :level, :created_at, :created_by)"
string(27) ":created_at 1672806059253 2"
string(108) "INSERT INTO log(__text, __level, __created_at, __created_by) VALUES(:text, :level, :created_at, :created_by)"
string(15) ":created_by 0 1"
string(108) "INSERT INTO log(__text, __level, __created_at, __created_by) VALUES(:text, :level, :created_at, :created_by)"
php -v;
PHP 8.1.10 (cli) (built: Aug 30 2022 18:05:49) (ZTS Visual C++ 2019 x64)
Thanks for any feedback
The answer to your question of why it inserted 0 is that bindParam() doesn't take the value of $y when you bind it. It takes the value of $y when you execute the query. But by then the loop has finished, so $y has a different value — the last value it had during the loop. The last value is 0, as we can see:
"created_by" => 0
You could fix this by using bindValue() instead of bindParam(). Then it would make a copy of the value of $y at the time you bind it, and use that when you eventually execute the query.
You could also fix this by simply passing an array of values to execute().
Here's how I would write it:
$queryColumns = implode(",", array_map(function($col) { return "`__$col`"; }, array_keys($values)));
$paramPlaceholders = implode(",", array_fill(0, count($values), "?");
$query = "INSERT INTO `$table`($queryColumns) VALUES($paramPlaceholders)";
$db = new PDO("mysql:host=127.0.0.1;dbname=db_shop", "root", "");
try {
$stmt = $db->prepare($query);
$stmt->execute(array_values($values));
} catch(PDOException $e) {
error_log($e);
// display a more friendly error to the client
}
There is no need to bind each value individually, and there is no need to use the PDO::PARAM_STR or other type constants when using the MySQL PDO driver. They are all treated as strings regardless of how you bind them.
Related
I have a DB where I have some tables, most of them irrelevant for this question:
Content_handler:
+-------------+-------------------+---------------------------+
| app_data_id | app_menu_entry_id | content_item_container_id |
+-------------+-------------------+---------------------------+
| 0 | 0 | NULL |
| 0 | 1 | bm9zb3Ryb3MtMC0w |
+-------------+-------------------+---------------------------+
App_menu_entry:
+-------------------+----------+---------+--------------+
| app_menu_entry_id | position | name | icon_id |
+-------------------+----------+---------+--------------+
| 0 | 0 | Nombre | asd |
| 1 | 1 | Precios | icontest.png |
+-------------------+----------+---------+--------------+
Content_item_container:
+---------------------------+--------------+
| content_item_container_id | content_name |
+---------------------------+--------------+
| bm9zb3Ryb3MtMC0w | Nosotros |
+---------------------------+--------------+
content_handler.app_menu_entry_id REFERENCES TO app_menu_entry.app_menu_entry_id
content_handler.content_item_container_id REFERENCES TO content_item_container.content_item_container_id
Now, when I want to update app_menu_entry I DELETE all records from this table (with cascade, so also deletes content from content_handler) and then I insert data with PHP from an array so I use a foreach with prepared statements like this:
$menuQuery = $connection->prepare("INSERT INTO app_menu_entry(app_menu_entry_id,position,name,icon_id) VALUES(?,?,?,?)");
$handlerQuery = $connection->prepare("INSERT INTO content_handler(app_data_id, app_menu_entry_id, content_item_container_id) VALUES(?,?,?)");
$id = 0;
if (!$menuQuery || !$handlerQuery) {
ErrorHandler::setError(ErrorHandler::DB_QUERY_ERROR, true);
throw new \Exception('Error al realizar la consulta');
}
foreach ($menu['menu_items'] as $menuItem) {
$name = $menuItem['name'];
$position = $menuItem['position'];
$icon = $menuItem['icon_id'];
$contentId = ($menuItem['content'] != null) ? base64_encode($menuItem['content'] . '-' . $userId . '-' . $appId) : null;
$menuQuery->bind_param('ssss', $id, $position, $name, $icon);
$menuQuery->execute();
$handlerQuery->bind_param('sss', $appId, $id, $contentId);
$handlerQuery->execute();
$id++;
}
The problem is that this loop insert data in app_menu_entry but not in content_handler. This can be solved adding sleep(1000); between execute() function and that make me think how can I prevent using sleep() and how this execute function works? Does it uses a thread, makes a async queue...?
Im trying to use a php function to select data from a database, Im using a "standard" php function:
function select($table, $where, $other=''){
try{
$a = array();
$w = "";
$other=" ".$other;
foreach ($where as $key => $value) {
$w .= " and " .$key. " like :".$key;
$a[":".$key] = $value;
}
$stmt = $this->bd->prepare("select * from ".$table." where 1=1 ". $w.$other);
$stmt->execute($a);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
if(count($rows)<=0){
$reponse["statut"] = "warning";
$reponse["message"] = "no data found.";
}else{
$reponse["statut"] = "Success";
$reponse["message"] = "Success";
}
$reponse["data"] = $rows;
}catch(Exception $e){
$reponse["statut"] = "Error";
$reponse["message"] = 'selection failed: ' .$e->getMessage();
$reponse["data"] = null;
}
return $reponse;
}
The function works satisfyingly I faced problems only when I aimed to use a join and selecting from two tables.
say we have these two tables:
id-cat | cat-name |
1 | aaaaaa |
2 | aaadda |
3 | saaaca |
4 | ahhaaa |
and
id-cat | sub-cat |
1 | 2 |
1 | 3 |
1 | 4 |
2 | 3 |
2 | 4 |
How to select the a category's subs name with given id-cat using MY FUNCTION ??
You just need a simple LEFT JOIN to the second table, using the column that exists in both tables.
$stmt = $this->bd->prepare("SELECT *
FROM $table
LEFT JOIN $table2 USING id-cat
WHERE 1=1 ". $w.$other);
I'm still new with PHP & MySQL. This quetion looks simple, but somehow I can't figure the recursive formulae to create tree-like hierarchy by using foreach and array correctly.
This is the table structure
CREATE TABLE IF NOT EXISTS `table` (
`id` int(2) NOT NULL,
`lecturer` varchar(50) NOT NULL,
`subject` varchar(9) NOT NULL,
`section` int(2) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=6 ;
INSERT INTO `table` (`id`, `lecturer`, `subject`, `section`) VALUES
(1, 'Prof A', 'info2222', 1),
(2, 'Prof A', 'info2222', 2),
(3, 'Prof A', 'info3333', 1),
(4, 'Prof A', 'info3333', 3),
(5, 'Prof B', 'info4444', 1);
This is sample output that I want:
=================================================
| lecturer > subject > section | count total |
=================================================
| Prof A | 4 |
| |---info2222 | 2 |
| | |---1 | 1 |
| | |---2 | 1 |
| | | |
| |---info3333 | 2 |
| |---1 | 1 |
| |---3 | 1 |
| | |
| Prof B | 1 |
| |---info4444 | 1 |
| |---1 | 1 |
=================================================
my full code (currently)
<html>
<head>
<?php
mysql_select_db('testing',mysql_connect('localhost','root',''))or die(mysql_error());
function count_recursive($array)
{
$c = 0;
foreach($array as $value)
{
if(is_array($value))
$c += count_recursive($value);
else
$c++;
return $c;
}
}
?>
</head>
<body>
<?php
$query = $pdo->query("Select * from table");
$arr = [];
while($data = $query->fetch())
{
$arr[$data['lecturer']][$data['subject']][] = $data['section'];
}
foreach($arr as $lecturer => $lvalues)
{
echo $query['lecturer'] ;
foreach($lvalues as $subject => $svalues)
{
echo $query['subject'] ;
foreach($svalues as $section)
{
echo $query['section'] ;
}
}
}
?>
</body>
</html>
Something like this should work:
$query = $pdo->query("Your select...");
$arr = [];
while($data = $query->fetch()){
$arr[$data['lecturer']][$data['subject']][] = $data['section'];
}
Afterwards you can foreach over the (3d) array:
foreach($arr as $lecturer => $lvalues){
//echo your lecturer here
foreach($lvalues as $subject => $svalues){
//echo your subject here
foreach($svalues as $section)
//echo sour section here
}
to count everything recursively you can use:
function count_recursive($array){
$c = 0;
foreach($array as $value)
if(is_array($value))
$c += count_recursive($value);
else
$c++;
return $c;
}
I get array(0){} where I should be returning rows;
<?php
require_once('auth.php');
$conn = new PDO("mysql:host=localhost;dbname=$dbname", $username, $pw);
$file = fopen('nameList1.txt','r');
$firstname = ''; $lastname = ''; $index = -1; $id = -1;
$tab = ' ';
try{
while ($line = fgets($file)) {
$index = strrpos($line, $tab);
$firstname = substr($line, 0, $index);
//var_dump($firstname);
$lastname = substr($line, $index + strlen($tab));
//var_dump($lastname);
$stmt = $conn->prepare("SELECT * FROM dbname WHERE FName = :firstname AND LName = :lastname");
$stmt->bindvalue(':firstname', $firstname);
$stmt->bindvalue(':lastname', $lastname);
$stmt->execute();
$result = $stmt->fetchAll();
var_dump($result);
}
}
catch ( Exception $e )
{
echo "Exception at $i";
}
fclose($file);
?>
********* working SQL *************
mysql> SELECT * FROM list WHERE FName='TARA' AND LName='AABA';
+----+--------------+---------------+-------+-------+-------+-----------+------------+-----------+--------+----------+-----------+-----------+-----------+-------------+-------------------+--------------------+------------+------------+----------+------------+------------+--------------+--------------+---------------------+-----------------------+-------+-------+-------+-------+----------+---------+-------------+------------------+--------------+------------+------------+
| id | StateVoterID | CountyVoterID | Title | FName | MName | LName | NameSuffix | Birthdate | Gender | RegStNum | RegStFrac | RegStName | RegStType | RegUnitType | RegStPreDirection | RegStPostDirection | RegUnitNum | RegCity | RegState | RegZipCode | CountyCode | PrecinctCode | PrecinctPart | LegislativeDistrict | CongressionalDistrict | Mail1 | Mail2 | Mail3 | Mail4 | MailCity | MailZip | MailCountry | Registrationdate | AbsenteeType | LastVoted | StatusCode |
+----+--------------+---------------+-------+-------+-------+-----------+------------+-----------+--------+----------+-----------+-----------+-----------+-------------+-------------------+--------------------+------------+------------+----------+------------+------------+--------------+--------------+---------------------+-----------------------+-------+-------+-------+-------+----------+---------+-------------+------------------+--------------+------------+------------+
| 9 | 0 | 458198 | | TARA | L | AABRA | | 2 | F | 5804 | | 1ST | PL | | | NE | 0 | MARYSVILLE | WA | 98271 | 0 | 101231 | 0 | 39 | 2 | | | | | | | | 05/05/2004 | P | 11/08/2011 | A |
*********** var_dump($result) **************
array(0) { } array(0) { } array(0) { } array(0) { } array(0) { } array(0) { } array(0) { } array(0) { } ....
Empty array returned by fetchAll means there was no data found.
This is a fact. While "working SQL" is working somewhere else.
So - you have to check your input data, database, environment, typos and such.
Sincerely,
So I wrote this method, for my chrome plugin (which does an ajax request to run this method), and when it runs, file_put_contents shows an id of what ever was inserted, but then when it gets to the insert ignore into songs, it puts in 0 for the artist_id. I have no idea why... Can someone help my find the part where I am going wrong?
<?php
public function saveLyrics($artist, $title, $lyric){
$this->db->query("insert ignore into artists (artist_name) value (:artist)", array("artist" => $artist));
$artist_id = (int)$this->db->insertID();
file_put_contents(__DIR__ . "/../process/page", "artist id: $artist_id");
//return;
if($artist_id == 0){
$artist_id = (int)$this->db->getOne("select artist_id from artists where artist_name = :artist", array("artist" => $artist));
}
if($artist_id == 0){
return false;
}
$this->db->query("insert ignore into songs (artist_id, song_name) values (:aid, :title)", array("aid" => $artist_id, "title" => $title));
$song_id = (int)$this->db->insertID();
if($song_id == 0){
$song_id = (int)$this->db->getOne("select song_id from songs where artist_id = aid and song_name = :title", array("aid" => $artist_id, "title" => $title));
}
}
PDO Wrapper:
<?php
/**
* #property PDO $pdo Description
* #property PDOStatement $sql Description
*/
class DB{
protected $sql = null;
protected $pdo = null;
public function connect(){
$this->pdo = new PDO("mysql:dbname=envne;host=xxx", "xxx", "xxx");
}
public function query($query, $params = array()){
if($this->pdo === null){
$this->connect();
}
$this->sql = $this->pdo->prepare($query);
foreach($params as $key => $value){
$this->sql->bindParam($key, $value);
}
$this->sql->execute();
if(!$this->sql)
return false;
return true;
}
public function insertID(){
return (int)$this->pdo->lastInsertId();
}
public function getAll($query, $params = array()){
$this->query($query, $params);
return $this->sql->fetchAll();
}
public function getOne($query, $params = array()){
$this->query($query, $params);
return $this->sql->fetchColumn();
}
}
Artists:
mysql> describe artists;
+-------------+------------------+------+-----+-------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+------------------+------+-----+-------------------+----------------+
| artist_id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| artist_name | char(50) | YES | UNI | NULL | |
| add_date | timestamp | YES | | CURRENT_TIMESTAMP | |
+-------------+------------------+------+-----+-------------------+----------------+
3 rows in set (0.00 sec)
Songs:
mysql> describe songs;
+------------+------------------+------+-----+-------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+------------------+------+-----+-------------------+----------------+
| song_id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| artist_id | int(11) unsigned | YES | MUL | NULL | |
| album_id | int(11) | YES | MUL | NULL | |
| song_name | char(50) | YES | | NULL | |
| track_id | int(11) | YES | | NULL | |
| date_added | timestamp | NO | | CURRENT_TIMESTAMP | |
+------------+------------------+------+-----+-------------------+----------------+
6 rows in set (0.01 sec)
I just decided to put the Id directly into the query, and that works.
$artist_id = (int)$this->db->insertID();
$this->db->query("insert ignore into songs (artist_id, song_name) values ($artist_id, :title)", array("title" => $title));
Another way that works is using a question mark instead
$artist_id = (int)$this->db->insertID();
$this->db->query("insert ignore into songs (artist_id, song_name) values (?, ?)", array($artist_id, $title));
I just had the same problem : new inserted items get an id of 0 even though the ID field is set to AUTO_INCRIMENT.
The solution I found is very similar to yours. Using your code this is what we get :
$this->db->query("insert ignore into songs (artist_id, song_name) values (LAST_INSERT_ID(), :title)", array("title" => $title));
As you can see, I replaced $artist_id = (int)$this->db->insertID(); and $artist_id with SQL function LAST_INSERT_ID().
I hope this can help someone someday :)
You're placeholders are incorrectly defined: (You're missing the colons)
I would do something like this:
public function saveLyrics($artist, $title, $lyric){
$this->db->query("insert ignore into artists (artist_name) value (:artist)", array(":artist" => $artist));
$artist_id = (int)$this->db->insertID();
file_put_contents(__DIR__ . "/../process/page", "artist id: $artist_id");
//return;
if($artist_id == 0){
$artist_id = (int)$this->db->getOne("select artist_id from artists where artist_name = :artist", array(":artist" => $artist));
return false;
}
$this->db->query("insert ignore into songs (artist_id, song_name) values (:aid, :title)", array(":aid"=>$artist_id, ":title"=>$title));
$song_id = (int)$this->db->insertID();
if($song_id == 0){
$song_id = (int)$this->db->getOne("select song_id from songs where artist_id = :aid and song_name = :title", array(":aid"=>$artist_id, ":title"=>$title));
}
}
Taking a look at your PDO-wrapper you have this code:
if(!$this->sql)
return false;
Because of this you would never notice an actual error. I guess the error is about the placeholder in this case.
(If $this->db->query("insert ignore into songs (... fails $song_id would just be false if there is an error when executing the query).
Use exceptions instead and catch the errors, that would be better.
I also noticed that:
$song_id = (int)$this->db->insertID();
would cast the value twice, first in above code and then in the actual function insertID() in the PDO-Wrapper. Maybe this is an issue also to consider.