Skip to content
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

Serializer default groups #32622

Closed
kingschnulli opened this issue Jul 19, 2019 · 17 comments · Fixed by #51514
Closed

Serializer default groups #32622

kingschnulli opened this issue Jul 19, 2019 · 17 comments · Fixed by #51514

Comments

@kingschnulli
Copy link

Let's say I have an entity Book:

/**
 * @ApiResource()
 */
class Book
{
    /**
     * @Groups({"read", "write"})
     */
    public $name;

    /**
     * @Groups("write")
     */
    public $author;

    /**
     * This property has no groups
     */
    public $foo;

    // ...
}

What I'm trying to achieve now is to serialize the $foo property, regardless of the current context groups. Is there some way to configure some kind of "default group" for properties where no group was given?

I know I could just add the group to my property, but I was hoping to configure something (or decorate something) so the serializer will return the property $foo.

TLDR: How can I define a default group for all properties? Is this already possible or might be a new feature?

fabpot added a commit that referenced this issue Aug 25, 2020
…allows any group (nrobinaubertin)

This PR was squashed before being merged into the 5.2-dev branch.

Discussion
----------

[Serializer] Add special '*' serialization group that allows any group

| Q             | A
| ------------- | ---
| Branch?       | master for features
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | yes
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #32622
| License       | MIT
| Doc PR        | symfony/symfony-docs#... <!-- required for new features -->

Hi,
I added support for a special serialization group: '*'.
This group lets any group serialize the attribute marked with it.

The BC break comes from the fact that someone could have used the '*' group in their code.

Commits
-------

54e24a8 [Serializer] Add special '*' serialization group that allows any group
@fabpot fabpot closed this as completed Aug 25, 2020
@noemi-salaun
Copy link
Contributor

I think this issue could be reopened. I don't think @kingschnulli was talking about a context group that serialize everything, like the *, but a default group that serialize only properties that don't have a group. Like the Default group with JMS Serializer.

See https://jmsyst.com/libs/serializer/master/cookbook/exclusion_strategies#creating-different-views-of-your-objects

Any property without an explicit @groups annotation will be included in a Default group, which can be used when specifying groups in the serialization context.

@fabpot should I create a new issue or can this one be reopened ?

@fabpot fabpot reopened this Nov 12, 2021
@noemi-salaun
Copy link
Contributor

Thanks. I already have a working POC that use a null context group.

I don't know what could be the best option to reduce the BC break.

  • A special char, like for the *. Could be _. (not a fan of this solution)
  • A special word, like Default in the JMS Serializer (high chance of BC breaks with existing project)
  • The null value
  • The empty string value

null or empty string value cannot be use in @Groups annotation, so we are sure there is no BC break with this solution

@kingschnulli
Copy link
Author

I think this issue could be reopened. I don't think @kingschnulli was talking about a context group that serialize everything, like the *, but a default group that serialize only properties that don't have a group. Like the Default group with JMS Serializer.

This is exactly what I was talking about, makes it way easier to integrate into existing code bases.

@ninsuo
Copy link

ninsuo commented Feb 4, 2022

Another example if it can help people understand the purpose of this feature:

Currently, I am using 3 classes to handle common and r/w operations:

  • Profile which contain "nickname",
  • ProfileRead which extends Profile and adds $pictureUrl without groups
  • ProfileWrite which extends Profile and adds $picture without groups

With a default group, everything could be simplified to a single class:

<?php

namespace App\DTO\Account;

use App\API\Model\File;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints\Image;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\Valid;

class Profile
{
    /** User's nickname */
    #[Length(max: 64)]
    public ?string $nickname = null;

    /** Profile picture's URL */
    #[Groups("read")]
    public ?string $pictureUrl = null;

    /** Profile picture update */
    #[Image(maxSize: '1M', detectCorrupted: true)]
    #[Valid]
    #[Groups("write")]
    public ?File $picture = null;
}

@TerjeBr
Copy link

TerjeBr commented Jun 20, 2022

