Skip to content

Commit 96e5680

Browse files
committed
WIP
1 parent 2e3396b commit 96e5680

File tree

11 files changed

+154
-15
lines changed

11 files changed

+154
-15
lines changed

.claude/settings.local.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"permissions": {
3+
"allow": [
4+
"Bash(composer list:*)",
5+
"Bash(find:*)",
6+
"Bash(composer scripts:*)",
7+
"Bash(rm:*)",
8+
"Bash(mv:*)",
9+
"WebFetch(domain:docs.sylius.com)"
10+
],
11+
"deny": []
12+
}
13+
}

CLAUDE.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Overview
6+
7+
This is the Sylius GridImportExportBundle, a Symfony bundle that provides grid import/export functionality for Sylius applications. It supports CSV and JSON export formats and uses Symfony Messenger for asynchronous processing.
8+
9+
## Development Commands
10+
11+
### Testing
12+
- Run tests: `vendor/bin/phpunit`
13+
- Tests are located in `tests/` directory with subdirectories for Integration, Api, and Unit tests
14+
15+
### Code Quality
16+
- Run coding standards check: `vendor/bin/ecs check`
17+
- Fix coding standards: `vendor/bin/ecs check --fix`
18+
- Run static analysis: `vendor/bin/phpstan analyse`
19+
20+
### Dependencies
21+
- Install dependencies: `composer install`
22+
- Update dependencies: `composer update`
23+
24+
## Architecture
25+
26+
### Core Components
27+
28+
**Entity Layer**
29+
- `Process` entity (`src/Entity/Process.php`) - Tracks export processes with UUID, status, format, and output
30+
- Uses Doctrine ORM with XML mapping (`config/doctrine/Process.orm.xml`)
31+
32+
**Export System**
33+
- `ExporterInterface` - Contract for all exporters
34+
- `AbstractExporter` - Base implementation with common functionality
35+
- `CsvExporter` and `JsonExporter` - Format-specific implementations
36+
- `ExporterResolver` - Factory for selecting appropriate exporter
37+
38+
**Data Providers**
39+
- `ResourceDataProviderInterface` - Contract for providing resource data
40+
- `DefaultResourceDataProvider` - Default implementation
41+
- `RequestBasedResourcesIdsProvider` - Provides resource IDs from HTTP requests
42+
43+
**Processing**
44+
- Uses Symfony Messenger for asynchronous processing
45+
- `ExportCommand` - Command message for export jobs
46+
- `ExportCommandHandler` - Handles export processing
47+
48+
**UI Integration**
49+
- Integrates with Sylius Admin UI using Twig hooks
50+
- Custom form types for export configuration
51+
- Grid listeners for adding export actions
52+
- Live Components for dynamic UI updates
53+
54+
### Key Directories
55+
- `src/Controller/` - HTTP endpoints for exports and downloads
56+
- `src/Messenger/` - Async command handling
57+
- `src/Grid/` - Grid integration and export actions
58+
- `src/Form/` - Form types and choice loaders
59+
- `templates/admin/` - Twig templates for admin interface
60+
- `config/` - Bundle configuration and service definitions
61+
- `assets/` - Frontend JavaScript controllers
62+
63+
### Configuration
64+
- Main config: `config/config.yaml`
65+
- Services: `config/services.xml`
66+
- Routes: `config/routes.yaml` and `config/routes/admin.yaml`
67+
- Grid config: `config/config/sylius_grid.yaml`
68+
69+
## Bundle Structure
70+
71+
This follows standard Symfony bundle conventions:
72+
- Main bundle class: `SyliusGridImportExportBundle`
73+
- DI Extension: `SyliusGridImportExportExtension`
74+
- PSR-4 autoloading with `Sylius\GridImportExport\` namespace
75+
- Uses Sylius coding standards and requires PHP 8.2+

config/config/sylius_grid.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ sylius_grid:
22
templates:
33
action:
44
export: '@SyliusGridImportExport\admin\import_export\grid\action\export.html.twig'
5+
download: '@SyliusGridImportExport\admin\import_export\grid\action\download.html.twig'
56
bulk_action:
67
export: '@SyliusGridImportExport\admin\import_export\grid\bulk_action\export.html.twig'
78

@@ -32,6 +33,14 @@ sylius_grid:
3233
template: '@SyliusGridImportExport\admin\import_export\grid\field\status.html.twig'
3334
actions:
3435
item:
36+
download:
37+
type: download
38+
label: sylius.ui.download
39+
options:
40+
link:
41+
route: sylius_grid_import_export_admin_process_download
42+
parameters:
43+
uuid: resource.uuid
3544
show:
3645
type: show
3746
delete:

config/doctrine/Process.orm.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
<field name="status" column="status" type="string" />
1414
<field name="resource" column="resource" type="string" />
1515
<field name="output" column="output" type="text" nullable="true" />
16+
<field name="errorMessage" column="error_message" type="text" nullable="true" />
1617
<field name="format" column="format" type="string" />
1718
<field name="resourceIds" column="resource_ids" type="json" nullable="true" />
1819
<field name="createdAt" column="created_at" type="datetime">

src/Controller/DownloadExportAction.php

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,21 +43,24 @@ public function __invoke(string $uuid): Response
4343
throw new NotFoundHttpException('Export file is not ready for download');
4444
}
4545

46-
$filename = $process->getOutput();
47-
if (empty($filename)) {
46+
$filePath = $process->getOutput();
47+
if (empty($filePath)) {
4848
throw new NotFoundHttpException('No output file available for this export');
4949
}
5050

51-
$filePath = $this->exportDirectory . '/' . $filename;
52-
5351
if (!file_exists($filePath)) {
54-
throw new NotFoundHttpException(sprintf('Export file "%s" does not exist', $filename));
52+
throw new NotFoundHttpException(sprintf('Export file "%s" does not exist', basename($filePath)));
5553
}
5654

5755
$response = new BinaryFileResponse($filePath);
56+
57+
// Sanitize filename by removing invalid characters
58+
$sanitizedResource = str_replace(['/', '\\', '.'], '_', $process->getResource());
59+
$sanitizedUuid = str_replace(['/', '\\'], '_', $process->getUuid());
60+
5861
$response->setContentDisposition(
5962
ResponseHeaderBag::DISPOSITION_ATTACHMENT,
60-
sprintf('export_%s_%s.%s', $process->getResource(), $process->getUuid(), $process->getFormat()),
63+
sprintf('export_%s_%s.%s', $sanitizedResource, $sanitizedUuid, $process->getFormat()),
6164
);
6265

6366
return $response;

src/Entity/Process.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ class Process implements ProcessInterface
3333

3434
protected string $output;
3535

36+
protected ?string $errorMessage = null;
37+
3638
public function getId(): string
3739
{
3840
return $this->uuid;
@@ -107,4 +109,14 @@ public function setOutput(string $output): void
107109
{
108110
$this->output = $output;
109111
}
112+
113+
public function getErrorMessage(): ?string
114+
{
115+
return $this->errorMessage;
116+
}
117+
118+
public function setErrorMessage(?string $errorMessage): void
119+
{
120+
$this->errorMessage = $errorMessage;
121+
}
110122
}

src/Entity/ProcessInterface.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,8 @@ public function setResourceIds(array $resourceIds): void;
4949
public function getOutput(): string;
5050

5151
public function setOutput(string $output): void;
52+
53+
public function getErrorMessage(): ?string;
54+
55+
public function setErrorMessage(?string $errorMessage): void;
5256
}

src/Messenger/Handler/ExportCommandHandler.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,10 @@ public function __invoke(ExportCommand $command): void
5454
$process->setOutput($outputPath);
5555
} catch (ExportFailedException $e) {
5656
$process->setStatus('failed');
57-
$process->setOutput($e->getTraceAsString());
57+
$process->setErrorMessage($e->getMessage());
58+
} catch (\Throwable $e) {
59+
$process->setStatus('failed');
60+
$process->setErrorMessage($e->getMessage());
5861
}
5962
}
6063
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{% if data.status == 'success' and data.output %}
2+
<a href="{{ path(options.link.route, options.link.parameters) }}" class="btn btn-sm btn-outline-primary">
3+
<i class="icon icon-download"></i>
4+
{{ action.label|trans }}
5+
</a>
6+
{% endif %}

templates/admin/import_export/show/content/sections/general/output.html.twig

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,25 @@
33
<div class="row">
44
<div class="col-12 col-md-4 fw-bold">{{ 'sylius_grid_import_export.ui.output_file'|trans }}</div>
55
<div class="col-12 col-md-8">
6-
{% if resource.output and resource.status == 'success' %}
7-
<a href="{{ path('sylius_grid_import_export_admin_process_download', {'uuid': resource.uuid}) }}" class="btn btn-sm btn-outline-primary">
8-
<i class="icon icon-download"></i>
9-
{{ resource.output }}
10-
</a>
11-
{% elseif resource.output %}
12-
<span class="text-muted">{{ resource.output }}</span>
13-
<small class="text-muted d-block">{{ 'sylius_grid_import_export.ui.file_not_ready'|trans }}</small>
6+
{% if resource.status == 'success' and resource.output %}
7+
<span class="text-success">
8+
<i class="icon icon-check"></i>
9+
{{ resource.output|split('/')|last }}
10+
</span>
11+
{% elseif resource.status == 'failed' %}
12+
<span class="text-danger">
13+
<i class="icon icon-exclamation-triangle"></i>
14+
{{ 'sylius_grid_import_export.ui.export_failed'|trans }}
15+
{% if resource.errorMessage %}
16+
<br>
17+
<small class="text-muted">{{ resource.errorMessage }}</small>
18+
{% endif %}
19+
</span>
20+
{% elseif resource.status == 'processing' %}
21+
<span class="text-muted">
22+
<i class="icon icon-spinner icon-spin"></i>
23+
{{ 'sylius_grid_import_export.ui.processing'|trans }}
24+
</span>
1425
{% else %}
1526
<em>{{ 'sylius_grid_import_export.ui.no_output'|trans }}</em>
1627
{% endif %}

0 commit comments

Comments
 (0)