Importieren einer CSV-Datei¶
In diesem Beispiel geht es um einen Workflow zum Import von CSV-Dateien.
Zuerst sollen in einem Verzeichnis nacheinander alle *.csv
Dateien eingelesen und
im Anschluß jede Zeile der eingelesenen Datei in ein anderes Verzeichnis abgelegt werden.
Der Workflow sieht folgendermassen aus:
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" title="" />
Zum Schutz vor der Verarbeitung, wird die Datei im Schritt readfile
nach der Identifizierung umbenannt.
Im Schritt archived
wird die Importdatei gelöscht.
Eine Archivierung der CSV-Datei kann optional hinzugefügt werden.
Container definieren¶
Als erstes wird eine Container-Klasse definiert. Ein Objekt dieser Klasse definiert den Status und enthält alle Informationen.
class Import extends \Alvine\Core\Alvine implements \Alvine\Application\Workflow\Container {
use \Alvine\Application\Workflow\Container\Implementation;
}
Aktion definieren¶
Als nächstes muss die eigentliche Aktion definiert werden. Hier soll für jede Zeile
in der CSV eine eigene Datei in ein anderes Verzeichnis /tmp/separat/
geschrieben werden.
Alternativ können die Daten auch direkt in eine Datenbank geschrieben oder andersweitig verarbeitet werden.
class SeparateAction extends \Alvine\Core\Alvine
implements \Alvine\Application\Workflow\Action {
public function run
(\Alvine\Application\Workflow\Container $container):
\Alvine\Application\Workflow\Container {
/** Ausgabeverzeichnis definieren und falls nicht vorhanden anlegen */
$dir=\Alvine\IO\File\TemporaryDirectory::getSystemDirectory()
->addChild('separat');
$dir->create();
/** Alle Einträge durchgehen */
foreach($container->rows AS $i=> $row) {
$j=0;
/** Verfügbaren Dateinamen ermitteln */
while(true) {
$fn='out-'.$i.'-'.$j++.'.csv';
$file=new \Alvine\IO\File\File($fn, $dir);
if(!$file->exists()) break;
}
/** Datei anlegen */
(new \Alvine\IO\CsvWriter($file->getOutputStream()))
->writeRow($row);
}
return $container;
}
public static function getInstanceFromParameterMap
(\Alvine\Types\Map\ParameterMap $data):
\Alvine\Application\Workflow\Action {
return new static();
}
}
XML einlesen und Workflow erstellen¶
Die XML wird eingelesen und verarbeitet.
$processor=(new \Alvine\Application\Workflow\Parser\XMLParser)->parse($xml);
Das Ergebnis ist eine Instanzen des Processors.
Einen neuen Container erstellen¶
Über die Factory-Methode wird ein neuer Container erstellt.
$container=$processor->create();
Workflow Automatisierung¶
Die Definition der Automatisierung sorgt dafür, das der gesamte Workflow durchlaufen wird.
Ausgabe der Containerinformationen¶
echo (string) $container;
Das Ergebnis des Workflows ist ein Container im Status archived
.
ID : 62f96686-b310-4d15-8a45-f51a5272eda0
State : archived
roundtrip : 3
⬤ 2019-06-01 10:00:00 import-csv
▶ container created
▷ 2019-06-01 10:00:00 import-csv ⬤ ▬▶ created
▶ container state changed from to created
⬗ 2019-06-01 10:00:00 import-csv
▶ importe file /tmp/import/a4.csv.
▷ 2019-06-01 10:00:00 import-csv created ▬▶ readfile
▶ container state changed from created to readfile
▷ 2019-06-01 10:00:00 import-csv readfile ▬▶ separated
▶ container state changed from readfile to separated
⬗ 2019-06-01 10:00:00 import-csv
▶ imported file /tmp/import/11cef568-fc73-4529-e195-5b4a7d2ce7e0 deleted.
▷ 2019-06-01 10:00:00 import-csv separated ▬▶ archived
▶ container state changed from separated to archived
Man sieht in dem Beispiel gut die einzelnen Übergänge und Aktionen.
XML-Datei¶
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!-- Definition des Workflows -->
<workflow name="import-csv">
<admission state="created"
container-class="\MyNamespace\Import" />
<states>
<state name="created" />
<state name="readfile" />
<state name="separated" />
<state name="archived" />
</states>
<steps>
<step name="readfile">
<actions>
<!-- Daten einlesen -->
<action class="\Alvine\Application\Workflow\Action\File\ImportCsv">
<parameters>
<parameter
name="path">/tmp/import/</parameter>
<parameter
name="extension">csv</parameter>
</parameters>
</action>
</actions>
</step>
<step name="separate">
<actions>
<!-- Einzelne Datensätze separieren -->
<action class="\MyNamespace\SeparateAction" />
</actions>
</step>
<step name="archive">
<actions>
<action class="\Alvine\Application\Workflow\Action\File\DeleteImported" />
</actions>
</step>
</steps>
<automation>
<rules>
<!-- Wurde ein leerer Container erstellt, so soll er
in den Status readfile überführt werden -->
<rule name="created"
on="\Alvine\Application\Workflow\Event\Created">
<actions>
<action class="\Alvine\Application\Workflow\Automation\Action\DoTransition">
<parameters>
<parameter
name="transition">createdToReadfile</parameter>
</parameters>
</action>
</actions>
</rule>
<rule name="handle-transistion-1"
on="\Alvine\Application\Workflow\Event\EndTransition">
<conditions>
<condition class="\Alvine\Application\Workflow\Automation\Condition\IsState">
<parameters>
<parameter
name="state">readfile</parameter>
</parameters>
</condition>
</conditions>
<actions>
<action class="\Alvine\Application\Workflow\Automation\Action\DoTransition">
<parameters>
<parameter
name="transition">readfileToSeparated</parameter>
</parameters>
</action>
</actions>
</rule>
<rule name="handle-transistion-2"
on="\Alvine\Application\Workflow\Event\EndTransition">
<conditions>
<condition
class="\Alvine\Application\Workflow\Automation\Condition\IsState">
<parameters>
<parameter
name="state">separated</parameter>
</parameters>
</condition>
</conditions>
<actions>
<action class="\Alvine\Application\Workflow\Automation\Action\DoTransition">
<parameters>
<parameter
name="transition">separatedToArchived</parameter>
</parameters>
</action>
</actions>
</rule>
</rules>
</automation>
<transitions>
<transition name="createdToReadfile"
from="created"
to="readfile"
with="readfile" />
<transition name="readfileToSeparated"
from="readfile"
to="separated"
with="separate" />
<transition name="separatedToArchived"
from="separated"
to="archived"
with="archive" />
</transitions>
</workflow>