Skip to content

Commit e73e717

Browse files
committed
add expression field
1 parent d654fe3 commit e73e717

File tree

5 files changed

+247
-0
lines changed

5 files changed

+247
-0
lines changed

docs/field_types.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,3 +319,82 @@ $field->setOptions([
319319
// Your options here
320320
]);
321321
```
322+
323+
Expression
324+
----------
325+
326+
The **Expression** column 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+
- `container`: The Symfony container (only if the `container` option is enabled).
333+
334+
You can also control whether special characters in the output are escaped by setting the `htmlspecialchars` option.
335+
By default, this is enabled, but you can disable it if the output contains HTML elements that should render as HTML.
336+
337+
<details open><summary>Yaml</summary>
338+
339+
```yaml
340+
# config/packages/sylius_grid.yaml
341+
342+
sylius_grid:
343+
grids:
344+
app_user:
345+
fields:
346+
price:
347+
type: expression
348+
label: app.ui.price
349+
options:
350+
expression: 'value ~ "$"'
351+
352+
role:
353+
type: expression
354+
label: app.ui.price
355+
options:
356+
expression: '"<strong>" ~ value ~ "</strong>"'
357+
htmlspecialchars: false
358+
359+
most_exensive_order_total:
360+
type: expression
361+
label: app.ui.most_exensive_order_total
362+
options:
363+
expression: 'container.get("sylius.repository.order").findMostExpensiveOrder(value).getTotal()'
364+
needContainer: true
365+
```
366+
367+
</details>
368+
369+
370+
<details open><summary>PHP</summary>
371+
<?php
372+
// config/packages/sylius_grid.php
373+
374+
use Sylius\Bundle\GridBundle\Builder\Field\ExpressionField;
375+
use Sylius\Bundle\GridBundle\Builder\GridBuilder;
376+
use Sylius\Bundle\GridBundle\Config\GridConfig;
377+
378+
return static function (GridConfig $grid): void {
379+
$grid->addGrid(GridBuilder::create('app_user', '%app.model.user.class%')
380+
->addField(
381+
ExpressionField::create('price', 'value ~ "$"')
382+
->setLabel('app.ui.price')
383+
)
384+
->addField(
385+
ExpressionField::create('role', '"<strong>" ~ value ~ "</strong>"', htmlspecialchars: false)
386+
->setLabel('app.ui.role')
387+
)
388+
->addField(
389+
ExpressionField::create(
390+
'most_expensive_order_total',
391+
'container.get("sylius.repository.order").findMostExpensiveOrder(value).getTotal()',
392+
needContainer: true,
393+
)
394+
->setLabel('app.ui.most_exensive_order_total')
395+
)
396+
);
397+
};
398+
```
399+
400+
</details>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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 $needsContainer = false,
22+
bool $htmlspecialchars = true,
23+
): FieldInterface
24+
{
25+
return Field::create($name, 'expression')
26+
->setOption('expression', $expression)
27+
->setOption('needsContainer', $needsContainer)
28+
->setOption('htmlspecialchars', $htmlspecialchars)
29+
;
30+
}
31+
}

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

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

