Skip to content

Commit

Permalink
add expression field
Browse files Browse the repository at this point in the history
  • Loading branch information
Florian-Merle committed Nov 20, 2024
1 parent d654fe3 commit 3fe352e
Show file tree
Hide file tree
Showing 27 changed files with 672 additions and 3 deletions.
5 changes: 3 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"symfony/dependency-injection": "^5.4 || ^6.4 || ^7.0",
"symfony/deprecation-contracts": "^2.2 || ^3.1",
"symfony/event-dispatcher": "^5.4 || ^6.4 || ^7.0",
"symfony/expression-language": "^5.4 || ^6.0 || ^7.0",
"symfony/form": "^5.4 || ^6.4 || ^7.0",
"symfony/framework-bundle": "^5.4 || ^6.4 || ^7.0",
"symfony/http-kernel": "^5.4 || ^6.4 || ^7.0",
Expand Down Expand Up @@ -97,8 +98,8 @@
},
"autoload-dev": {
"psr-4": {
"Sylius\\Bundle\\GridBundle\\spec\\": "src/Bundle/spec/",
"Sylius\\Component\\Grid\\spec\\": "src/Component/spec/",
"spec\\Sylius\\Bundle\\GridBundle\\": "src/Bundle/spec/",
"spec\\Sylius\\Component\\Grid\\": "src/Component/spec/",
"App\\": "tests/Application/src/",
"AppBundle\\": "src/Bundle/test/src/AppBundle/",
"App\\Tests\\Tmp\\": "tests/Application/tmp/"
Expand Down
77 changes: 77 additions & 0 deletions docs/field_types.md
Original file line number Diff line number Diff line change
Expand Up @@ -319,3 +319,80 @@ $field->setOptions([
// Your options here
]);
```

Expression
----------

The **Expression** column provides flexibility by allowing you to specify an expression that will be evaluated on the fly.
This feature uses Symfony's expression language, making it versatile and powerful without needing to create additional callbacks or templates.

The expression will be evaluated with the following context:

- `value`: The value of the row.

You can also control whether special characters in the output are escaped by setting the `htmlspecialchars` option.
By default, this is enabled, but you can disable it if the output contains HTML elements that should render as HTML.

<details open><summary>Yaml</summary>

```yaml
# config/packages/sylius_grid.yaml

sylius_grid:
grids:
app_user:
fields:
price:
type: expression
label: app.ui.price
options:
expression: 'value ~ "$"'

role:
type: expression
label: app.ui.price
options:
expression: '"<strong>" ~ value ~ "</strong>"'
htmlspecialchars: false

most_exensive_order_total:
type: expression
label: app.ui.most_exensive_order_total
options:
expression: 'container.get("sylius.repository.order").findMostExpensiveOrder(value).getTotal()'
```
</details>
<details open><summary>PHP</summary>
```php
<?php
// config/packages/sylius_grid.php

use Sylius\Bundle\GridBundle\Builder\Field\ExpressionField;
use Sylius\Bundle\GridBundle\Builder\GridBuilder;
use Sylius\Bundle\GridBundle\Config\GridConfig;

return static function (GridConfig $grid): void {
$grid->addGrid(GridBuilder::create('app_user', '%app.model.user.class%')
->addField(
ExpressionField::create('price', 'value ~ "$"')
->setLabel('app.ui.price')
)
->addField(
ExpressionField::create('role', '"<strong>" ~ value ~ "</strong>"', htmlspecialchars: false)
->setLabel('app.ui.role')
)
->addField(
ExpressionField::create(
'most_expensive_order_total',
'container.get("sylius.repository.order").findMostExpensiveOrder(value).getTotal()',
)
->setLabel('app.ui.most_exensive_order_total')
)
);
};
```

</details>
28 changes: 28 additions & 0 deletions src/Bundle/Builder/Field/ExpressionField.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?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 Sylius\Bundle\GridBundle\Builder\Field;

final class ExpressionField
{
public static function create(
string $name,
string $expression,
bool $htmlspecialchars = true,
): FieldInterface {
return Field::create($name, 'expression')
->setOption('expression', $expression)
->setOption('htmlspecialchars', $htmlspecialchars)
;
}
}
17 changes: 17 additions & 0 deletions src/Bundle/DependencyInjection/SyliusGridExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@
use Sylius\Bundle\CurrencyBundle\SyliusCurrencyBundle;
use Sylius\Bundle\GridBundle\Grid\GridInterface;
use Sylius\Bundle\GridBundle\SyliusGridBundle;
use Sylius\Component\Grid\Attribute\AsExpressionProvider;
use Sylius\Component\Grid\Attribute\AsExpressionVariables;
use Sylius\Component\Grid\Data\DataProviderInterface;
use Sylius\Component\Grid\Filtering\FilterInterface;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\Extension;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
Expand Down Expand Up @@ -67,6 +70,20 @@ public function load(array $configs, ContainerBuilder $container): void
$container->registerForAutoconfiguration(DataProviderInterface::class)
->addTag('sylius.grid_data_provider')
;

$container->registerAttributeForAutoconfiguration(
AsExpressionVariables::class,
static function (ChildDefinition $definition, AsExpressionVariables $attribute, \Reflector $reflector): void {
$definition->addTag(AsExpressionVariables::SERVICE_TAG);
},
);

$container->registerAttributeForAutoconfiguration(
AsExpressionProvider::class,
static function (ChildDefinition $definition, AsExpressionProvider $attribute, \Reflector $reflector): void {
$definition->addTag(AsExpressionProvider::SERVICE_TAG);
},
);
}

public function getConfiguration(array $config, ContainerBuilder $container): Configuration
Expand Down
1 change: 1 addition & 0 deletions src/Bundle/Resources/config/services.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

<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">
<imports>
<import resource="services/expression_language.xml" />
<import resource="services/field_types.xml" />
<import resource="services/filters.xml" />
<import resource="services/templating.xml" />
Expand Down
33 changes: 33 additions & 0 deletions src/Bundle/Resources/config/services/expression_language.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>

<!--
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.
-->

<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">
<services>
<service id="sylius_grid.expression_language.factory" class="Sylius\Component\Grid\ExpressionLanguage\ExpressionLanguageFactory">
<argument type="tagged_iterator" tag="sylius.grid.provider" />
</service>

<service id="sylius_grid.expression_language" class="Symfony\Component\ExpressionLanguage\ExpressionLanguage">
<factory service="sylius_grid.expression_language.factory" />
</service>

<service id="sylius_grid.expression_language.expression_evaluator" class="Sylius\Component\Grid\ExpressionLanguage\ExpressionEvaluator">
<argument type="service" id="sylius_grid.expression_language" />
<argument type="service" id="sylius_grid.expression_language.variables_collection_aggregate" />
</service>

<service id="sylius_grid.expression_language.variables_collection_aggregate" class="Sylius\Component\Grid\ExpressionLanguage\VariablesCollectionAggregate">
<argument type="tagged_iterator" tag="sylius.grid.variables" />
</service>
</services>
</container>
7 changes: 7 additions & 0 deletions src/Bundle/Resources/config/services/field_types.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@
</service>
<service id="sylius.grid_field.datetime" alias="Sylius\Component\Grid\FieldTypes\DatetimeFieldType" />

<service id="Sylius\Component\Grid\FieldTypes\ExpressionFieldType">
<argument type="service" id="sylius.grid.data_extractor" />
<argument type="service" id="sylius_grid.expression_language.expression_evaluator" />
<tag name="sylius.grid_field" type="expression" />
</service>
<service id="sylius.grid_field.expression" alias="Sylius\Component\Grid\FieldTypes\ExpressionFieldType" />

<service id="Sylius\Component\Grid\FieldTypes\StringFieldType">
<argument type="service" id="sylius.grid.data_extractor" />
<tag name="sylius.grid_field" type="string" />
Expand Down
31 changes: 31 additions & 0 deletions src/Bundle/Tests/Functional/GridUiTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,27 @@ public function it_filters_books_by_title(): void
$this->assertSame('Book 5', $titles[0]);
}

/** @test */
public function it_shows_books_prices(): void
{
$this->client->request('GET', '/books/');

$prices = $this->getBookPriceFromResponse();

$this->assertEquals([
'42 €',
'10 £',
'42 €',
'10 £',
'42 €',
'42 €',
'42 €',
'10 £',
'42 €',
'42 €',
], $prices);
}

/** @test */
public function it_filters_books_by_title_with_contains(): void
{
Expand Down Expand Up @@ -274,6 +295,16 @@ private function getBookAuthorNationalitiesFromResponse(): array
);
}

/** @return string[] */
private function getBookPriceFromResponse(): array
{
return $this->getCrawler()
->filter('[data-test-price]')
->each(
fn (Crawler $node): string => $node->text(),
);
}

/** @return string[] */
private function getAuthorNamesFromResponse(): array
{
Expand Down
1 change: 1 addition & 0 deletions src/Bundle/Tests/Provider/ServiceGridProviderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public function test_grids_inheritance(): void
$this->assertEquals([
'title',
'author',
'price',
'id',
], array_keys($gridDefinition->getFields()));

Expand Down
20 changes: 20 additions & 0 deletions src/Component/Attribute/AsExpressionProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?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 Sylius\Component\Grid\Attribute;

#[\Attribute(\Attribute::TARGET_CLASS)]
final class AsExpressionProvider
{
public const SERVICE_TAG = 'sylius.grid.provider';
}
20 changes: 20 additions & 0 deletions src/Component/Attribute/AsExpressionVariables.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?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 Sylius\Component\Grid\Attribute;

#[\Attribute(\Attribute::TARGET_CLASS)]
final class AsExpressionVariables
{
public const SERVICE_TAG = 'sylius.grid.variables';
}
39 changes: 39 additions & 0 deletions src/Component/ExpressionLanguage/ExpressionEvaluator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?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 Sylius\Component\Grid\ExpressionLanguage;

use Symfony\Component\ExpressionLanguage\ExpressionLanguage;

/**
* @experimental
*/
final class ExpressionEvaluator implements ExpressionEvaluatorInterface
{
public function __construct(
private ExpressionLanguage $expressionLanguage,
private VariablesCollectionInterface $variablesCollection,
) {
}

public function evaluateExpression(string $expression, array $variables = []): mixed
{
return $this->expressionLanguage->evaluate(
$expression,
array_merge(
$this->variablesCollection->getCollection(),
$variables,
),
);
}
}
22 changes: 22 additions & 0 deletions src/Component/ExpressionLanguage/ExpressionEvaluatorInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?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 Sylius\Component\Grid\ExpressionLanguage;

/**
* @experimental
*/
interface ExpressionEvaluatorInterface
{
public function evaluateExpression(string $expression, array $variables = []): mixed;
}
Loading

0 comments on commit 3fe352e

Please sign in to comment.