try {
$service = new ProductMappingService();
- $path = $service->exportToXlsx($filters);
+ $csv = $service->exportToCsvContent($filters);
} catch (\Throwable $e) {
Yii::error('Ошибка экспорта маппинга: ' . $e->getMessage() . "\n" . $e->getTraceAsString(), 'product-mapping');
Yii::$app->response->format = Response::FORMAT_JSON;
$fileName = 'product-mapping-' . date('Y-m-d_His') . '.csv';
- return Yii::$app->response
- ->sendFile($path, $fileName, [
- 'mimeType' => 'text/csv; charset=UTF-8',
- ])
- ->on(Response::EVENT_AFTER_SEND, static function ($event) use ($path) {
- if (is_file($path)) {
- @unlink($path);
- }
- });
+ $response = Yii::$app->response;
+ $response->format = Response::FORMAT_RAW;
+ $response->headers->set('Content-Type', 'text/csv; charset=UTF-8');
+ $response->headers->set('Content-Disposition', 'attachment; filename="' . $fileName . '"');
+ $response->headers->set('Content-Length', (string)strlen($csv));
+ $response->content = $csv;
+
+ return $response;
}
public function actionCreateForm(string $product_guid): string
*
* @return string Абсолютный путь к временному файлу (вызывающий обязан удалить)
*/
- public function exportToXlsx(ProductMappingFilterForm $filters): string
+ /**
+ * Генерирует CSV-контент маппинга в памяти и возвращает строку.
+ * Один SQL-запрос, никаких AR-объектов, никакого файла на диске.
+ */
+ public function exportToCsvContent(ProductMappingFilterForm $filters): string
{
- // Шаг 1: получаем отфильтрованные GUID-ы (только строки, не AR-объекты)
$guids = $this->buildFilteredQuery($filters)
->select(['n.id'])
->orderBy(['n.name' => SORT_ASC])
->column();
- $runtimeDir = Yii::getAlias('@runtime');
- if (!is_dir($runtimeDir)) {
- mkdir($runtimeDir, 0775, true);
- }
- $path = $runtimeDir . '/product-mapping-export-' . date('YmdHis') . '-' . uniqid() . '.csv';
-
- $fh = fopen($path, 'wb');
- if ($fh === false) {
- throw new \RuntimeException('Не удалось создать файл экспорта: ' . $path);
- }
+ $buf = fopen('php://temp', 'r+b');
+ fwrite($buf, "\xEF\xBB\xBF");
- fwrite($fh, "\xEF\xBB\xBF"); // UTF-8 BOM для Excel
-
- fputcsv($fh, [
+ fputcsv($buf, [
'GUID товара', 'Название товара 1С', 'Категория', 'Подкатегория', 'Вид',
'Поставщик', 'Название у поставщика', 'Плантация', 'Артикул', 'Штрихкод',
'Квант', 'Маркировки (коды)',
], ';');
if (!empty($guids)) {
- // Шаг 2: один JOIN-запрос — скаляры, не объекты, STRING_AGG для маркировок
$rows = (new Query())
->select([
- 'guid' => 'n.id',
- 'product_name' => 'n.name',
- 'category' => "COALESCE(n.category, '')",
- 'subcategory' => "COALESCE(n.subcategory, '')",
- 'species' => "COALESCE(n.species, '')",
- 'supplier_name' => "COALESCE(s.name, '')",
- 'supplier_product_name'=> "COALESCE(pm.supplier_product_name, '')",
- 'plantation_name' => "COALESCE(pl.name, '')",
- 'article' => "COALESCE(pm.article, '')",
- 'barcode' => "COALESCE(pm.barcode, '')",
- 'quant' => 'COALESCE(pm.quant, 0)',
- 'marking_codes' => new Expression(
+ 'guid' => 'n.id',
+ 'product_name' => 'n.name',
+ 'category' => "COALESCE(n.category, '')",
+ 'subcategory' => "COALESCE(n.subcategory, '')",
+ 'species' => "COALESCE(n.species, '')",
+ 'supplier_name' => "COALESCE(s.name, '')",
+ 'supplier_product_name' => "COALESCE(pm.supplier_product_name, '')",
+ 'plantation_name' => "COALESCE(pl.name, '')",
+ 'article' => "COALESCE(pm.article, '')",
+ 'barcode' => "COALESCE(pm.barcode, '')",
+ 'quant' => 'COALESCE(pm.quant, 0)',
+ 'marking_codes' => new Expression(
"COALESCE(STRING_AGG(mk.code, ', ' ORDER BY mk.code), '')"
),
])
->all();
foreach ($rows as $row) {
- fputcsv($fh, [
- $row['guid'],
- $row['product_name'],
- $row['category'],
- $row['subcategory'],
- $row['species'],
- $row['supplier_name'],
- $row['supplier_product_name'],
- $row['plantation_name'],
- $row['article'],
- $row['barcode'],
- $row['quant'],
- $row['marking_codes'],
+ fputcsv($buf, [
+ $row['guid'], $row['product_name'], $row['category'],
+ $row['subcategory'], $row['species'], $row['supplier_name'],
+ $row['supplier_product_name'], $row['plantation_name'], $row['article'],
+ $row['barcode'], $row['quant'], $row['marking_codes'],
], ';');
}
}
- fclose($fh);
+ rewind($buf);
+ $content = stream_get_contents($buf);
+ fclose($buf);
+
+ return $content;
+ }
- return $path;
+ /** @deprecated используйте exportToCsvContent */
+ public function exportToXlsx(ProductMappingFilterForm $filters): string
+ {
+ return '';
}
/**