diff --git a/src/Migration/Cache.php b/src/Migration/Cache.php index 3651bae7..a88d1c17 100644 --- a/src/Migration/Cache.php +++ b/src/Migration/Cache.php @@ -75,8 +75,7 @@ public function resolveResourceCacheKey(Resource $resource): string $keys[] = $resource->getSequence(); - $joinedKey = implode('_', $keys); - return $joinedKey; + return \implode('_', $keys); } /** @@ -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'); } diff --git a/src/Migration/Sources/CSV.php b/src/Migration/Sources/CSV.php index 8b99a5f5..0f69660e 100644 --- a/src/Migration/Sources/CSV.php +++ b/src/Migration/Sources/CSV.php @@ -19,6 +19,13 @@ class CSV extends Source { + private const ALLOWED_INTERNALS = [ + '$id' => true, + '$permissions' => true, + '$createdAt' => true, + '$updatedAt' => true, + ]; + private string $filePath; /** @@ -120,7 +127,6 @@ protected function exportGroupDatabases(int $batchSize, array $resources): void */ private function exportRows(int $batchSize): void { - $columns = []; $lastColumn = null; @@ -147,7 +153,9 @@ private function exportRows(int $batchSize): void } } - $arrayKeys = []; + $arrayKeys = [ + '$permissions' => true, + ]; $columnTypes = []; $manyToManyKeys = []; @@ -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; } @@ -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])) { @@ -216,7 +227,7 @@ private function exportRows(int $batchSize): void : array_values( array_filter( array_map( - 'trim', + trim(...), explode(',', $parsedValue) ) ) @@ -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) { @@ -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) { @@ -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));