Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions src/Migration/Cache.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,7 @@ public function resolveResourceCacheKey(Resource $resource): string

$keys[] = $resource->getSequence();

$joinedKey = implode('_', $keys);
return $joinedKey;
return \implode('_', $keys);
}

/**
Expand Down Expand Up @@ -173,7 +172,7 @@ public function remove(Resource $resource): void
throw new \Exception('Resource does not exist in cache');
}
}
if (! in_array($resource, $this->cache[$key])) {
if (! in_array($resource, $this->cache[$resource->getName()])) {
throw new \Exception('Resource does not exist in cache');
}

Expand Down
60 changes: 40 additions & 20 deletions src/Migration/Sources/CSV.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@

class CSV extends Source
{
private const ALLOWED_INTERNALS = [
'$id' => true,
'$permissions' => true,
'$createdAt' => true,
'$updatedAt' => true,
];

private string $filePath;

/**
Expand Down Expand Up @@ -120,7 +127,6 @@ protected function exportGroupDatabases(int $batchSize, array $resources): void
*/
private function exportRows(int $batchSize): void
{

$columns = [];
$lastColumn = null;

Expand All @@ -147,7 +153,9 @@ private function exportRows(int $batchSize): void
}
}

$arrayKeys = [];
$arrayKeys = [
'$permissions' => true,
];
$columnTypes = [];
$manyToManyKeys = [];

Expand Down Expand Up @@ -182,7 +190,7 @@ private function exportRows(int $batchSize): void

$this->withCSVStream(function ($stream, $delimiter) use ($columnTypes, $manyToManyKeys, $arrayKeys, $table, $batchSize) {
$headers = fgetcsv($stream);
if (! is_array($headers) || count($headers) === 0) {
if (!is_array($headers) || count($headers) === 0) {
return;
}

Expand All @@ -206,8 +214,11 @@ private function exportRows(int $batchSize): void
$parsedValue = trim($value);
$type = $columnTypes[$key] ?? null;

if (! isset($type)) {
continue;
if (!isset($type) && $key !== '$permissions') {
if (isset(self::ALLOWED_INTERNALS[$key])) {
continue;
}
throw new \Exception('Unexpected attribute in CSV: '.$key);
}

if (isset($manyToManyKeys[$key])) {
Expand All @@ -216,7 +227,7 @@ private function exportRows(int $batchSize): void
: array_values(
array_filter(
array_map(
'trim',
trim(...),
explode(',', $parsedValue)
)
)
Expand All @@ -229,7 +240,12 @@ private function exportRows(int $batchSize): void
$parsedData[$key] = [];
} else {
$arrayValues = str_getcsv($parsedValue);
$arrayValues = array_map('trim', $arrayValues);
$arrayValues = array_map(trim(...), $arrayValues);

// Special handling for permissions to unescape quotes
if ($key === '$permissions') {
$arrayValues = array_map(stripslashes(...), $arrayValues);
}

$parsedData[$key] = array_map(function ($item) use ($type) {
return match ($type) {
Expand All @@ -254,11 +270,18 @@ private function exportRows(int $batchSize): void
}

$rowId = $parsedData['$id'] ?? 'unique()';
$permissions = $parsedData['$permissions'] ?? [];


// `$id`, `$permissions` in the doc can cause issues!
unset($parsedData['$id'], $parsedData['$permissions']);

$row = new Row($rowId, $table, $parsedData);
$row = new Row(
$rowId,
$table,
$parsedData,
$permissions,
);

$buffer[] = $row;

if (count($buffer) === $batchSize) {
Expand Down Expand Up @@ -350,25 +373,22 @@ private function withCsvStream(callable $callback): void
*/
private function validateCSVHeaders(array $headers, array $columnTypes): void
{
$expectedColumns = array_keys($columnTypes);

// Ignore keys like $id, $permissions, etc.
$filteredHeaders = array_filter($headers, fn ($key) => ! str_starts_with($key, '$'));

$extraColumns = array_diff($filteredHeaders, $expectedColumns);
$missingColumns = array_diff($expectedColumns, $filteredHeaders);
$internalColumns = ['$id', '$permissions', '$createdAt', '$updatedAt'];
$expectedColumns = \array_keys($columnTypes);
$extraColumns = \array_diff($headers, $expectedColumns, $internalColumns);
$missingColumns = \array_diff($expectedColumns, $headers);

if (! empty($missingColumns) || ! empty($extraColumns)) {
$messages = [];

if (! empty($missingColumns)) {
$label = count($missingColumns) === 1 ? 'Missing column' : 'Missing columns';
$messages[] = "{$label}: '".implode("', '", $missingColumns)."'";
$label = count($missingColumns) === 1 ? 'Missing attribute' : 'Missing attributes';
$messages[] = "$label: '".implode("', '", $missingColumns)."'";
}

if (! empty($extraColumns)) {
$label = count($extraColumns) === 1 ? 'Unexpected column' : 'Unexpected columns';
$messages[] = "{$label}: '".implode("', '", $extraColumns)."'";
$label = count($extraColumns) === 1 ? 'Unexpected attribute' : 'Unexpected attributes';
$messages[] = "$label: '".implode("', '", $extraColumns)."'";
}

throw new \Exception('CSV header mismatch. '.implode(' | ', $messages));
Expand Down