Any progress on this? @noemi-salaun seems to have provided a PR, but that seems to have stopped?

@sergey-kravchenk0
Copy link

sergey-kravchenk0 commented Jul 6, 2022

Any progress on this? @noemi-salaun seems to have provided a PR, but that seems to have stopped?

@TerjeBr , as I can see, you can use the special char * as a default group.

@TerjeBr
Copy link

TerjeBr commented Jul 7, 2022

@sergey-kravchenk0 I do not think * will do what we want.

Let's say you have 7 properties on a class, and 2 of them has group annotation "cat" and 1 has group annotation "dog" and 4 has no group annotation. Then I want to serialize an object of that class, and I want what is in group "cat" and also what has no group annotation, but not what is in group "dog". As I have understood it if I specify * I will get everything, but I do not want that because I do not want what is in group "dog". That is why we want a default group, so we can say we want "cat" + "Default" and then I get the 4 properties that had no group annotation plus the two in the "cat" group.

@sergey-kravchenk0
Copy link

@TerjeBr I see, you are right, now there is no way to define the "Default" group. The only solution is to explicitly define the "Default" group for properties.

@carsonbot
Copy link

Thank you for this suggestion.
There has not been a lot of activity here for a while. Would you still like to see this feature?

@ninsuo
Copy link

ninsuo commented Jan 8, 2023

Ohh yes!

@carsonbot carsonbot removed the Stalled label Jan 8, 2023
@kadet1090
Copy link

kadet1090 commented Jan 15, 2023

I've created workaround for this issue by decorating the \Symfony\Component\Serializer\Mapping\Loader\LoaderInterface class:

<?php

namespace App\Serialization;

use Symfony\Component\Serializer\Mapping\ClassMetadataInterface;
use Symfony\Component\Serializer\Mapping\Loader\LoaderInterface;

class MetadataLoader implements LoaderInterface
{
    public function __construct(
        private readonly LoaderInterface $decorated
    ) {
    }

    public function loadClassMetadata(ClassMetadataInterface $classMetadata)
    {
        $loaded = $this->decorated->loadClassMetadata($classMetadata);

        foreach ($classMetadata->getAttributesMetadata() as $attributeMetadata) {
            if (empty($attributeMetadata->getGroups())) {
                $attributeMetadata->addGroup('default');
            }
        }

        return $loaded;
    }
}

services.yaml:

  App\Serialization\MetadataLoader:
    decorates: 'serializer.mapping.chain_loader'

Now if you apply the default group by default in the default context it should work as mentioned. I did that in Symfony 5.4, and don't know if it works in any other versions.

@carsonbot
Copy link

Thank you for this suggestion.
There has not been a lot of activity here for a while. Would you still like to see this feature?

@noemi-salaun
Copy link
Contributor

Keep it open please. There is also a PR for it already

@mtarld
Copy link
Contributor

mtarld commented Aug 29, 2023

Hey! I tried a different approach to solve the issue in this PR: #51514

@TerjeBr
Copy link

TerjeBr commented Nov 20, 2023

Should this be closed, since #51514 has been merged?

@mtarld
Copy link
Contributor

mtarld commented Nov 20, 2023

Yes, I think it can be 🙂 (when it'll be merged)

@nicolas-grekas
Copy link
Member

#51514 has not been merged yet

@fabpot fabpot closed this as completed Feb 3, 2024
fabpot added a commit that referenced this issue Feb 3, 2024
…ps (mtarld)

This PR was merged into the 7.1 branch.

Discussion
----------

[Serializer] Add Default and "class name" default groups

| Q             | A
| ------------- | ---
| Branch?       | 7.1
| Bug fix?      | no
| New feature?  | yes
| Deprecations? | no
| Tickets       | Fix #32622
| License       | MIT
| Doc PR        | TODO

Add `Default` and "class name" groups to the (de)normalization context, following Validator's component naming convention.

Commits
-------

de58759 [Serializer] Add default groups
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet