phpmysqlmysqlibindparam

Concatenate query in MySqli prepared statement PHP


I've a list of item I want to insert in my table with a concetenation of INSERT queries. I'm using MySQL and PHP.

I receive list of exercises names with POST (separated by commas), and want to save them in DB, associating them to an ID (i receive too from POST).

Instead of running several INSERT queries, I would like to prepare a unique query and just use one run, so that the entire query runs or fails.

Here is my code.

$exList= $_REQUEST['exList']; 
$routineID = $_REQUEST['routineID'];
$arrayEx = explode(',', $exList);
$countEx=count($arrayEx);
$placeholdersEx = implode(',', array_fill(0, $countEx, '?'));
$bindStrEx = str_repeat('si', $countEx);
$queryEx = str_repeat('INSERT INTO exercises_x_routines(exerciseID, routineID) VALUES(?,?);' ,$countEx);
$arrayRoutine = array_fill(0,$countEx,$routineID);

$stmt = mysqli_prepare($con, substr($queryEx, 0, -1));
mysqli_stmt_bind_param($stmt, $bindStrEx, ...$arrayEx,...$arrayRoutine));

if (!mysqli_stmt_execute($stmt))
    {
        //print mysqli_error($con);
        $output[]=array("code" => 3003, "error" => mysqli_error($con));
        print(json_encode($output));
        mysqli_close($con);
        exit;
    }

However, I don't know why but query execution is not successful and I don't get any error description from mysqli_error.


Solution

  • You can't prepare multiple queries. Instead, you could prepare the single query once and execute it multiple times:

    $exList= $_REQUEST['exList']; 
    $routineID = $_REQUEST['routineID'];
    $arrayEx = explode(',', $exList);
    
    $queryEx = 'INSERT INTO exercises_x_routines(exerciseID, routineID) VALUES(?,?)';
    $stmt = $con->prepare($queryEx);
    $stmt->bind_param('si', $ex, $routineID);
    foreach ($arrayEx as $ex) {
        if (!$stmt->execute()) {
            $output[]=array("code" => 3003, "error" => $con->error);
            print(json_encode($output));
            $con->close();
            exit;
        }
    }
    

    If you want to effectively make this a single insert with no change to the table if any insert fails, you can use a transaction:

    $con->beginTransaction();
    foreach ($arrayEx as $ex) {
        if (!$stmt->execute()) {
            $output[]=array("code" => 3003, "error" => $con->error);
            print(json_encode($output));
            $con->rollBack();
            $con->close();
            exit;
        }
    }
    $con->commit();