Skip to content

Commit

Permalink
Introduce ExpressionEvaluator
Browse files Browse the repository at this point in the history
  • Loading branch information
Florian-Merle committed Nov 15, 2024
1 parent e73e717 commit 678c268
Show file tree
Hide file tree
Showing 22 changed files with 418 additions and 31 deletions.
3 changes: 0 additions & 3 deletions docs/field_types.md
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,6 @@ This feature uses Symfony's expression language, making it versatile and powerfu
The expression will be evaluated with the following context:

- `value`: The value of the row.
- `container`: The Symfony container (only if the `container` option is enabled).

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.
Expand Down Expand Up @@ -361,7 +360,6 @@ sylius_grid:
label: app.ui.most_exensive_order_total
options:
expression: 'container.get("sylius.repository.order").findMostExpensiveOrder(value).getTotal()'
needContainer: true
```
</details>
Expand Down Expand Up @@ -389,7 +387,6 @@ return static function (GridConfig $grid): void {
ExpressionField::create(
'most_expensive_order_total',
'container.get("sylius.repository.order").findMostExpensiveOrder(value).getTotal()',
needContainer: true,
)
->setLabel('app.ui.most_exensive_order_total')
)
Expand Down
5 changes: 1 addition & 4 deletions src/Bundle/Builder/Field/ExpressionField.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,10 @@ final class ExpressionField
public static function create(
string $name,
string $expression,
bool $needsContainer = false,
bool $htmlspecialchars = true,
): FieldInterface
{
): FieldInterface {
return Field::create($name, 'expression')
->setOption('expression', $expression)
->setOption('needsContainer', $needsContainer)
->setOption('htmlspecialchars', $htmlspecialchars)
;
}
Expand Down
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\AsGridProvider;
use Sylius\Component\Grid\Attribute\AsGridVariables;
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(
AsGridVariables::class,
static function (ChildDefinition $definition, AsGridVariables $attribute, \Reflector $reflector): void {
$definition->addTag(AsGridVariables::SERVICE_TAG);
},
);

$container->registerAttributeForAutoconfiguration(
AsGridProvider::class,
static function (ChildDefinition $definition, AsGridProvider $attribute, \Reflector $reflector): void {
$definition->addTag(AsGridProvider::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.expression_language.grid.expression_language_factory" class="Sylius\Component\Grid\ExpressionLanguage\ExpressionLanguageFactory">
<argument type="tagged_iterator" tag="sylius.grid.provider" />
</service>

<service id="sylius.expression_language.grid.expression_language" class="Symfony\Component\ExpressionLanguage\ExpressionLanguage">
<factory service="sylius.expression_language.grid.expression_language_factory" />
</service>

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

<service id="sylius.expression_language.grid.variables_collection_aggregate" class="Sylius\Component\Grid\ExpressionLanguage\VariablesCollectionAggregate">
<argument type="tagged_iterator" tag="sylius.grid.variables" />
</service>
</services>
</container>
3 changes: 1 addition & 2 deletions src/Bundle/Resources/config/services/field_types.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@

<service id="Sylius\Component\Grid\FieldTypes\ExpressionFieldType">
<argument type="service" id="sylius.grid.data_extractor" />
<argument type="service" id="sylius.expression_language" />
<argument type="service" id="service_container" />
<argument type="service" id="sylius.expression_language.grid.expression_evaluator" />
<tag name="sylius.grid_field" type="expression" />
</service>
<service id="sylius.grid_field.expression" alias="Sylius\Component\Grid\FieldTypes\ExpressionFieldType" />
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
20 changes: 20 additions & 0 deletions src/Component/Attribute/AsGridProvider.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 AsGridProvider
{
public const SERVICE_TAG = 'sylius.grid.provider';
}
20 changes: 20 additions & 0 deletions src/Component/Attribute/AsGridVariables.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 AsGridVariables
{
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;
}
41 changes: 41 additions & 0 deletions src/Component/ExpressionLanguage/ExpressionLanguageFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?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\ExpressionFunctionProviderInterface;
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
use Webmozart\Assert\Assert;

/**
* @experimental
*/
final class ExpressionLanguageFactory
{
/** @param iterable<int, ExpressionFunctionProviderInterface> $providers */
public function __construct(private $providers)
{
Assert::allIsInstanceOf($this->providers, ExpressionFunctionProviderInterface::class);
}

public function __invoke(): ExpressionLanguage
{
$expressionLanguage = new ExpressionLanguage();

foreach ($this->providers as $provider) {
$expressionLanguage->registerProvider($provider);
}

return $expressionLanguage;
}
}
38 changes: 38 additions & 0 deletions src/Component/ExpressionLanguage/VariablesCollectionAggregate.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?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 Webmozart\Assert\Assert;

/**
* @experimental
*/
final class VariablesCollectionAggregate implements VariablesCollectionInterface
{
/** @param iterable<int, VariablesCollectionInterface> $variablesCollection */
public function __construct(private $variablesCollection)
{
Assert::allIsInstanceOf($this->variablesCollection, VariablesCollectionInterface::class);
}

public function getCollection(): array
{
$collections = [];
foreach ($this->variablesCollection as $collection) {
$collections[] = $collection->getCollection();
}

return array_merge(...$collections);
}
}
25 changes: 25 additions & 0 deletions src/Component/ExpressionLanguage/VariablesCollectionInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?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 VariablesCollectionInterface
{
/**
* @return array<string, mixed>
*/
public function getCollection(): array;
}
Loading

0 comments on commit 678c268

Please sign in to comment.