Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

FOSRestBundle: partial response in function of attributes asked in the request

Context

I found a lot of questions about partial API response with FOSRest and all the answers are based on the JMS serializer options (exlude, include, groups, etc). It works fine but I try to achieve something less "static".

Let's say I have a user with the following attributes: id username firstname lastname age sex

I retrieve this user with the endpoint GET /users/{id} and the following method:

/**
 * @View
 *
 * GET /users/{id}
 * @param integer $user (uses ParamConverter)
 */
public function getUserAction(User $user) {
    return $user;
}

The method returns the user with all his attributes.

Now I want to allow something like that: GET /users/{id}?attributes=id,username,sex

Question

Did I missed a functionality of FOSRestBUndle, JMSserializer or SensioFrameworkExtraBundle to achieve it automatically? An annotation, a method, a keyword in the request or something else?

Otherwise, what is the best way to achieve it?

Code

I thought to do something like that:

/**
 * @View
 * @QueryParam(name="attributes")
 *
 * GET /users/{id}
 *
 * @param integer $user (uses ParamConverter)
 */
public function getUserAction(User $user, $attributes) {
    $groups = $attributes ? explode(",", $attributes) : array("Default");

    $view = $this->view($user, 200)
        ->setSerializationContext(SerializationContext::create()->setGroups($groups));

    return $this->handleView($view);
}

And create a group for each attribute:

use JMS\Serializer\Annotation\Groups;

class User {

    /** @Groups({"id"}) */
    protected $id;

    /** @Groups({"username"}) */
    protected $username;

    /** @Groups({"firstname"}) */
    protected $firstname;

    //etc
}
like image 831
GG. Avatar asked Dec 11 '14 11:12

GG.


1 Answers

My implementation based on Igor's answer:

ExlusionStrategy:

use JMS\Serializer\Exclusion\ExclusionStrategyInterface;
use JMS\Serializer\Metadata\ClassMetadata;
use JMS\Serializer\Metadata\PropertyMetadata;
use JMS\Serializer\Context;

class FieldsExclusionStrategy implements ExclusionStrategyInterface {
    private $fields = array();

    public function __construct(array $fields) {
        $this->fields = $fields;
    }

    public function shouldSkipClass(ClassMetadata $metadata, Context $navigatorContext) {
        return false;
    }

    public function shouldSkipProperty(PropertyMetadata $property, Context $navigatorContext) {
        if (empty($this->fields)) {
            return false;
        }

        if (in_array($property->name, $this->fields)) {
            return false;
        }

        return true;
    }
}

Controller:

/**
 * @View
 * @QueryParam(name="fields")
 *
 * GET /users/{id}
 *
 * @param integer $user (uses ParamConverter)
 */
public function getUserAction(User $user, $fields) {
    $context = new SerializationContext();
    $context->addExclusionStrategy(new FieldsExclusionStrategy($fields ? explode(',', $fields) : array()));

    return $this->handleView($this->view($user)->setSerializationContext($context));
}

Endpoint:

GET /users/{id}?fields=id,username,sex
like image 89
GG. Avatar answered Sep 21 '22 17:09

GG.