Валидация данных при импорте товаров
Отметим: когда прайс от поставщика приходит с отрицательными ценами, пустыми артикулами и 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 часа после знакомства с вашими данными.







