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 .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ jobs:
${{ steps.composer-cache.outputs.dir }}
key: ${{ github.run_id }}-${{ runner.os }}-${{ hashFiles('composer.json') }}-symfony-${{ matrix.symfony }}

- name: 'Remove portphp/csv if needed'
if: matrix.symfony == '^8.0'
run: composer remove portphp/csv --no-update

- name: "Install dependencies"
run: composer update --no-interaction --no-scripts

Expand Down
6 changes: 6 additions & 0 deletions app/Entity/Book.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

use App\Grid\BookGrid;
use App\Repository\BookRepository;
use App\Responder\ExportGridToCsvResponder;
use Doctrine\ORM\Mapping as ORM;
use Sylius\Component\Resource\Annotation\SyliusCrudRoutes;
use Sylius\Component\Resource\Model\ResourceInterface;
Expand All @@ -40,6 +41,11 @@
template: '@SyliusAdminUi/crud/index.html.twig',
shortName: 'withoutGrid',
),
new Index(
shortName: 'export',
responder: ExportGridToCsvResponder::class,
grid: BookGrid::class,
),
new Delete(),
new BulkDelete(),
new Show(),
Expand Down
7 changes: 7 additions & 0 deletions app/Grid/BookGrid.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
namespace App\Grid;

use App\Entity\Book;
use Sylius\Bundle\GridBundle\Builder\Action\Action;
use Sylius\Bundle\GridBundle\Builder\Action\CreateAction;
use Sylius\Bundle\GridBundle\Builder\Action\DeleteAction;
use Sylius\Bundle\GridBundle\Builder\Action\ShowAction;
Expand Down Expand Up @@ -55,6 +56,12 @@ public function buildGrid(GridBuilderInterface $gridBuilder): void
->addActionGroup(
MainActionGroup::create(
CreateAction::create(),
Action::create(name: 'export', type: 'export')
->setOptions([
'link' => [
'route' => 'app_admin_book_export',
],
]),
),
)
->addActionGroup(
Expand Down
129 changes: 129 additions & 0 deletions app/Responder/ExportGridToCsvResponder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace App\Responder;

use Pagerfanta\PagerfantaInterface;
use Port\Csv\CsvWriter;
use Sylius\Component\Grid\Definition\Field;
use Sylius\Component\Grid\Renderer\GridRendererInterface;
use Sylius\Component\Grid\View\GridViewInterface;
use Sylius\Resource\Context\Context;
use Sylius\Resource\Metadata\Operation;
use Sylius\Resource\State\ResponderInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Contracts\Translation\TranslatorInterface;
use Webmozart\Assert\Assert;

final class ExportGridToCsvResponder implements ResponderInterface
{
public function __construct(
#[Autowire(service: 'sylius.grid.renderer')]
private readonly GridRendererInterface $gridRenderer,
private readonly TranslatorInterface $translator,
) {
}

/**
* @param GridViewInterface $data
*/
public function respond(mixed $data, Operation $operation, Context $context): mixed
{
Assert::isInstanceOf($data, GridViewInterface::class);

$response = new StreamedResponse(function () use ($data) {
$output = fopen('php://output', 'w');

if (false === $output) {
throw new \RuntimeException('Unable to open output stream.');
}

$writer = new CsvWriter();
$writer->setStream($output);

$fields = $this->sortFields($data->getDefinition()->getFields());
$this->writeHeaders($writer, $fields);
$this->writeRows($writer, $fields, $data);

$writer->finish();
});

$response->headers->set('Content-Type', 'text/csv; charset=UTF-8');
$response->headers->set('Content-Disposition', 'attachment; filename="export.csv"');

return $response;
}

/**
* @param Field[] $fields
*/
private function writeHeaders(CsvWriter $writer, array $fields): void
{
$labels = array_map(fn (Field $field) => $this->translator->trans($field->getLabel()), $fields);

$writer->writeItem($labels);
}

/**
* @param Field[] $fields
*/
private function writeRows(CsvWriter $writer, array $fields, GridViewInterface $gridView): void
{
/** @var PagerfantaInterface $paginator */
$paginator = $gridView->getData();
Assert::isInstanceOf($paginator, PagerfantaInterface::class);

for ($currentPage = 1; $currentPage <= $paginator->getNbPages(); ++$currentPage) {
$paginator->setCurrentPage($currentPage);
$this->writePageResults($writer, $fields, $gridView, $paginator->getCurrentPageResults());
}
}

/**
* @param Field[] $fields
* @param iterable<object> $pageResults
*/
private function writePageResults(CsvWriter $writer, array $fields, GridViewInterface $gridView, iterable $pageResults): void
{
foreach ($pageResults as $resource) {
$rows = [];
foreach ($fields as $field) {
$rows[] = $this->getFieldValue($gridView, $field, $resource);
}
$writer->writeItem($rows);
}
}

private function getFieldValue(GridViewInterface $gridView, Field $field, object $data): string
{
$renderedData = $this->gridRenderer->renderField($gridView, $field, $data);
$renderedData = str_replace(\PHP_EOL, '', $renderedData);

return trim(strip_tags($renderedData));
}

/**
* @param Field[] $fields
*
* @return Field[]
*/
private function sortFields(array $fields): array
{
$sortedFields = $fields;

uasort($sortedFields, fn (Field $fieldA, Field $fieldB) => $fieldA->getPosition() <=> $fieldB->getPosition());

return $sortedFields;
}
}
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"laminas/laminas-stdlib": "^3.18",
"pagerfanta/doctrine-orm-adapter": "^4.6",
"pagerfanta/twig": "^4.6",
"portphp/csv": "^2.0",
"sylius/grid-bundle": "^1.13 || ^1.15@alpha",
"sylius/resource-bundle": "^1.13 || ^1.14@alpha",
"symfony/asset": "^6.4 || ^7.4 || ^8.0",
Expand Down Expand Up @@ -109,7 +110,7 @@
},
"extra": {
"symfony": {
"require": "8.0.*"
"require": "7.4.*"
}
},
"scripts": {
Expand Down
2 changes: 2 additions & 0 deletions config/packages/sylius_grid.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
sylius_grid:
templates:
action:
export: 'shared/grid/action/export.html.twig'
filter:
'App\Grid\Filter\SpeakerFilter': '@SyliusBootstrapAdminUi/shared/grid/filter/entity.html.twig'
Binary file added docs/.gitbook/assets/grid_export.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions docs/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* [Customizing the page titles](cookbook/admin_panel/page_titles.md)
* [Customizing the metatags](cookbook/admin_panel/metatags.md)
* [Using autocompletes](cookbook/admin_panel/using-autocompletes.md)
* [Exporting grid data](cookbook/admin_panel/grid_export.md)
* [How to use in a DDD architecture](cookbook/ddd_architecture.md)
* [Architecture overview](cookbook/ddd_architecture/overview.md)
* [Resource configuration](cookbook/ddd_architecture/resource_configuration.md)
Expand Down
2 changes: 1 addition & 1 deletion docs/cookbook/admin_panel.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@
* [Customizing the page titles](admin_panel/page_titles.md)
* [Customizing the metatags](admin_panel/metatags.md)
* [Using autocompletes](admin_panel/using-autocompletes.md)

* [Exporting grid data](admin_panel/grid_export.md)
Loading