Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support editable mode on FieldDescriptionInterface::TYPE_ENUM
Browse files Browse the repository at this point in the history
onEXHovia committed Dec 4, 2024

Unverified

This commit is not signed, but one or more authors requires that any commit attributed to them is signed.
1 parent c4705be commit 38f9cef
Showing 7 changed files with 181 additions and 1 deletion.
2 changes: 1 addition & 1 deletion src/Action/SetObjectFieldValueAction.php
Original file line number Diff line number Diff line change
@@ -137,7 +137,7 @@ public function __invoke(Request $request): JsonResponse
$value = $dataTransformer->reverseTransform($value);
}

if (null === $value && FieldDescriptionInterface::TYPE_CHOICE === $fieldDescription->getType()) {
if (null === $value && \in_array($fieldDescription->getType(), [FieldDescriptionInterface::TYPE_CHOICE, FieldDescriptionInterface::TYPE_ENUM], true)) {
return new JsonResponse(\sprintf(
'Edit failed, object with id "%s" not found in association "%s".',
$objectId,
73 changes: 73 additions & 0 deletions src/Form/DataTransformer/BackedEnumTransformer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php

declare(strict_types=1);

/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Sonata\AdminBundle\Form\DataTransformer;

use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException;
use Symfony\Component\Form\Exception\UnexpectedTypeException;

/**
* @phpstan-template T of \BackedEnum
* @phpstan-implements DataTransformerInterface<T, int|string>
*/
final class BackedEnumTransformer implements DataTransformerInterface
{
/**
* @phpstan-param class-string<T> $className
*/
public function __construct(
private string $className,
) {
}

/**
* @param int|string|null $value
*
* @phpstan-return T|null
*/
public function reverseTransform($value): ?\BackedEnum
{
if (null === $value || '' === $value) {
return null;
}

if (!\is_int($value) && !\is_string($value)) {
throw new TransformationFailedException(\sprintf('Could not transform value: expecting an int or string, got "%s".', get_debug_type($value)));
}

try {
return $this->className::from($value);
} catch (\ValueError|\TypeError $e) {
throw new TransformationFailedException(\sprintf('Could not transform value "%s".', $value));
}
}

/**
* @param \BackedEnum|null $value
*
* @phpstan-param T|null $value
*/
public function transform($value): string|int|null
{
if (null === $value) {
return null;
}

if (!$value instanceof \BackedEnum) {
throw new UnexpectedTypeException($value, \BackedEnum::class);
}

return $value->value;
}
}
13 changes: 13 additions & 0 deletions src/Form/DataTransformerResolver.php
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@
namespace Sonata\AdminBundle\Form;

use Sonata\AdminBundle\FieldDescription\FieldDescriptionInterface;
use Sonata\AdminBundle\Form\DataTransformer\BackedEnumTransformer;
use Sonata\AdminBundle\Form\DataTransformer\ModelToIdTransformer;
use Sonata\AdminBundle\Model\ModelManagerInterface;
use Symfony\Component\Form\DataTransformerInterface;
@@ -75,6 +76,18 @@ public function resolve(
return $this->globalCustomTransformers[$fieldType];
}

if (FieldDescriptionInterface::TYPE_ENUM === $fieldType) {
$className = $fieldDescription->getOption('class');

if (
null !== $className
&& \is_string($className)
&& is_a($className, \BackedEnum::class, true)
) {
return new BackedEnumTransformer($className);
}
}

// Handle entity choice association type, transforming the value into entity
if (FieldDescriptionInterface::TYPE_CHOICE === $fieldType) {
$targetModel = $fieldDescription->getTargetModel();
2 changes: 2 additions & 0 deletions src/Resources/views/CRUD/base_list_field.html.twig
Original file line number Diff line number Diff line change
@@ -61,6 +61,8 @@ file that was distributed with this source code.
{% set data_value = value|date('Y-m-d', options.timezone|default(null)) %}
{% elseif field_description.type == constant('Sonata\\AdminBundle\\FieldDescription\\FieldDescriptionInterface::TYPE_BOOLEAN') and value is empty %}
{% set data_value = 0 %}
{% elseif field_description.type == constant('Sonata\\AdminBundle\\FieldDescription\\FieldDescriptionInterface::TYPE_ENUM') and value is not empty %}
{% set data_value = value.value %}
{% elseif value is iterable %}
{% set data_value = value|json_encode %}
{% else %}
15 changes: 15 additions & 0 deletions src/Resources/views/CRUD/list_enum.html.twig
Original file line number Diff line number Diff line change
@@ -11,6 +11,21 @@ file that was distributed with this source code.

{% extends get_admin_template('base_list_field', admin.code) %}

{% set is_editable =
field_description.option('editable', false) and
admin.hasAccess('edit', object)
%}
{% set x_editable_type = field_description.type|sonata_xeditable_type %}

{% block field_span_attributes %}
{% if is_editable and x_editable_type %}
{% apply spaceless %}
{{ parent() }}
data-source="{{ field_description|sonata_xeditable_choices|json_encode }}"
{% endapply %}
{% endif %}
{% endblock %}

{% block field %}
{%- include '@SonataAdmin/CRUD/display_enum.html.twig' with {
value: value,
6 changes: 6 additions & 0 deletions src/Twig/XEditableRuntime.php
Original file line number Diff line number Diff line change
@@ -20,6 +20,7 @@
final class XEditableRuntime implements RuntimeExtensionInterface
{
public const FIELD_DESCRIPTION_MAPPING = [
FieldDescriptionInterface::TYPE_ENUM => 'select',
FieldDescriptionInterface::TYPE_CHOICE => 'select',
FieldDescriptionInterface::TYPE_BOOLEAN => 'select',
FieldDescriptionInterface::TYPE_TEXTAREA => 'textarea',
@@ -83,6 +84,11 @@ public function getXEditableChoices(FieldDescriptionInterface $fieldDescription)
break;
}

if ($text instanceof \BackedEnum) {
$value = $text->value;
$text = $text->name;
}

if (\is_string($catalogue)) {
$text = $this->translator->trans($text, [], $catalogue);
}
71 changes: 71 additions & 0 deletions tests/Form/DataTransformer/BackedEnumTransformerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php

declare(strict_types=1);

/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Sonata\AdminBundle\Tests\Form\DataTransformer;

use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Sonata\AdminBundle\Form\DataTransformer\BackedEnumTransformer;
use Sonata\AdminBundle\Model\ModelManagerInterface;
use Sonata\AdminBundle\Tests\Fixtures\Enum\Suit;
use Symfony\Component\Form\Exception\TransformationFailedException;
use Symfony\Component\Form\Exception\UnexpectedTypeException;

final class BackedEnumTransformerTest extends TestCase
{
public function testReverseTransform(): void
{
$transformer = new BackedEnumTransformer(Suit::class);

static::assertNull($transformer->reverseTransform(null));
static::assertNull($transformer->reverseTransform(''));
static::assertSame(Suit::Hearts, $transformer->reverseTransform(Suit::Hearts->value));
}

public function testReverseTransformNotValidValue(): void
{
$this->expectException(TransformationFailedException::class);
$this->expectExceptionMessage('Could not transform value "not_valid_value".');

$transformer = new BackedEnumTransformer(Suit::class);
$transformer->reverseTransform('not_valid_value');
}

public function testReverseTransformNotScalar(): void
{
$this->expectException(TransformationFailedException::class);
$this->expectExceptionMessage('Could not transform value: expecting an int or string, got "stdClass".');

$transformer = new BackedEnumTransformer(Suit::class);
// @phpstan-ignore-next-line
$transformer->reverseTransform(new \stdClass());

Check failure on line 51 in tests/Form/DataTransformer/BackedEnumTransformerTest.php

GitHub Actions / Psalm

InvalidArgument

tests/Form/DataTransformer/BackedEnumTransformerTest.php:51:40: InvalidArgument: Argument 1 of Sonata\AdminBundle\Form\DataTransformer\BackedEnumTransformer::reverseTransform expects int|null|string, but stdClass provided (see https://psalm.dev/004)
}

public function testTransform(): void
{
$transformer = new BackedEnumTransformer(Suit::class);

static::assertNull($transformer->transform(null));
static::assertSame(Suit::Clubs->value, $transformer->transform(Suit::Clubs));
}

public function testTransformUnexpectedType(): void
{
$this->expectException(UnexpectedTypeException::class);
$this->expectExceptionMessage('Expected argument of type "BackedEnum", "stdClass" given');

$transformer = new BackedEnumTransformer(Suit::class);
// @phpstan-ignore-next-line
$transformer->transform(new \stdClass());

Check failure on line 69 in tests/Form/DataTransformer/BackedEnumTransformerTest.php

GitHub Actions / Psalm

InvalidArgument

tests/Form/DataTransformer/BackedEnumTransformerTest.php:69:33: InvalidArgument: Argument 1 of Sonata\AdminBundle\Form\DataTransformer\BackedEnumTransformer::transform expects Sonata\AdminBundle\Tests\Fixtures\Enum\Suit|null, but stdClass provided (see https://psalm.dev/004)
}
}

0 comments on commit 38f9cef

Please sign in to comment.