JMS #Discriminator filed doesn't appear if specific group is serializing - symfony

I'm using Symfony 2.8, FOSRestBundle and JMSSerializerBundle.
Problem
Discriminator field type of Document entity doesn't apear in serialized model when I serialize specific group ("api" group in folowing example) of entity Citizen.
Doctrine Entities
Document:
namespace MyBundle\Entity;
use JMS\Serializer\Annotation as JMS;
…
/**
* #JMS\Discriminator(field = "type", map = {
* "doc1" = "MyBundle\Entity\Document1",
* "doc2" = "MyBundle\Entity\Document2"
* })
*/
class Document
…
Citizen:
class Citizen
{
…
/**
* #var ArrayCollection
*
* #ORM\OneToMany(
* targetEntity="MyBundle\Entity\Document",
* cascade={ "PERSIST", "REMOVE" },
* orphanRemoval=true,
* mappedBy="citizen"
* )
*
* #JMS\Groups({"api"})
*/
private $documents;
…
What I get
{
…
"documents": [
{
"number": "000000",
"date": "01.01.1970",
"serial": "0000",
"place": ""
}
],
…
}
What I need
{
…
"documents": [
{
"type": "doc1",
"number": "000000",
"date": "01.01.1970",
"serial": "0000",
"place": ""
}
],
…
}
If I remove specific serialization group, then type field is present in serialized output.
Thanks in advance

Just found issue on github.
Seems for now, workaround with Default group is needed, see lordelph's comment

Related

How can i write this JSON swagger-php below with annotations?

I'm using OpenAPI 3.0 to document my Symfony API. this is the JSON code that authenticates the user to send requests:
"securitySchemes": {
"Bearer": {
"type": "http",
"description": "Entrer le token JST",
"scheme": "bearer",
"bearerFormat": "JWT"
}
}
},
"security": [
{
"Bearer": []
}
]
How can i write this with annotations in controller ?
thanks
You almost can; currently swagger-php does not support the bearerFormat property.
=> https://github.com/zircote/swagger-php/issues/1258
The rest would look something like this globally.
/**
* #OA\SecurityScheme(
* securityScheme="bearerAuth",
* type="http",
* scheme="bearer",
* description="Entrer le token JST"
* )
*/
For a controller to require security you'd add this:
/**
* #OA\Get(
* path="/api/endpoint",
* ...
* security={{ "bearerAuth": {} }}
* )
*/

Api Platform custom swagger/openapi context body

I am using Api Platform with Symfony 4, and I want to create a custom endpoint. Everything works fine, but I cannot change 2 things: body and response format (in openapi documentation).
Parameters and responses status codes works just fine.
* "login"={
* "route_name"="api_login",
* "method" = "post",
* "openapi_context" = {
* "parameters" = {},
* "body" = {
* "description" ="Username and password",
* "schema" = {
* "type" = "object",
* "required" = {"email","password"},
* "properties" = {
* "email" = {
* "type" = "string"
* },
* "password" = {
* "type" = "string"
* }
* }
* }
* },
* "responses" = {
* "200" = {
* "description" = "User logged in",
* "schema" = {
* "type" = "object",
* "required" = {
* "token",
* "refresh_token"
* },
* "properties" = {
* "token" = {
* "type" = "string"
* },
* "refresh_token" = {
* "type" = "string"
* }
* }
* }
* },
* "401" = {
* "description" = "invalid password or email"
* }
* },
* "summary" = "Login user in application",
* "consumes" = {
* "application/json",
* "text/html",
* },
* "produces" = {
* "application/json"
* }
* }
* }
This works for me, please see the documentation.
https://swagger.io/docs/specification/describing-request-body/
* #ApiResource(
* collectionOperations={
* "get": {
* "method": "GET",
* "access_control": "is_granted('ROLE_USER', object)",
* },
* "post": {
* "method": "POST",
* "access_control": "is_granted('ROLE_USER', object)",
* "openapi_context": {
* "requestBody": {
* "content": {
* "application/ld+json": {
* "schema": {
* "type": "object",
* "properties": {
* "token": {"type": "string", "example": "email#example.com"},
* "refresh_token": {"type": "string", "example": "123456"},
* },
* },
* },
* },
* },
* },
* },
* }
* )
Take a look at this response on api-platform issue (documentation here is really nicely formatted in yaml instead of keeping them in php arrays, quite nice idea) and read the docs, they will probably help You to decorate documentation in any way You want to.

Save entity with subresource into redis with Symfony & Api Platform

