So I have the following piece of code so that when I delete a row from the database I also delete the files associated with it, the code runs smoothly and I'm actually able to delete the rows from the database but somehow I'm unable to delete the files from the server directory, note that row "photo_filename" contains a name such as "photo.png" or so, also calling _ DIR _ from the file returns a path like this "...\Desktop\project/procedures", I'm not even getting any warnings I tried echoing a a string if unlink was successful and some other string if not successful, but the weird thing is I don't get any output, it is as if the loop doesn't even run, can someone point me towards the right direction on what I'm missing right here. Thank you
try {
$db->beginTransaction(); // Begin transaction
$query = "DELETE FROM properties "
. " WHERE property_id = :property_id"; // Delete requested property.
$stmt = $db->prepare($query);
$stmt->bindParam(":property_id", $property["property_id"], PDO::PARAM_INT);
$stmt->execute();
$query = "SELECT * FROM photos "
. " WHERE property_id = :property_id";
$stmt = $db->prepare($query);
$stmt->bindParam(":property_id", $property["property_id"], PDO::PARAM_INT);
$stmt->execute();
foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $photo) {
try {
unlink(__DIR__ . "/../img/" . $photo["image_filename"])
} catch (Exception $e) {
throw $e;
}
}
$query = "DELETE FROM photos "
. " WHERE property_id = :property_id";
$stmt = $db->prepare($query);
$stmt->bindParam(":property_id", $property["property_id"], PDO::PARAM_INT);
$stmt->execute();
$db->commit();
} catch (Exception $e) { // If there is a problem
$db->rollBack(); //If there was a problem undo the whole attempt to insert
$session->getFlashBag()->add("error", "Hubo un problema" . $e->getMessage()); // Display a message
redirect("/show.php?id=".$property_id); // And redirect
exit;
}
I just realized my dumb mistake, so "properties" table was linked to "photos" table so when I began the transaction and deleted the property from the table the photos associated with it were also automatically deleted, so when I selected and looped through the photos table to get the files there weren't any, leaving this here just in case someone runs into something similar, thank you guys for responding.
Related
I am having a problem where a prepared MySQL stored procedure call runs fine in a transaction, and I see the expected results from the stored procedure, but the changes do not appear to be saving to the actual database.
The PHP side of things looks like this:
$options = array();
$db = new PDO("mysql:host=localhost;dbname=mydb", "myuser", "mypass", $options);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
// ..... .... ... .. .
$response["error"] = true;
if ($db->beginTransaction() == true)
{
try
{
$stmt = $db->prepare("call Layout_Row_Add(:pageid, :position);");
// $jason->page_id
$stmt->bindValue(':pageid', (int)$jason->page_id, PDO::PARAM_INT);
// $jason->position
$stmt->bindValue(':position', (int)$jason->position, PDO::PARAM_INT);
$stmt->execute();
$response["dbg1"] = $jason->page_id;
$response["dbg2"] = $jason->position;
$response["intrans1"] = $db->inTransaction();
$row = $stmt->fetch();
$db->commit();
$response["intrans2"] = $db->inTransaction();
$response["new_row_id"] = $row["NewRowId"];
$response["error"] = false;
}
catch (PDOException $e)
{
$db->rollBack();
$response["errortext"] = "PDO exception: " . $e->getMessage();
}
catch (Exception $exc)
{
$db->rollBack();
$response["errortext"] = "Exception: " . $e->getMessage();
}
}
else
{
$response["errortext"] = "Couldn't start transaction";
}
The $response variable gets encoded into JSON and sent back to the browser, which gets this:
error false
dbg1 1
dbg2 3
intrans1 true
intrans2 false
new_row_id 21
Everything looks exactly like it should, new_row_id is at its expected value meaning the autoincrement field ticked up, and the debug fields and transaction info is as expected.
However, doing a select * in MySQL Workbench doesn't return any of these rows that were supposedly added by the procedure. Running the procedure itself in MySQL Workbench works fine, as in, the commit actually sticks. Here's the procedure itself:
CREATE DEFINER=`myuser`#`myhost` PROCEDURE `Layout_Row_Add`(PageId int, Position int)
BEGIN
declare NewRowId int unsigned default 0;
update pages_layout_rows set ordinal = ordinal + 1 where page_id = PageId and ordinal >= Position;
insert into pages_layout_rows (page_id, ordinal) values (PageId, Position);
set NewRowId = last_insert_id();
select NewRowId;
END
The table is set to InnoDB, so transaction support should be available. I don't really know what to try next.
Found it - it looks like if you don't consume all the resultsets, the transaction appears to get rolled back in the end anyway. A stored procedure call adds an empty resultset as the last resultset, so that's what's happening.
// ...
$row = $stmt->fetch();
// let's consume all resultsets
while($stmt->nextRowset() && $stmt->columnCount());
$sol->db->commit();
// ...
I have some queries that have run perfectly up until now, but I wanted to wrap them in a transaction to decrease the likelihood of data corruption. After running the code, everything APPEARS to work (i.e. no exception is thrown), but the query is never committed to the database. I've looked at other questions on S.O. but I haven't found any that apply to my case.
Here's my code:
db::$pdo->beginTransaction(); // accesses PDO object method
try {
$sql = "INSERT INTO expenses SET date=:date, amount=:amount, accountId=:account; ";
$sql .= "UPDATE accounts SET balance = balance - :amount WHERE id = :account";
$s = db::$pdo->prepare($sql);
$s->bindValue(':date', $date);
$s->bindValue(':amount', $amount);
$s->bindValue(':account', $account);
$s->execute();
db::$pdo->commit();
echo 'success';
}
catch (PDOException $e) {
db::$pdo->rollback();
echo '<p>Failed: ' . $e->getMessage() . '</p>';
}
When I run the code, it outputs the success message, but like I said, nothing gets committed to the database.
Since it's relevant, I should also note that my PDO error mode is set to ERRMODE_EXCEPTION, so I don't think that's what's causing the problem. I'm running a MySQL database (InnoDB)
Any thoughts?
I am not completely sure how running multiple queries in a single query statement works (if it works) because I typically do transactions by running each query separately in a prepare/execute statement. The transaction will still queue up all the changes until commit/rollback as expected. According to this SO answer it appears to work, however the example is not binding values and so that might be another issue.
I would suggest just splitting up the queries into multiple prepare/bind/execute statements and it should work as expected.
The success message will allways be displayed because it's just there to echo.. if you want to show succes only after the execute you should use if statement.. for the not committing part it could be many things.. are you sure all values $date, $amount, $account are being supplied ? you can also try starting with the simple insert and if that works do the update..
$sql = 'INSERT INTO expenses (date, amount, account)
VALUES(:date, :amount, :account)';
$stmt = $this->pdo->prepare($sql);
$stmt->bindValue(':date', $);
$stmt->bindValue(':amount', $amount);
$stmt->bindValue(':account', $account);
if($stmt->execute()) {
db::$pdo->commit();
echo 'success';
}
not sure about the db::$pdo->commit(); part because I use some kind of OOP way, but hope this might help.
I think I had similar problem in one of the projects, please add another catch block just after the one you have so the code will look like:
catch (PDOException $e) {
db::$pdo->rollback();
echo '<p>Failed: ' . $e->getMessage() . '</p>';
}
catch (Exception $exc) {
db::$pdo->rollback();
echo '<p>Failed: ' . $exc->getMessage() . '</p>';
}
It will allow you to catch Exceptions of class other than PDOException, so you could ensure more.
UPDATE:
Please consider splitting two queries into 2 PDO statements llike this:
$sql = "INSERT INTO expenses SET date=:date, amount=:amount, accountId=:account; ";
$s = db::$pdo->prepare($sql);
$s->bindValue(':date', $date);
$s->bindValue(':amount', $amount);
$s->bindValue(':account', $account);
$s->execute();
$sql2 = "UPDATE accounts SET balance = balance - :amount WHERE id = :account;";
$s2 = db::$pdo->prepare($sql2);
$s2->bindValue(':amount', $amount);
$s2->bindValue(':account', $account);
$s2->execute();
I use this approach in my entire project with success so I hope it might help.
Don't use db::$pdo->commit();
Use
$sql = "INSERT INTO expenses SET date=:date, amount=:amount, accountId=:account;";
$sql .= "UPDATE accounts SET balance = balance - :amount WHERE id = :account;COMMIT;"; // change here
kind of silly ;(
So I have a piece of code that will check if you have followed a user or not. And basically let you follow them if you haven't. So here it is
if($_SESSION['loggedIn'] == true){
$result = $con->prepare("SELECT * FROM followers WHERE follow_from = :username AND follow_to = :post_id");
$result->bindParam(':username', $username);
$result->bindParam(':post_id', $follower);
$result->execute();
$reprint = $result->fetchAll(PDO::FETCH_ASSOC);
}
print_r($reprint);
if($reprint < 1){
$stmt = $con->prepare("INSERT INTO followers (follow_from, follow_to) VALUES (:ff, :ft)");
$stmt->bindValue(':ff', $follower, PDO::PARAM_STR);
$stmt->bindValue(':ft', $username, PDO::PARAM_STR);
$stmt->execute();
}
else{
echo 'Error';
exit();
}
//Display follower
$stmt1 = $con->prepare("SELECT COUNT(*) AS count FROM followers WHERE follow_to = :username");
$stmt1->bindValue(':username', $username, PDO::PARAM_STR);
$stmt1->execute();
$likes = $stmt1->fetchAll(PDO::FETCH_ASSOC);
print_r($likes);
So when I run it once. I get the else statement echoed. My question is why does this happen? In the database I have no record, so I'd expect it to go in once. I get no errors at all. loggedIn is true. And variables are being passed through successfully.
Any ideas?
You're misusing the result you get from fetchAll(). It's an associative array, not a scalar value. It could, as you've probably guessed, be empty.
But, more significantly than that, your code has a potential race condition. What happens if two different sessions are trying to set this same followers row? (Admittedly, in a small system that is unlikely, but in a large system it might happen).
What you actually do is just the INSERT operation. If your followers row has a unique key on the (follow_from,follow_to) columns, then, if that row is already there you'll get a 'Duplicate entry' error on the INSERT. Otherwise it will just happen. You can just ignore the 'Duplicate entry' error, because all you want is for that row to make it into that table.
So your code would go like this:
$stmt = $con->prepare("INSERT
INTO followers (follow_from, follow_to)
VALUES (:ff, :ft)");
$stmt->bindValue(':ff', $follower, PDO::PARAM_STR);
$stmt->bindValue(':ft', $username, PDO::PARAM_STR);
$result = $stmt->execute();
if ($result) {
/* this follow pair was successfully added */
} else {
/* MySQL may return the error 'Duplicate entry' */
if (false == stripos($stmt->errorCode,'Duplicate')){
echo 'Something failed in the insert: ' . '$stmt->errorCode';
}
else {
/* this follow pair was already in your table */
}
}
Pro tip: Don't use SELECT * in software; it can mess up query optimization; it often sends more data than you need from the server to your program, and it makes your program less resilient if your change your table definitions.
Pro tip: If you must count rows matching a particular WHERE statement, use COUNT() rather than fetching the rows and counting them in the client. What if you get a million rows?
You'd want to use count($reprint) other that a direct comparison. $reprint is an array, not a number
if(count($reprint) < 1)
{
$stmt = $con->prepare("INSERT INTO followers (follow_from, follow_to) VALUES (:ff, :ft)");
$stmt->bindValue(':ff', $follower, PDO::PARAM_STR);
$stmt->bindValue(':ft', $username, PDO::PARAM_STR);
$stmt->execute();
}
else
{
echo 'Error';
exit();
}
PDOStatement::fetchAll
PDOStatement::fetchAll — Returns an array containing all of the result set rows
If you check the size of the array then you would actually know if something happened.
Using proper error handling can tell you if something's failing deep down:
try
{
...
}
catch (PDOException $e)
{
echo $e->getMessage();
}
You will need to enable PDO error-displaying:
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
If checking the size of the array doesn't do it and you get no errors then it's simply some logic error.
Most likely the logic error is that
$reprint = $result->fetchAll(PDO::FETCH_ASSOC);
doesn't get executed (wrapping the error handling around that should tell you why), so
$reprint = $result->fetchAll(PDO::FETCH_ASSOC);
isn't given a proper value, meaning you'll always hit the else statement.
Edit
Your original problem was that "So when I run it once. I get the else statement echoed. [...] In the database I have no record" but now you're saying "It adds the record, but doesn't limit it to me one".
Can you be more clear about the actual, current, problem?
I am getting an error when updating a database using PDO.
I am new to PDO so maybe the problem is a small one and I just don't understand.
Funny thing about the error, the command works fine and the database does actually get updated.
But it still returns an error back at me.
Code:
try {
$stmt = $pdo->prepare("UPDATE $page SET $section = :new_content WHERE $section = '$old_content'");
$stmt->execute(array(
'new_content' => $new_content
));
$result = $stmt->fetchAll();
echo "Database updated!";
}
catch(PDOException $e) {
echo 'ERROR UPDATING CONTENT: ' . $e->getMessage();
}
Error:
ERROR UPDATING CONTENT: SQLSTATE[HY000]: General error
I literally have no idea where the problem could be because its very vaque and I haven't been able to find anyone with the same problem.
You do not use fetchAll(),as in
$result = $stmt->fetchAll();
with update or insert queries. Removing this statement should rectify the problem.
Just to note, another possible reason for this error is if you make a second database call with the variable $stmt inside of an existing parent $stmt loop.
$stmt = $conn->query($sql);
while ($row = $stmt->fetch()) { //second use of $stmt here inside loop
Good afternoon everyone, I have a doubt.
I have a SELECT with PDO. More must be done within another SELECT WHILE to get data for that select, giving it more
Error (Error: There Is Already an active transaction).
If anyone can help me be grateful.
Example code.
try{
$this->conex->beginTransaction();
$query = $this->conex->prepare("SELECT idUser FROM usuario WHERE id = :id ORDER BY data DESC LIMIT $pagin, $paginaF");
$query->bindParam(":id", $ID, PDO::PARAM_INT, 20);
$query->execute();
while ($lista = $query->fetch()){
$idUser = $lista['idUser'];
echo "<div id='avatar'>"box::avatar($idUser)."</div>"
}
//Here he works out of WHILE. Inside it does not work...
echo box::avatar($idUser);
$this->conex->commit();
}catch (PDOException $ex) {
echo "Erro: " . $ex->getMessage();
}
public function avatar($idUser){
$idUser = (int) $idUser;
$query = $this->conex->prepare("SELECT avatar FROM login WHERE id = :id LIMIT 1");
$query->bindParam(":id", $idUser, PDO::PARAM_INT, 20);
$query->execute();
while ($avatar = $query->fetch()){
$avatar = $avatar['avatar'];
}
return $avatar;
}
You have to close the cursor before calling for a new transaction.
Besides, may I ask why are you creating transactions since you're only doing simple *select*s?
Nested Transactions are not possible (as mentioned by the error message). In the first line, you start one transaction. Within the loop, you call avatar(), that starts another transaction, which fails, because there is already one.
However, for SELECT-Queries you dont need transactions at all. Just omit it.
You may also think about JOIN, so you can handle all in just one Query (and then transactions are really useless).
May not be an answer to the problem for now but this line :
echo "<div id="avatar">".$box = box::avatar($id)."</div>"
is full of errors.
" aren't escaped.
no ; at the end.
You're trying to give a value to a variable inside an echo. This'll throw an error.
If you don't use the avatar() function elsewhere, you can do :
try{
$this->conex->beginTransaction();
$query = $this->conex->prepare("SELECT usuario.id as id, login.avatar as avatar FROM usuario, login WHERE usuario.id = :id and usuario.id ORDER BY data DESC LIMIT $pagin, $paginaF");
$query->bindParam(":id", $ID, PDO::PARAM_INT, 20);
$query->execute();
while ($lista = $query->fetch()){
$id = $lista['id'];
$avatar = $lista['avatar']
echo '<div id="avatar">'.$avatar.'</div>';
}
}catch (PDOException $ex) {
echo "Erro: " . $ex->getMessage();
}