PHP code example of jul6art / dataflow-bundle

1. Go to this page and download the library: Download jul6art/dataflow-bundle library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

jul6art / dataflow-bundle example snippets


Jul6Art\DataflowBundle\DataflowBundle::class => ['all' => true],

use Jul6Art\DataflowBundle\Io\Dialect\CsvDialect;
use Jul6Art\DataflowBundle\Io\Writer\CsvWriter;
use Symfony\Component\HttpFoundation\StreamedResponse;

$writer = new CsvWriter(CsvDialect::excelFr());

$response = new StreamedResponse(static function () use ($writer, $invoices): void {
    $writer->write(
        ['number', 'customer', 'total'],
        (static function () use ($invoices): \Generator {
            foreach ($invoices as $invoice) {          // a Doctrine `toIterable()`, ideally
                yield [$invoice->getNumber(), $invoice->getCustomer()->getName(), $invoice->getTotal()];
            }
        })(),
        static function (string $chunk): void { echo $chunk; },
    );
});
$response->headers->set('Content-Type', $writer->contentType());

public function __construct(
    #[AutowireIterator(tag: 'dataflow.tabular_writer')]
    private readonly iterable $writers,
) {}

private function writer(string $format): TabularWriterInterface
{
    foreach ($this->writers as $writer) {
        if ($writer->code() === $format) {
            return $writer;
        }
    }

    throw new \InvalidArgumentException(...);
}

use Jul6Art\DataflowBundle\Io\Http\TabularResponseFactory;

return $this->responses->stream(
    $writer,
    ['number', 'customer', 'total'],
    $rows,
    TabularResponseFactory::basename(['invoices', $organization->getSlug()]),
);

use Jul6Art\DataflowBundle\Report\Spec\ReportSpecInterpreter;

$spec   = new ReportSpecInterpreter()->interpret($savedDefinition);   // your stored payload
$result = $runner->run($spec, $actor, limit: 50_000, scope: $this->scopeToTenant(...));

return $this->responses->stream($writer, $result->header(), $result->rows(), 'invoices');

use Jul6Art\DataflowBundle\Report\Format\ColumnFormat;
use Jul6Art\DataflowBundle\Report\Spec\ReportColumn;

new ReportColumn('total', 'Total', format: ColumnFormat::money('EUR'));
new ReportColumn('vatRate', 'VAT', format: ColumnFormat::percent(1));
new ReportColumn('issuedAt', 'Issued', format: ColumnFormat::date());

final readonly class NumberFormatterAdapter implements NumberFormatterInterface
{
    public function __construct(private \Jul6Art\CoreBundle\Service\NumberFormatter $inner) {}

    public function format(int|float|string|null $value, ?int $decimals = null): string
    {
        return $this->inner->format($value, $decimals);
    }

    public function formatMoney(int|float|string|null $value, string $currency, ?int $decimals = null): string
    {
        return $this->inner->formatMoney($value, $currency, $decimals);
    }

    public function formatPercent(int|float|string|null $value, int $decimals = 0): string
    {
        return $this->inner->formatPercent($value, $decimals);
    }
}

return new JsonResponse([
    'fields' => array_map(
        static fn (ReportField $field): array => $field->toArray(),
        $fields->listFor($rootFqcn, $user, $limits->fieldMaxDepth)
    ),
]);

final class CrmReportableEntityProvider implements ReportableEntityProviderInterface
{
    public function entities(): array
    {
        return [
            Contact::class => new ReportableEntity('report.entity.contact', 'crm:contact:read', 'crm.manage'),
        ];
    }
}

use Jul6Art\DataflowBundle\Io\Dialect\CsvDialect;
use Jul6Art\DataflowBundle\Io\Reader\CsvReader;

$reader = new CsvReader(CsvDialect::excelFr());

foreach ($reader->read($path) as $record => $cells) {
    // $record is 1-based and the header is record 1
}

use Jul6Art\DataflowBundle\Io\Reader\XlsxReader;

$reader = new XlsxReader();

