Symfony JMSSerializer create object entity and relation entities and set - php

I use api full contact service and when I have json data from service I need set in DB. I have entity ApiFullContact and relation OneToMany entity socialProfiles, because in response have many social profiles and need set data like with JMS serializer set only ApiFullContact and set relation entity, I try use Accessor then VirtualProperty but still not set socialProfiles entities, this my entities:
* #ExclusionPolicy("all")
*/
class ApiFullContact
{
use Timestampable;
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #Expose()
*/
protected $id;
/**
* #var string
*
* #ORM\Column(name="status", type="integer", nullable=true)
* #Expose()
*/
protected $status;
/**
* #var string
*
* #ORM\Column(name="likelihood", type="integer", nullable=true)
* #Expose()
*/
protected $likelihood;
/**
* #var string
*
* #ORM\Column(name="requestId", type="string", nullable=true)
* #Expose()
* #SerializedName("requestId")
*/
protected $requestId;
/**
* #var \Artel\ProfileBundle\Entity\CodeProfileLinks
*
* #ORM\OneToMany(targetEntity="Artel\ProfileBundle\Entity\CodeProfileLinks", mappedBy="apiFullContact", cascade={"persist", "remove"})
* #Expose()
* #SerializedName("socialProfiles")
* #Accessor()
* #Type("Artel\ProfileBundle\Entity\CodeProfileLinks")
*/
private $socialProfiles;
and relation entity
class CodeProfileLinks
{
use Timestampable;
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var string
*
* #ORM\Column(name="url", type="string", nullable=true)
* #Expose()
*/
private $url;
/**
* #var string
*
* #ORM\Column(name="type", type="string", nullable=true)
* #Expose()
*/
private $type;
/**
* #var string
*
* #ORM\Column(name="username", type="string", length=255, nullable=true)
* #Expose()
*/
protected $username;
in my action
$apiFullContact = $this->get('serializer')->deserialize(
$data,
'Artel\ProfileBundle\Entity\ApiFullContact',
'json'
);
and need set
and response from service:
{
"status": 200,
"likelihood": 0,
"requestId": "4076a34b-f14e-4331-a999-641be7feb2d5",
"contactInfo": {
"familyName": "Name",
"fullName": "Your Name",
"givenName": "Your"
},
"organizations": [],
"demographics": [],
"socialProfiles" : {
"twitter" : [ {
"followers" : 1,
"following" : 1,
"type" : "twitter",
"typeId" : "twitter",
"typeName" : "Twitter",
"url" : "https://twitter.com/Shuba_Vanya",
"username" : "Shuba_Vanya",
"id" : "2294488585"
} ],
"github" : [ {
"type" : "github",
"typeId" : "github",
"typeName" : "Github",
"url" : "https://github.com/shubaivan",
"username" : "shubaivan"
} ]
},
}
and how set entity ApiFullContact with relation entity socialProfiles data, how to set relation entities ???

Related

Why my API-Plateform resource give me always and only 2 attributes in response?

I try to create a new API rest on monolithic project with symfony 4.4, api-platform 2.6.8, i have to use yaml configuration, but i have the same issue with annotation.
One of my resource send 2 attributes (ID and label) when i try GET request on collection, whatever the configuration.
The entity :
/**
* Structure.
*
* #Gedmo\Tree(type="nested")
* #ORM\Table(name="por_structure")
* #ORM\Entity(repositoryClass="Common\StructureBundle\Repository\StructureRepository")
* #UniqueEntity(fields={"parent", "code", "status"}, errorPath="code", message="common.structure.code.unique")
* #UniqueEntity(fields={"parent", "label", "status"}, errorPath="label", message="common.structure.label.unique")
*/
class Structure
{
/**
* ID of the structure node.
*
* #var int
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* Label of the structure node.
*
* #var string
* #Assert\NotBlank()
* #ORM\Column(name="label", type="string", length=255, nullable=false)
*/
private $label;
/**
* External ID of the structure node.
*
* #var string
* #Assert\NotBlank()
* #ORM\Column(name="code", type="string", length=255, nullable=false)
*/
private $code;
/**
* Accounting code of the structure node.
*
* #var string
* #ORM\Column(name="accounting_code", type="string", length=255, nullable=true)
*/
private $accountingCode;
/**
* Status of the node (1: enabled, 0:disabled).
*
* #var bool
* #ORM\Column(name="status", type="boolean")
*/
private $status;
/**
* #Gedmo\TreeLeft
* #ORM\Column(name="lft", type="integer")
*/
private $lft;
/**
* #Gedmo\TreeLevel
* #ORM\Column(name="lvl", type="integer")
*/
private $lvl;
/**
* #Gedmo\TreeRight
* #ORM\Column(name="rgt", type="integer")
*/
private $rgt;
/**
* #var Structure
* #Gedmo\TreeRoot
* #ORM\ManyToOne(targetEntity="Common\StructureBundle\Entity\Structure")
* #ORM\JoinColumn(name="root", referencedColumnName="id", onDelete="CASCADE")
*/
private $root;
[...]
The resources.yaml :
resources:
#Structure
Common\StructureBundle\Entity\Structure:
shortName: 'Common/Structure'
properties:
id:
identifier: true
collectionOperations:
get:
openapi_context:
summary: 'Récupère une collection de Structure'
description: 'Récupère une collection de Structure'
normalization_context:
groups: [ 'a2:read:Structure']
openapi_definition_name: 'collection-read'
And the serializer.yaml (result is the same without any attribute in it):
Common\StructureBundle\Entity\Structure:
attributes:
id:
groups: [ 'a2:read:Structure' ]
label:
groups: [ 'a2:read:Structure' ]
code:
groups: [ 'a2:read:Structure' ]
status:
groups: [ 'a2:read:Structure' ]
The GET answer is still :
{
"#context": "\/api\/contexts\/Common\/Structure",
"#id": "\/api\/v2\/common\/structures",
"#type": "hydra:Collection",
"hydra:member": [
{
"id": 1,
"label": "SOCIETE 1 (FR)"
},
{
"id": 2,
"label": "Défaut"
},
{
"id": 3,
"label": "ZForfait jours"
},
[...]
],
"hydra:totalItems": 42
}
What did i do wrong ?
OK i found the problem.
I work on a big application and somewhere i didn't know, there is a serialization process which used classes implementing NormalizerInterface.
in these classes there is the definition which locked the output.
I change NormalizerInterface, ContextAwareNormalizerInterface, and add something special in the context when thes are called by the other part of the app.

Api-Platform: Eager Loading a Subresource

Hello guys I just started using Api-Platform and I got stuck on this problem for several hours.
I have a Symfony4 Project and two Entities: Bill & Abo
Bill:
/**
* #ORM\Entity(repositoryClass="App\Repository\BillRepository")
* #ApiResource
*/
class Bill {
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #var \DateTime
*
* #ORM\Column(name="date", type="date", nullable=false)
*/
private $date;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Abo", inversedBy="bills")
* #ORM\JoinColumn
* #ApiSubresource
*/
private $abo;
}
Abo:
/**
* #ORM\Entity(repositoryClass="App\Repository\AboRepository")
* #ApiResource
*/
class Abo
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=50, nullable=false)
*/
private $name;
/**
* #var integer
*
* #ORM\Column(name="price", type="integer", nullable=false)
*/
private $price;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Bill", mappedBy="abo")
*/
private $bills;
}
When I now call this url, /api/bills, I get get back this data:
{
"#id": "/api/bills/14",
"#type": "Bill",
"id": 14,
"date": "2018-03-08T00:00:00+00:00",
"abo": "/api/abos/1"
},
...
But instead of this "abo": "/api/abos/1" I want the Abo data already loaded, something like this:
"abo": {
"name": "TestAbo",
"price": 25
}
Is this possible and if yes how can I achieve this?
Thanks for your time and help!
You can use serialization groups for this. Make sure that your relation field $abo and it's member fields groups are exposed.
use Symfony\Component\Serializer\Annotation\Groups;
/**
* #ORM\Entity(repositoryClass="App\Repository\BillRepository")
* #ApiResource(attributes={
* "normalization_context"={"groups"={"bill", "bill-abo", "abo"}}
* })
*/
class Bill {
...
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Abo", inversedBy="bills")
* #ORM\JoinColumn
* #ApiSubresource
* #Groups("bill-abo")
*/
private $abo;
...
}
class Abo {
...
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=50, nullable=false)
* #Groups("abo")
*/
private $name;
...
}
You can read more in the docs.

