Prepared statements from security viewpoint
This looks pretty good.  I'm glad to see the improvement.

You inexplicably call $connect-&gt;bind_params() using variables that have not yet been set.

The main issue I see now is $QuizAdministrationid = $maxId + 1,  which is prone to a race condition.  If two users submit the form simultaneously, it's possible that both pages could see the same result from SELECT MAX(id) … and generate the same next $QuizAdministrationid.

The solution is to ask the database to generate the next ID.  Every SQL database supports this in a slightly different way.  For MySQL, you should declare the id column of QuizAdministration to be AUTO_INCREMENT.  Then, leave id column unspecified when you do the INSERT.  MySQL will automatically fill in the id column with the next available number.  To find out what number MySQL picked for you, use $connect-&gt;insert_id.

To make this work for you, you'll need to insert a row into QuizAdministration first, then use the id of the new row to insert the PlayerAnswers.

$stmt2 = $connect-&gt;prepare("INSERT INTO QuizAdministration (`quizNumber`, `cookie`, `ip`, `score`) VALUES (?, ?, ?, ?)");
$quizNumber = 1;
$playerCookie = session_id();
$playerIP = getUserIp();
$playersPercent = 90;
$stmt2-&gt;bind_param('issi', $quizNumber, $playerCookie, $playerIP, $playersPercent);
$stmt2-&gt;execute();
$stmt2-&gt;close();

$quizAdministrationid = $connect-&gt;insert_id;

$stmt3 = $connect-&gt;prepare("INSERT INTO PlayerAnswers (`QuizAdministrationId`, `questionNumber`, `playerAnswer`) VALUES (?, ?, ?)");
for ($yty = 1; $yty &lt;= 20; $yty += 1) {
    $questnum = $yty;
    $questansw = $_POST['qora'.$yty];
    $stmt3-&gt;bind_param('iii', $QuizAdministrationid, $questnum, $questansw);
    $stmt3-&gt;execute();
}
$stmt3-&gt;close();