foreach ($reader->read($path) as $record => $cells) {
    // Same contract as CsvReader: 1-based, header 

public function __construct(
    #[AutowireIterator(tag: 'dataflow.tabular_reader')]
    private readonly iterable $readers,
) {}

private function readerFor(string $filePath): TabularReaderInterface
{
    foreach ($this->readers as $reader) {
        if ($reader->supports($filePath)) {
            return $reader;
        }
    }

    throw UnreadableFileException::cannotOpen($filePath);
}

use Jul6Art\DataflowBundle\Import\HeaderInspector;

$inspector = new HeaderInspector();
$headers = $inspector->peek($reader, $path);          // reads ONE record
$inspection = $inspector->inspect($headers, $mapper->fields());

$inspection->mapping;     // [0 => 'firstName', 2 => 'email'] — column INDEX → field
$inspection->ambiguous;   // indices whose header collides with another's
$inspection->unknown;     // indices no field matched — normal, not an error
$inspection->missing;     // fields no column supplied

use Jul6Art\DataflowBundle\Import\ImportRunner;
use Jul6Art\DataflowBundle\Import\Spec\ImportSpec;

$spec = new ImportSpec($path, $inspection->mapping, dryRun: true);
$report = $runner->run($spec, $mapper, $reader, $resolver);

$report->imported();            // rows that would land — see isDryRun() before wording this
$report->updated();             // rows that would overwrite an existing record — see onDuplicate below
$report->skipped();
$report->errorCount();          // exact
$report->errors();              // the first 100
$report->errorsWereTruncated();

use Jul6Art\DataflowBundle\Import\Spec\DuplicatePolicy;
use Jul6Art\DataflowBundle\Import\Spec\ImportSpec;

$spec = new ImportSpec($path, $inspection->mapping, onDuplicate: DuplicatePolicy::Update);
$report = $runner->run($spec, $mapper, $reader, $resolver);

$report->updated();   // rows that overwrote an existing record, disjoint from imported()

public function map(array $row, ?object $existing = null): Customer
{
    $customer = $existing ?? new Customer();
    $customer->name = $row['name'];

    return $customer;
}

use Jul6Art\DataflowBundle\Import\Sink\CsvErrorSink;

$handle = fopen('php://temp', 'w+');
$sink = new CsvErrorSink($handle);

$report = $runner->run($spec, $mapper, $reader, $resolver, errorSink: $sink);

if ($report->errorCount() > 0) {
    rewind($handle);
    // stream $handle back as a download — every error, not the report's hundred-row sample
}
fclose($handle);

use Jul6Art\DataflowBundle\Import\TemplateBuilder;
use Jul6Art\DataflowBundle\Io\Http\TabularResponseFactory;

$builder = new TemplateBuilder();

return $this->responses->stream(
    $writer,
    $builder->header($mapper),
    $builder->rows($mapper),
    TabularResponseFactory::basename(['template', 'customers']),
);

use Jul6Art\DataflowBundle\Import\Async\ImportMessage;

$bus->dispatch(new ImportMessage(
    importId: (string) Uuid::v4(),      // yours: the key ImportProgressStoreInterface keys on
    filePath: $uploadedPath,
    mapping: $inspection->mapping,
    mapperId: 'customer-import',        // yours: what ImportMapperFactoryInterface turns back into a mapper
    context: ['accountId' => $account->getId()],
));

final readonly class MyMapperFactory implements ImportMapperFactoryInterface
{
    public function __construct(private EntityManagerInterface $em) {}

    public function mapper(string $mapperId, array $context): RowMapperInterface
    {
        return match ($mapperId) {
            'customer-import' => new CustomerRowMapper($this->em->getReference(Account::class, $context['accountId'])),
            default => throw new \InvalidArgumentException($mapperId),
        };
    }

    public function resolver(?string $resolverId, array $context): ?DuplicateResolverInterface
    {
        return null;
    }
}

public function save(Request $request, ReportBuilderCsrfChecker $csrf): JsonResponse
{
    if (!$csrf->isValid($request)) {
        throw new AccessDeniedHttpException('invalid_csrf_token');
    }
    // …
}

protected static function declaredKeys(): array          // the BROWSER's catalogue
{
    return static::getContainer()->get(DeclaredTranslationKeys::class)->keys();
}