354 lines
14 KiB
PHP
354 lines
14 KiB
PHP
<?php
|
|
ini_set('display_errors', 1);
|
|
error_reporting(E_ALL);
|
|
|
|
require_once 'db/config.php';
|
|
|
|
$messages = [];
|
|
$conn = db();
|
|
|
|
function create_lifecycle_table($conn) {
|
|
$messages = [];
|
|
try {
|
|
$sql_lifecycle = "
|
|
CREATE TABLE IF NOT EXISTS lifecycle (
|
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
product_id VARCHAR(255),
|
|
custcode VARCHAR(255),
|
|
first_ship_month VARCHAR(255),
|
|
last_ship_month VARCHAR(255),
|
|
total_ship INT,
|
|
avg_ship FLOAT,
|
|
maxstores INT,
|
|
mock_pct FLOAT,
|
|
store_count INT,
|
|
runrate FLOAT
|
|
);";
|
|
$conn->exec($sql_lifecycle);
|
|
$messages['success'][] = "Table 'lifecycle' created or already exists.";
|
|
|
|
} catch (PDOException $e) {
|
|
$messages['danger'][] = "Table creation failed: " . $e->getMessage();
|
|
}
|
|
return $messages;
|
|
}
|
|
|
|
function create_customers_table($conn) {
|
|
$messages = [];
|
|
try {
|
|
$sql_customers = "
|
|
CREATE TABLE IF NOT EXISTS customers (
|
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
custcode VARCHAR(255),
|
|
custname VARCHAR(255),
|
|
channel VARCHAR(255),
|
|
country VARCHAR(255),
|
|
maxstores INT
|
|
);";
|
|
$conn->exec($sql_customers);
|
|
$messages['success'][] = "Table 'customers' created or already exists.";
|
|
|
|
} catch (PDOException $e) {
|
|
$messages['danger'][] = "Table creation failed: " . $e->getMessage();
|
|
}
|
|
return $messages;
|
|
}
|
|
|
|
function create_products_table($conn) {
|
|
$messages = [];
|
|
try {
|
|
$sql_products = "
|
|
CREATE TABLE IF NOT EXISTS products (
|
|
product_id VARCHAR(255) PRIMARY KEY,
|
|
prodname VARCHAR(255),
|
|
brand VARCHAR(255),
|
|
subbrand VARCHAR(255),
|
|
sub_brand_abbreviation VARCHAR(255),
|
|
listprice FLOAT
|
|
);";
|
|
$conn->exec($sql_products);
|
|
$messages['success'][] = "Table 'products' created or already exists.";
|
|
|
|
} catch (PDOException $e) {
|
|
$messages['danger'][] = "Table creation failed: " . $e->getMessage();
|
|
}
|
|
return $messages;
|
|
}
|
|
|
|
function create_shipments_table($conn) {
|
|
$messages = [];
|
|
try {
|
|
$sql_shipments = "
|
|
CREATE TABLE IF NOT EXISTS shipments (
|
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
product_id VARCHAR(255),
|
|
month VARCHAR(255),
|
|
custcode VARCHAR(255),
|
|
ship INT
|
|
);";
|
|
$conn->exec($sql_shipments);
|
|
$messages['success'][] = "Table 'shipments' created or already exists.";
|
|
|
|
} catch (PDOException $e) {
|
|
$messages['danger'][] = "Table creation failed: " . $e->getMessage();
|
|
}
|
|
return $messages;
|
|
}
|
|
|
|
function import_csv($conn, $filePath, $tableName, $fieldMap, &$messages) {
|
|
try {
|
|
$conn->exec("TRUNCATE TABLE `$tableName`");
|
|
} catch (PDOException $e) {
|
|
$messages['danger'][] = "Error truncating table $tableName: " . $e->getMessage();
|
|
return ['count' => 0, 'error' => "Error truncating table: " . $e->getMessage()];
|
|
}
|
|
|
|
if (!file_exists($filePath) || !is_readable($filePath)) {
|
|
return ['count' => 0, 'error' => "File not found or not readable: $filePath"];
|
|
}
|
|
|
|
$file = fopen($filePath, 'r');
|
|
if (!$file) {
|
|
return ['count' => 0, 'error' => "Failed to open file: $filePath"];
|
|
}
|
|
|
|
$header = fgetcsv($file);
|
|
if (!$header) {
|
|
return ['count' => 0, 'error' => "Failed to read header from file: $filePath"];
|
|
}
|
|
|
|
$indices = [];
|
|
foreach ($fieldMap as $dbField => $csvField) {
|
|
$index = array_search($csvField, $header);
|
|
if ($index === false) {
|
|
// Allow for flexibility with headers that have dots
|
|
$csvFieldWithDot = str_replace('_', '.', $csvField);
|
|
if (in_array($csvFieldWithDot, $header)) {
|
|
$index = array_search($csvFieldWithDot, $header);
|
|
} else {
|
|
return ['count' => 0, 'error' => "Column '$csvField' (or '$csvFieldWithDot') not found in $tableName CSV."];
|
|
}
|
|
}
|
|
$indices[$dbField] = $index;
|
|
}
|
|
|
|
$placeholders = implode(', ', array_fill(0, count($fieldMap), '?'));
|
|
$columns = implode(', ', array_keys($fieldMap));
|
|
|
|
if ($tableName === 'products') {
|
|
$sql = "INSERT IGNORE INTO $tableName ($columns) VALUES ($placeholders)";
|
|
} else {
|
|
$sql = "INSERT INTO $tableName ($columns) VALUES ($placeholders)";
|
|
}
|
|
$stmt = $conn->prepare($sql);
|
|
|
|
$count = 0;
|
|
$conn->beginTransaction();
|
|
while (($row = fgetcsv($file)) !== false) {
|
|
$params = [];
|
|
|
|
foreach ($fieldMap as $dbField => $csvField) {
|
|
$value = $row[$indices[$dbField]];
|
|
if ($tableName === 'lifecycle') {
|
|
if (in_array($dbField, ['total_ship', 'maxstores', 'store_count'])) {
|
|
$params[] = (int)filter_var($value, FILTER_SANITIZE_NUMBER_INT);
|
|
} elseif (in_array($dbField, ['avg_ship', 'mock_pct', 'runrate'])) {
|
|
$params[] = (float)filter_var($value, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
|
|
} else {
|
|
$params[] = trim($value);
|
|
}
|
|
} elseif ($tableName === 'customers') {
|
|
if (in_array($dbField, ['maxstores'])) {
|
|
$params[] = (int)filter_var($value, FILTER_SANITIZE_NUMBER_INT);
|
|
} else {
|
|
$params[] = trim($value);
|
|
}
|
|
} elseif ($tableName === 'products') {
|
|
if (in_array($dbField, ['listprice'])) {
|
|
$params[] = (float)filter_var($value, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
|
|
} else {
|
|
$params[] = trim($value);
|
|
}
|
|
} elseif ($tableName === 'shipments') {
|
|
if (in_array($dbField, ['ship'])) {
|
|
$params[] = (int)filter_var($value, FILTER_SANITIZE_NUMBER_INT);
|
|
} else {
|
|
$params[] = trim($value);
|
|
}
|
|
} else {
|
|
$params[] = trim($value);
|
|
}
|
|
}
|
|
$stmt->execute($params);
|
|
$count++;
|
|
}
|
|
$conn->commit();
|
|
fclose($file);
|
|
|
|
return ['count' => $count, 'error' => null];
|
|
}
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
$messages = array_merge_recursive($messages, create_lifecycle_table($conn));
|
|
$messages = array_merge_recursive($messages, create_customers_table($conn));
|
|
$messages = array_merge_recursive($messages, create_products_table($conn));
|
|
$messages = array_merge_recursive($messages, create_shipments_table($conn));
|
|
|
|
$uploadDir = 'uploads/';
|
|
if (!is_dir($uploadDir)) {
|
|
mkdir($uploadDir, 0777, true);
|
|
}
|
|
|
|
$files = [
|
|
'lifecycle' => ['lifecycle', [
|
|
'product_id' => 'product_id',
|
|
'custcode' => 'custcode',
|
|
'first_ship_month' => 'first_ship_month',
|
|
'last_ship_month' => 'last_ship_month',
|
|
'total_ship' => 'total_ship',
|
|
'avg_ship' => 'avg_ship',
|
|
'maxstores' => 'maxstores',
|
|
'mock_pct' => 'mock_pct',
|
|
'store_count' => 'store_count',
|
|
'runrate' => 'runrate'
|
|
]],
|
|
'customers' => ['customers', [
|
|
'custcode' => 'custcode',
|
|
'custname' => 'custname',
|
|
'channel' => 'channel',
|
|
'country' => 'country',
|
|
'maxstores' => 'maxstores'
|
|
]],
|
|
'products' => ['products', [
|
|
'product_id' => 'product_id',
|
|
'prodname' => 'prodname',
|
|
'brand' => 'brand',
|
|
'subbrand' => 'subbrand',
|
|
'sub_brand_abbreviation' => 'sub_brand.abbreviation',
|
|
'listprice' => 'listprice'
|
|
]],
|
|
'shipments' => ['shipments', [
|
|
'product_id' => 'product_id',
|
|
'month' => 'month',
|
|
'custcode' => 'custcode',
|
|
'ship' => 'ship'
|
|
]]
|
|
];
|
|
|
|
foreach ($files as $fileKey => $fileInfo) {
|
|
if (isset($_FILES[$fileKey]) && $_FILES[$fileKey]['error'] == UPLOAD_ERR_OK) {
|
|
$tableName = $fileInfo[0];
|
|
$fieldMap = $fileInfo[1];
|
|
$tmpName = $_FILES[$fileKey]['tmp_name'];
|
|
$filePath = $uploadDir . basename($_FILES[$fileKey]['name']);
|
|
|
|
if (move_uploaded_file($tmpName, $filePath)) {
|
|
$result = import_csv($conn, $filePath, $tableName, $fieldMap, $messages);
|
|
if ($result['error']) {
|
|
$messages['danger'][] = "Error importing $tableName: " . $result['error'];
|
|
} else {
|
|
$messages['success'][] = "Successfully imported " . $result['count'] . " records into '$tableName'.";
|
|
}
|
|
} else {
|
|
$messages['danger'][] = "Failed to move uploaded file for $fileKey.";
|
|
}
|
|
} elseif (isset($_FILES[$fileKey]) && !empty($_FILES[$fileKey]['name'])) {
|
|
$errorCode = $_FILES[$fileKey]['error'];
|
|
$errorMessage = "An unknown error occurred.";
|
|
if ($errorCode === UPLOAD_ERR_INI_SIZE) {
|
|
$maxSize = ini_get('upload_max_filesize');
|
|
$errorMessage = "The file is too large. The server's maximum upload size is {$maxSize}.";
|
|
}
|
|
$messages['warning'][] = "An error occurred with the '$fileKey' upload: " . $errorMessage;
|
|
}
|
|
}
|
|
}
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Data Importer</title>
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
|
</head>
|
|
<body>
|
|
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
|
<div class="container-fluid">
|
|
<a class="navbar-brand" href="/">CRUD App</a>
|
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
|
<span class="navbar-toggler-icon"></span>
|
|
</button>
|
|
<div class="collapse navbar-collapse" id="navbarNav">
|
|
<ul class="navbar-nav">
|
|
<li class="nav-item">
|
|
<a class="nav-link" href="/">Home</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link active" aria-current="page" href="/import.php">Import Data</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
|
|
<div class="container mt-5">
|
|
<div class="row">
|
|
<div class="col-md-8 offset-md-2">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h1 class="card-title text-center">Data Importer</h1>
|
|
</div>
|
|
<div class="card-body">
|
|
<p class="card-text">Upload your CSV files to populate the database. The system will create the necessary tables and import the data.</p>
|
|
|
|
<?php if (!empty($messages)): ?>
|
|
<div class="mb-4">
|
|
<?php foreach ($messages as $type => $msgs): ?>
|
|
<?php foreach ($msgs as $msg): ?>
|
|
<div class="alert alert-<?php echo htmlspecialchars($type); ?>" role="alert">
|
|
<?php echo htmlspecialchars($msg); ?>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<form action="import.php" method="post" enctype="multipart/form-data">
|
|
<div class="mb-3">
|
|
<label for="lifecycle" class="form-label">Lifecycle CSV File</label>
|
|
<input class="form-control" type="file" id="lifecycle" name="lifecycle">
|
|
<div class="form-text">Fields: product_id, custcode, first_ship_month, last_ship_month, total_ship, avg_ship, maxstores, mock_pct, store_count, runrate</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="customers" class="form-label">Customers CSV File</label>
|
|
<input class="form-control" type="file" id="customers" name="customers">
|
|
<div class="form-text">Fields: custcode, custname, channel, country, maxstores</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="products" class="form-label">Products CSV File</label>
|
|
<input class="form-control" type="file" id="products" name="products">
|
|
<div class="form-text">Fields: product_id, prodname, brand, subbrand, sub_brand.abbreviation, listprice</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="shipments" class="form-label">Shipments CSV File</label>
|
|
<input class="form-control" type="file" id="shipments" name="shipments">
|
|
<div class="form-text">Fields: product_id, month, custcode, ship</div>
|
|
</div>
|
|
<div class="d-grid">
|
|
<button type="submit" class="btn btn-primary btn-lg"><i class="bi bi-cloud-upload-fill"></i> Preview & Import</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<div class="card-footer text-muted">
|
|
Ensure your CSV files have a header row that matches the specified fields.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
|
</body>
|
|
</html>
|