API not returning auto generated ID - symfony

Am working on Symfony API Platform, to add and retrieve something. Table has two fields id and title.
But when i run the GET query the API is returning title only not id.
How to return ID too?
My Annotation:-
* #ORM\Table(
* name="school",
* #ApiResource(
* attributes={
* "order"={"title": "ASC"},
* "normalization_context"={"groups"={"school.read"},
"enable_max_depth"=true},
* },
* itemOperations={
* "get",
* "put"
* },
* collectionOperations={
* "get"={
* "normalization_context"={
* "groups"={"school.read"}
* }
* }
* },
* normalizationContext={
* "groups"={"school.read"}
* },
* denormalizationContext={
* "groups"={"school.write"}
* }
* )
* #ORM\Entity(repositoryClass="Eqsgroup\Repository\SchoolRepository")
* #UniqueEntity(
* "title",
* repositoryMethod="findByUniqueCriteria",
* message="School already exists."
* )
*/
This is the Entity class
class School
{
/**
* #var string the id of this School
*
* #ORM\Id
* #ORM\Column(type="guid", unique=true)
* #ORM\GeneratedValue(strategy="CUSTOM")
* #ORM\CustomIdGenerator(class="Ramsey\Uuid\Doctrine\UuidGenerator")
* #Groups({"school.read, school.write"})
*/
private $id;
/**
* #var string The title of the school
*
* #ORM\Column(type="string", length=255)
* #Assert\NotNull(message="school should not be empty")
* #Assert\NotBlank(message="school should not be empty")
* #Assert\Length(
* min = 1,
* max = 250,
* minMessage = "length.min,{{ limit }}",
* maxMessage = "length.max,{{ limit }}"
* )
* #Groups({"school.read", "school.write"})
*/
private $title;
public function __construct(){ }
public function getId(): ?string
{
return $this->id;
}
public function getTitle(): ?string
{
return $this->title;
}
public function setTitle(string $title): self
{
$this->title = $title;
return $this;
}
This is the output am currently getting:
[
{
"title": "Test"
},
{
"title": "Test2"
},
]
The expected output will include the auto generated is along with title.

Add the #Groups to allow Apli-Platform to read each field you want like this :
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
* #Groups({"school.read", "school.write"})
*/
private $id;
Have a look to the documentation here :
https://api-platform.com/docs/core/serialization/#the-serialization-context-groups-and-relations

Did you configure which attributes you want to expose ?
If not, configure it in the yaml of your entity :
# I don't know the path to your entity, just modify what you need to
XXXX\XXXX\XXXX\XXXX\School:
exclusion_policy: ALL
properties:
id:
expose: true
title:
expose: true

Related

Get collection without deleted resources API-Platform

How can I select a resource without it's releted resources deleted using API-Platform ?
I have this relation Don ManyTOOne Donateur. In the Don resouce, I have $isDeleted as field. When its value is true, and a try to select a item form Donateur, I get it.
Show here:
// The GET operation http://localhost:8000/api/donateurs/3
{
"#context": "\/api\/contexts\/Donateur",
"#id": "\/api\/donateurs\/3",
"#type": "Donateur",
"id": 3,
"nom": "Yazid Ibn Amr",
"solde": 135000,
"isDeleted": false,
"dons": [
{
"#id": "\/api\/dons\/1",
"#type": "Don",
"id": 1,
"date": "2021-07-26T00:00:00+00:00",
"montant": 35000,
"isDeleted": true
},
{
"#id": "\/api\/dons\/2",
"#type": "Don",
"id": 2,
"date": "2021-07-28T00:00:00+00:00",
"montant": 60000,
"isDeleted": false
},
{
"#id": "\/api\/dons\/3",
"#type": "Don",
"id": 3,
"date": "2021-07-28T00:00:00+00:00",
"montant": 75000,
"isDeleted": false
}
]
}
I don't need deleted resource in this returned collection of Don when getting a Donateur item.
Here is my code
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use App\Repository\DonRepository;
use ApiPlatform\Core\Annotation\ApiResource;
use Symfony\Component\Serializer\Annotation\Groups;
/**
* #ApiResource(
* normalizationContext={
* "groups"={
* "read:Don:item",
* "read:Don:collection"
* }
* },
* denormalizationContext={
* "groups"={
* "write:Don"
* }
* },
* collectionOperations={
* "get", "post"
* },
* itemOperations={
* "get"={
* "normalization_context"={
* "groups"={
* "read:Don:item",
* "read:Don:collection"
* }
* }
* },
* "patch", "delete"
* }
* )
* #ORM\Entity(repositoryClass=DonRepository::class)
*/
class Don
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
* #Groups({"read:Donateur:item"})
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity=Donateur::class, inversedBy="dons")
* #ORM\JoinColumn(nullable=false)
* #Groups({"read:Don:collection", "write:Don"})
*/
private $donateur;
/**
* #ORM\Column(type="date", nullable=true)
* #Groups({"read:Don:collection", "write:Don", "read:Donateur:item"})
*/
private $date;
/**
* #ORM\Column(type="float")
* #Groups({"read:Don:collection", "write:Don", "read:Donateur:item"})
*/
private $montant;
And
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use App\Repository\DonateurRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;
/**
* #ApiResource(
* normalizationContext={
* "groups"={
* "read:Donateur:item",
* "read:Donateur:collection"
* }
* },
* denormalizationContext={
* "groups"={
* "write:Donateur"
* }
* },
* collectionOperations={
* "get", "post"
* },
* itemOperations={
* "get", "patch", "delete"
* }
* )
* #ORM\Entity(repositoryClass=DonateurRepository::class)
*/
class Donateur
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
* #Groups("read:Donateur:collection")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
* #Groups({"read:Donateur:collection", "write:Donateur"})
*/
private $nom;
/**
* #ORM\Column(type="text", nullable=true)
* #Groups({"read:Donateur:collection", "write:Donateur"})
*/
private $presentation;
/**
* #ORM\Column(type="float")
* #Groups({"read:Donateur:collection", "write:Don"})
*/
private $solde;
/**
* #ORM\Column(type="boolean")
* #Groups("read:Donateur:collection")
*/
private $isDeleted;
/**
* #ORM\Column(type="datetime", nullable=true)
*/
private $createdAt;
/**
* #ORM\ManyToOne(targetEntity=User::class)
*/
private $createdBy;
/**
* #ORM\Column(type="datetime", nullable=true)
*/
private $updatedAt;
/**
* #ORM\ManyToOne(targetEntity=User::class)
*/
private $updatedBy;
/**
* #ORM\Column(type="datetime", nullable=true)
*/
private $deletedAt;
/**
* #ORM\ManyToOne(targetEntity=User::class)
*/
private $deletedBy;
/**
* #ORM\OneToMany(targetEntity=Don::class, mappedBy="donateur")
* #Groups({"read:Donateur:item"})
*/
private $dons;
And this one
<?php
namespace App\Doctrine;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\QueryCollectionExtensionInterface;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\QueryItemExtensionInterface;
use Doctrine\ORM\QueryBuilder;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGeneratorInterface;
use App\Entity\Don;
use App\Entity\Donateur;
use App\Entity\TypeOeuvre;
class NoneDeletedResources implements QueryCollectionExtensionInterface, QueryItemExtensionInterface
{
public function addWhere(QueryBuilder $queryBuilder, string $resourceClass)
{
if($resourceClass === TypeOeuvre::class or $resourceClass === Don::class or $resourceClass === Donateur::class){
$rootAlias = $queryBuilder->getRootAliases()[0];
$queryBuilder->andWhere("$rootAlias.isDeleted = :isDeleted");
$queryBuilder->orderBy("$rootAlias.id", "DESC");
$queryBuilder->setParameter("isDeleted", false);
}
}
public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, ?string $operationName = null)
{
$this->addWhere($queryBuilder, $resourceClass);
}
public function applyToItem(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, array $identifiers, ?string $operationName = null, array $context = [])
{
// dd($context);
$this->addWhere($queryBuilder, $resourceClass);
}
}
Thank you for helping
$queryBuilder->andWhere("$rootAlias.isDeleted = :isDeleted");
With this code, you're only filtering on the root alias, not on its relations. You should alter your code in such a way that it filters on those relations as well.
public function addWhere(QueryBuilder $queryBuilder, string $resourceClass)
{
if($resourceClass === Donateur::class) {
$rootAlias = $queryBuilder->getRootAliases()[0];
$queryBuilder->andWhere("$rootAlias.isDeleted = :isDeleted");
$queryBuilder->orderBy("$rootAlias.id", "DESC");
$queryBuilder->setParameter("isDeleted", false);
// add something like this:
$queryBuilder->andWhere("dons.isDeleted = :isDeleted");
}
}
The exact name of the alias (I used dons as an example) depends on your use case. Maybe you should join Don before you can use it.
I hope you can use my suggestion to fix your problem. If not, please let me know; I'll help with adding some implementation details.
public function addWhere(QueryBuilder $queryBuilder, string $resourceClass)
{
if($resourceClass === TypeOeuvre::class or $resourceClass === Don::class or $resourceClass === Donateur::class or $resourceClass === Oeuvre::class or $resourceClass === DonOeuvre::class){
$rootAlias = $queryBuilder->getRootAliases()[0];
if($resourceClass === Donateur::class) {
$queryBuilder->join("$rootAlias.dons", "d");
$queryBuilder->andWhere("d.isDeleted = :isDeleted");
}
if($resourceClass === Oeuvre::class or $resourceClass === Don::class) {
$queryBuilder->join("$rootAlias.donOeuvres", "d");
$queryBuilder->andWhere("d.isDeleted = :isDeleted");
// dd($queryBuilder);
}
$queryBuilder->andWhere("$rootAlias.isDeleted = :isDeleted");
$queryBuilder->orderBy("$rootAlias.id", "DESC");
$queryBuilder->setParameter("isDeleted", false);
}
}
The first if is working correctly. But not the second
This is my resources code:
Don.php
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use App\Repository\DonRepository;
use ApiPlatform\Core\Annotation\ApiResource;
use Symfony\Component\Serializer\Annotation\Groups;
/**
* #ApiResource(
* normalizationContext={
* "groups"={
* "read:Don:item",
* "read:Don:collection"
* }
* },
* denormalizationContext={
* "groups"={
* "write:Don"
* }
* },
* collectionOperations={
* "get", "post"
* },
* itemOperations={
* "get"={
* "normalization_context"={
* "groups"={
* "read:Don:item",
* "read:Don:collection"
* }
* }
* },
* "utilisation_don" = {
* "path" = "/{id}/utilisation-don,
* "controller" =
* }
* "patch", "delete"
* }
* )
* #ORM\Entity(repositoryClass=DonRepository::class)
*/
class Don
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
* #Groups({"read:Donateur:item"})
*/
private $id;
...
/**
* #ORM\OneToMany(targetEntity=DonOeuvre::class, mappedBy="don")
*/
private $donOeuvres;
Oeuvre.php
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Symfony\Component\Serializer\Annotation\Groups;
use App\Repository\OeuvreRepository;
use Doctrine\ORM\Mapping as ORM;
/**
* #ApiResource(
* normalizationContext={
* "groups"={
* "read:Oeuvre:collection"
* }
* },
* denormalizationContext={
* "groups"={
* "write:Oeuvre"
* }
* },
* collectionOperations={
* "get",
* "post" = {
* "denormalization_context"={
* "groups"={
* "write:Oeuvre"
* }
* }
* }
* },
* itemOperations={
* "get",
* "patch" = {
* "denormalization_context"={
* "groups"={
* "update:Oeuvre:patch"
* }
* }
* },
* "delete"
* }
* )
* #ORM\Entity(repositoryClass=OeuvreRepository::class)
*/
class Oeuvre
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
* #Groups("read:Oeuvre:collection")
*/
private $id;
...
/**
* #ORM\OneToMany(targetEntity=DonOeuvre::class, mappedBy="oeuvre")
* #Groups({"write:Oeuvre"})
*/
private $donOeuvres;
and DonOeuvre.php
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use App\Repository\DonOeuvreRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;
/**
* #ApiResource(
* normalizationContext={
* "groups"={
* "read:DonOeuvre:collection"
* }
* },
* denormalizationContext={
* "groups"={
* "write:DonOeuvre"
* }
* },
* collectionOperations={
* "get", "post"
* },
* itemOperations={
* "get", "patch", "delete"
* }
* )
* #ORM\Entity(repositoryClass=DonOeuvreRepository::class)
*/
class DonOeuvre
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
* #Groups({"read:DonOeuvre:collection", "read:Don:item"})
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity=Don::class, inversedBy="donOeuvres")
* #ORM\JoinColumn(nullable=false)
* #Groups({"read:DonOeuvre:collection", "write:DonOeuvre", "write:Oeuvre"})
*/
private $don;
/**
* #ORM\ManyToOne(targetEntity=Oeuvre::class, inversedBy="donOeuvres", cascade={"persist"})
* #ORM\JoinColumn(nullable=false)
* #Groups({"read:DonOeuvre:collection", "write:DonOeuvre", "read:Don:item"})
*/
private $oeuvre;