I can't build a specific request in the repository with querybuilder

I have 3 tables "User", "Events" an "typeEvents" I would like to get a list of user with one where (ex : type = "Festival").
As well user entity is in another bundle that Events and typeEvents. but this is not the problem.
In one of Entity, I have a manyToMany relationship...so I can't do this query builder request.
User.php :
class User extends BaseUser
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var string
*
* #ORM\Column(name="pseudo", type="string", length=30, nullable=true)
*/
private $pseudo;
/**
* #ORM\Column(name="name", type="string", length=255)
*
* #Assert\NotBlank(message="Please enter your name.", groups={"Registration", "Profile"})
* #Assert\Length(
* min=3,
* max=255,
* minMessage="The name is too short.",
* maxMessage="The name is too long.",
* groups={"Registration", "Profile"}
* )
*/
private $name;
/**
* #var ArrayCollection $events
* #ORM\OneToMany(targetEntity="BISSAP\BenevolesBundle\Entity\Events", mappedBy="user", cascade={"persist", "remove", "merge"})
*/
private $events;
[...]
Events.php:
class Events
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
*
* #ORM\ManyToOne(targetEntity="TypeEvents", inversedBy="events", cascade={"persist"})
*
*/
private $typeEvents;
/**
*
* #ORM\ManyToOne(targetEntity="GenreEvents", inversedBy="events", cascade={"persist"})
*
*/
private $genreEvents;
/**
*
* #ORM\ManyToOne(targetEntity="BISSAP\UserBundle\Entity\User", inversedBy="events", cascade={"persist"})
*
*/
private $user;
[...]
TypeEvents.php:
class TypeEvents
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var ArrayCollection $events
* #ORM\OneToMany(targetEntity="Events", mappedBy="typeEvents", cascade={"persist", "remove", "merge"})
*/
private $events;
/**
* #var string
*
* #ORM\Column(name="type", type="string", length=255)
*/
public $type;
[...]
So in the userRepository I'm trying with :
public function findUserEvent()
{
$qb = $this
->createQueryBuilder('u')
->leftJoin('u.events', 'ev')
->addSelect('ev')
->where('BISSAPBenevolesBundle:TypeEvents.type = :type')
->setParameter('type', 'Festival')
;
return $qb->getQuery()->getResult();
}
And I get this error : [Semantical Error] line 0, col 78 near 'BISSAPBenevolesBundle:TypeEvents.type': Error: 'BISSAPBenevolesBundle:TypeEvents' is not defined.
Try with:
public function findUserEvent()
{
$qb = $this
->createQueryBuilder('u')
->leftJoin('u.events', 'ev')
->leftJoin('ev.types', 'evt')
->where('evt.type = :type')
->setParameter('type', 'Festival')
;
return $qb->getQuery()->getResult();
}
Hope this help

