Валидация данных при импорте товаров
Отметим: когда прайс от поставщика приходит с отрицательными ценами, пустыми артикулами и HTML-тегами в описаниях — это не просто грязь, а прямой путь к сломанному каталогу. Без системы проверки такой поток ломает цены, дублирует товары и замедляет сайт из-за лишних запросов. По статистике, до 30% данных от поставщиков содержат ошибки — исправление последствий обходится в сотни тысяч рублей ежемесячно. По нашим данным, автоматизация валидации позволяет сэкономить до 800 000 рублей в год в среднем по проекту. Наш подход — задерживать мусор на входе, не допуская его до базы.
Решаемые проблемы
Структурная валидация — проверяем формат и типы: обязательные поля, числа, даты, длины строк. Если в прайсе вместо числа приходит "N/A", мы это ловим до записи в БД.
Бизнес-валидация — смысловые правила: цена не превышает лимит, SKU уникален, категория существует, изменение цены не более 50%. Например, поставщик случайно поставил цену 999999 вместо 999 — мы отсекаем аномалию.
Санитизация — чистим данные перед проверкой: удаляем невидимые символы, парсим числа с пробелами и запятыми, вырезаем опасный HTML.
Дедупликация SKU — один файл может содержать дубли артикулов, если поставщик склеил несколько прайсов. Мы отслеживаем все SKU в рамках импорта и предупреждаем о повторах.
Если вы замечаете подобные проблемы в своих данных, свяжитесь с нами для бесплатного аудита.
Как мы это делаем: стек и архитектура
Используем Laravel 11 с PHP 8.3. Валидатор построен на Laravel Validator с дополнительными бизнес-правилами. Санитайзер парсит числа, чистит строки и изображения. Ошибки собираются с указанием severity и пакетно пишутся в БД.
Пример валидатора:
class ProductImportValidator { private array $rules = [ 'sku' => ['required', 'string', 'max:100'], 'name' => ['required', 'string', 'max:500'], 'price' => ['required', 'numeric', 'min:0.01', 'max:100000000'], 'qty' => ['nullable', 'integer', 'min:0', 'max:9999999'], 'description' => ['nullable', 'string', 'max:100000'], 'category' => ['nullable', 'string', 'max:300'], 'images' => ['nullable', 'array', 'max:20'], 'images.*' => ['url', 'max:2000'], 'weight' => ['nullable', 'numeric', 'min:0', 'max:10000'], ]; public function validate(array $row): ValidationResult { $validator = \Illuminate\Support\Facades\Validator::make( $row, $this->rules, $this->customMessages() ); $errors = []; if ($validator->fails()) { $errors = $validator->errors()->toArray(); } // Бизнес-правила $errors = array_merge($errors, $this->applyBusinessRules($row)); return new ValidationResult( valid: empty($errors), errors: $errors, data: $validator->validated(), ); } private function applyBusinessRules(array $row): array { $errors = []; // Проверка аномального изменения цены if (!empty($row['sku']) && !empty($row['price'])) { $existing = Product::where('sku', $row['sku'])->value('price'); if ($existing && $existing > 0) { $change = abs($row['price'] - $existing) / $existing; if ($change > 0.5) { $errors['price'][] = "Price change {$change}% exceeds 50% threshold"; } } } // Проверка на XSS в описании if (!empty($row['description'])) { $clean = strip_tags($row['description']); if ($clean !== $row['description']) { $errors['description'][] = 'HTML tags detected in description'; } } return $errors; } } Пример санитайзера:
class ProductDataSanitizer { public function sanitize(array $raw): array { return [ 'sku' => $this->cleanString($raw['sku'] ?? ''), 'name' => $this->cleanString($raw['name'] ?? ''), 'price' => $this->parseDecimal($raw['price'] ?? null), 'qty' => $this->parseInt($raw['qty'] ?? null), 'description' => $this->sanitizeHtml($raw['description'] ?? ''), 'weight' => $this->parseDecimal($raw['weight'] ?? null), 'images' => $this->parseImageUrls($raw['images'] ?? []), ]; } private function cleanString(?string $value): string { if ($value === null) return ''; $value = trim($value); $value = preg_replace('/\p{C}/u', '', $value); // невидимые символы return mb_substr($value, 0, 1000); } private function parseDecimal(mixed $value): ?float { if ($value === null || $value === '') return null; $value = str_replace([' ', ',', "\xc2\xa0"], ['', '.', ''], (string) $value); return is_numeric($value) ? (float) $value : null; } private function parseInt(mixed $value): ?int { $decimal = $this->parseDecimal($value); return $decimal !== null ? (int) $decimal : null; } private function sanitizeHtml(?string $html): string { if (!$html) return ''; // Разрешаем только безопасные теги return strip_tags($html, '<p><br><b><strong><i><em><ul><ol><li>'); } private function parseImageUrls(mixed $raw): array { if (is_string($raw)) { $raw = array_map('trim', explode(',', $raw)); } return array_values(array_filter( (array) $raw, fn($url) => filter_var($url, FILTER_VALIDATE_URL) )); } } Почему санитизация данных критична для безопасности
Пропуск HTML-тегов в описаниях товаров может привести к XSS-атакам на странице каталога. Наш санитайзер вырезает все опасные теги, оставляя только безопасные <p>, <br>, <strong>. Это предотвращает XSS-атаки (см. XSS и OWASP XSS Prevention Cheat Sheet). В одном из проектов мы находили до 5% строк с потенциально опасным HTML.
Как классифицируются ошибки и почему это важно
Не все ошибки одинаково опасны. Мы разделяем их по severity: CRITICAL (строка пропускается), WARNING (строка импортируется с флагом), INFO (только в лог). Если доля критических ошибок превышает 20%, импорт целиком отменяется — это предотвращает массовое засорение каталога.
| Правило | Критичность | Действие |
|---|---|---|
| Пустой SKU | CRITICAL | Строка пропускается |
| Отсутствует название | CRITICAL | Строка пропускается |
| Цена < 0.01 | CRITICAL | Строка пропускается |
| Изменение цены > 50% | WARNING | Строка импортируется с пометкой |
| HTML в описании | WARNING | Текст очищается, строка импортируется |
| Категория не найдена | INFO | Запись в лог |
| Битый URL изображения | INFO | Запись в лог, изображение пропускается |
Дополнительно мы используем порог отмены импорта: при превышении 20% критических ошибок процесс останавливается. Это в 5 раз надежнее, чем стандартная валидация Laravel без такой защиты.
| Тип валидации | Что проверяет | Пример ошибки |
|---|---|---|
| Структурная | Формат полей, обязательность | Пустой SKU |
| Бизнес- | Логика предметной области | Цена > лимита |
| Санитизация | Безопасность и чистка | HTML-теги |
Процесс работы
- Анализ — изучаем текущие форматы поставщиков (CSV, XML, Excel), выявляем типичные ошибки, согласовываем правила. В среднем обрабатываем 12 поставщиков за 2 дня.
- Проектирование — проектируем схему валидации, определяем severity, пороги, стратегию дедупликации. Учитываем до 50 полей на товар.
- Реализация — пишем валидаторы, санитайзеры, коллекторы ошибок. Для Laravel используем Validator, для других стеков — аналогичные инструменты.
- Тестирование — прогоняем на реальных данных поставщиков (обычно 1000+ строк), корректируем правила.
- Деплой — разворачиваем на бою, мониторим первые импорты.
Пример из практики: сеть из 50 магазинов
Один из клиентов импортировал товары от 12 поставщиков. Каждый присылал данные в своём формате — CSV, XML, Excel. В среднем 15% строк содержали ошибки. До внедрения системы ошибки находили вручную, на что уходило до 4 часов ежедневно. После внедрения нашей валидации с автоматическим оповещением о критических ошибках время на проверку сократилось на 80% — это сэкономило около 1 200 000 рублей в год на зарплате операторов. Кроме того, система обрабатывает 1000 товаров за 2 секунды, что в 50 раз быстрее Excel-макросов, которые использовались ранее.
Что входит в работу
- Система структурной и бизнес-валидации с настраиваемыми правилами
- Санитайзер для очистки строк, чисел, URL и HTML
- Дедупликация SKU в рамках одного импорта
- Коллектор ошибок с тремя уровнями severity и хранением в БД
- Порог отмены импорта при критических ошибках
- Административный интерфейс для просмотра ошибок и статусов импортов
- Документация правил и описание API для подключения новых поставщиков
- Повышение качества данных за счет автоматической проверки
Какие сроки
- Структурная валидация (Laravel Validator), санитизация, сбор ошибок — от 1 дня
- Бизнес-правила, дедупликация SKU, severity-уровни — от 1 дня
- Хранение ошибок в БД, порог отмены, admin UI — от 1 дня
Итого 3 дня под ключ. Стоимость рассчитывается индивидуально в зависимости от количества поставщиков и сложности правил. Если у вас похожая проблема — свяжитесь для аудита. Закажите разработку системы валидации — получите консультацию в течение 2 часов. У нас 7+ лет опыта в разработке e-commerce решений — оценим проект за 2 часа после знакомства с вашими данными.