Discriminator column : Class has no field or association named type

I have a class contract that contains the abstract attribute contractInfo Type
/**
* #var ContractInfoType|null
*
* #ORM\OneToOne(targetEntity="ContractInfoType", cascade={"persist"})
* #ORM\JoinColumn(name="contract_info_type_id", referencedColumnName="id", nullable=true)
*/
private $contractInfoType;
ContractInfoType is an abstract entity : ContractInfoPro and ContractInfoAsso inherit from them
/**
* ContractInfoType
*
* #ORM\Table(name="contract_info_type",
* indexes={
* #ORM\Index(name="IDX_TYPE", columns={"type"}),
* }
* )
* #ORM\Entity(repositoryClass="App\Repository\Contract\ContractInfoTypeRepository")
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="type", type="string")
* #ORM\DiscriminatorMap(
* {
* "pro" = "App\Entity\Contract\Type\ContractInfoPro",
* "asso" = "App\Entity\Contract\Type\ContractInfoAsso"
* }
* )
* #ORM\HasLifecycleCallbacks
*/
abstract class ContractInfoType
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
public function getId(): int
{
return $this->id;
}
public function setId(int $id): self
{
$this->id = $id;
return $this;
}
abstract public function getType(): string;
}
and the class that inherit ContractInfoType
/**
* ContractInfoAsso
*
* #ORM\Table(name="contract_info_asso")
* #ORM\Entity(repositoryClass="App\Repository\Contract\ContractInfoAssoRepository")
* #ORM\HasLifecycleCallbacks
*/
class ContractInfoAsso extends ContractInfoType
{
const TYPE = 'asso';
/**
* #var string
*
* #ORM\Column(name="num_prefecture", type="string", length=40, nullable=true)
*
* #Assert\Type(
* type="string",
* message="The value {{ value }} is not a valid {{ type }}."
* )
* #Assert\Length(
* max = 40,
* maxMessage = "num_prefecture value cannot be longer than {{ limit }} characters"
* )
*/
private $numPrefecture;
/**
* #return string
*/
public function getNumPrefecture(): string
{
return $this->numPrefecture;
}
/**
* #param string $numPrefecture
*/
public function setNumPrefecture(string $numPrefecture): self
{
$this->numPrefecture = $numPrefecture;
return $this;
}
public function getType(): string
{
return self::TYPE;
}
}
My goal is to filter the contract by the discriminator column type like I would do in SQL:
select * from contract left join contract_info_type cit on contract.contract_info_type_id = cit.id where cit.type LIKE "pro"
However when I try to do it with doctrine this way
$queryBuilder->leftJoin('contract.contractInfoType', 'type');
$queryBuilder->andWhere('type.type = :type");
I got the error
[Semantical Error] line 0, col 183 near 'type = :type': Error: Class App\\Entity\\Contract\\ContractInfoType has no field or association named type
Which make sense since I have no real field type declared in the entity.
How should I make this work with doctrine ?
It seems like you cannot use the discriminator in such a way. In your DQL try using "INSTANCE OF" instead of a join.
More info
Possible duplicate

Edit Label will change the Form Type in Symfony4 with EasyAdminBundle

First, I'm a french beginner in Symfony4,
Second, I already searched in Symfony Documentation, asked some friends, called my mom..
I'm working on EasyAdminBundle on the form Edit / New Entity.
I have to change the label of my entities but when I do it, my form type is changing.
Here's when picture of my view before editing:
I want to change ' id_equipe' to 'Domicile (home for english )' and id_equipe_equipes to 'exterieur (Visitors)'
So when I tried this :
fields:
- { property: 'id_equipe', label: equipe_domicile}
- { property: 'id_equipe_equipes', label: equipe_extérieur}
The type of the properties is changing to TextArea and I don't know why.
I tried to put a new type like this :
- { property: 'id_equipe', label: equipe_domicile, type: 'choice'}
but my select is blank, I cannot choose anything.
This is what I get:
Thanks you guys
Ps: sorry for ten years old english
So, look, if I understand correctly - id_equipe is a key to external entity. So, this way, to get it worked - you need to use type: 'entity' and also add type_options option, something like that:
- { property: 'id_equipe', label: 'Domicile', type: 'entity', type_options: { class: 'App\Entity\Equipe', multiple: true } }
UPDATE:
So, due to discussion found that the problem was in the property naming. Right to be property: 'idEquipe' not property: 'id_equipe'. So the property names must be same as in the entity, not as the name of the field in the database.
Here's my Matchs entity.
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* Matchs
*
* #ORM\Table(name="matchs", indexes={#ORM\Index(name="MATCHS_EQUIPES1_FK", columns={"ID_EQUIPE_EQUIPES"}), #ORM\Index(name="MATCHS_EQUIPES_FK", columns={"ID_EQUIPE"}), #ORM\Index(name="MATCHS_JOURNEE0_FK", columns={"ID_JOURNEE"})})
* #ORM\Entity
*/
class Matchs
{
/**
* #var int
*
* #ORM\Column(name="ID_MATCHS", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $idMatchs;
/**
* #var \DateTime
*
* #ORM\Column(name="DATE_MATCHS", type="datetime", nullable=false)
*/
private $dateMatchs;
/**
* #var string|null
*
* #ORM\Column(name="RESUME_MATCHS", type="text", length=65535, nullable=true)
*/
private $resumeMatchs;
/**
* #var string|null
*
* #ORM\Column(name="TITRE_MATCH", type="string", length=255, nullable=true)
*/
private $titreMatch;
/**
* #var int
*
* #ORM\Column(name="SCORE_EQUIPE1", type="integer", nullable=false)
*/
private $scoreEquipe1;
/**
* #var int
*
* #ORM\Column(name="SCORE_EQUIPE2", type="integer", nullable=false)
*/
private $scoreEquipe2;
/**
* #var \Equipes
*
* #ORM\ManyToOne(targetEntity="Equipes")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="ID_EQUIPE_EQUIPES", referencedColumnName="ID_EQUIPE")
* })
*/
private $idEquipeEquipes;
/**
* #var \Equipes
*
* #ORM\ManyToOne(targetEntity="Equipes")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="ID_EQUIPE", referencedColumnName="ID_EQUIPE")
* })
*/
private $idEquipe;
/**
* #var \Journee
*
* #ORM\ManyToOne(targetEntity="Journee")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="ID_JOURNEE", referencedColumnName="ID_JOURNEE")
* })
*/
private $idJournee;
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\ManyToMany(targetEntity="JoueursEquipe", inversedBy="idMatchs")
* #ORM\JoinTable(name="jouer",
* joinColumns={
* #ORM\JoinColumn(name="ID_MATCHS", referencedColumnName="ID_MATCHS")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="ID_JOUEUR_EQUIPE", referencedColumnName="ID_JOUEUR_EQUIPE")
* }
* )
*/
private $idJoueurEquipe;
/**
* Constructor
*/
public function __construct()
{
$this->idJoueurEquipe = new \Doctrine\Common\Collections\ArrayCollection();
}
public function getIdMatchs()
{
return $this->idMatchs;
}
public function setIdMatchs(int $idMatchs)
{
$this->idMatchs = $idMatchs;
return $this;
}
/**
* Get the value of dateMatchs
*
* #return \DateTime
*/
public function getDateMatchs()
{
return $this->dateMatchs;
}
/**
* Set the value of dateMatchs
*
* #param \DateTime $dateMatchs
*
* #return self
*/
public function setDateMatchs(\DateTime $dateMatchs)
{
$this->dateMatchs = $dateMatchs;
return $this;
}
/**
* Get the value of resumeMatchs
*
* #return string|null
*/
public function getResumeMatchs()
{
return $this->resumeMatchs;
}
/**
* Set the value of resumeMatchs
*
* #param string|null $resumeMatchs
*
* #return self
*/
public function setResumeMatchs($resumeMatchs)
{
$this->resumeMatchs = $resumeMatchs;
return $this;
}
/**
* Get the value of scoreEquipe1
*
* #return int
*/
public function getScoreEquipe1()
{
return $this->scoreEquipe1;
}
/**
* Set the value of scoreEquipe1
*
* #param int $scoreEquipe1
*
* #return self
*/
public function setScoreEquipe1(int $scoreEquipe1)
{
$this->scoreEquipe1 = $scoreEquipe1;
return $this;
}
/**
* Get the value of scoreEquipe2
*
* #return int
*/
public function getScoreEquipe2()
{
return $this->scoreEquipe2;
}
/**
* Set the value of scoreEquipe2
*
* #param int $scoreEquipe2
*
* #return self
*/
public function setScoreEquipe2(int $scoreEquipe2)
{
$this->scoreEquipe2 = $scoreEquipe2;
return $this;
}
/**
* Get the value of idEquipeEquipes
*
* #return \Equipes
*/
public function getIdEquipeEquipes()
{
return $this->idEquipeEquipes;
}
/**
* Set the value of idEquipeEquipes
*
* #param \Equipes $idEquipeEquipes
*
* #return self
*/
public function setIdEquipeEquipes(\Equipes $idEquipeEquipes)
{
$this->idEquipeEquipes = $idEquipeEquipes;
return $this;
}
/**
* Get the value of idEquipe
*
* #return \Equipes
*/
public function getIdEquipe()
{
return $this->idEquipe;
}
/**
* Set the value of idEquipe
*
* #param \Equipes $idEquipe
*
* #return self
*/
public function setIdEquipe(\Equipes $idEquipe)
{
$this->idEquipe = $idEquipe;
return $this;
}
public function __toString()
{
return $this->nomEquipe;
}
/**
* Get the value of idJournee
*
* #return \Journee
*/
public function getIdJournee()
{
return $this->idJournee;
}
/**
* Set the value of idJournee
*
* #param \Journee $idJournee
*
* #return self
*/
public function setIdJournee(\Journee $idJournee)
{
$this->idJournee = $idJournee;
return $this;
}
public function getTitreMatch(): ?string
{
return $this->titreMatch;
}
public function setTitreMatch(string $titreMatch): self
{
$this->titreMatch = $titreMatch;
return $this;
}
/**
* Get the value of idJoueurEquipe
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getIdJoueurEquipe()
{
return $this->idJoueurEquipe;
}
/**
* Set the value of idJoueurEquipe
*
* #param \Doctrine\Common\Collections\Collection $idJoueurEquipe
*
* #return self
*/
public function setIdJoueurEquipe(\Doctrine\Common\Collections\Collection $idJoueurEquipe)
{
$this->idJoueurEquipe = $idJoueurEquipe;
return $this;
}
public function addIdJoueurEquipe(JoueursEquipe $idJoueurEquipe): self
{
if (!$this->idJoueurEquipe->contains($idJoueurEquipe)) {
$this->idJoueurEquipe[] = $idJoueurEquipe;
}
return $this;
}
public function removeIdJoueurEquipe(JoueursEquipe $idJoueurEquipe): self
{
if ($this->idJoueurEquipe->contains($idJoueurEquipe)) {
$this->idJoueurEquipe->removeElement($idJoueurEquipe);
}
return $this;
}
}

