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

Dynamic expose, aka 'fields' query param #195

Closed
vlastv opened this issue Dec 3, 2013 · 12 comments
Closed

Dynamic expose, aka 'fields' query param #195

vlastv opened this issue Dec 3, 2013 · 12 comments

Comments

@vlastv
Copy link

vlastv commented Dec 3, 2013

Hi,

Prompt if I understand.

If I need a dynamic inclusion of fields, such as in Facebook "http://api/users?fields=username,picture" I need to create a custom class that implements an interface Exclusion Strategy or are there other ways?

Implemented through the group I do not like, because functional groups I want to leave for other purposes and use chain strategies.

@stof
Copy link
Contributor

stof commented Dec 3, 2013

a custom strategy is probably the cleaner way to implement it

@damienalexandre
Copy link

👍 this can be implemented simply as explained here: http://jolicode.com/blog/how-to-implement-your-own-fields-inclusion-rules-with-jms-serializer

Also I think this cannot be handled by the serializer in a native way as the ExclusionStrategy must know on which object it must apply which restriction. This may require a new Annotation.

@korotovsky
Copy link

Hi there!

In our project we successfully implemented dynamic expose with ExclusionStrategyInterface as @damienalexandre said.

But we faced really serious limitation in https://github.com/schmittjoh/serializer.

Imagine, that we have associative array of Acme\Bundle\HelloBundle\Model\Foo\BazModel objects. Each object is indexed by its key. For example (foo, bar).

This is a typical mapping for such situation:

Acme\Bundle\HelloBundle\Model\Foo\BarModel:
    exclusion_policy: ALL
    virtual_properties:
        getId:
            explose:        true
            type:           integer
            since_version:  1.0

        getHash:
            explose:        true
            type:           array<string, Acme\Bundle\HelloBundle\Model\Foo\BazModel>
            since_version:  1.0

So, what we can do with that?

  1. GET /users/1?_fields=id - query only id field.
  2. GET /users/1?_fields=id,hash(id,type) - query id and hash with id and type fields inside hash field.

What we can't do?

We completely can't know which key is serialized at this moment and that's why we can't do queries like: GET /users/1?_fields=hash[foo](id,type)

Problem is here: https://github.com/schmittjoh/serializer/blob/master/src/JMS/Serializer/GenericSerializationVisitor.php#L101

    /**
     * @param array $data
     * @param array $type
     */
    public function visitArray($data, array $type, Context $context)
    {
        // ...
        foreach ($data as $k => $v) {
            $v = $this->navigator->accept($v, $this->getElementType($type), $context);
        // ...
    }

$k is string - a current serialized key (of this hash, foo for example), but this information gets lost when we call ->accept().

When each field of Acme\Bundle\HelloBundle\Model\Foo\BazModel object is serialized I don't know what value was assigned to $k (before) and can't decide whether I should skip class or not https://github.com/schmittjoh/serializer/blob/master/src/JMS/Serializer/Exclusion/ExclusionStrategyInterface.php#L39

@vlastv @stof @damienalexandre

@sandermarechal
Copy link

I too would like the key (or path) to be available in the ExclusionStrategyInterface. I can get the currect stack of objects from the Context. What would be ideal is to also have the stack of ClassMetadata/PropertyMetadata associated with the object stack to be available. That way you have all the metadata for the entire stack available, not just the metadata for the class/property you are currently serializing.

@sandermarechal
Copy link

I just saw Context->getMetadataStack(), which can be used to get the entire metadata stack and so allow advanced filtering using e.g. dynamic expose or the JSON API include: http://jsonapi.org/format/#fetching-includes

@korotovsky
Copy link

@sandermarechal Unfortunately, you still can't querying hashes by its keys.

@sandermarechal
Copy link

@korotovsky I don't think I understand what you mean by hash, but the metadataStack in combination with the current class/property metadata gives me enough information to implement both the "Inclusion of related resources" and "Sparse fieldsets" section of the JSON API spec.

@korotovsky
Copy link

@sandermarechal Imagine that you have associative array, like:

$hash = [
    'foo' => new MyModel(),
    'bar' => new MyModel(),
    'baz' => new MyModel(),
];

And you can't serialize only foo and baz keys with such request: GET /users/1?_fields=hash[foo,baz](id,type) to get JSON like

{"hash": {"foo": {"id":1, "type": "one"}, "baz": {"id": 2, "type": "two"}}}}

because

    /**
     * @param array $data
     * @param array $type
     */
    public function visitArray($data, array $type, Context $context)
    {
        // ...
        foreach ($data as $k => $v) {
            $v = $this->navigator->accept($v, $this->getElementType($type), $context);
        // ...
    }

$k will has values foo, bar and baz while serialization, but this information gets lost when JMS call ->accept().

But the metadataStack in combination with the current class/property metadata is enough for typical cases, except this one :)

@sandermarechal
Copy link

@korotovsky Perhaps you should turn your hash array into a proper object then. That way you get a PropertyMetadata that contains the key name :-)

@korotovsky
Copy link

@sandermarechal It looks like a little overhead when JMSSerializer provides array<K, V> datatype :)

@jcrombez
Copy link

Is there a way to do the custom exclusion strategy as explained in the jolicode article but globally, not in a controller ?
I want it to be applied everytime but and i have to add the exclusion strategy to the context everywhere i serialize in my app, wich is a bit is problematic...

I tried adding it to the context available in a pre_serialize event, but it's protected for change.

@goetas
Copy link
Collaborator

goetas commented Dec 30, 2016

Will be solved by #673 in 1.5.0

@goetas goetas closed this as completed Dec 30, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants