Add command to convert from CSV to PHP (#10)

This commit is contained in:
Daniel Siepmann 2023-11-09 10:05:45 +01:00 committed by GitHub
parent 3e594a0d15
commit 7589a2bf9a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 346 additions and 0 deletions

View file

@ -1,6 +1,7 @@
<?php
$finder = (new PhpCsFixer\Finder())
->ignoreVCSIgnored(true)
->exclude('Tests/Functional/Converter/Fixtures/Csv/')
->exclude('Tests/Functional/Converter/Fixtures/Xml/')
->in(realpath(__DIR__))
;

View file

@ -1,5 +1,11 @@
# Changelog
## v1.4.0 - 2023-11-07
### Added
- Add command `convert-from-csv`.
## v1.3.1 - 2023-08-10
### Added

View file

@ -0,0 +1,62 @@
<?php
declare(strict_types=1);
/*
* Copyright (C) 2023 Daniel Siepmann <coding@daniel-siepmann.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
namespace Codappix\Typo3PhpDatasets\Command;
use Codappix\Typo3PhpDatasets\Converter\Csv;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class ConvertFromCsv extends Command
{
protected static $defaultName = 'convert-from-csv';
protected static $defaultDescription = 'Converts CSV data-sets to PHP data-sets.';
protected function configure(): void
{
$this->addArgument('file', InputArgument::IS_ARRAY, 'The file(s) to convert.');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$files = $input->getArgument('file');
if (is_array($files) === false) {
$output->writeln('File needs to be an array.');
return Command::INVALID;
}
$converter = new Csv();
foreach ($files as $file) {
try {
$converter->convert(realpath($file) ?: $file);
} catch (\Exception $e) {
$output->writeln($e->getMessage());
return Command::INVALID;
}
}
return Command::SUCCESS;
}
}

99
Classes/Converter/Csv.php Normal file
View file

@ -0,0 +1,99 @@
<?php
declare(strict_types=1);
/*
* Copyright (C) 2023 Daniel Siepmann <coding@daniel-siepmann.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
namespace Codappix\Typo3PhpDatasets\Converter;
use InvalidArgumentException;
use SplFileObject;
class Csv implements Converter
{
public function convert(string $fileName): string
{
if (file_exists($fileName) === false) {
throw new InvalidArgumentException('Given file "' . $fileName . '" does not exist.', 1681283739);
}
$phpFileName = $this->getNewFileName($fileName);
$incomingFile = new SplFileObject($fileName);
try {
file_put_contents($phpFileName, $this->buildContent($incomingFile));
} catch (\Exception $e) {
throw new \Exception('Could not generate new file.', 1681287881, $e);
}
return $phpFileName;
}
private function getNewFileName(string $fileName): string
{
$file = new SplFileObject($fileName);
return str_replace(
$file->getBasename(),
$file->getBasename($file->getExtension()) . 'php',
$file->getRealPath()
);
}
private function buildContent(SplFileObject $incomingFile): string
{
$phpArray = [];
$tableName = '';
$columns = [];
$incomingFile->setFlags(SplFileObject::READ_CSV);
$incomingFile->setCsvControl(',', '"', '"');
foreach ($incomingFile as $line) {
if (is_array($line) && count($line) === 1 && is_null($line[0])) {
// End of file
break;
}
if (is_array($line) && count($line) === 1 && is_string($line[0])) {
// Line is a new table, introducing also new columns o next row
$tableName = $line[0];
$columns = [];
continue;
}
if ($columns === [] && is_array($line)) {
$columns = array_slice($line, 1);
continue;
}
if (is_array($line)) {
$values = array_slice($line, 1);
$phpArray[$tableName][] = array_combine($columns, $values);
}
}
return implode(PHP_EOL, [
'<?php',
'',
'return ' . var_export($phpArray, true) . ';',
'',
]);
}
}

View file

@ -0,0 +1,104 @@
<?php
declare(strict_types=1);
/*
* Copyright (C) 2023 Daniel Siepmann <coding@daniel-siepmann.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
namespace Codappix\Typo3PhpDatasets\Tests\Functional\Converter;
use Codappix\Typo3PhpDatasets\Converter\Csv;
use GlobIterator;
use InvalidArgumentException;
use PHPUnit\Framework\TestCase;
/**
* @covers \Codappix\Typo3PhpDatasets\Converter\Csv
* @testdox The CSV converter
*/
class CsvTest extends TestCase
{
protected function tearDown(): void
{
$filesToDelete = new GlobIterator(__DIR__ . '/Fixtures/Csv/*Incoming.php');
foreach ($filesToDelete as $file) {
unlink((string)$file);
}
parent::tearDown();
}
/**
* @test
*/
public function canBeCreated(): void
{
$subject = new Csv();
self::assertInstanceOf(
Csv::class,
$subject
);
}
/**
* @test
*/
public function throwsExceptionForNoneExistingFile(): void
{
$subject = new Csv();
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionCode(1681283739);
$this->expectExceptionMessage('Given file "NoneExistingFile.csv" does not exist.');
$subject->convert('NoneExistingFile.csv');
}
/**
* @test
* @dataProvider possibleCsvFiles
* @testdox Converts $_dataName CSV to PHP
*/
public function convertsCsvFileToPhpFile(
string $incomingCsvFile,
string $expectedResultFile
): void {
$subject = new Csv();
$result = $subject->convert($incomingCsvFile);
self::assertFileEquals($expectedResultFile, $result);
}
public static function possibleCsvFiles(): array
{
return [
'Simple' => [
'incomingCsvFile' => __DIR__ . '/Fixtures/Csv/SimpleIncoming.csv',
'expectedResultFile' => __DIR__ . '/Fixtures/Csv/SimpleAssert.php',
],
'Multiple records in single table' => [
'incomingCsvFile' => __DIR__ . '/Fixtures/Csv/MultipleRecordsInSingleTableIncoming.csv',
'expectedResultFile' => __DIR__ . '/Fixtures/Csv/MultipleRecordsInSingleTableAssert.php',
],
'Records in different tables' => [
'incomingCsvFile' => __DIR__ . '/Fixtures/Csv/RecordsInDifferentTablesIncoming.csv',
'expectedResultFile' => __DIR__ . '/Fixtures/Csv/RecordsInDifferentTablesAssert.php',
],
];
}
}

View file

@ -0,0 +1,21 @@
<?php
return array (
'pages' =>
array (
0 =>
array (
'uid' => '1',
'pid' => '0',
'title' => 'Page with uid 1 below 0',
'deleted' => '0',
),
1 =>
array (
'uid' => '2',
'pid' => '1',
'title' => 'Page with uid 2 below 1',
'deleted' => '0',
),
),
);

View file

@ -0,0 +1,4 @@
pages
,uid,pid,title,deleted
,1,0,Page with uid 1 below 0,0
,2,1,Page with uid 2 below 1,0
1 pages
2 ,uid,pid,title,deleted
3 ,1,0,Page with uid 1 below 0,0
4 ,2,1,Page with uid 2 below 1,0

View file

@ -0,0 +1,24 @@
<?php
return array (
'pages' =>
array (
0 =>
array (
'uid' => '1',
'pid' => '0',
'title' => 'Page with uid 1 below 0',
'deleted' => '0',
),
),
'tt_content' =>
array (
0 =>
array (
'uid' => '1',
'pid' => '1',
'header' => 'Content with uid 1 on page 1',
'deleted' => '0',
),
),
);

View file

@ -0,0 +1,6 @@
pages
,uid,pid,title,deleted
,1,0,Page with uid 1 below 0,0
tt_content
,uid,pid,header,deleted
,1,1,Content with uid 1 on page 1,0
1 pages
2 ,uid,pid,title,deleted
3 ,1,0,Page with uid 1 below 0,0
4 tt_content
5 ,uid,pid,header,deleted
6 ,1,1,Content with uid 1 on page 1,0

View file

@ -0,0 +1,14 @@
<?php
return array (
'pages' =>
array (
0 =>
array (
'uid' => '1',
'pid' => '0',
'title' => 'Page with uid 1 below 0',
'deleted' => '0',
),
),
);

View file

@ -0,0 +1,3 @@
pages
,uid,pid,title,deleted
,1,0,Page with uid 1 below 0,0
1 pages
2 ,uid,pid,title,deleted
3 ,1,0,Page with uid 1 below 0,0

View file

@ -3,11 +3,13 @@
require $_composer_autoload_path ?? __DIR__ . '/../vendor/autoload.php';
use Codappix\Typo3PhpDatasets\Command\ConvertFromCsv;
use Codappix\Typo3PhpDatasets\Command\ConvertFromXml;
use Symfony\Component\Console\Application;
$application = new Application();
$application->add(new ConvertFromXml());
$application->add(new ConvertFromCsv());
$application->run();