I have 2 entities, Event & User. So basically A user can have multiple event.
I have an EventItemDataProvider and in the getItem method I did something like that:
public function getItem(string $resourceClass, $id, string $operationName = null, array $context = []): ?Event
{
if ($event = $this->cache->get($id, Event::class)) {
return $event;
}
$event = $this->em->getRepository(Event::class)->find($id);
if($event) {
$this->cache->set($id, $event, 120);
}
return $event;
}
When the data is returned without the cache I have this result that is correct with the right username:
{
"#context": "/contexts/Event",
"#id": "/events/20",
"#type": "Event",
"title": "new global event 3",
"description": "A big global event 3",
"createdAt": "2019-07-05T09:20:48+00:00",
"owner": {
"#id": "/users/3",
"#type": "User",
"username": "test3"
}
}
But if I hit the same method a second time, it will retrieve the data from the cache (redis) but this time username will be empty like that:
{
"#context": "/contexts/Event",
"#id": "/events/20",
"#type": "Event",
"title": "new global event 3",
"description": "A big global event 3",
"createdAt": "2019-07-05T09:20:48+00:00",
"owner": {
"#id": "/users/3",
"#type": "User",
"username": ""
}
}
Why is my username empty when I retrieve it from redis?
Basically my cache class has these methods:
public function get(string $uniq, string $className)
{
$value = $this->cache->getItem('test_key')->get();
return $value;
}
public function set(string $uniq, object $entity, int $expires = 60)
{
$save = $this->cache->getItem('test_key');
$save->set($entity);
$save->expiresAfter($expires);
$saved = $this->cache->save($save);
return $saved;
}
The owner field in my Event entity:
/**
* #ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="events")
* #ORM\JoinColumn(nullable=false)
* #Groups({"event:read", "event:write", "event:update"})
* #Assert\Valid()
*/
private $owner;
The username field in my User entity:
/**
* #ORM\Column(type="string", length=25, unique=true)
* #Groups({"user:read", "user:write", "user:update", "event:item:get", "event:update"})
* #Assert\NotBlank(message="Please provide a username")
*/
private $username;
My redis cache configuration:
cache:
app: cache.adapter.redis
default_redis_provider: "redis://redis"
pools:
custom.cache.entity:
adapter: cache.app
default_lifetime: 120
Why my username is filled when I retrieve it from Doctrine Repository but empty when I retrieve it from Redis?
With Xdebug I can see that if I retrieve the data from Doctrine I have something like that:
But if I retrieve the data from the cache I have something like that:
In this second case, initializer -> this seems infinite, so I think that the problem is probably here, but what should I do to solve this issue?

Symfony4 JMS\Serializer\SerializedName will be ignored on deserialize()

I have a JSON that will be deserialize() with JMS.
The JSON looks like
{
"creator": [
{
"value": 234,
"label": "Hello"
},
{
"value": 223,
"label": "World"
}
]
}
The JSON will be deserialized with my created Model
$this->serializer->deserialize($json, Model::class, 'json');
and my Model class has:
/**
* #Serializer\Type("array")
*/
private $creator;
This works perfectly fine, the deserializer convert the JSON into my Model and I receive creator with array-items.
I would like to change the variable name from creator to customer in my model. I was thinking it works with the annotation #Serializer\SerializedName().
But when im using this
/**
* #Serializer\Type("array")
* #Serializer\SerializedName("customer")
*/
private $creator;
the model will be not filled at all.
Do I fundamentally misunderstand this function?

How to have absolute url with Hateoas Bundle on Symfony2

I just fresh installed the bundle from Willdurant's github and I got relative url like this:
"_links": {
"self": {
"href": "/1.0/users/?page=1&limit=10"
},
"first": {
"href": "/1.0/users/?page=1&limit=10"
},
"last": {
"href": "/1.0/users/?page=2&limit=10"
},
"next": {
"href": "/1.0/users/?page=2&limit=10"
}
}
For my Hateoas url, I really prefer absolute url but I don't find anything on google to change that. 2 hours of search, trying multiple keywords and nothing...
Thanks for your help.
You can use it many ways. All depends on the way of generating links.
If you use #Route annotation to generate link, it has a parameter absolute that need to be set to true:
/**
* #Hateoas\Relation(
* name = "self",
* href = #Hateoas\Route(
* "user_get",
* parameters = { "id" = "expr(object.getId())" },
* absolute = true
* )
* )
*/
If you use expression language to generate link you can pass true as third parameter to the link() function:
/**
* #Hateoas\Relation(
* "user",
* href = "expr(link(object.getUser(), 'self', true))"
* )
*/

Resources