Doctrine ORM / Symfony - Can update child objects from parent object update?

I have two entities, Quiz and Question with retlationship OneToMany (1 Quiz can have many questions).
I am trying to update the quiz object(id=19) through a put action in RestApi
adding to array questions 2 ids of question objects.
These ids are till that momment orphans, the quiz_id of them is null.
Quiz id 19 Before update:
{
"id": 19
"alias": "Test Quiz",
"questions": [],
"hasFifty": false,
"hasTip": false,
"hasNext": false
}
Json Data on Put action (Update Quiz object 19):
{
"alias": "quiz-bill",
"questions": [42,43],
"hasFifty": true,
"hasTip": true,
"hasNext": true
}
The response of put request shows me the update quiz object :
{
"id": 19,
"alias": "quiz-bill",
"questions": [
{
"id": 42,
"content": "test test test",
"helpText": "dummy dummy dummy"
},
{
"id": 43,
"content": "test test",
"helpText": "dummy"
}
],
"hasFifty": true,
"hasTip": true,
"hasNext": true
}
But this object is fake , when i select these the questions from database, they still have quiz_id null.
I was hopping to update the parent field(quiz_id) of these child objects from parent(Quiz) update, but this seems not cappable.
Is there anyone who has done something like that with doctrine and Symfony framework? Or can help me in this ?
Quiz Entity:
/**
* Quiz.
*
* #ORM\Table(name="quiz")
* #ORM\Entity(repositoryClass="ApiBundle\Repository\QuizRepository")
* #JMS\ExclusionPolicy("all")
*/
class Quiz
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #JMS\Groups({"task", "quiz"})
* #JMS\Expose
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="alias", type="string", length=255)
* #JMS\Groups({"quiz"})
* #JMS\Expose
*/
private $alias;
/**
* #var ArrayCollection
*
* #ORM\OneToMany(targetEntity="Question", mappedBy="quiz")
* #JMS\Groups({"quiz"})
* #JMS\Expose
*/
private $questions;
/**
* #var bool
*
* #ORM\Column(name="hasFifty", type="boolean", nullable=true)
* #JMS\Groups({"quiz"})
* #JMS\Expose
*/
private $hasFifty;
/**
* #var bool
*
* #ORM\Column(name="hasTip", type="boolean", nullable=true)
* #JMS\Groups({"quiz"})
* #JMS\Expose
*/
private $hasTip;
/**
* #var bool
*
* #ORM\Column(name="hasNext", type="boolean", nullable=true)
* #JMS\Groups({"quiz"})
* #JMS\Expose
*/
private $hasNext;
/**
* Get id.
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set alias.
*
* #param string $alias
*
* #return Quiz
*/
public function setAlias($alias)
{
$this->alias = $alias;
return $this;
}
/**
* Get alias.
*
* #return string
*/
public function getAlias()
{
return $this->alias;
}
/**
* Set hasFifty.
*
* #param bool $hasFifty
*
* #return Quiz
*/
public function setHasFifty($hasFifty)
{
$this->hasFifty = $hasFifty;
return $this;
}
/**
* Get hasFifty.
*
* #return bool
*/
public function getHasFifty()
{
return $this->hasFifty;
}
/**
* Set hasTip.
*
* #param bool $hasTip
*
* #return Quiz
*/
public function setHasTip($hasTip)
{
$this->hasTip = $hasTip;
return $this;
}
/**
* Get hasTip.
*
* #return bool
*/
public function getHasTip()
{
return $this->hasTip;
}
/**
* Add question.
*
* #param \ApiBundle\Entity\Question $question
*
* #return Quiz
*/
public function addQuestion(\ApiBundle\Entity\Question $question)
{
$this->questions[] = $question;
return $this;
}
/**
* Remove question.
*
* #param \ApiBundle\Entity\Question $question
*/
public function removeQuestion(\ApiBundle\Entity\Question $question)
{
$this->questions->removeElement($question);
}
/**
* Get questions.
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getQuestions()
{
return $this->questions;
}
/**
* Set hasNext.
*
* #param bool $hasNext
*
* #return Quiz
*/
public function setHasNext($hasNext)
{
$this->hasNext = $hasNext;
return $this;
}
/**
* Get hasNext.
*
* #return bool
*/
public function getHasNext()
{
return $this->hasNext;
}
}
Question Entity:
/**
* Question.
*
* #ORM\Table(name="question")
* #ORM\Entity(repositoryClass="ApiBundle\Repository\QuestionRepository")
* #JMS\ExclusionPolicy("all")
*/
class Question
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #JMS\Groups({"quiz" ,"question"})
* #JMS\Expose
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="content", type="text")
* #JMS\Groups({"quiz" ,"question"})
* #JMS\Expose
*/
private $content;
/**
* #var string
*
* #ORM\Column(name="help", type="text", nullable=true)
* #JMS\Groups({"quiz" ,"question"})
* #JMS\Expose
*/
private $helpText;
/**
* #var \ApiBundle\Entity\Quiz
*
* #ORM\ManyToOne(targetEntity="Quiz", inversedBy="questions")
* #ORM\JoinColumn(name="quiz_id", referencedColumnName="id")
*/
protected $quiz;
/**
* #var \DateTime
*
* #Gedmo\Timestampable(on="create")
* #ORM\Column(name="createdAt", type="datetime")
* #JMS\Groups({"quiz" ,"question"})
* #JMS\Expose
*/
private $createdAt;
/**
* #var \DateTime
*
* #Gedmo\Timestampable(on="update")
* #ORM\Column(name="updatedAt", type="datetime", nullable=true)
* #JMS\Groups({"quiz" ,"question"})
* #JMS\Expose
*/
private $updatedAt;
public function __construct()
{
$this->answers = new ArrayCollection();
}
/**
* Get id.
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set content.
*
* #param string $content
*
* #return Question
*/
public function setContent($content)
{
$this->content = $content;
return $this;
}
/**
* Get content.
*
* #return string
*/
public function getContent()
{
return $this->content;
}
/**
* Set createdAt.
*
* #param \DateTime $createdAt
*
* #return Question
*/
public function setCreatedAt($createdAt)
{
$this->createdAt = $createdAt;
return $this;
}
/**
* Get createdAt.
*
* #return \DateTime
*/
public function getCreatedAt()
{
return $this->createdAt;
}
/**
* Set updatedAt.
*
* #param \DateTime $updatedAt
*
* #return Question
*/
public function setUpdatedAt($updatedAt)
{
$this->updatedAt = $updatedAt;
return $this;
}
/**
* Get updatedAt.
*
* #return \DateTime
*/
public function getUpdatedAt()
{
return $this->updatedAt;
}
/**
* Set helpText.
*
* #param string $helpText
*
* #return Question
*/
public function setHelpText($helpText)
{
$this->helpText = $helpText;
return $this;
}
/**
* Get helpText.
*
* #return string
*/
public function getHelpText()
{
return $this->helpText;
}
/**
* Set quiz.
*
* #param \ApiBundle\Entity\Quiz $quiz
*
* #return Question
*/
public function setQuiz(\ApiBundle\Entity\Quiz $quiz = null)
{
$this->quiz = $quiz;
return $this;
}
/**
* Get quiz.
*
* #return \ApiBundle\Entity\Quiz
*/
public function getQuiz()
{
return $this->quiz;
}
}
QuizController Put action:
/**
* Update an existing Quiz.
*
* #param Request $request
* #param int $id
*
* #return mixed
*
* #Operation(
* tags={"Quiz"},
* summary="Update an existing Quiz.",
* #SWG\Response(
* response="204",
* description="Returned when an existing Quiz has been successful updated"
* ),
* #SWG\Response(
* response="400",
* description="Return when errors"
* ),
* #SWG\Response(
* response="401",
* description="Returned when access is not authorized"
* ),
* #SWG\Response(
* response="404",
* description="Return when not found"
* )
* )
*
*
* #Rest\View(serializerGroups={"quiz"})
*/
public function putAction(Request $request, $id)
{
$quiz = $this->getDoctrine()->getRepository('ApiBundle:Quiz')->find($id);
if (null === $quiz || empty($quiz)) {
return new View(null, Response::HTTP_NOT_FOUND);
}
$form = $this->createForm(QuizType::class, $quiz, [
'method' => 'PUT',
'csrf_protection' => false,
]);
$form->submit($request->request->all(), false);
if (!$form->isValid()) {
return $form;
}
$em = $this->getDoctrine()->getManager();
$em->persist($quiz);
$em->flush();
return $quiz;
}
QuizType Form:
<?php
namespace ApiBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class QuizType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('alias')
->add('hasFifty')
->add('hasTip')
->add('hasNext')
->add('videoUrl')
->add('questions')
->add('task');
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'ApiBundle\Entity\Quiz',
'csrf_protection' => false,
'allow_extra_fields' => true,
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'apibundle_quiz';
}
}
In the controller action you only persist the Quiz entity but you need to persist the related Question entities too. In a many to one relation the connection is saved in the table of the entity that can only have one related entity.
foreach($quiz->getQuestions() as $question) {
// I don't know if you need this line
$question->setQuiz($quiz);
$em->persist($question);
}
$em->persist($quiz);
As you are adding already persisted questions to your quiz you do not need to cascade persist but you still need to set your quiz to the added question:
// inside Quiz entity
public function addQuestion(\ApiBundle\Entity\Question $question)
{
$question->setQuiz($this);
$this->questions[] = $question;
return $this;
}
This is because (quoting from Doctrine ORM Documentation)
Doctrine will only check the owning side of an association for
changes.
Changes made only to the inverse side of an association are ignored.
Make sure to update both sides of a bidirectional association (or at
least the owning side, from Doctrine's point of view)
In your case the inverse side of the association is Quiz entity and the owning side is the Question entity.

ReflectionException in JMS

I am developing a simple app in symfony to learn symfony.It has Terms,Example entities.TermExamples is one more entity is one more entitty which is a mapping table between Term and Examples.All is fine but when i try to serialize using JMS serializer ,I get a Reflection exception
These are my entities
Term
/**
* Term
*
* #ORM\Table(name="terms")
* #ORM\Entity
*/
class Term
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="termName", type="string", length=255)
*/
private $termName;
/**
* #var string
*
* #ORM\Column(name="description", type="string", length=2000)
*/
private $description;
/**
* #var string
*
* #ORM\Column(name="mnemonic", type="string", length=2000)
*/
private $mnemonic;
/**
* #var ArrayCollection
*
* #ORM\OneToMany(targetEntity="RelatedTerm",
* mappedBy="term1",cascade={"persist"})
*/
private $relatedTerms;
/**
* #var ArrayCollection
*
* #ORM\OneToMany(targetEntity="TermExample",
* mappedBy="term",cascade={"persist"})
*/
private $termExamples;
public function __construct()
{
$this->relatedTerms = new ArrayCollection();
$this->termExamples = new ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Get Related Terms
*/
public function getRelatedTerms()
{
return $this->relatedTerms ;
}
/**
* Add related term
*/
public function addRelatedTerm($relatedTerm)
{
$this->relatedTerms[] = $relatedTerm;
}
/**
* Clear related Terms
*/
public function clearRelatedTerms()
{
$this->relatedTerms->clear();
}
/**
* Remove a related Term
*/
public function removeRelatedTerm($relatedTerm)
{
$this->relatedTerms->removeElement($relatedTerm);
}
/**
* Get Term Examples
*/
public function getTermExamples()
{
return $this->termExamples ;
}
/**
* Add term example
*/
public function addTermExample($termExample)
{
$this->termExamples[] = $termExample;
}
/**
* Clear term examples
*/
public function clearTermExamples()
{
$this->termExamples->clear() ;
}
/**
* Remove a Term Example
*/
public function removeTermExample($termExample)
{
$this->termExamples->removeElement($termExample);
}
/**
* Set termName
*
* #param string $termName
* #return Term
*/
public function setTermName($termName)
{
$this->termName = $termName;
return $this;
}
/**
* Get termName
*
* #return string
*/
public function getTermName()
{
return $this->termName;
}
/**
* Set description
*
* #param string $description
* #return Term
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Get description
*
* #return string
*/
public function getDescription()
{
return $this->description;
}
/**
* Set mnemonic
*
* #param string $mnemonic
* #return Term
*/
public function setMnemonic($mnemonic)
{
$this->mnemonic = $mnemonic;
return $this;
}
/**
* Get mnemonic
*
* #return string
*/
public function getMnemonic()
{
return $this->mnemonic;
}
}
Example
/**
* Example
*
* #ORM\Table(name="examples")
* #ORM\Entity
*/
class Example
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="example_sentence", type="string")
*/
private $exampleSentence;
/**
* #var term
*
* #ORM\OneToMany(targetEntity="TermExample",
* mappedBy="example")
*/
private $termExamples;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Get ExampleSentence
*
* #return string
*/
public function getExampleSentence()
{
return $this->exampleSentence;
}
/**
* Set Example sentence
*
* #param string $exampleSentence
*/
public function setExampleSentence($exampleSentence)
{
$this->exampleSentence = $exampleSentence;
return $this;
}
}
?>
TermExample
/**
* TermExample
*
* #ORM\Table(name="term_examples")
* #ORM\Entity
*/
class TermExample
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Term",
* inversedBy="termExamples",
* cascade={"persist"})
*/
private $term;
/**
* #ORM\ManyToOne(targetEntity="Example",
* inversedBy="termExamples",
* cascade={"persist"})
*/
private $example;
/**
* #var string
*
* #ORM\Column(name="pos",nullable=true)
*/
private $pos = "NO POS";
//public $exampleSentence;
public function __construct()
{
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Get Term
*
* #return Term
*/
public function getTerm()
{
return $this->term ;
}
/**
* Set Term
* #param Term $term
*
* #return TermExample
*/
public function setTerm($term)
{
$this->term = $term;
$term->addTermExample($this);
}
/**
* Get Example
*
* #return Example
*/
public function getExample()
{
return $this->example ;
}
/**
* Set Example
*
* #return TermExample
*/
public function setExample($example)
{
$this->example = $example;
}
/**
* Get pos
*
* #return string
*/
public function getPos()
{
return $this->pos ;
}
/**
* Set pos
*
* #param string $pos -Parts of speech
*/
public function setPos($pos)
{
$this->pos = $pos;
}
}
When i try to add a term with example sentence,Everything goes fine into the db,but when i try to serialise the term, It shows me an error
"message": "Property Madhuri\\TermsBundle\\Entity\\TermExample::$exampleSentence does not exist",
"class": "ReflectionException",
"trace": [
{
"namespace": "",
"short_class": "",
"class": "",
"type": "",
"function": "",
"file": "/Users/madhuripalagummi/Documents/Dictionary/vendor/jms/metadata/src/Metadata/PropertyMetadata.php",
"line": 75,
"args": []
},
I tried clearing the doctrine cache metadata.But still dint work.
And when i added a field $exampleSentence in TermExample entity,It worked.But why should there be a $exampleSentence in TermExample?
Can anyone please help me?
JMS stores its serialization cache separately to Doctrine so I would try blitzing your cache (i.e. deleting the contents of the cache directory, not just doing a cache:clear command) and trying again. It sounds like the serializer is holding on to old annotations.

Resources