-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Data Transformers in list fields editable #5937
Data Transformers in list fields editable #5937
Conversation
|
||
// Handle boolean type transforming the value into a boolean | ||
if ('boolean' === $fieldDescription->getType()) { | ||
$data_transformer = new BooleanToStringTransformer('1'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure that we need a date transformer here. Using a Symfony BooleanToStringTransformer here may break BC. At least the tests use a non-string.
We can use CallbackTransformer
here, so as not to create custom transformer.
$data_transformer = new CallbackTransformer(static function ($value): string {
return (string) (int) $value;
}, static function ($value): ?bool {
return filter_var($value, FILTER_VALIDATE_BOOLEAN);
});
18e2cc2
to
966c1de
Compare
Try rebuild |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
any docs changes needed?
@OskarStark this bug fix works more like a feature. It would be nice to document this feature. |
@OskarStark i add example here a little bit later. |
36bdc2a
to
56dccfd
Compare
Try rebuild |
@@ -164,4 +161,37 @@ public function __invoke(Request $request): JsonResponse | |||
|
|||
return new JsonResponse($content, Response::HTTP_OK); | |||
} | |||
|
|||
private function getFieldDataTransformer( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer a more generic way like a DataTransformerResolver
. This way we can add new transformers more easily.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you suggest just putting this method in a separate class?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like this?
final class DataTransformerResolver
{
private $modelManager;
public function __construct($modelManager)
{
$this->modelManager = $modelManager;
}
public function resolve(FieldDescriptionInterface $fieldDescription): ?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()) {
return new DateTimeToStringTransformer();
}
// Handle boolean type transforming the value into a boolean
if ('boolean' === $fieldDescription->getType()) {
return 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()) {
return new ModelToIdTransformer($this->modelManager, $className);
}
}
return null;
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or maybe you are interested in the option with individual services that are binded into a resolver?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like this?
final class ResolverFieldDataTransformer implements FieldDataTransformer
{
private $dataTransformers;
public function __construct(array $dataTransformers)
{
$this->dataTransformers = $dataTransformers;
}
public function transform($value, FieldDescriptionInterface $fieldDescription)
{
$dataTransformer = $fieldDescription->getOption('data_transformer');
// allow override predefined transformers
if ($dataTransformer instanceof DataTransformerInterface) {
return $dataTransformer->reverseTransform($value);
}
$type = $fieldDescription->getType();
if (array_key_exists($type, $this->dataTransformers)) {
return $this->dataTransformers[$type]->transform($value, $fieldDescription);
}
return $value;
}
}
final class ChoiceFieldDataTransformer implements FieldDataTransformer
{
private $modelManager;
public function __construct($modelManager)
{
$this->modelManager = $modelManager;
}
public function transform($value, FieldDescriptionInterface $fieldDescription)
{
$className = $fieldDescription->getOption('class');
if (null !== $className && $className === $fieldDescription->getTargetEntity()) {
return (new ModelToIdTransformer($this->modelManager, $className))->reverseTransform($value);
}
return $value;
}
}
$resolver = new ResolverFieldDataTransformer([
'choice' => new ChoiceFieldDataTransformer($modelManager),
]);
$value = $admin->getModelManager()->find($fieldDescription->getOption('class'), $value); | ||
|
||
if (!$value) { | ||
if (!$value && 'choice' === $fieldDescription->getType()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@core23 i am very confused by the tight binding to the field type here. I understand why this is done, but it still looks like a dirty hack.
94c51a8
to
cf71cc8
Compare
} | ||
|
||
return null; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@core23 please review DataTransformerResolver
service
Can you fix the phpstan check ? @sonata-project/contributors @core23 Please review. |
@VincentLanglet problem is not on my side. |
I fixed it ; you just have to rebase now |
Could you please rebase your PR and fix merge conflicts? |
75998dd
to
eac6d88
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome work.
Dismissing it now, but you can wait some days if you want to :)
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 add full docs about Symfony Data Transformers optimize resolve Data Transformer fix docs create DataTransformerResolver service add type hint for BooleanToStringTransformer::$trueValue allow add a custom global transformers field type should be a string correct default value for $globalCustomTransformers correct test DataTransformerResolverTest::testAddCustomGlobalTransformer() add BC support usage of DataTransformerResolver Update tests/Action/SetObjectFieldValueActionTest.php Update tests/Form/DataTransformer/BooleanToStringTransformerTest.php Update tests/Form/DataTransformerResolverTest.php Update src/Action/SetObjectFieldValueAction.php change "entity" word to "model" in documentations change deprecated error message add datetime in editable date form types correct test transform datetime and date form types test DateTime object in assertSame() fix typo restore getTemplate() return value in SetObjectFieldValueActionTest use Yoda conditions lazy-load predefined data transformers add DataTransformerResolverInterface use constants for determinate a field type test laze-load data transformers test usage DataTransformerResolver::addCustomGlobalTransformer() create simple function in DataTransformerResolverTest Process deprecation of FieldDescriptionInterface::getTargetEntity() Use FieldDescriptionInterface::getTargetModel if exists sonata-project#6208 change usage getTargetEntity() -> getTargetModel() in DataTransformerResolverTest merge changes from PR sonata-project#6167 register BooleanToStringTransformer as a service merge changes from PR sonata-project#6144 merge changes from PR sonata-project#6284 compare date with time in DataTransformerResolverTest
cece78f
eac6d88
to
cece78f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can use DateTimeInterface
, anyway IMO we can accept this.
|
||
$resultDate = $dataTransformer->reverseTransform($value); | ||
|
||
$this->assertInstanceOf(\DateTime::class, $resultDate); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$this->assertInstanceOf(\DateTime::class, $resultDate); | |
$this->assertInstanceOf(\DateTimeInterface::class, $resultDate); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now the transformer always returns the \DateTime
. If we expect the interface in tests, we may not notice how we break BC by replacing \DateTime
with \DateTimeImmutable
in the transformer.
$resultDate = $dataTransformer->reverseTransform($value); | ||
|
||
$this->assertInstanceOf(\DateTime::class, $resultDate); | ||
$this->assertSame($expectedDate->format(\DateTime::ATOM), $resultDate->format(\DateTime::ATOM)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$this->assertSame($expectedDate->format(\DateTime::ATOM), $resultDate->format(\DateTime::ATOM)); | |
$this->assertSame($expectedDate->format(\DateTimeInterface::ATOM), $resultDate->format(\DateTime::ATOM)); |
@core23 @phansys @jordisala1991 can you please give us a final review? Thanks |
Thanks ! |
Subject
Allow use Symfony Data Transformers in
configureListFields()
like:Closes #5693
Changelog