Writers

For arlanix/product-feed 1.0.0

Writers

This page is for the PHP developer whose platform reads a file in a format the module does not write. A writer turns a feed's rows into a file. The module registers csv, tsv, xml (RSS 2.0 with Google's namespace), json and ndjson. Once yours is registered, it is listed in the Format field of every channel that publishes a file: File, SFTP, and any file channel your own module adds; see Channels.

Write a writer

Many platforms ask for a CSV file separated by semicolons. The following writer builds on Arlanix\Core\Model\Writer\DelimitedWriter, the base of the module's own CSV and TSV writers:

<?php
declare(strict_types=1);

namespace Vendor\Module\Model\Writer;

use Arlanix\Core\Model\Writer\DelimitedWriter;

class SemicolonCsvWriter extends DelimitedWriter
{
    protected const DELIMITER = ';';
    protected const EXTENSION = 'csv';
    protected const CONTENT_TYPE = 'text/csv';
    protected const LABEL = 'CSV, semicolon-separated';

    protected function escapeDelimiter(string $value): string
    {
        if (!str_contains($value, ';') && !str_contains($value, '"')) {
            return $value;
        }

        return '"' . str_replace('"', '""', $value) . '"';
    }
}

DelimitedWriter writes a header line of the field names, then one line per row. It turns each value into text, joining a list with commas, and replaces line breaks in a value with spaces before escapeDelimiter() protects the delimiter.

Write any format

A format that is not one line per row implements Arlanix\Core\Model\Writer\WriterInterface:

Method Does
write(WriteInterface $stream, iterable $rows, WriterContext $context): void Writes the whole file to $stream with $stream->write(): what comes before the rows, every row in the order given, what comes after.
getExtension(): string The file extension, without the dot: csv.
getContentType(): string The media type of the file: text/csv.
getLabel(): string The format's name in the Format field: CSV, semicolon-separated.

$stream is a Magento\Framework\Filesystem\File\WriteInterface on a temporary file. Each row is an array of field name to value, and the fields come from $context->getFields(), in the order of the mapping. Write each field from the context, not each key of the row: a row has no key for a field it has no value for.

A value is text, a number, null or a list: a multiselect attribute gives a list of labels, and Additional image URLs a list of addresses. Arlanix\Core\Model\Text\FlatText::of($value) gives the text a delimited format writes: '' for null, list members joined with commas.

$rows is read once, as the rows are mapped, so that a large catalogue never sits in memory whole. Write each row as it comes; do not collect them into an array first.

To refuse to write, throw Arlanix\Core\Model\Writer\WriterException. The run fails with its message, the temporary file is discarded, and the published file stays as it was.

Register it

In your module's etc/di.xml, add the writer to WriterPool under its alias:

<type name="Arlanix\Core\Model\Writer\WriterPool">
    <arguments>
        <argument name="writers" xsi:type="array">
            <item name="csv_semicolon" xsi:type="object">Vendor\Module\Model\Writer\SemicolonCsvWriter</item>
        </argument>
    </arguments>
</type>

The alias is what a feed stores as its format. An item under one of the module's aliases replaces the module's writer.

Then run bin/magento setup:upgrade and bin/magento cache:clean config. Open a feed with the File channel, choose CSV, semicolon-separated in Format, save, and run Generate.

The file name and its type

When the feed's File name is empty, the published file is named after the feed's identifier with the writer's extension: google-shopping.csv. A file name the store types is kept, but saving the feed sets its extension to the writer's: products and products.xml both become products.csv under a CSV writer.

A file published on the store is served by the web server, which decides its Content-Type from the extension. For an extension other than the module's five, make sure the web server knows it; see Where the files are.