Read-Only Import Dry Run
Validates incoming import rows and reports inserts, updates, duplicates, and errors without writing to the database.
Purpose
Validates incoming import rows and reports inserts, updates, duplicates, and errors without writing to the database.
Snippet details
ContextDatabaseLevelPracticalCopy-and-paste statusMarked safe after review.Categories
- Security
Code
<?php
/*
* Copyright (c) 2026 Jeffery L. Paris <jparis@phpog.com>.
* Free for personal and internal use. Paid project use requires visible credit
* to Jeffery L. Paris. Corporate use requires a paid license fee unless a
* separate written license states otherwise.
*/
/**
* Read-Only Import Dry Run.
*
* Purpose:
* Reviews incoming rows before an import writes anything to storage.
*
* @param array $incoming_rows Rows prepared for import.
* @param array $required_fields Field names required on every row.
* @param array $existing_slugs Existing slugs already present in storage.
* @return array Dry-run report with counts, row errors, and planned actions.
*/
function ogSnippetReadOnlyImportDryRun(array $incoming_rows, array $required_fields, array $existing_slugs): array {
$existing_slug_map = array();
$row_errors = array();
$planned_actions = array();
$insert_count = 0;
$update_count = 0;
$duplicate_count = 0;
foreach ($existing_slugs as $existing_slug) {
$existing_slug = strtolower(trim((string) $existing_slug));
if ($existing_slug !== '') {
$existing_slug_map[$existing_slug] = true;
}
}
$row_number = 0;
foreach ($incoming_rows as $row) {
$row_number++;
$errors = array();
if (is_array($row) === false) {
$row_errors[] = array(
'row' => $row_number,
'errors' => array('Row is not an array.')
);
continue;
}
foreach ($required_fields as $field_name) {
$field_name = trim((string) $field_name);
if ($field_name === '') {
continue;
}
if (isset($row[$field_name]) === false || trim((string) $row[$field_name]) === '') {
$errors[] = 'Missing required field: '.$field_name;
}
}
$slug = '';
if (isset($row['slug']) === true) {
$slug = strtolower(trim((string) $row['slug']));
}
if ($slug === '') {
$errors[] = 'Missing slug.';
}
if (count($errors) > 0) {
$row_errors[] = array(
'row' => $row_number,
'errors' => $errors
);
continue;
}
if (isset($existing_slug_map[$slug]) === true) {
$update_count++;
$duplicate_count++;
$planned_actions[] = array(
'row' => $row_number,
'slug' => $slug,
'action' => 'review-update'
);
} else {
$insert_count++;
$existing_slug_map[$slug] = true;
$planned_actions[] = array(
'row' => $row_number,
'slug' => $slug,
'action' => 'insert'
);
}
}
return array(
'row_count' => count($incoming_rows),
'insert_count' => $insert_count,
'update_review_count' => $update_count,
'duplicate_slug_count' => $duplicate_count,
'error_count' => count($row_errors),
'row_errors' => $row_errors,
'planned_actions' => $planned_actions
);
}
$incoming_manifest = array(
array('title' => 'Stargate Dialing Library', 'slug' => 'stargate-dialing-library'),
array('title' => 'Rocinante Flight Log', 'slug' => 'rocinante-flight-log'),
array('title' => '', 'slug' => 'broken-import-row')
);
$dry_run = ogSnippetReadOnlyImportDryRun($incoming_manifest, array('title', 'slug'), array('rocinante-flight-log'));
echo 'Import dry-run errors: '.$dry_run['error_count'];