Skip to content

Commit 6a1c438

Browse files
committed
add callback field
1 parent e079cfb commit 6a1c438

File tree

5 files changed

+251
-0
lines changed

5 files changed

+251
-0
lines changed

docs/field_types.md

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,3 +312,89 @@ $field->setOptions([
312312
// Your options here
313313
]);
314314
```
315+
316+
Callback
317+
--------
318+
319+
The Callback column aims to offer almost as much flexibility as the Twig column, but without requiring the creation of a template.
320+
You simply need to specify a callback, which allows you to transform the 'data' variable on the fly.
321+
322+
By default it uses the name of the field, but you can specify the path
323+
alternatively. For example:
324+
325+
<details open><summary>PHP</summary>
326+
327+
```php
328+
<?php
329+
// config/packages/sylius_grid.php
330+
331+
use Sylius\Bundle\GridBundle\Builder\Field\CallbackField;
332+
use Sylius\Bundle\GridBundle\Builder\GridBuilder;
333+
use Sylius\Bundle\GridBundle\Config\GridConfig;
334+
335+
return static function (GridConfig $grid): void {
336+
$grid->addGrid(GridBuilder::create('app_user', '%app.model.user.class%')
337+
->addField(
338+
CallbackField::create('roles' fn (array $roles): string => implode(', ', $roles))
339+
->setLabel('app.ui.roles') // # each filed type can have a label, we suggest using translation keys instead of messages
340+
->setPath('roles')
341+
)
342+
->addField(
343+
CallbackField::create('status' fn (array $status): string => "<strong>$status</strong>", false) // the third argument allows to disable htmlspecialchars if set to false
344+
->setLabel('app.ui.status') // # each filed type can have a label, we suggest using translation keys instead of messages
345+
->setPath('status')
346+
)
347+
)
348+
};
349+
```
350+
351+
OR
352+
353+
```php
354+
<?php
355+
# src/Grid/UserGrid.php
356+
357+
declare(strict_types=1);
358+
359+
namespace App\Grid;
360+
361+
use App\Entity\User;
362+
use Sylius\Bundle\GridBundle\Builder\Field\CallbackField;
363+
use Sylius\Bundle\GridBundle\Builder\GridBuilderInterface;
364+
use Sylius\Bundle\GridBundle\Grid\AbstractGrid;
365+
use Sylius\Bundle\GridBundle\Grid\ResourceAwareGridInterface;
366+
367+
final class UserGrid extends AbstractGrid implements ResourceAwareGridInterface
368+
{
369+
public static function getName(): string
370+
{
371+
return 'app_user';
372+
}
373+
374+
public function buildGrid(GridBuilderInterface $gridBuilder): void
375+
{
376+
$gridBuilder
377+
->addField(
378+
CallbackField::create('roles' fn (array $roles): string => implode(', ', $roles))
379+
->setLabel('app.ui.roles') // # each filed type can have a label, we suggest using translation keys instead of messages
380+
->setPath('roles')
381+
)
382+
->addField(
383+
CallbackField::create('status' fn (array $status): string => "<strong>$status</strong>", false) // the third argument allows to disable htmlspecialchars if set to false
384+
->setLabel('app.ui.status') // # each filed type can have a label, we suggest using translation keys instead of messages
385+
->setPath('status')
386+
)
387+
;
388+
}
389+
390+
public function getResourceClass(): string
391+
{
392+
return User::class;
393+
}
394+
}
395+
```
396+
397+
</details>
398+
399+
This configuration will display each role of a customer separated with a comma.
400+
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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 CallbackField
17+
{
18+
public static function create(string $name, callable $callback, bool $htmlspecialchars = true): FieldInterface
19+
{
20+
return Field::create($name, 'callback')
21+
->setOption('callback', $callback)
22+
->setOption('htmlspecialchars', $htmlspecialchars)
23+
;
24+
}
25+
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@
1515
<services>
1616
<defaults public="true" />
1717

18+
<service id="Sylius\Component\Grid\FieldTypes\CallbackFieldType">
19+
<argument type="service" id="sylius.grid.data_extractor" />
20+
<tag name="sylius.grid_field" type="callback" />
21+
</service>
22+
<service id="sylius.grid_field.string" alias="Sylius\Component\Grid\FieldTypes\StringFieldType" />
23+
1824
<service id="Sylius\Component\Grid\FieldTypes\DatetimeFieldType">
1925
<argument type="service" id="sylius.grid.data_extractor" />
2026
<tag name="sylius.grid_field" type="datetime" />
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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\Component\Grid\DataExtractor\DataExtractorInterface;
17+
use Sylius\Component\Grid\Definition\Field;
18+
use Symfony\Component\OptionsResolver\OptionsResolver;
19+
20+
final class CallbackFieldType implements FieldTypeInterface
21+
{
22+
private DataExtractorInterface $dataExtractor;
23+
24+
public function __construct(DataExtractorInterface $dataExtractor)
25+
{
26+
$this->dataExtractor = $dataExtractor;
27+
}
28+
29+
public function render(Field $field, $data, array $options): string
30+
{
31+
$value = $this->dataExtractor->get($field, $data);
32+
$value = call_user_func($options['callback'], $value);
33+
34+
if ($options['htmlspecialchars'] !== true) {
35+
return (string) $value;
36+
}
37+
38+
return htmlspecialchars((string) $value);
39+
}
40+
41+
public function configureOptions(OptionsResolver $resolver): void
42+
{
43+
$resolver->setRequired('callback');
44+
$resolver->setAllowedTypes('callback', 'callable');
45+
46+
$resolver->setDefault('htmlspecialchars', true);
47+
$resolver->setAllowedTypes('htmlspecialchars', 'bool');
48+
}
49+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
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\Component\Grid\DataExtractor\DataExtractorInterface;
18+
use Sylius\Component\Grid\Definition\Field;
19+
use Sylius\Component\Grid\FieldTypes\FieldTypeInterface;
20+
21+
final class CallbackFieldTypeSpec extends ObjectBehavior
22+
{
23+
function let(DataExtractorInterface $dataExtractor): void
24+
{
25+
$this->beConstructedWith($dataExtractor);
26+
}
27+
28+
function it_is_a_grid_field_type(): void
29+
{
30+
$this->shouldImplement(FieldTypeInterface::class);
31+
}
32+
33+
function it_uses_data_extractor_to_obtain_data_and_passes_it_to_a_callback_with_htmlspecialchars(
34+
DataExtractorInterface $dataExtractor,
35+
Field $field,
36+
): void {
37+
$dataExtractor->get($field, ['foo' => 'bar'])->willReturn('bar');
38+
39+
$this->render($field, ['foo' => 'bar'], [
40+
'callback' => fn (string $value): string => "<strong>$value</strong>",
41+
'htmlspecialchars' => true,
42+
])->shouldReturn('&lt;strong&gt;bar&lt;/strong&gt;');
43+
}
44+
45+
function it_uses_data_extractor_to_obtain_data_and_passes_it_to_a_callback_without_htmlspecialchars(
46+
DataExtractorInterface $dataExtractor,
47+
Field $field,
48+
): void {
49+
$dataExtractor->get($field, ['foo' => 'bar'])->willReturn('bar');
50+
51+
$this->render($field, ['foo' => 'bar'], [
52+
'callback' => fn (string $value): string => "<strong>$value</strong>",
53+
'htmlspecialchars' => false,
54+
])->shouldReturn('<strong>bar</strong>');
55+
}
56+
57+
function it_uses_data_extractor_to_obtain_data_and_passes_it_to_a_function_callback(
58+
DataExtractorInterface $dataExtractor,
59+
Field $field,
60+
): void {
61+
$dataExtractor->get($field, ['foo' => 'bar'])->willReturn('bar');
62+
63+
$this->render($field, ['foo' => 'bar'], [
64+
'callback' => 'strtoupper',
65+
'htmlspecialchars' => true,
66+
])->shouldReturn('BAR');
67+
}
68+
69+
function it_uses_data_extractor_to_obtain_data_and_passes_it_to_a_static_callback(
70+
DataExtractorInterface $dataExtractor,
71+
Field $field,
72+
): void {
73+
$dataExtractor->get($field, ['foo' => 'bar'])->willReturn('BAR');
74+
75+
$this->render($field, ['foo' => 'bar'], [
76+
'callback' => [self::class, 'callable'],
77+
'htmlspecialchars' => true,
78+
])->shouldReturn('bar');
79+
}
80+
81+
static function callable($value)
82+
{
83+
return strtolower($value);
84+
}
85+
}

0 commit comments

Comments
 (0)