Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
# Changelog
## Unreleased
### Add
-Alternative product export command based on a queue system

## [v5.4.0] - 2026.08.05
### Add
- Add support for Atlas AI
Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,15 @@ Command name: `factfinder:export [TYPE]`. You can add execution of this command
- store - define a store, which the product data will be taken from
- skip-ftp-upload - skips the ftp upload
- skip-push-import - skips triggering import

#### Running export with a worker

Since version 5.5.0, we've introduced a new export flow based on the worker.
The biggest advantage of this export method is the reduced memory usage, which is especially helpful when you have a large or complex products catalog.
Currently, this command is only available from the CLI and can be executed by:

php [MAGENTO_ROOT]/bin/magento factfinder:worker-export


## Web Component Integration

Expand Down
80 changes: 80 additions & 0 deletions src/Console/Command/ExportBatch.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php

declare(strict_types=1);

namespace Omikron\Factfinder\Console\Command;

use Magento\Framework\App\State;
use Omikron\Factfinder\Api\StreamInterfaceFactory;
use Omikron\Factfinder\Model\Export\FeedFactory as FeedGeneratorFactory;
use Omikron\Factfinder\Model\StoreEmulation;
use Omikron\Factfinder\Service\FeedFileService;
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 ExportBatch extends Command
{
public function __construct(
private readonly FeedGeneratorFactory $feedGeneratorFactory,
private readonly StoreEmulation $storeEmulation,
private readonly StreamInterfaceFactory $streamFactory,
private readonly State $state,
private readonly FeedFileService $feedFileService
) {
parent::__construct();
}

protected function configure(): void
{
$this->setName('factfinder:export:batch')
->setDescription('Internal worker command for exporting a batch of products')
->setHidden(true);

$this->addArgument('type', InputArgument::REQUIRED, 'Type of data to export');
$this->addArgument('store', InputArgument::REQUIRED, 'Store ID');
$this->addArgument('offset', InputArgument::REQUIRED, 'Offset');
$this->addArgument('limit', InputArgument::REQUIRED, 'Limit');
$this->addArgument('file_path', InputArgument::REQUIRED, 'Path to output file');

parent::configure();
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->state->setAreaCode('frontend');

$type = $input->getArgument('type');
$storeId = (int) $input->getArgument('store');
$offset = (int) $input->getArgument('offset');
$limit = (int) $input->getArgument('limit');
$filePath = $input->getArgument('file_path');

$processedCount = 0;

$this->storeEmulation->runInStore($storeId, function () use ($type, $offset, $limit, $filePath, &$processedCount) {
$mode = ($offset === 0) ? 'w+' : 'a+';
$stream = $this->streamFactory->create([
'filename' => $filePath,
'mode' => $mode
]);

$generator = $this->feedGeneratorFactory->create($type);
$processedCount = $generator->generateBatch($stream, $offset, $limit);
});

$memoryUsageMB = memory_get_usage(true) / 1024 / 1024;
$peakMemoryMB = memory_get_peak_usage(true) / 1024 / 1024;

$result = [
'count' => $processedCount,
'memory' => round($memoryUsageMB, 2),
'peak' => round($peakMemoryMB, 2),
];

$output->write(json_encode($result));

return Command::SUCCESS;
}
}
Loading
Loading