Skip to content
Advertisement

Update MySQL Database With Data From Multiple Input Elements Inside A While Loop, And A Single Submit Button Outside of The While Loop

I have a page that fetches images from a database after an initial upload, that allows the user to add titles and tags to the images.

I have set the while loop to output the individual images inside one form. Each image has two related input fields. What I would like to do is have it so when the form is submitted all of the information is updated in the database in one go (even if image details from the relevant input fields remain blank/are not filled in).

To achieve this I thought I could have the form’s submit button outside of the while loop. This isn’t working as foreseen though. It basically updates one image at a time, each time I press submit (it updates the last image in the loop).

How do I get it so it processes all of the information for all of the images inside the while loop in one go, on submit? If I place the submit button inside the loop I get a button for every image, which I don’t want.

Note: the <img/> source path I’ve just hardcoded below to save adding the different variables that achieve this, thus hopefully keeping the code simpler.

Fetch data from database and output HMTL form

<?php isset($_REQUEST['username']) ? $username = $_REQUEST['username'] : header("Location: login.php"); ?>

<form method="post" enctype="multipart/form-data">

    <?php

        if (isset($_SESSION['logged_in'])) {
            $user_id = $_SESSION['logged_in'];
        }

        $stmt = $connection->prepare("SELECT * FROM lj_imageposts WHERE user_id = :user_id");

        $stmt->execute([
            ':user_id' => $user_id
        ]); 

        while ($row = $stmt->fetch()) {

            $db_image_filename = htmlspecialchars($row['filename']);

    ?>

    <div class="upload-details-component">                
        <div class="form-row">
            <img src="/project/images/image.jpg">
        </div>
        <div class="edit-zone">
            <div class="form-row">
                <label for="upload-details-title">Image Title</label>
                <input id="upload-details-title" type="text" name="image-title">
            </div>
            <div class="form-row">
                <label for="upload-details-tags">Comma Separated Image Tags</label>
                <textarea id="upload-details-tags" type="text" name="image-tags"></textarea>
            </div>
            <div class="form-row">
                <input type="hidden" name="username" value="<?php echo $username; ?>">
                <input type="hidden" name="image-filename" value="<?php echo $db_image_filename; ?>">
            </div>
        </div>
    </div>

    <?php } ?>

    <button type="submit" name="upload-submit">COMPLETE UPLOAD</button>
</form>

Update Database On Form submission

<?php 

if(isset($_POST['upload-submit'])) {

    $image_title = $_POST['image-title'];
    $image_tags = $_POST['image-tags'];
    $form_filename = $_POST['image-filename']; // value attribute from hidden form element

        try {

            $sql = "UPDATE lj_imageposts SET
            image_title = :image_title,
            image_tags = :image_tags

            WHERE filename = :filename";
                
            $stmt = $connection->prepare($sql);
    
            $stmt->execute([
                ':image_title' => $image_title,
                ':image_tags' => $image_tags,
                ':filename' => $form_filename
            ]);
            
            // This is the URL to this actual page (basically refreshes the page)
            header("Location: upload-details.php?username={$username}");

        } catch (PDOException $e) {
            echo "Error: " . $e->getMessage();
        }

        // give values an empty string to avoid an error being thrown before form submission if empty
        $image_title = $image_tags = "";

}

?>

Advertisement

Answer

The problem with your current approach is that bcs each set of inputs uses the same name, they overwrite each other. Say you have 3 records, 3 sets of form inputs on your front end. If you enter for image-title values of one, two, and three, on the back-end you’ll only ever see three.

You can verify this yourself: print_r($_POST);.

The solution is to send the data as arrays, and you can do this by simply using [] in your input names:

<?php while ($row = $stmt->fetch()) { ?>
    <input type="text" name="image-title[]">
    <textarea type="text" name="image-tags[]"></textarea>
    <input type="hidden" name="image-filename[]" value="<?php echo $db_image_filename; ?>">
<?php } ?>
<button type="submit" name="upload-submit">COMPLETE UPLOAD</button>

Now you’ll get arrays of input on the back end, like (again, use print_r($_POST); to see for yourself):

[image-title] => Array
    (
        [0] => First Title
        [1] => 2nd Title
    )

[image-tags] => Array
    (
        [0] => foo
        [1] => bar
    )

[image-filename] => Array
    (
        [0] => filename
        [1] => another-filename
    )

And so you can loop through them, processing them one at a time:

$sql = "UPDATE lj_imageposts SET
    image_title = :image_title,
    image_tags = :image_tags
    WHERE filename = :filename";
$stmt = $connection->prepare($sql);

foreach ($_POST['image-title'] as $key => $value) {
    $stmt->execute([
        ':image_title' => $_POST['image-title'][$key],
        ':image_tags'  => $_POST['image-tags'][$key],
        ':filename'    => $_POST['image-filename'][$key]
    ]);
}

Note that using the filename as the unique identifier is not ideal – that’s what IDs are for. There is potentially the risk of non-unique filenames, but also your DB queries will be much more efficient for queries like (assuming your table has a primary key id) UPDATE ... WHERE id=:id. You can do that by replacing your hidden file input with a hidden ID input:

<input type="hidden" name="id[]" value="<?php echo $row->id; ?>">

And then of course on the back end update your query:

WHERE id = :id
// ...
':id'    => $_POST['id'][$key]

Even better, you can use the id in the input name, to explicitly identify the set of values:

<input type="text" name="image-title[<?php echo $row->id; ?>]">
<textarea type="text" name="image-tags[<?php echo $row->id; ?>]"></textarea>

In this case you don’t even need to pass the id (or image-filename) as a separate, hidden input – you already have all the IDs in the input names. On the back end you could then do:

$sql = "UPDATE lj_imageposts SET
    image_title = :image_title,
    image_tags = :image_tags
    WHERE id = :id";
$stmt = $connection->prepare($sql);

foreach ($_POST['image-title'] as $id => $value) {
    $stmt->execute([
        ':image_title' => $_POST['image-title'][$id],
        ':image_tags'  => $_POST['image-tags'][$id],
        ':id'          => $id
    ]);
}

As you can imagine this is a common problem, and there are many other solutions here to reference:

PHP docs reference:

User contributions licensed under: CC BY-SA
5 People found this is helpful
Advertisement