Symfony Rest Controller Limiting serialization depth

I have entity bit and relation ManyToMany developer, for developer I use * #Groups({"for_project"}) and when I use this anotation, in response not see fields developer, I look documentation and see * #MaxDepth(2) and use this but still have null. Why ?
{
"0": {
"id": 501,
"created": "2015-11-27T12:25:11+0200",
"developer_id": {},
"rate": 4,
"comment": "fsefsf"
},
"1": {
"id": 502,
"created": "2015-11-27T12:25:46+0200",
"developer_id": {},
"rate": 3,
"comment": "feasf"
}
}
class Bit
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #Expose()
* #Groups({"for_project"})
*/
private $id;
/**
* #var datetime $created
*
* #Gedmo\Timestampable(on="create")
* #ORM\Column(type="datetime")
* #Expose()
* #Groups({"for_project"})
*/
private $created;
/**
* #var datetime $updated
*
* #Gedmo\Timestampable(on="update")
* #ORM\Column(type="datetime")
* #Groups({"for_project"})
*/
private $updated;
/**
* #var \Artel\ProfileBundle\Entity\Developer
*
* #ORM\ManyToOne(targetEntity="Developer")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="developer_id", referencedColumnName="id", onDelete="CASCADE")
* })
* #Expose()
* #Groups({"for_project"})
* #MaxDepth(2)
*/
private $developerId;
class BitController extends FOSRestController
...anotaion block
public function getBitByProjectAction($id, $token)
{
$this->getDoctrine()->getRepository('ArtelProfileBundle:Users')->findOneBySecuritytoken($token);
if (!empty($user) || $security->isGranted('ROLE_ADMIN') ) {
$bits = $this->getDoctrine()->getManager()
->getRepository('ArtelProfileBundle:Bit')
->findBitByProject($id, $token);
if (!$bits) {
throw new NotFoundHttpException();
}
return View::create()
->setStatusCode(200)
->setData($bits)
->setSerializationContext(
SerializationContext::create()
->enableMaxDepthChecks()
->setGroups(array("for_project"))
);
SOLVED
I understand, need add * #Groups({"for_project"}) for fields entity developer
but when I deleted #MaxDepth I still have fields entity developer, why need #MaxDepth ?
I understand, when use without #MaxDept, we have maximum depth for relation fields, Example I have entity Bit, bit have Developer and developer have User, if I want visible fields entity User I need add #MaxDept(3) for field developer in entity Bit
when use without #MaxDept, we have maximum depth for relation fields, Example I have entity Bit, bit have Developer and developer have User, if I want visible fields entity User I need add #MaxDept(3) for field developer in entity Bit
in action:
return View::create()
->setStatusCode(200)
->setData($bits)
->setSerializationContext(
SerializationContext::create()
->enableMaxDepthChecks()
->setGroups(array("for_project"))
);
and in response
[
{
"id": 501,
"created": "2015-11-30T17:49:19+0200",
"developer_id": {
"id": 201,
"rate": 0,
"user": [
{
"first_name": "Ivan",
"last_name": "Shuba"
}
]
},
"rate": 4,
"comment": "fsefse"
}
]
and Entity
class Bit
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #Expose()
* #Groups({"for_project"})
*/
private $id;
/**
* #var datetime $created
*
* #Gedmo\Timestampable(on="create")
* #ORM\Column(type="datetime")
* #Expose()
* #Groups({"for_project"})
*/
private $created;
/**
* #var datetime $updated
*
* #Gedmo\Timestampable(on="update")
* #ORM\Column(type="datetime")
* #Groups({"for_project"})
*/
private $updated;
/**
* #var \Artel\ProfileBundle\Entity\Developer
*
* #ORM\ManyToOne(targetEntity="Developer")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="developer_id", referencedColumnName="id", onDelete="CASCADE")
* })
* #Expose()
* #Groups({"for_project"})
* #MaxDepth(3)
*/
private $developerId;
class Developer
{
/**
* #ORM\OneToMany(targetEntity="Artel\ProfileBundle\Entity\Users", mappedBy="developer", cascade={"persist", "remove"})
* #Expose()
* #Groups({"for_project"})
*/
protected $user;
class Users implements UserInterface
{
/**
* #var string
*
* #ORM\Column(name="first_name", type="string", length=255, nullable=false)
* #Expose()
* #Assert\Length(min=3, max=255)
* #Groups({"for_project"})
*/
protected $firstName;
/**
* #var string
*
* #ORM\Column(name="last_name", type="string", length=255, nullable=true)
* #Expose()
* #Groups({"for_project"})
*/
protected $lastName;
This works for me using fos rest version: 2* and JMS serializer version: 3*
return $this->view($data)->setContext((new Context())->enableMaxDepth());

Symfony2 - Serialize object with relations (ManyToMany, OneToMany ...)

Can someone tell me if it's possible to serialize an entity that have relations ?
json_encode already works but my object looks like this :
{
"id": 1,
"lot": 32,
"num": "533987",
"date_modification": {
"date": "2015-02-17 14:24:52",
"timezone_type": 3,
"timezone": "Europe/Paris"
},
"customer": {
"id": 1,
"lastname": "DUFRESNE",
"firstname": "CHRISTOPHE",
}
}
But i would like to serialize data withotu sub objects. in fact like this :
{
"id": 1,
"lot": 32,
"num": "533987",
"date_modification": "2015-02-17",
"customer": "DUFRESNE CHRISTOPHE",
}
}
So i checked the doc : http://symfony.com/doc/current/components/serializer.html
But i can't see if it's possible to works with relations like ManyToMany, etc and how to do it ?
Here is my Entity :
/**
* Subscription (BS)
*
* #ORM\Table(name="subscription")
* #ORM\Entity(repositoryClass="Jcd\LiteyearBundle\Entity\SubscriptionRepository")
* #ORM\HasLifecycleCallbacks
*/
class Subscription {
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="lot", type="smallint")
*/
private $lot;
/**
* #var string
* #ORM\Column(name="num", type="string", length=9)
*/
private $num;
/**
* #var \DateTime
* #ORM\Column(name="date_creation", type="datetime", nullable=true)
*/
private $date_creation;
/**
* #var \DateTime
* #ORM\Column(name="date_modification", type="datetime", nullable=true)
*/
private $date_modification;
/**
* #var \DateTime
* #ORM\Column(name="date_completeness", type="datetime", nullable=true)
*/
private $date_completeness;
/**
* #var \DateTime
* #ORM\Column(name="date_reception", type="datetime", nullable=true)
*/
private $date_reception;
/**
* #var \DateTime
* #ORM\Column(name="date_signature", type="datetime", nullable=true)
*/
private $date_signature;
/**
* #var \DateTime
* #ORM\Column(name="date_entry", type="datetime", nullable=true)
*/
private $date_entry;
/**
* #var \Integer
* #ORM\Column(name="statement", type="integer", nullable=true)
*/
private $statement; // Releve du compteur
/**
* #var \Integer
* #ORM\Column(name="statement_kind", type="integer", nullable=true)
*/
private $statement_kind; // Personne qui a relevé le compteur
/**
* #var \Integer
* #ORM\Column(name="car", type="integer", nullable=true)
*/
private $car; // Consommation annuelle de reference
/**
* #var boolean
* #ORM\Column(name="pro", type="boolean", options={"default" = false}, nullable=true)
*/
private $pro;
/**
* #ORM\ManyToOne(targetEntity="Jcd\LiteyearBundle\Entity\User", cascade={"persist"}, inversedBy="adv_subscriptions")
* #ORM\JoinColumn(name="adv_id", referencedColumnName="id", nullable=true)
*/
private $adv;
/**
* #ORM\ManyToOne(targetEntity="Jcd\LiteyearBundle\Entity\User", cascade={"persist"}, inversedBy="vendor_subscriptions")
* #ORM\JoinColumn(name="vendor_id", referencedColumnName="id", nullable=true)
*/
private $vendor;
/**
* #var \Integer
* #ORM\Column(name="provider", type="integer")
*/
private $provider;
/**
* #var \Integer
* #ORM\Column(name="state", type="integer")
*/
private $state;
/**
* #var \Integer
* #ORM\Column(name="payment", type="integer")
*/
private $payment;
/**
* #var \Integer
* #ORM\Column(name="billing", type="integer")
*/
private $billing;
/**
* #var string
* #ORM\Column(name="comment", type="text", nullable=true)
*/
private $comment;
/**
* #var float
* #ORM\Column(name="wage", type="float", nullable=true)
*/
private $wage; // Anciennement Rem
/**
* #var float
* #ORM\Column(name="com", type="float", nullable=true)
*/
private $com; // Anciennement Commission
/**
* #var string
* #ORM\Column(name="offer", type="text", nullable=true)
*/
private $offer; // Anciennement Prix fixe
/**
* #var \Integer
* #ORM\Column(name="mail_kind", type="integer", nullable=true)
*/
private $mail_kind; //Anciennement Mail
/**
* #ORM\OneToMany(targetEntity="SubscriptionTrack", cascade={"persist", "remove"}, mappedBy="subscription")
*/
private $tracks;
/**
* #ORM\ManyToMany(targetEntity="SubscriptionSalary", cascade={"persist"}, mappedBy="subscriptions")
**/
private $salaries;
/**
* #ORM\OneToOne(targetEntity="Jcd\LiteyearBundle\Entity\Customer")
* #ORM\JoinColumn(name="customer_id", referencedColumnName="id")
**/
private $customer;
Thank you for you answer !
The easiest way to achieve what you want is to use JMSSerializerBundle that has many features that you might want to use.
Give it a try: http://jmsyst.com/bundles/JMSSerializerBundle

Categories