Generating EDI files with custom XML schemas pt. 3

In the final part of this article, we are going to join together what we created in Part 1 (the mass action that generates the file) and Part 2 (the schema and configuration for the file).

In Part 2 we put off implementation of a couple of classes and methods for later and marked them with TODO. In our export processor class Magently\Dropshipping\ExportProcessor\Edi we use contentGeneratorFactory that should be injected in the constructor. Its type is Magently\Dropshipping\ContentGenerator\EdiFactory and it will help us to create instances of Magently\Dropshipping\ContentGenerator\Edi. The latter is a simple class:

app/code/Magently/Dropshipping/ContentGenerator/Edi.php

php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Edi { private $ediFactory; private $edi; public function __construct(FileGenerator\EdiFactory $ediFactory) { $this->ediFactory = $ediFactory; } public function generate(array $orderData) { if (!$this->edi) { $this->edi = $this->ediFactory->create(); } $this->edi->writeContent($orderData); } public function getOutput(): string { return $this->edi->output(); } }

It uses yet another factory for creating a Magently\Dropshipping\ContentGenerator\FileGenerator\Edi instance where the actual file content is generated. In the code above you can see that the file generator object has two public methods: output and writeContent. Here is how they are implemented:

app/code/Magently/Dropshipping/ContentGenerator/FileGenerator/Edi.php

html
1 2 3 4 5 6 7 8 9 public function output() { return $this->content; } public function writeContent(array $orderData) { $this->writeOrderHeaderRow($orderData); }

It uses orderHeaderFieldReader. This is the virtual type Magently\Dropshipping\ContentGenerator\FileGenerator\Edi\FieldReader\OrderHeader that we created in Part 2 and can now inject into the file generator object through di.xml:

app/code/Magently/Dropshipping/etc/adminhtml/di.xml

html
1 2 3 4 5 <type name="Magently\Dropshipping\ContentGenerator\FileGenerator\Edi"> <arguments> <argument name="orderHeaderFieldReader" xsi:type="object">Magently\Dropshipping\ContentGenerator\FileGenerator\Edi\FieldReader\OrderHeader</argument> </arguments> </type>

The writeRow method called in writeOrderHeaderRow does what its name implies. It’s implemented this way:

app/code/Magently/Dropshipping/ContentGenerator/FileGenerator/Edi.php

php
1 2 3 4 5 6 7 8 9 private function writeRow(array $config, array $data) { foreach ($config['fields'] as &$field) { $this->mergeConfig($field, $data); } $values = array_column($config['fields'], 'value'); $this->content .= implode(self::DATA_SEPARATOR, $values) . "\n"; }

There are two new elements there. self::DATA_SEPARATOR is a separator of choice, for example “;”. mergeConfig is a private method that handles the mapping attribute that we used in our XML schema. Here’s its implementation:

app/code/Magently/Dropshipping/ContentGenerator/FileGenerator/Edi.php

php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 private function mergeConfig(array &$field, array $data) { if (empty($field['mapping'])) { $fieldName = $field['name']; if (array_key_exists($fieldName, $data)) { $field['value'] = $data[$fieldName]; } return; } $pathElements = explode('\\', $field['mapping']); $pathElements[] = $field['name']; $currentArray = $data; foreach ($pathElements as $element) { if (!array_key_exists($element, $currentArray)) { return; } if (is_array($currentArray[$element])) { $currentArray = $currentArray[$element]; continue; } $field['value'] = $currentArray[$element]; } }

Now you can go to the admin panel, check some orders and select Generate EDI File. The downloaded file should look similar to this:

The P1 package row has been added the same way as the header row (using edi_order_package.xml file).

We’ve covered lots of ground creating this feature. EDI files can follow many standards specific to industries and regions. You could define different kinds of rows by creating XML configuration files and injecting them by DI. The code presented was rather verbose, but it was written with extensibility in mind. You could make it even more so by leveraging more interfaces.