diff --git a/docs/field_types.md b/docs/field_types.md
index dd69877a..673179f5 100644
--- a/docs/field_types.md
+++ b/docs/field_types.md
@@ -326,8 +326,34 @@ Callback
The Callback column aims to offer almost as much flexibility as the Twig column, but without requiring the creation of a template.
You simply need to specify a callback, which allows you to transform the 'data' variable on the fly.
-By default it uses the name of the field, but you can specify the path
-alternatively. For example:
+When defining callbacks in YAML, only string representations of callables are supported.
+When configuring grids using PHP (as opposed to service grid configuration), both string and array callables are supported. However, closures cannot be used due to restrictions in Symfony's configuration (values of type "Closure" are not permitted in service configuration files).
+By contrast, when configuring grids with service definitions, you can use both callables and closures.
+
+Here are some examples of what you can do:
+
+Yaml
+
+```yaml
+# config/packages/sylius_grid.yaml
+
+sylius_grid:
+ grids:
+ app_user:
+ fields:
+ id:
+ type: callback
+ options:
+ callback: "callback:App\\Helper\\GridHelper::addHashPrefix"
+ label: app.ui.id
+ name:
+ type: callback
+ options:
+ callback: "callback:strtoupper"
+ label: app.ui.name
+```
+
+
PHP
@@ -342,14 +368,18 @@ use Sylius\Bundle\GridBundle\Config\GridConfig;
return static function (GridConfig $grid): void {
$grid->addGrid(GridBuilder::create('app_user', '%app.model.user.class%')
->addField(
- CallbackField::create('roles' fn (array $roles): string => implode(', ', $roles))
- ->setLabel('app.ui.roles') // # each filed type can have a label, we suggest using translation keys instead of messages
- ->setPath('roles')
+ CallbackField::create('id', 'App\\Helper\\GridHelper::addHashPrefix')
+ ->setLabel('app.ui.id')
)
+ // or
->addField(
- CallbackField::create('status' fn (array $status): string => "$status", false) // the third argument allows to disable htmlspecialchars if set to false
- ->setLabel('app.ui.status') // # each filed type can have a label, we suggest using translation keys instead of messages
- ->setPath('status')
+ CallbackField::create('id', ['App\\Helper\\GridHelper', 'addHashPrefix'])
+ ->setLabel('app.ui.id')
+ )
+
+ ->addField(
+ CallbackField::create('name', 'strtoupper')
+ ->setLabel('app.ui.name')
)
)
};
@@ -382,14 +412,16 @@ final class UserGrid extends AbstractGrid implements ResourceAwareGridInterface
{
$gridBuilder
->addField(
- CallbackField::create('roles' fn (array $roles): string => implode(', ', $roles))
- ->setLabel('app.ui.roles') // # each filed type can have a label, we suggest using translation keys instead of messages
- ->setPath('roles')
+ CallbackField::create('id', GridHelper::addHashPrefix(...))
+ ->setLabel('app.ui.id')
)
->addField(
- CallbackField::create('status' fn (array $status): string => "$status", false) // the third argument allows to disable htmlspecialchars if set to false
- ->setLabel('app.ui.status') // # each filed type can have a label, we suggest using translation keys instead of messages
- ->setPath('status')
+ CallbackField::create('name', 'strtoupper')
+ ->setLabel('app.ui.name')
+ )
+ ->addField(
+ CallbackField::create('roles' fn (array $roles): string => implode(', ', $roles))
+ ->setLabel('app.ui.roles')
)
;
}
@@ -402,6 +434,3 @@ final class UserGrid extends AbstractGrid implements ResourceAwareGridInterface
```
-
-This configuration will display each role of a customer separated with a comma.
-
diff --git a/src/Bundle/Parser/OptionsParser.php b/src/Bundle/Parser/OptionsParser.php
new file mode 100644
index 00000000..b09d0c0e
--- /dev/null
+++ b/src/Bundle/Parser/OptionsParser.php
@@ -0,0 +1,62 @@
+parseOptions($parameter);
+ }
+
+ return $this->parseOption($parameter);
+ },
+ $parameters,
+ );
+ }
+
+ /**
+ * @param mixed $parameter
+ *
+ * @return mixed
+ */
+ private function parseOption($parameter)
+ {
+ if (!is_string($parameter)) {
+ return $parameter;
+ }
+
+ if (0 === strpos($parameter, 'callback:')) {
+ return $this->parseOptionCallback(substr($parameter, 9));
+ }
+
+ return $parameter;
+ }
+
+ /**
+ * @return mixed
+ */
+ private function parseOptionCallback(string $callback): \Closure
+ {
+ return $callback(...);
+ }
+}
diff --git a/src/Bundle/Parser/OptionsParserInterface.php b/src/Bundle/Parser/OptionsParserInterface.php
new file mode 100644
index 00000000..2e988190
--- /dev/null
+++ b/src/Bundle/Parser/OptionsParserInterface.php
@@ -0,0 +1,19 @@
+fieldsRegistry = $fieldsRegistry;
$this->formFactory = $formFactory;
$this->formTypeRegistry = $formTypeRegistry;
+ $this->optionsParser = $optionsParser;
$this->defaultTemplate = $defaultTemplate;
$this->actionTemplates = $actionTemplates;
$this->filterTemplates = $filterTemplates;
@@ -71,7 +76,8 @@ public function renderField(GridViewInterface $gridView, Field $field, $data)
$fieldType = $this->fieldsRegistry->get($field->getType());
$resolver = new OptionsResolver();
$fieldType->configureOptions($resolver);
- $options = $resolver->resolve($field->getOptions());
+
+ $options = $resolver->resolve($this->optionsParser->parseOptions($field->getOptions()));
return $fieldType->render($field, $data, $options);
}
diff --git a/src/Bundle/Resources/config/services.xml b/src/Bundle/Resources/config/services.xml
index 348f777b..b88489f7 100644
--- a/src/Bundle/Resources/config/services.xml
+++ b/src/Bundle/Resources/config/services.xml
@@ -128,5 +128,8 @@
+
+
+
diff --git a/src/Bundle/Resources/config/services/twig.xml b/src/Bundle/Resources/config/services/twig.xml
index daabcc3a..987e95cf 100644
--- a/src/Bundle/Resources/config/services/twig.xml
+++ b/src/Bundle/Resources/config/services/twig.xml
@@ -20,6 +20,7 @@
+
@SyliusGrid/_grid.html.twig
%sylius.grid.templates.action%
%sylius.grid.templates.filter%
diff --git a/src/Bundle/Tests/Functional/GridUiTest.php b/src/Bundle/Tests/Functional/GridUiTest.php
index 627768e1..90723e68 100644
--- a/src/Bundle/Tests/Functional/GridUiTest.php
+++ b/src/Bundle/Tests/Functional/GridUiTest.php
@@ -41,6 +41,20 @@ public function it_shows_authors_grid(): void
$this->assertCount(10, $this->getAuthorNamesFromResponse());
}
+ /** @test */
+ public function it_shows_authors_ids(): void
+ {
+ $this->client->request('GET', '/authors/?limit=100');
+
+ $ids = $this->getAuthorIdsFromResponse();
+
+ $this->assertNotEmpty($ids);
+ $this->assertSame(
+ array_filter($ids, fn (string $id) => str_starts_with($id, '#')),
+ $ids,
+ );
+ }
+
/** @test */
public function it_sorts_authors_by_name_ascending_by_default(): void
{
@@ -98,7 +112,7 @@ public function it_filters_books_by_title(): void
$titles = $this->getBookTitlesFromResponse();
$this->assertCount(1, $titles);
- $this->assertSame('Book 5', $titles[0]);
+ $this->assertSame('BOOK 5', $titles[0]);
}
/** @test */
@@ -112,7 +126,7 @@ public function it_filters_books_by_title_with_contains(): void
$titles = $this->getBookTitlesFromResponse();
$this->assertCount(1, $titles);
- $this->assertSame('Jurassic Park', $titles[0]);
+ $this->assertSame('JURASSIC PARK', $titles[0]);
}
/** @test */
@@ -125,7 +139,7 @@ public function it_filters_books_by_author(): void
$titles = $this->getBookTitlesFromResponse();
$this->assertCount(2, $titles);
- $this->assertSame('Jurassic Park', $titles[0]);
+ $this->assertSame('JURASSIC PARK', $titles[0]);
}
/** @test */
@@ -139,7 +153,7 @@ public function it_filters_books_by_authors(): void
$titles = $this->getBookTitlesFromResponse();
$this->assertCount(3, $titles);
- $this->assertSame('A Study in Scarlet', $titles[0]);
+ $this->assertSame('A STUDY IN SCARLET', $titles[0]);
}
/** @test */
@@ -152,7 +166,7 @@ public function it_filters_books_by_authors_nationality(): void
$titles = $this->getBookTitlesFromResponse();
$this->assertCount(2, $titles);
- $this->assertSame('Jurassic Park', $titles[0]);
+ $this->assertSame('JURASSIC PARK', $titles[0]);
}
/** @test */
@@ -165,7 +179,7 @@ public function it_filters_books_by_author_and_currency(): void
$titles = $this->getBookTitlesFromResponse();
$this->assertCount(1, $titles);
- $this->assertSame('Jurassic Park', $titles[0]);
+ $this->assertSame('JURASSIC PARK', $titles[0]);
}
/** @test */
@@ -274,6 +288,16 @@ private function getBookAuthorNationalitiesFromResponse(): array
);
}
+ /** @return string[] */
+ private function getAuthorIdsFromResponse(): array
+ {
+ return $this->getCrawler()
+ ->filter('[data-test-id]')
+ ->each(
+ fn (Crawler $node): string => $node->text(),
+ );
+ }
+
/** @return string[] */
private function getAuthorNamesFromResponse(): array
{
diff --git a/src/Bundle/spec/Renderer/TwigGridRendererSpec.php b/src/Bundle/spec/Renderer/TwigGridRendererSpec.php
index 1aeedb45..1f22f54a 100644
--- a/src/Bundle/spec/Renderer/TwigGridRendererSpec.php
+++ b/src/Bundle/spec/Renderer/TwigGridRendererSpec.php
@@ -16,6 +16,7 @@
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Sylius\Bundle\GridBundle\Form\Registry\FormTypeRegistryInterface;
+use Sylius\Bundle\GridBundle\Parser\OptionsParserInterface;
use Sylius\Component\Grid\Definition\Action;
use Sylius\Component\Grid\Definition\Field;
use Sylius\Component\Grid\FieldTypes\FieldTypeInterface;
@@ -35,6 +36,7 @@ function let(
ServiceRegistryInterface $fieldsRegistry,
FormFactoryInterface $formFactory,
FormTypeRegistryInterface $formTypeRegistry,
+ OptionsParserInterface $optionsParser,
): void {
$actionTemplates = [
'link' => '@SyliusGrid/Action/_link.html.twig',
@@ -49,6 +51,7 @@ function let(
$fieldsRegistry,
$formFactory,
$formTypeRegistry,
+ $optionsParser,
'"@SyliusGrid/default"',
$actionTemplates,
$filterTemplates,
@@ -94,6 +97,7 @@ function it_renders_a_field_with_data_via_appropriate_field_type(
Field $field,
ServiceRegistryInterface $fieldsRegistry,
FieldTypeInterface $fieldType,
+ OptionsParserInterface $optionsParser,
): void {
$field->getType()->willReturn('string');
$fieldsRegistry->get('string')->willReturn($fieldType);
@@ -106,6 +110,7 @@ function it_renders_a_field_with_data_via_appropriate_field_type(
$field->getOptions()->willReturn([
'foo' => 'bar',
]);
+ $optionsParser->parseOptions(['foo' => 'bar'])->willReturn(['foo' => 'bar']);
$fieldType->render($field, 'Value', ['foo' => 'bar'])->willReturn('Value');
$this->renderField($gridView, $field, 'Value')->shouldReturn('Value');
diff --git a/tests/Application/config/sylius/grids.yaml b/tests/Application/config/sylius/grids.yaml
index beb008f6..cd5affa1 100644
--- a/tests/Application/config/sylius/grids.yaml
+++ b/tests/Application/config/sylius/grids.yaml
@@ -33,7 +33,9 @@ sylius_grid:
title: asc
fields:
title:
- type: string
+ type: callback
+ options:
+ callback: "callback:strtoupper"
label: Title
sortable: ~
author:
@@ -60,10 +62,11 @@ sylius_grid:
name: asc
fields:
id:
- type: string
+ type: callback
+ options:
+ callback: "callback:App\\Helper\\GridHelper::addHashPrefix"
label: ID
sortable: ~
- enabled: false
name:
type: string
label: Name
diff --git a/tests/Application/config/sylius/grids/author.php b/tests/Application/config/sylius/grids/author.php
index 89e7203f..93c26c29 100644
--- a/tests/Application/config/sylius/grids/author.php
+++ b/tests/Application/config/sylius/grids/author.php
@@ -11,6 +11,7 @@
declare(strict_types=1);
+use Sylius\Bundle\GridBundle\Builder\Field\CallbackField;
use Sylius\Bundle\GridBundle\Builder\Field\StringField;
use Sylius\Bundle\GridBundle\Builder\Filter\StringFilter;
use Sylius\Bundle\GridBundle\Builder\GridBuilder;
@@ -22,9 +23,8 @@
->addFilter(StringFilter::create('name'))
->orderBy('name', 'asc')
->addField(
- StringField::create('id')
- ->setSortable(true)
- ->setEnabled(false),
+ CallbackField::create('id', ['App\\Helper\\GridHelper', 'addHashPrefix'])
+ ->setSortable(true),
)
->addField(
StringField::create('name')
diff --git a/tests/Application/config/sylius/grids/book.php b/tests/Application/config/sylius/grids/book.php
index 460521a3..66d929ad 100644
--- a/tests/Application/config/sylius/grids/book.php
+++ b/tests/Application/config/sylius/grids/book.php
@@ -14,6 +14,7 @@
use App\Entity\Author;
use App\Entity\Book;
use App\Grid\Builder\NationalityFilter;
+use Sylius\Bundle\GridBundle\Builder\Field\CallbackField;
use Sylius\Bundle\GridBundle\Builder\Field\StringField;
use Sylius\Bundle\GridBundle\Builder\Filter\EntityFilter;
use Sylius\Bundle\GridBundle\Builder\Filter\SelectFilter;
@@ -48,7 +49,7 @@
)
->orderBy('title', 'asc')
->addField(
- StringField::create('title')
+ CallbackField::create('title', 'strtoupper')
->setLabel('Title')
->setSortable(true),
)
diff --git a/tests/Application/src/Grid/AuthorGrid.php b/tests/Application/src/Grid/AuthorGrid.php
index f930efab..fbc05eec 100644
--- a/tests/Application/src/Grid/AuthorGrid.php
+++ b/tests/Application/src/Grid/AuthorGrid.php
@@ -13,6 +13,8 @@
namespace App\Grid;
+use App\Helper\GridHelper;
+use Sylius\Bundle\GridBundle\Builder\Field\CallbackField;
use Sylius\Bundle\GridBundle\Builder\Field\StringField;
use Sylius\Bundle\GridBundle\Builder\Filter\Filter;
use Sylius\Bundle\GridBundle\Builder\GridBuilderInterface;
@@ -44,9 +46,8 @@ public function buildGrid(GridBuilderInterface $gridBuilder): void
->addFilter(Filter::create('name', 'string'))
->orderBy('name', 'asc')
->addField(
- StringField::create('id')
- ->setSortable(true)
- ->setEnabled(false),
+ CallbackField::create('id', GridHelper::addHashPrefix(...))
+ ->setSortable(true),
)
->addField(
StringField::create('name')
diff --git a/tests/Application/src/Grid/BookGrid.php b/tests/Application/src/Grid/BookGrid.php
index 9c0ee24c..fa9e080d 100644
--- a/tests/Application/src/Grid/BookGrid.php
+++ b/tests/Application/src/Grid/BookGrid.php
@@ -22,6 +22,7 @@
use Sylius\Bundle\GridBundle\Builder\Action\UpdateAction;
use Sylius\Bundle\GridBundle\Builder\ActionGroup\ItemActionGroup;
use Sylius\Bundle\GridBundle\Builder\ActionGroup\MainActionGroup;
+use Sylius\Bundle\GridBundle\Builder\Field\CallbackField;
use Sylius\Bundle\GridBundle\Builder\Field\StringField;
use Sylius\Bundle\GridBundle\Builder\Filter\Filter;
use Sylius\Bundle\GridBundle\Builder\GridBuilderInterface;
@@ -73,7 +74,7 @@ public function buildGrid(GridBuilderInterface $gridBuilder): void
)
->orderBy('title', 'asc')
->addField(
- StringField::create('title')
+ CallbackField::create('title', 'strtoupper')
->setLabel('Title')
->setSortable(true),
)
diff --git a/tests/Application/src/Helper/GridHelper.php b/tests/Application/src/Helper/GridHelper.php
new file mode 100644
index 00000000..b4761edf
--- /dev/null
+++ b/tests/Application/src/Helper/GridHelper.php
@@ -0,0 +1,22 @@
+