Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I've read every question/tutorial I could find on inserting things via foreach & PDO, but am still left scratching my head.

I am basically trying to do a batch insert/update of a table based on who's paid dues in my business fraternity (hence the 'brother'/'bro).

The fields are all being retrieved from POST, field 1 is the dues amount ($amount), field 2 is number of people who have paid ($num_paid) and field 3 is an array of user ids for those who have paid ($paid) which is created via check-boxes created by a foreach loop in another file.

The best way I could think to check to make sure things were entered correctly was to compare count($paid) to $num_paid, and proceed only if they matched. I'm open to suggestions on doing that better, but it seems to work the way it is.

I'm trying to loop through the array from field 3 ($paid) using 'foreach' to insert new rows in the table as a 'credit' for each person who has paid, but for some reason it only inserts in the first loop, then somehow inserts empty rows for the rest.

The table is set up with columns: user_id(manually input), transaction_id(AI), date, credit, debit, info.

Using: "INSERT INTO finance VALUES(?,NULL,now(),?,?,?)"; If I insert 5 users, it will insert the first with transaction_id x, then run up the auto-increment count 4 more, but insert nothing. So the next time I try inserting, the transaction_id is 5 more than the first.

e.g. -- Try 1 inserts a row with transaction_id 43. Try 2 inserts one row, with transaction_id 48, but there's nothing in between them.

I've tried passing the array into the function, then looping through it with 'foreach' that way, and also tried using a 'foreach' loop which calls the function and passes it a string each time to insert. Both seem to work the same. I'm pretty lost, because it seems like if the auto incrementing column is counting up, it should be inserting data?

I think this is all the relevant code...(PHP tags were added to make it more readable.)

Processing of form data which calls the function

<?php
if (isset($_POST['amount']) && 
        isset($_POST['num_paid']) && isset($_POST['paid'])) {           

            $amount = saniString($_POST['amount']);
            $num_paid = saniString($_POST['num_paid']);
            $paid = $_POST['paid']; #this is an array of user id's for 
                                       #who has paid.   

            echo "<br /><br />";
            var_dump($paid);
            echo "<br /><br />";
            var_dump($num_paid);
            echo "<br /><br />";
            var_dump($amount);
            echo "<br /><br />";

            if (count($paid) != $num_paid) {
                echo "<p><b>You did not check the correct number of
                    brothers. Please check your entries and try again.</b></p>";
            }
            elseif (count($paid) == $num_paid) {
                $match = true;
                echo "<p><b>Congrats. The numbers match.</b></p>";
            }

            if ($match === true) {

                if ($amount > 0) {
                    $credit = $amount;
                    $debit = 0;
                }
                elseif ($amount < 0) {
                    $credit = 0;
                    $debit = $amount;
                }
                else { $credit = 0;
                       $debit = 0;
                }           
                foreach ($paid as $dirtyId) {

                    $result = updateFin($dirtyId,$broid,$credit,$debit,$dbh);

                var_dump($result);

                }                       
            }           
        }
?>

Function to prepare/bind/insert

<?php
function updateFin($dirtyId,$broid,$credit,$debit,$dbh) {

    $sql = "INSERT INTO finance VALUES(?,NULL,now(),?,?,?)";

    $query = $dbh->prepare($sql);

    #foreach ($paid as $dirtyId) { // please notice this is commented out

        $bro_paid = saniString($dirtyId);

        $info = "Dues from bro-ID: " . $bro_paid;

        $query->bindValue(1, $broid);
        $query->bindValue(2, $credit);
        $query->bindValue(3, $debit);
        $query->bindValue(4, $info);

        try {
            $result = $query->execute();                
        }
        catch (PDOException $e) {
            echo "<br />Insert of $credit, with info $info failed.";
            echo $e->getMessage();
            $result = false;
        }

        return $result;
    #}
}
?> 

var_dumping the values I'm trying to pass in gives me what I'd expect - $amount is a string (50.00), $num_paid is also a string (5), and $paid is an array full of the IDs I'm trying to insert...

The 'saniString' function uses strip_tags, htmlentities, and stripslashes, if that's relevant...

If I var_dump($result) it returns bool(true),bool(false),bool(false),bool(false),bool(false). Which is what I'd expect given my results, except I have no idea why the queries are failing and I'm not getting any error messages from PDO?

share|improve this question
 
where is array $paid ? –  Gopal Jan 2 at 6:29
 
$paid is coming from $_POST['paid'] which var_dumps as a numerically indexed array. –  LouisK Jan 2 at 6:30
1  
What is the table definition for finance? (Especially with regard to primary key/unique keys) –  towr Jan 2 at 6:42
2  
try $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); before calling updateFin() this will give you error if any. –  Dharmesh Patel Jan 2 at 6:45
 
No unique keys, but trans_id is the primary key and is auto incremented. info is indexed, user_id - smallint, unsigned NOT NULL, no key. trans_id - smallint, unsigned NOT NULL primary key auto_increment, date - datetime, unique. credit - smallint NOT NULL, debit - smallint NOT NULL, info - varchar INDEXED And I think that may have solved my problem...I suspect the datetime being unique but being set with now() was probably causing the issue...but now I'm not sure how to remove that to test it? –  LouisK Jan 2 at 6:47
show 5 more comments

2 Answers

up vote 1 down vote accepted

The issue is caused by the unique on date. The subsequent inserts have a date the same down to the second and so they're ignore (while the autoincrement is incremented). So

alter table finance drop index date

[EDITED] The setting $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); should have caused the duplicate inserts to throw an exception with SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry (It does when I test it) But this attribute isn't set by default when the PDO-object is created.

When in doubt, you can always fall back to checking the error info, even if there isn't an exception. $err = $query->errorInfo();, for a duplicate entry you'll get $err[1] == 1062

share|improve this answer
 
"you can't rely on exceptions" - surely you have a proof for such a statement? –  Your Common Sense Jan 2 at 7:09
 
May be you just have to tell PDO to throw exceptions, instead of writing such a screenful of code after every query execution? –  Your Common Sense Jan 2 at 7:22
 
Possibly. But LouisK said he set them when he created the PDO object. (But that part of code wasn't provided.) I'll check (after work). –  towr Jan 2 at 7:28
 
What does object creation to do with exceptions? Of course you have to create an object in order to make PDO work. –  Your Common Sense Jan 2 at 7:31
 
@YourCommonSense I've double checked and you're right. I wonder if LouisK just assumed that attribute was set when a new PDO object was created, or if something else was going on. Anyway, thanks. –  towr Jan 2 at 8:40
show 1 more comment

In this line if (count($paid) != $num_paid) { you are not defining $match and if this statement gets true than this line will raise an error of undefined variable $match if ($match === true) {

Replace this code block

if (count($paid) != $num_paid) {
    echo "<p><b>You did not check the correct number of
        brothers. Please check your entries and try again.</b></p>";
    $match = false;
}
elseif (count($paid) == $num_paid) {
    $match = true;
    echo "<p><b>Congrats. The numbers match.</b></p>";
}
share|improve this answer
 
Good catch. Unfortunately that didn't fix it :( Still only inserting the first row, and returning bool(false) for the rest. –  LouisK Jan 2 at 6:40
 
$broid and $dbh are also undefined. Where you have defined them? –  Khawer Zeshan Jan 2 at 6:44
 
$broid is defined when the page first loads, if the user is logged in. $dbh is the PDO object which is created when the connection is set. –  LouisK Jan 2 at 6:53
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.