Массовый импорт в PHP обычно начинается с CSV, отправленного клиентом, экспорта от поставщика или выгрузки из устаревшей системы. Риск возникает, когда к нему относятся как к простому чтению файла с последующими вставками в базу данных. Строка может иметь корректный формат и всё же создать дубликат, нарушить бизнес-правило, перезаписать актуальную информацию или дважды инициировать внешний эффект.
Импорт следует проектировать как операционный процесс с определённым циклом: приём, анализ, валидация, предварительный просмотр, подтверждение, выполнение, проверка и восстановление. Такой подход позволяет продуктовой команде понимать, что будет добавлено, операциям вмешиваться при исключениях, а технической команде ограничивать воздействие дефектных данных.
Рассматривать импорт как бизнес-процесс

Файл сам по себе не является источником истины: это запрос на изменение состояния приложения. Поэтому целесообразно создать сущность импорта с собственным идентификатором, пользователем или системой, которая его инициировала, датой получения, типом данных, версией контракта, файлом или безопасной ссылкой на него, общим статусом и сводкой результатов.
Общие статусы должны отражать управляемую ситуацию, а не только булево значение. Например: получен, проанализирован, ожидает подтверждения, выполняется, завершён, завершён с инцидентами, остановлен или отменён. Импорт с 9 800 принятыми и 200 отклонёнными строками не обязательно является неудачей; он может считаться завершённым с инцидентами, если отклонённые строки изолированы и объяснены.
Также необходимо решить, какие последствия относятся к загрузке. Создание заказа, обновление каталога или добавление контактов могут требовать вычислений, аудита или уведомлений. Отделение основной персистентности от побочных эффектов снижает риск того, что повторная попытка отправит повторные сообщения или неконтролируемо выполнит интеграции.
Определить входной контракт до приёма файлов
Контракт импорта определяет, что ожидается получить и как это будет интерпретироваться. Он должен включать допустимый формат, кодировку, разделитель, заголовки, типы данных, обязательные поля, формат дат, правила нормализации, ограничения размера и максимальное число строк. Если принимаются электронные таблицы, необходимо также определить соответствующий лист и порядок обработки пустых ячеек, формул и автоматически преобразованных значений.
Поля, связывающие исходную и целевую системы, заслуживают особого внимания. Стабильный внешний идентификатор, например код клиента в исходной системе, предпочтительнее использования в качестве ссылки номера строки или имени. Следует уточнить, создаёт ли этот идентификатор запись, обновляет существующую или разрешены обе операции.
Разделять уровни валидации
Структурная валидация отвечает на механические вопросы: можно ли прочитать файл? существуют ли обязательные столбцы? имеет ли дата допустимый формат? является ли сумма числовой? соблюдает ли строка ограничение длины? Этот уровень должен рано выявлять проблемы, препятствующие интерпретации данных.
Валидация домена применяет бизнес-правила: статус может быть недопустимым, дата увольнения не может предшествовать дате приёма, процент должен оставаться в пределах своего диапазона или комбинация полей может оказаться несовместимой. Наконец, проверки по существующим данным контролируют ссылки, разрешения, уникальность и допустимые переходы. Например, существует ли код поставщика, может ли пользователь работать с этой организацией или не заблокирована ли запись.
Такое разделение улучшает сообщения и диагностику. Сообщение об отсутствующем столбце отличается от сообщения о несуществующей ссылке или неавторизованном изменении. Кроме того, правила домена должны повторно использоваться и из приложения, и из импортёра, чтобы загрузка не превратилась в обходной путь, минующий обычные контроли.
Предварительно просматривать изменения и подтверждать явное намерение
Предварительный просмотр не должен обещать точное выполнение, если данные могут измениться между анализом и подтверждением, но он должен предоставлять проверяемую оценку. Покажите общее количество прочитанных строк, допустимых строк, строк с предупреждениями, отклонённых строк, запланированных созданий, запланированных обновлений и записей, которые не будут изменены.
Предупреждения предназначены для случаев, требующих внимания, но не делающих строку автоматически недействительной: например, нормализованный телефон, описание, усечённое по известному правилу, или пустое необязательное поле. Предупреждения не должны скрывать отклонения. Для каждого результата необходимы стабильный код, понятное сообщение и, когда это безопасно, полученное и нормализованное значения.
Подтверждение должно быть связано с конкретной версией анализа. Если пользователь заменяет файл, исправляет строки в интерфейсе или изменяет релевантные параметры, система должна аннулировать предыдущий предварительный просмотр и потребовать нового анализа. Так предотвращается подтверждение сводки, которая больше не отражает фактическую загрузку.
Обрабатывать пакетами без неоднозначных статусов
Обработка всех строк в одной транзакции кажется безопасной, но может слишком долго удерживать блокировки, превышать лимиты выполнения или превращать единичный сбой в дорогостоящий откат. Обработка одной строки в транзакции, напротив, может создать слишком большие накладные расходы и усложнить координацию связанных операций.
Единицу работы следует выбирать с учётом зависимости между записями, объёма и стоимости отката. Во многих случаях небольшой ограниченный пакет позволяет подтверждать изменения постепенно. Для каждого пакета должны фиксироваться начало, завершение, число обработанных строк и результат. Обработчик должен иметь возможность возобновляться без зависимости от открытой HTTP-сессии: загрузка подтверждается из интерфейса, но выполняется как фоновая задача.
Не храните полные файлы в памяти. Читайте последовательно, нормализуйте каждую строку и сохраняйте рабочее представление или результат валидации, когда это необходимо для аудита и возобновления. Устанавливайте ограничения на размер, число строк, время и конкурентность. Неожиданно большой файл не должен блокировать ресурсы, обслуживающие ежедневную работу.
для каждого ожидающего пакета:
пометить пакет как в_процессе
для каждой строки пакета:
применить ожидающие валидации
сохранить или отправить в карантин
зарегистрировать результат по строке
зафиксировать изменения пакета
пометить пакет как завершённыйЕсли обработчик прерывается, недостаточно просто повторно выполнить пакет вслепую. Нужен механизм блокировки с истечением срока действия или восстановлением, а статус по строкам должен позволять отличать ожидающее от уже подтверждённого.
Изолировать ошибки в карантине и сохранять доказательства
Карантин позволяет недопустимым строкам не блокировать корректные, не исчезая из процесса. Строка в карантине должна сохранять номер или идентификатор источника, полученные данные под контролем доступа, нормализованные данные при их наличии, коды ошибок, момент обнаружения и статус проверки.
Не все ошибки обрабатываются одинаково. Файл без обязательного заголовка — это ошибка файла, которая может остановить весь анализ. Ссылка на несуществующего поставщика может быть отклонением строки. Временный сбой базы данных или интеграции — это техническая ошибка, допускающая повторную попытку, и её не следует помечать как дефект данных.
Операционный интерфейс должен позволять фильтровать по причине, экспортировать только разрешённые инциденты и понимать, какое исправление ожидается. Исправление в приложении может быть полезно для небольшого числа случаев; для множества записей обычно более контролируемо выгрузить инциденты, исправить их в источнике и представить новую загрузку. В обоих случаях сохраняйте историю: изменение строки в карантине не должно удалять исходное значение или первоначальную причину.
Гарантировать идемпотентность и избегать дубликатов
Идемпотентность означает, что повторение операции с тем же намерением не изменяет результат более одного раза. Это необходимо, поскольку повторные попытки происходят: истекает время ожидания, процесс перезапускается или оператор повторно подтверждает действие при неопределённом ответе.
Ключ идемпотентности может строиться на основе типа импорта, целевой организации и стабильного внешнего идентификатора. Он должен подкрепляться ограничениями уникальности в базе данных, когда это допускает модель; сначала проверить, а затем вставить не устраняет состояния гонки между конкурентными обработчиками.
Для обновлений определите явную политику: заменять поля, применять только непустые значения, отклонять конфликты или требовать ожидаемую версию записи. Последний вариант помогает обнаружить, что пользователь изменил данные после предварительного просмотра. Не используйте хеш файла как единственный механизм: одно и то же содержимое может представлять разное намерение, а исправленный файл может сохранять много уже обработанных строк.
Прослеживаемость, повторные попытки и выборочное восстановление

Результат по строке — центральный элемент для поддержки и восстановления. Фиксируйте статус, например ожидает, обработана, отклонена, в карантине, ожидает повторной попытки или пропущена; внешний идентификатор; пакет; коды причин; временные метки; и ссылку на созданную или обновлённую запись. Защищайте персональные данные и секреты: прослеживаемость должна быть достаточной для расследования, а не представлять собой неизбирательную копию чувствительной информации в логах.
Повторные попытки должны быть выборочными. Автоматически повторяйте временные технические ошибки с ограничениями и прогрессивной задержкой; не повторяйте бесконечно нарушенное правило домена. Когда исправлена отсутствующая ссылка или недопустимые данные, повторно обработайте только соответствующие строки в карантине. При сбое пакета продолжайте с ожидающих строк и используйте уже обработанные как доказательство прогресса.
Перед вводом потока в промышленную эксплуатацию протестируйте пустые файлы, изменённые заголовки, неожиданные кодировки, дубликаты в одном файле, дубликаты по отношению к базе данных, прерывания между пакетами, возобновления и недостаточные разрешения. Массовый импорт в PHP надёжен, когда его поведение при сбое спроектировано с той же точностью, что и успешный сценарий.



