Skip to content

Commit eed11b4

Browse files
committed
add expression field
1 parent e2902ed commit eed11b4

28 files changed

+676
-5
lines changed

.github/workflows/build.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ jobs:
8484
composer analyse
8585
(cd src/Component && composer validate --strict)
8686
87+
-
88+
name: Run spec tests
89+
run: vendor/bin/phpspec run
90+
8791
-
8892
name: Run component tests
8993
run: (cd src/Component && vendor/bin/phpspec run)

composer.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"symfony/dependency-injection": "^5.4 || ^6.4 || ^7.0",
3636
"symfony/deprecation-contracts": "^2.2 || ^3.1",
3737
"symfony/event-dispatcher": "^5.4 || ^6.4 || ^7.0",
38+
"symfony/expression-language": "^5.4 || ^6.4 || ^7.0",
3839
"symfony/form": "^5.4 || ^6.4 || ^7.0",
3940
"symfony/framework-bundle": "^5.4 || ^6.4 || ^7.0",
4041
"symfony/http-kernel": "^5.4 || ^6.4 || ^7.0",
@@ -97,11 +98,11 @@
9798
},
9899
"autoload-dev": {
99100
"psr-4": {
100-
"Sylius\\Bundle\\GridBundle\\spec\\": "src/Bundle/spec/",
101-
"Sylius\\Component\\Grid\\spec\\": "src/Component/spec/",
102101
"App\\": "tests/Application/src/",
103102
"AppBundle\\": "src/Bundle/test/src/AppBundle/",
104-
"App\\Tests\\Tmp\\": "tests/Application/tmp/"
103+
"App\\Tests\\Tmp\\": "tests/Application/tmp/",
104+
"spec\\Sylius\\Bundle\\GridBundle\\": "src/Bundle/spec/",
105+
"spec\\Sylius\\Component\\Grid\\": "src/Component/spec/"
105106
}
106107
},
107108
"config": {
@@ -121,7 +122,6 @@
121122
"vendor/bin/ecs check --fix"
122123
],
123124
"test-yaml-config": [
124-
"vendor/bin/phpspec run --ansi --no-interaction",
125125
"APP_ENV=test_grids_with_yaml_config vendor/bin/phpunit --colors=always"
126126
],
127127
"test-php-config": [

docs/field_types.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,3 +319,78 @@ $field->setOptions([
319319
// Your options here
320320
]);
321321
```
322+
323+
Expression
324+
----------
325+
326+
The **Expression** field type provides flexibility by allowing you to specify an expression that will be evaluated on the fly.
327+
This feature uses Symfony's expression language, making it versatile and powerful without needing to create additional callbacks or templates.
328+
329+
The expression will be evaluated with the following context:
330+
331+
- `value`: The value of the row.
332+
333+
You can also control whether special characters in the output are escaped by setting the `htmlspecialchars` option.
334+
By default, this is enabled, but you can disable it if the output contains HTML elements that should render as HTML.
335+
336+
<details open><summary>Yaml</summary>
337+
338+
```yaml
339+
# config/packages/sylius_grid.yaml
340+
341+
sylius_grid:
342+
grids:
343+
app_user:
344+
fields:
345+
price:
346+
type: expression
347+
label: app.ui.price
348+
options:
349+
expression: 'value ~ "$"'
350+
role:
351+
type: expression
352+
label: app.ui.price
353+
options:
354+
expression: '"<strong>" ~ value ~ "</strong>"'
355+
htmlspecialchars: false
356+
most_exensive_order_total:
357+
type: expression
358+
label: app.ui.most_exensive_order_total
359+
options:
360+
expression: 'container.get("sylius.repository.order").findMostExpensiveOrder(value).getTotal()'
361+
```
362+
363+
</details>
364+
365+
<details open><summary>PHP</summary>
366+
367+
```php
368+
<?php
369+
// config/packages/sylius_grid.php
370+
371+
use Sylius\Bundle\GridBundle\Builder\Field\ExpressionField;
372+
use Sylius\Bundle\GridBundle\Builder\GridBuilder;
373+
use Sylius\Bundle\GridBundle\Config\GridConfig;
374+
375+
return static function (GridConfig $grid): void {
376+
$grid->addGrid(GridBuilder::create('app_user', '%app.model.user.class%')
377+
->addField(
378+
ExpressionField::create('price', 'value ~ "$"')
379+
->setLabel('app.ui.price')
380+
)
381+
->addField(
382+
ExpressionField::create('role', '"<strong>" ~ value ~ "</strong>"', htmlspecialchars: false)
383+
->setLabel('app.ui.role')
384+
)
385+
->addField(
386+
ExpressionField::create(
387+
'most_expensive_order_total',
388+
'container.get("sylius.repository.order").findMostExpensiveOrder(value).getTotal()',
389+
)
390+
->setLabel('app.ui.most_exensive_order_total')
391+
)
392+
);
393+
};
394+
```
395+
396+
</details>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Sylius package.
5+
*
6+
* (c) Sylius Sp. z o.o.
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace Sylius\Bundle\GridBundle\Builder\Field;
15+
16+
final class ExpressionField
17+
{
18+
public static function create(
19+
string $name,
20+
string $expression,
21+
bool $htmlspecialchars = true,
22+
): FieldInterface {
23+
return Field::create($name, 'expression')
24+
->setOption('expression', $expression)
25+
->setOption('htmlspecialchars', $htmlspecialchars)
26+
;
27+
}
28+
}

src/Bundle/DependencyInjection/SyliusGridExtension.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,12 @@
1616
use Sylius\Bundle\CurrencyBundle\SyliusCurrencyBundle;
1717
use Sylius\Bundle\GridBundle\Grid\GridInterface;
1818
use Sylius\Bundle\GridBundle\SyliusGridBundle;
19+
use Sylius\Component\Grid\Attribute\AsExpressionProvider;
20+
use Sylius\Component\Grid\Attribute\AsExpressionVariables;
1921
use Sylius\Component\Grid\Data\DataProviderInterface;
2022
use Sylius\Component\Grid\Filtering\FilterInterface;
2123
use Symfony\Component\Config\FileLocator;
24+
use Symfony\Component\DependencyInjection\ChildDefinition;
2225
use Symfony\Component\DependencyInjection\ContainerBuilder;
2326
use Symfony\Component\DependencyInjection\Extension\Extension;
2427
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
@@ -67,6 +70,20 @@ public function load(array $configs, ContainerBuilder $container): void
6770
$container->registerForAutoconfiguration(DataProviderInterface::class)
6871
->addTag('sylius.grid_data_provider')
6972
;
73+
74+
$container->registerAttributeForAutoconfiguration(
75+
AsExpressionVariables::class,
76+
static function (ChildDefinition $definition, AsExpressionVariables $attribute, \Reflector $reflector): void {
77+
$definition->addTag(AsExpressionVariables::SERVICE_TAG);
78+
},
79+
);
80+
81+
$container->registerAttributeForAutoconfiguration(
82+
AsExpressionProvider::class,
83+
static function (ChildDefinition $definition, AsExpressionProvider $attribute, \Reflector $reflector): void {
84+
$definition->addTag(AsExpressionProvider::SERVICE_TAG);
85+
},
86+
);
7087
}
7188

7289
public function getConfiguration(array $config, ContainerBuilder $container): Configuration

src/Bundle/Resources/config/services.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
1515
<imports>
16+
<import resource="services/expression_language.xml" />
1617
<import resource="services/field_types.xml" />
1718
<import resource="services/filters.xml" />
1819
<import resource="services/templating.xml" />
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
3+
<!--
4+
This file is part of the Sylius package.
5+
(c) Sylius Sp. z o.o.
6+
For the full copyright and license information, please view the LICENSE
7+
file that was distributed with this source code.
8+
-->
9+
10+
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
11+
<services>
12+
<service id="sylius_grid.expression_language.factory" class="Sylius\Component\Grid\ExpressionLanguage\ExpressionLanguageFactory">
13+
<argument type="tagged_iterator" tag="sylius.grid.provider" />
14+
</service>
15+
<service id="Sylius\Component\Grid\ExpressionLanguage\ExpressionLanguageFactory" alias="sylius_grid.expression_language.factory" />
16+
17+
<service id="sylius_grid.expression_language" class="Symfony\Component\ExpressionLanguage\ExpressionLanguage">
18+
<factory service="sylius_grid.expression_language.factory" />
19+
</service>
20+
21+
<service id="sylius_grid.expression_language.expression_evaluator" class="Sylius\Component\Grid\ExpressionLanguage\ExpressionEvaluator">
22+
<argument type="service" id="sylius_grid.expression_language" />
23+
<argument type="service" id="sylius_grid.expression_language.variables_collection_aggregate" />
24+
</service>
25+
<service id="Sylius\Component\Grid\ExpressionLanguage\ExpressionEvaluator" alias="sylius_grid.expression_language.expression_evaluator" />
26+
27+
<service id="sylius_grid.expression_language.variables_collection_aggregate" class="Sylius\Component\Grid\ExpressionLanguage\VariablesCollectionAggregate">
28+
<argument type="tagged_iterator" tag="sylius.grid.variables" />
29+
</service>
30+
<service id="Sylius\Component\Grid\ExpressionLanguage\VariablesCollectionAggregate" alias="sylius_grid.expression_language.variables_collection_aggregate" />
31+
</services>
32+
</container>

src/Bundle/Resources/config/services/field_types.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@
2222
</service>
2323
<service id="Sylius\Component\Grid\FieldTypes\DatetimeFieldType" alias="sylius.grid_field.datetime" />
2424

25+
<service id="sylius.grid_field.expression" class="Sylius\Component\Grid\FieldTypes\ExpressionFieldType">
26+
<argument type="service" id="sylius.grid.data_extractor" />
27+
<argument type="service" id="sylius_grid.expression_language.expression_evaluator" />
28+
<tag name="sylius.grid_field" type="expression" />
29+
</service>
30+
<service id="Sylius\Component\Grid\FieldTypes\ExpressionFieldType" alias="sylius.grid_field.expression" />
31+
2532
<service id="sylius.grid_field.string" class="Sylius\Component\Grid\FieldTypes\StringFieldType">
2633
<argument type="service" id="sylius.grid.data_extractor" />
2734
<tag name="sylius.grid_field" type="string" />

src/Bundle/Tests/Functional/GridUiTest.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,19 @@ public function it_filters_books_by_title(): void
101101
$this->assertSame('Book 5', $titles[0]);
102102
}
103103

104+
/** @test */
105+
public function it_shows_books_prices(): void
106+
{
107+
$this->client->request('GET', '/books/');
108+
109+
$prices = $this->getBookPriceFromResponse();
110+
111+
$this->assertListContainsOnly($prices, [
112+
'42 €',
113+
'10 £',
114+
]);
115+
}
116+
104117
/** @test */
105118
public function it_filters_books_by_title_with_contains(): void
106119
{
@@ -274,6 +287,16 @@ private function getBookAuthorNationalitiesFromResponse(): array
274287
);
275288
}
276289

290+
/** @return string[] */
291+
private function getBookPriceFromResponse(): array
292+
{
293+
return $this->getCrawler()
294+
->filter('[data-test-price]')
295+
->each(
296+
fn (Crawler $node): string => $node->text(),
297+
);
298+
}
299+
277300
/** @return string[] */
278301
private function getAuthorNamesFromResponse(): array
279302
{
@@ -293,4 +316,14 @@ protected function buildMatcher(): Matcher
293316
{
294317
return $this->matcherFactory->createMatcher(new VoidBacktrace());
295318
}
319+
320+
private function assertListContainsOnly(array $list, array $allowedValues)
321+
{
322+
foreach ($list as $item) {
323+
$this->assertTrue(
324+
in_array($item, $allowedValues, true),
325+
"Item '$item' is not in the allowed list.",
326+
);
327+
}
328+
}
296329
}

src/Bundle/Tests/Provider/ServiceGridProviderTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public function test_grids_inheritance(): void
3535
$this->assertEquals([
3636
'title',
3737
'author',
38+
'price',
3839
'id',
3940
], array_keys($gridDefinition->getFields()));
4041

0 commit comments

Comments
 (0)