Skip to content

Commit

Permalink
Data Transformers in list fields editable fix sonata-project#5693
Browse files Browse the repository at this point in the history
allow use data_transformer in SetObjectFieldValueAction

create BooleanToStringTransformer for allows to use non-strings

update SetObjectFieldValueActionTest

use yoda conditions

fix errors in HelperControllerTest

test BooleanToStringTransformer

allow override transformers for 'date', 'boolean' and 'choice' field types

mark BooleanToStringTransformer and BooleanToStringTransformer classes as final

add example of using the data_transformer option in docs
  • Loading branch information
peter-gribanov committed Apr 28, 2020
1 parent 965c6b7 commit 1039d72
Show file tree
Hide file tree
Showing 9 changed files with 335 additions and 26 deletions.
8 changes: 8 additions & 0 deletions docs/reference/action_list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,14 @@ Here is an example::
],
])

// you can use Symfony Data Transformers to transform value into the data form expected by your
// application. For example, in a Value Object
->add('permission', 'choice', [
'editable' => true,
'choices' => Permission::choices(),
'data_transformer' => new PermissionDataTransformer(),
])

// we can add options to the field depending on the type
->add('price', 'currency', [
'currency' => $this->currencyDetector->getCurrency()->getLabel()
Expand Down
68 changes: 49 additions & 19 deletions src/Action/SetObjectFieldValueAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,14 @@

namespace Sonata\AdminBundle\Action;

use Sonata\AdminBundle\Admin\FieldDescriptionInterface;
use Sonata\AdminBundle\Admin\Pool;
use Sonata\AdminBundle\Form\DataTransformer\BooleanToStringTransformer;
use Sonata\AdminBundle\Form\DataTransformer\ModelToIdTransformer;
use Sonata\AdminBundle\Model\ModelManagerInterface;
use Sonata\AdminBundle\Twig\Extension\SonataAdminExtension;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToStringTransformer;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
Expand Down Expand Up @@ -113,34 +119,25 @@ public function __invoke(Request $request): JsonResponse
$propertyPath = new PropertyPath($field);
}

// Handle date type has setter expect a DateTime object
if ('' !== $value && 'date' === $fieldDescription->getType()) {
$value = new \DateTime($value);
}
if ('' === $value) {
$this->pool->getPropertyAccessor()->setValue($object, $propertyPath, null);
} else {
$dataTransformer = $this->getFieldDataTransformer($fieldDescription, $admin->getModelManager());

// Handle boolean type transforming the value into a boolean
if ('' !== $value && 'boolean' === $fieldDescription->getType()) {
$value = filter_var($value, FILTER_VALIDATE_BOOLEAN);
}
if ($dataTransformer instanceof DataTransformerInterface) {
$value = $dataTransformer->reverseTransform($value);
}

// Handle entity choice association type, transforming the value into entity
if ('' !== $value
&& 'choice' === $fieldDescription->getType()
&& null !== $fieldDescription->getOption('class')
&& $fieldDescription->getOption('class') === $fieldDescription->getTargetEntity()
) {
$value = $admin->getModelManager()->find($fieldDescription->getOption('class'), $value);

if (!$value) {
if (!$value && 'choice' === $fieldDescription->getType()) {
return new JsonResponse(sprintf(
'Edit failed, object with id: %s not found in association: %s.',
$originalValue,
$field
), Response::HTTP_NOT_FOUND);
}
}

$this->pool->getPropertyAccessor()->setValue($object, $propertyPath, '' !== $value ? $value : null);
$this->pool->getPropertyAccessor()->setValue($object, $propertyPath, $value);
}

$violations = $this->validator->validate($object);

Expand All @@ -164,4 +161,37 @@ public function __invoke(Request $request): JsonResponse

return new JsonResponse($content, Response::HTTP_OK);
}

private function getFieldDataTransformer(
FieldDescriptionInterface $fieldDescription,
ModelManagerInterface $modelManager
): ?DataTransformerInterface {
$dataTransformer = $fieldDescription->getOption('data_transformer');

// allow override transformers for 'date', 'boolean' and 'choice' field types
if ($dataTransformer instanceof DataTransformerInterface) {
return $dataTransformer;
}

// Handle date type has setter expect a DateTime object
if ('date' === $fieldDescription->getType()) {
$dataTransformer = new DateTimeToStringTransformer();
}

// Handle boolean type transforming the value into a boolean
if ('boolean' === $fieldDescription->getType()) {
$dataTransformer = new BooleanToStringTransformer('1');
}

// Handle entity choice association type, transforming the value into entity
if ('choice' === $fieldDescription->getType()) {
$className = $fieldDescription->getOption('class');

if (null !== $className && $className === $fieldDescription->getTargetEntity()) {
$dataTransformer = new ModelToIdTransformer($modelManager, $className);
}
}

return $dataTransformer;
}
}
42 changes: 42 additions & 0 deletions src/Form/DataTransformer/BooleanToStringTransformer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

declare(strict_types=1);

/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <[email protected]>
*
* 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;

/**
* This is analog of Symfony\Component\Form\Extension\Core\DataTransformer\BooleanToStringTransformer
* which allows you to use non-strings in reverseTransform() method.
*
* @author Peter Gribanov <[email protected]>
*/
final class BooleanToStringTransformer implements DataTransformerInterface
{
private $trueValue;

public function __construct(string $trueValue)
{
$this->trueValue = $trueValue;
}

public function transform($value): ?string
{
return $value ? $this->trueValue : null;
}

public function reverseTransform($value): bool
{
return filter_var($value, FILTER_VALIDATE_BOOLEAN);
}
}
10 changes: 9 additions & 1 deletion tests/Action/Bar.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,15 @@

class Bar
{
public function setEnabled($value): void
private $enabled;

public function getEnabled(): bool
{
return $this->enabled;
}

public function setEnabled(bool $enabled): void
{
$this->enabled = $enabled;
}
}
2 changes: 1 addition & 1 deletion tests/Action/Baz.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public function setBar(Bar $bar): void
$this->bar = $bar;
}

public function getBar()
public function getBar(): Bar
{
return $this->bar;
}
Expand Down
10 changes: 9 additions & 1 deletion tests/Action/Foo.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,15 @@

class Foo
{
public function setEnabled($value): void
private $enabled;

public function getEnabled(): bool
{
return $this->enabled;
}

public function setEnabled(bool $enabled): void
{
$this->enabled = $enabled;
}
}
Loading

0 comments on commit 1039d72

Please sign in to comment.