25+
<service id="Sylius\Component\Grid\FieldTypes\ExpressionFieldType">
26+
<argument type="service" id="sylius.grid.data_extractor" />
27+
<argument type="service" id="sylius.expression_language" />
28+
<argument type="service" id="service_container" />
29+
<tag name="sylius.grid_field" type="expression" />
30+
</service>
31+
<service id="sylius.grid_field.expression" alias="Sylius\Component\Grid\FieldTypes\ExpressionFieldType" />
32+
2533
<service id="Sylius\Component\Grid\FieldTypes\StringFieldType">
2634
<argument type="service" id="sylius.grid.data_extractor" />
2735
<tag name="sylius.grid_field" type="string" />
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
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\Component\Grid\FieldTypes;
15+
16+
use Sylius\Bundle\ResourceBundle\ExpressionLanguage\ExpressionLanguage;
17+
use Sylius\Component\Grid\DataExtractor\DataExtractorInterface;
18+
use Sylius\Component\Grid\Definition\Field;
19+
use Symfony\Component\DependencyInjection\ContainerInterface;
20+
use Symfony\Component\OptionsResolver\OptionsResolver;
21+
22+
final class ExpressionFieldType implements FieldTypeInterface
23+
{
24+
public function __construct(
25+
private DataExtractorInterface $dataExtractor,
26+
private ExpressionLanguage $expressionLanguage,
27+
private ContainerInterface $container,
28+
)
29+
{
30+
}
31+
32+
public function render(Field $field, $data, array $options): string
33+
{
34+
$values = [
35+
'value' => $this->dataExtractor->get($field, $data),
36+
];
37+
38+
if ($options['needsContainer'] === true) {
39+
$values = array_merge($values, [
40+
'container' => $this->container,
41+
]);
42+
}
43+
44+
$value = (string) $this->expressionLanguage->evaluate($options['expression'], $values);
45+
46+
if ($options['htmlspecialchars']) {
47+
$value = htmlspecialchars($value);
48+
}
49+
50+
return $value;
51+
}
52+
53+
public function configureOptions(OptionsResolver $resolver): void
54+
{
55+
$resolver->setRequired('expression');
56+
$resolver->setAllowedTypes('expression', 'string');
57+
58+
$resolver->setDefault('needsContainer', false);
59+
$resolver->setAllowedTypes('needsContainer', 'bool');
60+
61+
$resolver->setDefault('htmlspecialchars', true);
62+
$resolver->setAllowedTypes('htmlspecialchars', 'bool');
63+
}
64+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
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 spec\Sylius\Component\Grid\FieldTypes;
15+
16+
use PhpSpec\ObjectBehavior;
17+
use Sylius\Bundle\ResourceBundle\ExpressionLanguage\ExpressionLanguage;
18+
use Sylius\Component\Grid\DataExtractor\DataExtractorInterface;
19+
use Sylius\Component\Grid\Definition\Field;
20+
use Sylius\Component\Grid\FieldTypes\FieldTypeInterface;
21+
use Symfony\Component\DependencyInjection\ContainerInterface;
22+
23+
final class ExpressionFieldTypeSpec extends ObjectBehavior
24+
{
25+
function let(
26+
DataExtractorInterface $dataExtractor,
27+
ContainerInterface $container,
28+
): void
29+
{
30+
$this->beConstructedWith(
31+
$dataExtractor,
32+
new ExpressionLanguage(),
33+
$container,
34+
);
35+
}
36+
37+
function it_is_a_grid_field_type(): void
38+
{
39+
$this->shouldImplement(FieldTypeInterface::class);
40+
}
41+
42+
function it_uses_data_extractor_to_obtain_data_and_evaluates_it_with_htmlspecialchars(
43+
DataExtractorInterface $dataExtractor,
44+
Field $field,
45+
): void {
46+
$dataExtractor->get($field, ['foo' => 5])->willReturn(5);
47+
48+
$this->render($field, ['foo' => 5], [
49+
'expression' => '"<strong>" ~ value * 100 ~ "$</strong>"',
50+
'htmlspecialchars' => true,
51+
])->shouldReturn('&lt;strong&gt;500$&lt;/strong&gt;');
52+
}
53+
54+
function it_uses_data_extractor_to_obtain_data_and_evaluates_it_without_htmlspecialchars(
55+
DataExtractorInterface $dataExtractor,
56+
Field $field,
57+
): void {
58+
$dataExtractor->get($field, ['foo' => 5])->willReturn(5);
59+
60+
$this->render($field, ['foo' => 5], [
61+
'expression' => '"<strong>" ~ value * 100 ~ "$</strong>"',
62+
'htmlspecialchars' => false,
63+
])->shouldReturn('<strong>500$</strong>');
64+
}
65+
}

0 commit comments

Comments
 (0)