Skip to content

Commit

Permalink
add callback field
Browse files Browse the repository at this point in the history
  • Loading branch information
Florian-Merle committed Mar 22, 2024
1 parent e079cfb commit 14fba76
Show file tree
Hide file tree
Showing 5 changed files with 263 additions and 0 deletions.
86 changes: 86 additions & 0 deletions docs/field_types.md
Original file line number Diff line number Diff line change
Expand Up @@ -312,3 +312,89 @@ $field->setOptions([
// Your options here
]);
```

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:

<details open><summary>PHP</summary>

```php
<?php
// config/packages/sylius_grid.php

use Sylius\Bundle\GridBundle\Builder\Field\CallbackField;
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(
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')
)
->addField(
CallbackField::create('status' fn (array $status): string => "<strong>$status</strong>", 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')
)
)
};
```

OR

```php
<?php
# src/Grid/UserGrid.php

declare(strict_types=1);

namespace App\Grid;

use App\Entity\User;
use Sylius\Bundle\GridBundle\Builder\Field\CallbackField;
use Sylius\Bundle\GridBundle\Builder\GridBuilderInterface;
use Sylius\Bundle\GridBundle\Grid\AbstractGrid;
use Sylius\Bundle\GridBundle\Grid\ResourceAwareGridInterface;

final class UserGrid extends AbstractGrid implements ResourceAwareGridInterface
{
public static function getName(): string
{
return 'app_user';
}

public function buildGrid(GridBuilderInterface $gridBuilder): void
{
$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')
)
->addField(
CallbackField::create('status' fn (array $status): string => "<strong>$status</strong>", 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')
)
;
}

public function getResourceClass(): string
{
return User::class;
}
}
```

</details>

This configuration will display each role of a customer separated with a comma.

25 changes: 25 additions & 0 deletions src/Bundle/Builder/Field/CallbackField.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\Bundle\GridBundle\Builder\Field;

final class CallbackField
{
public static function create(string $name, callable $callback, bool $htmlspecialchars = true): FieldInterface
{
return Field::create($name, 'callback')
->setOption('callback', $callback)
->setOption('htmlspecialchars', $htmlspecialchars)
;
}
}
6 changes: 6 additions & 0 deletions src/Bundle/Resources/config/services/field_types.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@
<services>
<defaults public="true" />

<service id="Sylius\Component\Grid\FieldTypes\CallbackFieldType">
<argument type="service" id="sylius.grid.data_extractor" />
<tag name="sylius.grid_field" type="callback" />
</service>
<service id="sylius.grid_field.string" alias="Sylius\Component\Grid\FieldTypes\StringFieldType" />

<service id="Sylius\Component\Grid\FieldTypes\DatetimeFieldType">
<argument type="service" id="sylius.grid.data_extractor" />
<tag name="sylius.grid_field" type="datetime" />
Expand Down
49 changes: 49 additions & 0 deletions src/Component/FieldTypes/CallbackFieldType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?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\FieldTypes;

use Sylius\Component\Grid\DataExtractor\DataExtractorInterface;
use Sylius\Component\Grid\Definition\Field;
use Symfony\Component\OptionsResolver\OptionsResolver;

final class CallbackFieldType implements FieldTypeInterface
{
private DataExtractorInterface $dataExtractor;

public function __construct(DataExtractorInterface $dataExtractor)
{
$this->dataExtractor = $dataExtractor;
}

public function render(Field $field, $data, array $options): string
{
$value = $this->dataExtractor->get($field, $data);
$value = call_user_func($options['callback'], $value);

if ($options['htmlspecialchars'] !== true) {
return $value;

Check failure on line 35 in src/Component/FieldTypes/CallbackFieldType.php

View workflow job for this annotation

GitHub Actions / PHP 8.1, Symfony ^5.4

Method Sylius\Component\Grid\FieldTypes\CallbackFieldType::render() should return string but returns mixed.

Check failure on line 35 in src/Component/FieldTypes/CallbackFieldType.php

View workflow job for this annotation

GitHub Actions / PHP 8.1, Symfony ^6.0

Method Sylius\Component\Grid\FieldTypes\CallbackFieldType::render() should return string but returns mixed.
}

return htmlspecialchars((string) $value);
}

public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setRequired('callback');
$resolver->setAllowedTypes('callback', 'callable');

$resolver->setDefault('htmlspecialchars', true);
$resolver->setAllowedTypes('htmlspecialchars', 'bool');
}
}
97 changes: 97 additions & 0 deletions src/Component/spec/FieldTypes/CallbackFieldTypeSpec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?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 spec\Sylius\Component\Grid\FieldTypes;

use PhpSpec\ObjectBehavior;
use Sylius\Component\Grid\DataExtractor\DataExtractorInterface;
use Sylius\Component\Grid\Definition\Field;
use Sylius\Component\Grid\FieldTypes\FieldTypeInterface;

final class CallbackFieldTypeSpec extends ObjectBehavior
{
function let(DataExtractorInterface $dataExtractor): void
{
$this->beConstructedWith($dataExtractor);
}

function it_is_a_grid_field_type(): void
{
$this->shouldImplement(FieldTypeInterface::class);
}

function it_uses_data_extractor_to_obtain_data_and_passes_it_to_a_callback_with_htmlspecialchars(
DataExtractorInterface $dataExtractor,
Field $field,
): void {
$dataExtractor->get($field, ['foo' => 'bar'])->willReturn('bar');

$this->render($field, ['foo' => 'bar'], [
'callback' => fn (string $value): string => "<strong>$value</strong>",
'htmlspecialchars' => true,
])->shouldReturn('&lt;strong&gt;bar&lt;/strong&gt;');
}

function it_uses_data_extractor_to_obtain_data_and_passes_it_to_a_callback_without_htmlspecialchars(
DataExtractorInterface $dataExtractor,
Field $field,
): void {
$dataExtractor->get($field, ['foo' => 'bar'])->willReturn('bar');

$this->render($field, ['foo' => 'bar'], [
'callback' => fn (string $value): string => "<strong>$value</strong>",
'htmlspecialchars' => false,
])->shouldReturn('<strong>bar</strong>');
}

function it_uses_data_extractor_to_obtain_data_and_passes_it_to_a_function_callback(
DataExtractorInterface $dataExtractor,
Field $field,
): void {
$dataExtractor->get($field, ['foo' => 'bar'])->willReturn('bar');

$this->render($field, ['foo' => 'bar'], [
'callback' => 'strtoupper',
'htmlspecialchars' => true,
])->shouldReturn('BAR');
}

function it_uses_data_extractor_to_obtain_data_and_passes_it_to_a_closure_callback(
DataExtractorInterface $dataExtractor,
Field $field,
): void {
$dataExtractor->get($field, ['foo' => 'bar'])->willReturn('bar');

$this->render($field, ['foo' => 'bar'], [
'callback' => strtoupper(...),
'htmlspecialchars' => true,
])->shouldReturn('BAR');
}

function it_uses_data_extractor_to_obtain_data_and_passes_it_to_a_static_callback(
DataExtractorInterface $dataExtractor,
Field $field,
): void {
$dataExtractor->get($field, ['foo' => 'bar'])->willReturn('BAR');

$this->render($field, ['foo' => 'bar'], [
'callback' => [self::class, 'callable'],
'htmlspecialchars' => true,
])->shouldReturn('bar');
}

static function callable($value)
{
return strtolower($value);
}
}

0 comments on commit 14fba76

Please sign in to comment.