Во-первых, у вас дважды будет одинаковый l oop. Сначала как foreach, а затем как for. Поскольку вам нужны ключи numeri c из этого странного типа массива $ _FILES, лучше всего использовать только для l oop. Эти двойные циклы уже настолько беспорядочные, что могут вызвать неожиданные проблемы, если, например, в одном из файлов есть проблема.
Но ваша основная проблема в том, что вы в основном проверяете только одно изображение, а затем загружаете его . Если процесс проверки или успешность пройдены, в конце отображается exit();
. Убивает не только l oop, но и весь скрипт. Вы не позволяете второму изображению l oop продолжать работу, так как первое его убивает .. либо в случае успеха, либо при ошибке.
Решением было бы дождаться, пока цикл завершится sh (добавление кода после скобки циклов) и поместив туда код, связанный с успехом. Если внутри циклов обнаруживается ошибка, скрипт никогда не зайдет так далеко.
I have no idea, how you are actually linking the images to service, but I tried to clean up your code and make the order correct. I also did my best at explaining why and where. Hopefully, you understand the problem better from this or even better, find better options to optimise your code:
// TESTING: Lets see what is inside post values:
echo '$_POST values'; print_r($_POST); echo '
';
// TESTING: Lets see what is inside the files values:
echo '
$_FILES values'; print_r($_FILES); echo '
';
// Above is for testing only..
// Probably better place to load important configs:
require 'config.php';
// Since these are the conditions for uploads, then they are global:
// no need for them to be inside the loop:
$allowed = array('jpg', 'png', 'jpeg');
// Maximum allowed filesize:
$max_allowed_file_size = 15000000; // which is 15mb
// We detect the submit buttons trigger name:
if (isset($_POST['add-service'])) {
// Do the escape thingy:
// NOTE: You should be using some mysqli class for database handling:
$shop_name = mysqli_real_escape_string($conn, $_POST['shop_name']);
$service_cat = mysqli_real_escape_string($conn, $_POST['service_cat']);
$service_name = mysqli_real_escape_string($conn, $_POST['service_name']);
$service_desc = mysqli_real_escape_string($conn, $_POST['service_desc']);
$service_price = mysqli_real_escape_string($conn, $_POST['service_price']);
$service_type = mysqli_real_escape_string($conn, $_POST['service_type']);
$service_images = $_FILES['service_images'];
// Lets deal with the errors before going forward with the rest of the script:
// You don't need elseif here, because your callback is to redirect and exit anyways..
if (empty($shop_name) || empty($service_cat) || empty($service_name) || empty($service_desc) || empty($service_price) || empty($service_type)) {
header('Location: ../services.php?error=emptyFields');
exit();
}
if (!preg_match('/^[a-zA-Z0-9]*$/', $shop_name) && !preg_match('/^[a-zA-Z0-9\s]*$/', $service_name) && !preg_match('/^[a-zA-Z0-9\s \. \-]*$/', $service_desc) && !preg_match('/^[0-9\.]*$/', $service_price) && !preg_match('/^[a-zA-Z0-9\s \.]*$/', $service_type)) {
header('Location: ../services.php?error=invalidInputs');
exit();
}
if (!preg_match('/^[a-zA-Z0-9]*$/', $shop_name)) {
header('Location: ../services.php?error=invalidShopName');
exit();
}
if (!preg_match('/^[a-zA-Z0-9\s]*$/', $service_name)) {
header('Location: ../services.php?error=invalidserviceName');
exit();
}
if (!preg_match('/^[a-zA-Z0-9\s \. \-]*$/', $service_desc)) {
header('Location: ../services.php?error=invalidDescription');
exit();
}
if (!preg_match('/^[0-9\.]*$/', $service_price)) {
header('Location: ../services.php?error=invalidPrice');
exit();
}
if (!preg_match('/^[a-zA-Z0-9\s \.]*$/', $service_type)) {
header('Location: ../services.php?error=invalidStyle');
exit();
}
// Nothing happened above, so that means the form validation should be fine and we can go forward with the images:
// So as in your script, we count the images:
$a = count($_FILES['service_images']['name']);
// Now we do a "numeric loop", not an array loop, which is foreach:
for ($i = 0; $i < $a; $i++) {
// Since we have the key as numeric now, we can do what you did before, but without the foreach loop:
$file_name = $_FILES['service_images']['name'][$i];
$file_type = $_FILES['service_images']['type'][$i];
$file_tempName = $_FILES['service_images']['tmp_name'][$i];
$file_error = $_FILES['service_images']['error'][$i];
$file_size = $_FILES['service_images']['size'][$i];
// Get the file extension:
// NOTE: This is not good, as you should really check the mime type of the file, not the extension.
$fileActualExt = strtolower(end(explode('.', $file_name)));
// TESTING: We check print out the data to make sure, that all looks fine:
echo 'File with the key: ' . $i .' -- $file_name: ' . $file_name . '; $file_type: ' . $file_type . '; $file_tempName: ' . $file_tempName . '; $file_error: ' . $file_error . '; $file_size: ' . $file_size . '
';
// Instead of making the code ugly, lets deal with errors, by killing the script before
// NOTE: This is not good approach, you should be using Exceptions:
//
https://www.php.net/manual/en/language.exceptions.php
// Check if the file extension is NOT in the allowed array
if (!in_array($fileActualExt, $allowed)) {
// Redirect:
header('Location: ../services.php?error=invalidImageType');
// Kill the script:
exit('invalidImageType');
}
// Check if the file had an error:
if ($file_error) {
// Redirect:
header('Location: ../services.php?error=invalidImage');
// Kill the script:
exit('invalidImage');
}
// Check if the image bytes are BIGGER > then max allowed file size variable:
if ($file_size > $max_allowed_file_size) {
// Redirect:
header('Location: ../services.php?error=invalidSize');
// Kill the script:
exit();
}
// At this stage, hopefully, there has not been any errors above and we can deal with file freely:
// Make new file name:
$newFileName = preg_replace('/\s+/', '', $service_name) . $i . '.' . $fileActualExt;
// echo $newFileName . "
";
// Set the new destination:
$fileDestination = '../../services/' . $newFileName;
// Lets move the file already.
// NOTE: Make sure that you have some bash code from server side, that deletes outdated / old temp files, so they dont take space:
move_uploaded_file($file_tempName = $_FILES["service_images"]["tmp_name"][$i], $fileDestination);
// Insert the image to database:
// NOTE: Im not sure about your specific code, but just this is there location for that:
$sql_images = "INSERT INTO service_images (shop_name, service_name) VALUES ('$shop_name', '$service_name')";
$result = mysqli_query($conn, $sql_images);
// PROBLEM: This is where you originally had the success message redirect and exit.
// This means, you KILL the script and there for the loop.
// But you have to understand, that you have two images or more, so the loop has to continue freely,
// and you can do this sort of stuff at after the loop!
//
// header("Location: ../services.php?success");
// exit();
}
// If nothing happened above, then the image uploads went trough nicely and we can deal with success messages or adding the service itself:
// I have not used mysqli stmpt before, so I have no idea what is going on in this area..:
// .. but this the locatin to deal with the services as this is the parent and the children are above.
$sql = "INSERT INTO services (shop_name, service_cat, service_name, service_desc, service_price, service_type) VALUES (?,?,?,?,?,?)";
$stmt = mysqli_stmt_init($conn);
// I don't think you need this at all, but whatever:
// Shouldnt this be above
if (!mysqli_stmt_prepare($stmt, $sql)) {
// Redirect:
header("Location: ../services.php?error=SaveError");
// Kill the script:
exit();
}
// This is adding the service I assume, it has to be outside the loop, as single submit = single service. But images are multiple.
mysqli_stmt_bind_param($stmt, 'ssssss', $shop_name, $service_cat, $service_name, $service_desc, $service_price, $service_type);
mysqli_stmt_execute($stmt);
// This is where you can have the success redirect and exit, as this is after the loop:
header("Location: ../services.php?success");
exit();
}
NOTES:
- You should be using Исключения для обработки ошибок.
- Узнайте разницу между foreach и для циклов.
- Расширения файлов можно обмануть, вместо этого проверьте тип файла mime
- Массив разрешенных типов файлов внутри l oop не очень умен, так как вы будете его использовать больше чем один раз за все l oop циклов. Лучше всего оставить его в верхней части скрипта, чтобы его было легче настроить в будущем. То же самое и с переменной размера файла.
- Было бы гораздо разумнее определять типы файлов и размеры через javascript еще до того, как они попадут на ваш сервер. Таким образом, вы в основном сохраняете проблемы с пространством в папке временных файлов и пропускной способностью.
- Я не понимаю, где вы на самом деле используете $ result из mysql. Или где вы связываете изображения из таблицы service_images с фактическим сервисом.
- Используйте
<input type="file" name="service_images[]" multiple accept=".jpg, .png, .jpeg">
( multiple accept = ". Jpg, .png, .jpeg" ) в форме чтобы пользователь не мог выбирать другие расширения. Вы также можете использовать значение «images» для всех изображений.