From ef2d0469439ae4b69a74886272356faed588789d Mon Sep 17 00:00:00 2001 From: Notnochka Date: Fri, 7 Feb 2025 23:52:28 +0700 Subject: [PATCH 1/3] =?UTF-8?q?=D0=B2=D1=85=D0=BE=D0=B4=20=D0=BD=D0=B0=20?= =?UTF-8?q?=D1=81=D0=B0=D0=B9=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- functions/database.php | 306 ----------------------------------- functions/db.php | 339 +++++++++++++++++++++++++++++++++++++++ functions/functions.php | 77 --------- functions/validation.php | 188 ---------------------- functions/validators.php | 310 +++++++++++++++++++++++++++++++++++ 5 files changed, 649 insertions(+), 571 deletions(-) delete mode 100644 functions/database.php create mode 100644 functions/db.php delete mode 100644 functions/functions.php delete mode 100644 functions/validation.php create mode 100644 functions/validators.php diff --git a/functions/database.php b/functions/database.php deleted file mode 100644 index bb11147..0000000 --- a/functions/database.php +++ /dev/null @@ -1,306 +0,0 @@ - NOW() - GROUP BY l.id, l.title, l.initial_price, l.img, c.designation, b.amount - ORDER BY l.date_create DESC;'; - $result = mysqli_query($con, $sql); - - if (!$result) { - $error = mysqli_error($con); - print('Ошибка SQL:' . $error); - return []; - } - - return mysqli_fetch_all($result, MYSQLI_ASSOC); -} - -/** - * получение списка категорий - * @param mysqli $con - * @return array - */ - -function getCategories(mysqli $con): array -{ - $sql = 'SELECT * FROM categories;'; - $result = mysqli_query($con, $sql); - - if (!$result) { - $error = mysqli_error($con); - print('Ошибка SQL:' . $error); - return []; - } - - return mysqli_fetch_all($result, MYSQLI_ASSOC); -} - -/** - * получение лота по id - * @param mysqli $con - * @param int $id - * @return array|false|null - */ - -function getLotById(mysqli $con, int $id): array|false|null -{ - $sql = "SELECT l.*, - c.name AS category, - r.amount AS last_rate, - l.rate_step - FROM lots l - JOIN categories c ON l.category_id = c.id - LEFT JOIN rates r ON l.id = r.lot_id - WHERE l.id = $id - ORDER BY r.created_at DESC - LIMIT 1;"; - - $result = mysqli_query($con, $sql); - - if (!$result) { - $error = mysqli_error($con); - error_log("Ошибка SQL: $error"); - return []; - } - - return mysqli_fetch_assoc($result); -} - -/** - * загрузка файла с валидацией - * @param array $file данные о файле из формы - * @param string $uploadDir папка, куда надо сохранить файл - * @return string|null имя файла при успешной загрузке, null в противном случае - */ -function processFileUpload(array $file, string $uploadDir): ?string -{ - $allowedMimeTypes = ['image/jpeg', 'image/png']; - - if (empty($file['name']) || !is_uploaded_file($file['tmp_name'])) { - return null; - } - - $fileTmpPath = $file['tmp_name']; - $fileMimeType = mime_content_type($fileTmpPath); - - if (!in_array($fileMimeType, $allowedMimeTypes)) { - return null; - } - - $fileExtension = $fileMimeType === 'image/jpeg' ? '.jpg' : '.png'; - $fileName = uniqid() . $fileExtension; - $destinationPath = $uploadDir . '/' . $fileName; - - if (!move_uploaded_file($fileTmpPath, $destinationPath)) { - return null; - } - - return $fileName; -} - -/** - * создает подготовленное выражение на основе готового SQL запроса и переданных данных - * @param $link mysqli ресурс соединения - * @param $sql string SQL запрос с плейсхолдерами вместо значений - * @param array $data данные для вставки на место плейсхолдеров - * @return mysqli_stmt Подготовленное выражение - */ -function dbGetPrepareStmt($link, $sql, $data = []) -{ - $stmt = mysqli_prepare($link, $sql); - - if ($stmt === false) { - $errorMsg = 'Не удалось инициализировать подготовленное выражение: ' . mysqli_error($link); - die($errorMsg); - } - - if ($data) { - $types = ''; - $stmt_data = []; - - foreach ($data as $value) { - $type = 's'; - - if (is_int($value)) { - $type = 'i'; - } - else if (is_string($value)) { - $type = 's'; - } - else if (is_double($value)) { - $type = 'd'; - } - - if ($type) { - $types .= $type; - $stmt_data[] = $value; - } - } - - $values = array_merge([$stmt, $types], $stmt_data); - - $func = 'mysqli_stmt_bind_param'; - $func(...$values); - - if (mysqli_errno($link) > 0) { - $errorMsg = 'Не удалось связать подготовленное выражение с параметрами: ' . mysqli_error($link); - die($errorMsg); - } - } - - return $stmt; -} - -/** - * добавление нового лота в базу данных - * @param array $lotData данные из формы - * @param mysqli $con ресурс соединения - * @return array массив успех|id нового лота|ошибка - */ - -function addLot(array $lotData, mysqli $con): array -{ - $response = [ - 'success' => false, - 'lotId' => null, - 'error' => null - ]; - - $sql = 'INSERT INTO lots (created_at, title, category_id, description, image_url, start_price, rate_step, author_id, ended_at) - VALUES (NOW(), ?, ?, ?, ?, ?, ?, 1, ?)'; - - $stmt = dbGetPrepareStmt($con, $sql, $lotData); - - if (!mysqli_stmt_execute($stmt)) { - $response['error'] = "Ошибка выполнения запроса: " . mysqli_error($con); - return $response; - } - - $response['success'] = true; - $response['lotId'] = mysqli_insert_id($con); - - return $response; -} - -/** - * обработка формы добавления лота - * @param array $form данные из формы - * @param array $file данные о загруженных файлах - * @param mysqli $db соединение с базой данных - * @param array $categories список категорий (для валидации) - * @return array массив с результатом обработки ['success' => bool, 'content' => string, 'errors' => array] - */ - -function addLotForm(array $form, array $file, mysqli $db, array $categories): array -{ - $errors = validateAddLot($form, $db); - - $fileName = null; - - if (!isset($errors['file'])) { - $fileName = processFileUpload($file['lot-img'], 'uploads'); - - if ($fileName === null) { - $errors['file'] = "Ошибка при загрузке изображения."; - } - } - - if (empty($errors)) { - $newLotData = [ - $form['lot-name'], // title - (int)$form['category'], // category_id - $form['message'], // description - 'uploads/' . $fileName, // image - (float)$form['lot-rate'], // start_price - (int)$form['lot-step'], // rate_step - $form['lot-date'], // ended_at - ]; - - $result = addLot($newLotData, $db); - - if ($result['success']) { - return [ - 'success' => true, - 'redirect' => 'lot.php?id=' . $result['lotId'] - ]; - } else { - $errors['database'] = $result['error']; - } - } - - return [ - 'success' => false, - 'content' => includeTemplate('add.php', [ - 'lotData' => $form, - 'categories' => $categories, - 'errors' => $errors, - ]), - 'errors' => $errors - ]; -} - -/** - * проверка запроса на метод POST - * @param array $form данные из формы - * @param array $file данные файла - * @param mysqli $db соединение с базой данных - * @param array $categories - * @return array массив с результатами: - * [если запрос был POST, то возвращает массив с ключами: - * 'success' (bool): Успех операции. - * 'redirect' (string): URL, на который нужно выполнить редирект в случае успешной операции. - * 'content' (string): HTML-контент, который будет выведен на странице. - * если запрос не был POST, возвращается шаблон страницы с формой добавления лота.] - */ - -function request(array $form, array $file, mysqli $db, array $categories): array -{ - if ($_SERVER['REQUEST_METHOD'] === 'POST') { - $result = addLotForm($form, $file, $db, $categories); - - if ($result['success']) { - header('Location: ' . $result['redirect']); - exit; - } - - return [ - 'content' => $result['content'] - ]; - } - - return [ - 'content' => includeTemplate('add.php', [ - 'categories' => $categories, - ]) - ]; -} diff --git a/functions/db.php b/functions/db.php new file mode 100644 index 0000000..582cf90 --- /dev/null +++ b/functions/db.php @@ -0,0 +1,339 @@ + NOW() + GROUP BY l.id, l.title, l.initial_price, l.img, l.date_create, l.date_end, c.id, c.designation + ORDER BY l.date_end, l.date_create DESC;"; + + $result = mysqli_query($con, $sql); + } else { + $sql = "SELECT l.id, l.title, l.initial_price, l.img, l.date_create, l.date_end, c.id as category_id, c.designation AS category + COALESCE(MAX(r.amount), l.start_price) AS current_price + FROM lots l + JOIN categories c ON c.id = l.category_id + LEFT JOIN rates b ON b.lot_id = l.id + WHERE l.ended_at > NOW() AND l.category_id = ? + GROUP BY l.id, l.title, l.initial_price, l.img, l.date_create, l.date_end, c.id, c.designation + ORDER BY l.date_end, l.date_create DESC;"; + + $stmt = mysqli_prepare($con, $sql); + mysqli_stmt_bind_param($stmt, "i", $categoryId); + mysqli_stmt_execute($stmt); + $result = mysqli_stmt_get_result($stmt); + } + + if (!$result) { + $error = mysqli_error($con); + error_log("SQL Error: $error"); + return []; + } + + return mysqli_fetch_all($result, MYSQLI_ASSOC); +} + +/** + * получение списка категорий + * @param mysqli $con + * @return array + */ + +function getCategories(mysqli $con): array +{ + $sql = "SELECT * FROM categories;"; + $result = mysqli_query($con, $sql); + + if (!$result) { + $error = mysqli_error($con); + error_log("SQL Error: $error"); + return []; + } + + return mysqli_fetch_all($result, MYSQLI_ASSOC); +} + +/** + * добавление нового лота в базу данных + * @param array $lotData + * @param mysqli $con + * @return array + */ + +function addLot(array $lotData, mysqli $con): array +{ + $response = [ + 'success' => false, + 'lotId' => null, + 'error' => null + ]; + + $sql = 'INSERT INTO lots (sign_up_date, title, category_id, description, img, initial_price, bet_step, author_id, date_end) + VALUES (NOW(), ?, ?, ?, ?, ?, ?, ?, ?)'; + + $stmt = dbGetPrepareStmt($con, $sql, $lotData); + + if (!mysqli_stmt_execute($stmt)) { + $response['error'] = "Ошибка выполнения запроса: " . mysqli_error($con); + return $response; + } + + $response['success'] = true; + $response['lotId'] = mysqli_insert_id($con); + + return $response; +} + +/** + * получение лота по id + * @param mysqli $con + * @param int $id + * @return array|false|null + */ + +function getLotById(mysqli $con, int $id): array|false|null +{ + if ($id < 0 || $id == null || $id == '') { + $error = mysqli_error($con); + error_log("SQL Error: $error"); + return []; + } + + $sql = "SELECT l.*, + c.designation AS category, + r.amount AS last_rate, + l.bet_step + FROM lots l + JOIN categories c ON l.category_id = c.id + LEFT JOIN rates r ON l.id = r.lot_id + WHERE l.id = $id + ORDER BY r.sign_up_date DESC + LIMIT 1;"; + + $result = mysqli_query($con, $sql); + + if (!$result) { + $error = mysqli_error($con); + error_log("SQL Error: $error"); + return []; + } + + return mysqli_fetch_assoc($result); +} + +/** + * добавление нового пользователя в базу данных + * @param array $formData + * @param mysqli $db + * @return bool + */ + +function addUser(array $formData, mysqli $db): bool +{ + $passwordHash = password_hash($formData['password'], PASSWORD_DEFAULT); + $sql = "INSERT INTO users (email, designation, password, contacts) VALUES (?, ?, ?, ?)"; + $stmt = dbGetPrepareStmt($db, $sql, [ + $formData['email'], + $formData['designation'], + $passwordHash, + $formData['contacts'] + ]); + + return mysqli_stmt_execute($stmt); +} + +/** + * создаёт подготовленное выражение на основе готового SQL запроса и переданных данных + * @param $link mysqli + * @param $sql string + * @param array $data + * @return mysqli_stmt + */ + +function dbGetPrepareStmt(mysqli $link, string $sql, array $data = []): mysqli_stmt +{ + $stmt = mysqli_prepare($link, $sql); + + if ($stmt === false) { + $errorMsg = 'Не удалось инициализировать подготовленное выражение: ' . mysqli_error($link); + die($errorMsg); + } + + if ($data) { + $types = ''; + $stmt_data = []; + + foreach ($data as $value) { + $type = 's'; + + if (is_int($value)) { + $type = 'i'; + } else { + if (is_string($value)) { + $type = 's'; + } else { + if (is_double($value)) { + $type = 'd'; + } + } + } + + if ($type) { + $types .= $type; + $stmt_data[] = $value; + } + } + + $values = array_merge([$stmt, $types], $stmt_data); + + $func = 'mysqli_stmt_bind_param'; + $func(...$values); + + if (mysqli_errno($link) > 0) { + $errorMsg = 'Не удалось связать подготовленное выражение с параметрами: ' . mysqli_error($link); + die($errorMsg); + } + } + + return $stmt; +} + +/** + * обработка формы добавления лота + * @param array $postData + * @param array $fileData + * @param mysqli $db + * @param array $categories + * @return array + */ + +function addLotForm(array $postData, array $fileData, mysqli $db, array $categories, int $userId): array +{ + $errors = validateAddLotForm($postData, $db); + + $fileName = null; + + if (!isset($errors['file'])) { + $fileName = processFileUpload($fileData['lot-img'], 'uploads'); + + if ($fileName === null) { + $errors['file'] = "Ошибка при загрузке изображения."; + } + } + + if (empty($errors)) { + $newLotData = [ + $postData['title'], // title + (int)$postData['category'], // category_id + $postData['description'], // description + 'uploads/' . $fileName, // img + (float)$postData['initial-price'], // initial_price + (int)$postData['bet-step'], // bet_step + $userId, // author_id + $postData['date-end'], // date_end + ]; + + $result = addLot($newLotData, $db); + + if ($result['success']) { + return [ + 'success' => true, + 'redirect' => 'lot.php?id=' . $result['lotId'] + ]; + } else { + $errors['database'] = $result['error']; + } + } + + return [ + 'success' => false, + 'content' => includeTemplate('add.php', [ + 'lotData' => $postData, + 'categories' => $categories, + 'errors' => $errors, + ]), + 'errors' => $errors + ]; +} + +/** + * загрузка файла с валидацией по MIME-типу + * @param array $file + * @param string $uploadDir + * @return string|null + */ + +function processFileUpload(array $file, string $uploadDir): ?string +{ + $allowedMimeTypes = ['image/jpeg', 'image/png']; + + if (empty($file['designation']) || !is_uploaded_file($file['tmp_name'])) { + return null; + } + + $fileTmpPath = $file['tmp_name']; + $fileMimeType = mime_content_type($fileTmpPath); + + if (!in_array($fileMimeType, $allowedMimeTypes)) { + return null; + } + + $fileExtension = $fileMimeType === 'image/jpeg' ? '.jpg' : '.png'; + $fileName = uniqid() . $fileExtension; + $destinationPath = $uploadDir . '/' . $fileName; + + if (!move_uploaded_file($fileTmpPath, $destinationPath)) { + return null; + } + + return $fileName; +} diff --git a/functions/functions.php b/functions/functions.php deleted file mode 100644 index 852100c..0000000 --- a/functions/functions.php +++ /dev/null @@ -1,77 +0,0 @@ -= 1000) { - $formattedAmount = number_format($amount, 0, '', ' '); - } else { - $formattedAmount = (string)$amount; - } - - return $formattedAmount . ' ₽'; -} - -/** - * подсчитывает время до окончания показа лота - * @param string $expiringDate - * @return array - */ - -function getTimeRemaining(string $expiringDate): array -{ - $timeDifference = strtotime($expiringDate) - time(); - - if ($timeDifference <= 0) { - return [0, 0]; - } - - $hours = floor($timeDifference / 3600); - $minutes = floor(($timeDifference % 3600) / 60); - - return [$hours, $minutes]; -} - -/** - * подключает шаблон, передает туда данные и возвращает итоговый HTML контент - * @param string $name путь к файлу шаблона относительно папки templates - * @param array $data ассоциативный массив с данными для шаблона - * @return string итоговый HTML - */ - -function includeTemplate($name, array $data = []): string -{ - $name = 'templates/' . $name; - $result = ''; - - if (!is_readable($name)) { - return $result; - } - - ob_start(); - extract($data); - require $name; - - $result = ob_get_clean(); - - return $result; -} - -/** - * показывает страницу с ошибками - * @param $content - * @param $error - * @return void - */ - -function error(&$content, $error): void -{ - $content = includeTemplate('error.php', ['error' => $error]); -} diff --git a/functions/validation.php b/functions/validation.php deleted file mode 100644 index ad1f59f..0000000 --- a/functions/validation.php +++ /dev/null @@ -1,188 +0,0 @@ - 255) { - return "Название лота слишком длинное."; - } - return null; -} - -/** - * валидация формы добавления лота - * @param array $form данные полученные из формы - * @param mysqli $db ресурс соединения - * @return array массив с ошибками - */ -function validateAddLot(array $form, mysqli $db): array -{ - $errorMessages = [ - 'name' => 'Введите наименование лота', - 'category' => 'Выберите категорию', - 'message' => 'Напишите описание лота', - 'img' => 'Загрузите изображение', - 'initial-price' => 'Введите начальную цену', - 'bet-step' => 'Введите шаг ставки', - 'date-end' => 'Введите дату завершения торгов' - ]; - - $rules = [ - 'name' => function ($value) { - return validateLotName($value); - }, - 'initial-price' => function ($value) { - return validatePositiveFloat($value); - }, - 'bet-step' => function ($value) { - return validatePositiveInt($value); - }, - 'lot-date' => function ($value) { - return validateDate($value); - } - ]; - - $required = ['name', 'category', 'message', 'img', 'initial-price', 'bet-step', 'date-end']; - $errors = []; - - foreach ($required as $field) { - if (empty($form[$field]) && $field !== 'img') { - $errors[$field] = $errorMessages[$field]; - } - } - - foreach ($rules as $field => $rule) { - if (!empty($form[$field]) && $rule($form[$field])) { - $errors[$field] = $rule($form[$field]); - } - } - - if (empty($form['category']) || $form['category'] === 'Выберите категорию') { - $errors['category'] = "Выберите категорию из списка"; - } else { - $categoryId = (int)$_POST['category']; - - $categoryExistsQuery = "SELECT id FROM categories WHERE id = ?"; - $stmt = dbGetPrepareStmt($db, $categoryExistsQuery, [$categoryId]); - mysqli_stmt_execute($stmt); - $result = mysqli_stmt_get_result($stmt); - - if (mysqli_num_rows($result) === 0) { - $errors['category'] = "Выбранная категория не существует"; - } - } - - return $errors; -} - -/** - * Возвращает корректную форму множественного числа - * Ограничения: только для целых чисел - * - * Пример использования: - * $remaining_minutes = 5; - * echo "Я поставил таймер на {$remaining_minutes} " . - * get_noun_plural_form( - * $remaining_minutes, - * 'минута', - * 'минуты', - * 'минут' - * ); - * Результат: "Я поставил таймер на 5 минут" - * - * @param int $number Число, по которому вычисляем форму множественного числа - * @param string $one Форма единственного числа: яблоко, час, минута - * @param string $two Форма множественного числа для 2, 3, 4: яблока, часа, минуты - * @param string $many Форма множественного числа для остальных чисел - * - * @return string Рассчитанная форма множественнго числа - */ -function getNounPluralForm (int $number, string $one, string $two, string $many): string -{ - $number = (int) $number; - $mod10 = $number % 10; - $mod100 = $number % 100; - - switch (true) { - case ($mod100 >= 11 && $mod100 <= 20): - return $many; - - case ($mod10 > 5): - return $many; - - case ($mod10 === 1): - return $one; - - case ($mod10 >= 2 && $mod10 <= 4): - return $two; - - default: - return $many; - } -} diff --git a/functions/validators.php b/functions/validators.php new file mode 100644 index 0000000..8c1a97b --- /dev/null +++ b/functions/validators.php @@ -0,0 +1,310 @@ + ['email' => 'Пользователь с этим email не найден.']]; + } + + if (!password_verify($password, $user['password'])) { + return ['errors' => ['password' => 'Неверный пароль.']]; + } + + return ['success' => true, 'user' => $user]; +} + +/** валидация формы авторизации + * @param array $form + * @return array + */ + +function validateLoginForm(array $form): array { + $errors = []; + $required = ['email', 'password']; + + foreach ($required as $field) { + if (empty(trim($form[$field] ?? ''))) { + $errors[$field] = 'Поле обязательно для заполнения.'; + } + } + + return $errors; +} + +/** + * проверка авторизации пользователя + * @param mysqli $db + * @return int + */ + +function isUserAuthenticated(mysqli $db): int { + if (!isset($_SESSION['user'])) { + return 0; + } + + $userId = $_SESSION['user']['id']; + $query = "SELECT id FROM users WHERE id = ?"; + $stmt = dbGetPrepareStmt($db, $query, [$userId]); + mysqli_stmt_execute($stmt); + $result = mysqli_stmt_get_result($stmt); + + if (mysqli_num_rows($result) === 0) { + unset($_SESSION['user']); + return 0; + } + + return 1; +} + +/** + * валидация данных формы регистрации + * @param array $formData + * @return array + */ + +function validateSignUpForm(array $formData): array +{ + $errors = []; + + if (empty($formData['email'])) { + $errors['email'] = 'Введите e-mail'; + } elseif (($emailLengthError = validateEmailLength($formData['email'])) !== null) { + $errors['email'] = $emailLengthError; + } elseif (!filter_var($formData['email'], FILTER_VALIDATE_EMAIL)) { + $errors['email'] = 'Введите корректный e-mail.'; + } + if (empty($formData['password'])) { + $errors['password'] = 'Введите пароль.'; + } + if (empty($formData['designation'])) { + $errors['designation'] = 'Введите имя.'; + } + if (empty($formData['contacts'])) { + $errors['contacts'] = 'Укажите способы связи с Вами.'; + } + + return $errors; +} + +/** + * проверка уникальности e-mail + * @param string $email + * @param mysqli $db + * @return bool + */ + +function isEmailUnique(string $email, mysqli $db): bool +{ + $query = "SELECT id FROM users WHERE email = ?"; + $stmt = dbGetPrepareStmt($db, $query, [$email]); + mysqli_stmt_execute($stmt); + $result = mysqli_stmt_get_result($stmt); + + return mysqli_num_rows($result) === 0; +} + +/** + * валидация длины email + * @param string $name + * @return string|null + */ + +function validateEmailLength(string $name): ?string +{ + if (mb_strlen($name) > 255) { + return "Email слишком длинный."; + } + return null; +} + +/** + * валидация длины имени лота + * @param string $name + * @return string|null + */ + +function validateLotName(string $name): ?string +{ + if (mb_strlen($name) > 255) { + return "Имя лота слишком длинное."; + } + return null; +} + +/** + * проверка на положительное число + * @param mixed $value + * @return string|null + */ + +function validatePositiveFloat (mixed $value): ?string +{ + if (!is_numeric($value)) { + return "Значение должно быть числом."; + } elseif ($value <= 0) { + return "Число должно быть больше нуля."; + } + + return null; +} + +/** + * проверка на целое положительное число + * @param mixed $value + * @return string|null + */ + +function validatePositiveInt (mixed $value): ?string { + if (!is_numeric($value)) { + return "Шаг ставки должен быть целым числом больше 0."; + } elseif ($value <= 0) { + return "Число должно быть больше нуля."; + } + + return null; +} + +/** + * валидация даты + * @param string $value + * @return string|null + */ + +function validateDate (string $value): ?string +{ + if (!isDateValid($value)) { + return "Введите дату в формате 'ГГГГ-ММ-ДД'."; + } + + $dateNow = date("Y-m-d"); + $timeDiff = strtotime($value) - strtotime($dateNow); + + if ($timeDiff < 24*60*60) { + return "Укажите дату минимум через 24 часа."; + } + + return null; +} + +/** + * проверка переданной дату на соответствие формату + * @param string $date + * @return bool + */ + +function isDateValid(string $date): bool +{ + $format_to_check = 'Y-m-d'; + $dateTimeObj = date_create_from_format($format_to_check, $date); + + return $dateTimeObj !== false; +} + +/** + * валидация формы добавления лота + * @param array $postData + * @param mysqli $db + * @return array + */ + +function validateAddLotForm(array $postData, mysqli $db): array +{ + $errorMessages = [ + 'title' => 'Введите наименование лота.', + 'category' => 'Выберите категорию.', + 'description' => 'Введите описание лота.', + 'img' => 'Загрузите изображение.', + 'initial-price' => 'Введите начальную цену.', + 'bet-step' => 'Введите шаг ставки.', + 'date-end' => 'Введите дату завершения торгов.' + ]; + + $rules = [ + 'title' => function ($value) { + return validateLotName($value); + }, + 'bet-step' => function ($value) { + return validatePositiveFloat($value); + }, + 'initial-price' => function ($value) { + return validatePositiveInt($value); + }, + 'date-end' => function ($value) { + return validateDate($value); + } + ]; + + $required = ['title', 'category', 'description', 'img', 'initial-price', 'bet-step', 'date-end']; + $errors = []; + + foreach ($required as $field) { + if (empty($postData[$field]) && $field !== 'lot-img') { + $errors[$field] = $errorMessages[$field]; + } + } + + foreach ($rules as $field => $rule) { + if (!empty($postData[$field]) && $rule($postData[$field])) { + $errors[$field] = $rule($postData[$field]); + } + } + + if (empty($postData['category']) || $postData['category'] === 'Выберите категорию') { + $errors['category'] = "Выберите категорию из списка."; + } else { + $categoryId = (int)$_POST['category']; + + $categoryExistsQuery = "SELECT id FROM categories WHERE id = ?"; + $stmt = dbGetPrepareStmt($db, $categoryExistsQuery, [$categoryId]); + mysqli_stmt_execute($stmt); + $result = mysqli_stmt_get_result($stmt); + + if (mysqli_num_rows($result) === 0) { + $errors['category'] = "Выбранная категория не существует."; + } + } + + return $errors; +} + +/** + * возвращает корректную форму множественного числа + * @param int $number + * @param string $one + * @param string $two + * @param string $many + * @return string + */ + +function getNounPluralForm(int $number, string $one, string $two, string $many): string +{ + $number = (int)$number; + $mod10 = $number % 10; + $mod100 = $number % 100; + + switch (true) { + case ($mod100 >= 11 && $mod100 <= 20): + return $many; + + case ($mod10 > 5): + return $many; + + case ($mod10 === 1): + return $one; + + case ($mod10 >= 2 && $mod10 <= 4): + return $two; + + default: + return $many; + } +} From 7049fe2f85caa5bdd386445738511e9f70476f31 Mon Sep 17 00:00:00 2001 From: Notnochka Date: Sat, 8 Feb 2025 00:11:31 +0700 Subject: [PATCH 2/3] =?UTF-8?q?=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- functions/db.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/functions/db.php b/functions/db.php index 582cf90..b1dc1c5 100644 --- a/functions/db.php +++ b/functions/db.php @@ -51,7 +51,7 @@ function getLots(mysqli $con, ?int $categoryId = null): array COALESCE(MAX(b.amount), l.initial_price) AS current_price FROM lots l JOIN categories c ON c.id = l.category_id - LEFT JOIN rates b ON b.lot_id = l.id + LEFT JOIN bets b ON b.lot_id = l.id WHERE l.ended_at > NOW() GROUP BY l.id, l.title, l.initial_price, l.img, l.date_create, l.date_end, c.id, c.designation ORDER BY l.date_end, l.date_create DESC;"; @@ -62,7 +62,7 @@ function getLots(mysqli $con, ?int $categoryId = null): array COALESCE(MAX(r.amount), l.start_price) AS current_price FROM lots l JOIN categories c ON c.id = l.category_id - LEFT JOIN rates b ON b.lot_id = l.id + LEFT JOIN bets b ON b.lot_id = l.id WHERE l.ended_at > NOW() AND l.category_id = ? GROUP BY l.id, l.title, l.initial_price, l.img, l.date_create, l.date_end, c.id, c.designation ORDER BY l.date_end, l.date_create DESC;"; From 5737e30ef39be8deabc84d56328f45b7e452feca Mon Sep 17 00:00:00 2001 From: Notnochka Date: Sat, 8 Feb 2025 00:16:40 +0700 Subject: [PATCH 3/3] =?UTF-8?q?=D1=81=D0=BD=D0=BE=D0=B2=D0=B0=20=D0=B8?= =?UTF-8?q?=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- functions/db.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/functions/db.php b/functions/db.php index b1dc1c5..a50f174 100644 --- a/functions/db.php +++ b/functions/db.php @@ -150,13 +150,13 @@ function getLotById(mysqli $con, int $id): array|false|null $sql = "SELECT l.*, c.designation AS category, - r.amount AS last_rate, + b.amount AS last_bet, l.bet_step FROM lots l JOIN categories c ON l.category_id = c.id - LEFT JOIN rates r ON l.id = r.lot_id + LEFT JOIN bets b ON l.id = b.lot_id WHERE l.id = $id - ORDER BY r.sign_up_date DESC + ORDER BY b.sign_up_date DESC LIMIT 1;"; $result = mysqli_query($con, $sql);