RidRoid
RidRoid

Reputation: 961

API Platform model attributes are readOnly

I'm struggling with this strange behavior with my API: some of the attributes are set to readOnly: true.

EDIT: This is how my entities are defined

/**
 * @ApiResource(
 *     normalizationContext={"groups"={"read_partenaire"}},
 *     denormalizationContext={"groups"={"write_partenaire"}}
 * )
 * @ORM\Entity(repositoryClass="App\Repository\ProfessionnelRepository")
 * @ApiFilter(SearchFilter::class, properties={"nom": "partial", "id": "exact"})
 */

class Professionnel
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     * @Groups({"read_partenaire"})
     */
    private $id;

    /**
     * @ORM\OneToOne(targetEntity="App\Entity\Partenaire", cascade={"persist"})
     * @ORM\JoinColumn(nullable=false)
     * @Groups({"read_partenaire","write_partenaire"})
     *
     */
    private $partenaire;

    /**
     * @ORM\Column(type="string", length=4)
     * @Groups({"read_partenaire","write_partenaire"})
     *
     */
    private $civilite;

    /**
     * @ORM\Column(type="string", length=100)
     * @Groups({"read_partenaire","write_partenaire"})
     *
     */
    private $nom;

    /**
     * @ORM\Column(type="string", length=100)
     * @Groups({"read_partenaire","write_partenaire"})
     *
     */
    private $prenom;

The second entity :

/**
 * @ApiResource(
 *     normalizationContext={"groups"={"read_partenaire"}},
 *     denormalizationContext={"groups"={"write_partenaire"}}
 * )
 * @ApiFilter(SearchFilter::class, properties={"id": "exact"})
 * @ORM\Entity(repositoryClass="App\Repository\PartenaireRepository")
 */
class Partenaire
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     * @Groups({"read_partenaire"})
     */
    private $id;

    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\Ban", inversedBy="partenaires", cascade={"persist"})
     * @ORM\JoinColumn(nullable=false)
     * @Groups({"read_partenaire","write_partenaire"})

     */
    private $ban;

The third entity:

/**
 * @ApiResource()
 * @ORM\Entity(repositoryClass="App\Repository\BanRepository")
 */
class Ban
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     * @Groups({"read_partenaire"})
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=255)
     * @Groups({"read_partenaire","write_partenaire"})
     *
     */
    private $nom_voie;

To sum it up, my Professionnel entity is nested to Partenaire which is nested to Ban. So by creating a new Professionnel a new Partenaire and Ban should be created as well.

Please keep in mind that all the properties of my 3 entities have get and set functions (except id's of course)...but for some reason, the property nom_voie of my third entity is set to readOnly (and because of that, the insert of all the entities fail...) Here is the entity documentation I get

I'm not sure how exactly this two-level nesting should be expressed using Groups I tried many combinations but no luck...

Upvotes: 1

Views: 2214

Answers (3)

Vasilis
Vasilis

Reputation: 41

if you automatically generated getter and setter methods for $nom_voie attribute with "php bin/console make:entity" that probably would be getNomVoie() and setNomVoie() .

Api platform is looking for a setter method to decide if attribute is readonly and cannot associate $nom_voie with setNomVoie. Either rename attribute in camelCase format ($nomVoie) or rename the setter method to setNom_voie()

Upvotes: 2

Hassan Juniedi
Hassan Juniedi

Reputation: 413

You have to update your entity as follow, you cant have the same group name for normalization and denormalization, now for properties that you need to submit and read its value later add the both groups to it {"ban_read", "ban_write"}, for Id for example only "ban_read" needed because you will not submit id value.

 * @ApiResource(
 *     normalizationContext={"groups"={"ban_read"}, "swagger_definition_name": "read"},
 *     denormalizationContext={"groups"={"ban_write"}, "swagger_definition_name": "write"}
 * )
 */ 
Class Address {
     //...  


     /**
     * @ORM\Column(type="integer")
     * @Groups({"ban_read"})
     */
    private $numero;

    /**
     * @ORM\Column(type="integer")
     * @Groups({"ban_write","ban_read"})
     */
    private $code_insee;

   //here you can see they both have identical getter and setter :

    public function getNumero(): ?int
    {
        return $this->numero;
    }

    public function setNumero(int $numero): self
    {
        $this->numero = $numero;

        return $this;
    }
    public function getCodeInsee(): ?int
    {
        return $this->code_insee;
    }

    public function setCodeInsee(int $code_insee): self
    {
        $this->code_insee = $code_insee;

        return $this;
       }
    }

UPDATE: Create unique groups for each entity for example read_ban, write_ban then in the Professionnel entity update the denormalizationContext to include all other groups.

denormalizationContext={"groups"={"write_professionnel","write_partenaire", "write_ban"}}

Upvotes: 0

Yoann MIR
Yoann MIR

Reputation: 821

If you want to handle readonly attributes, i think you can use normalization/denormalization context instead of "getter/ no setter"

Class Address {

 //...  


 /**
 * @ORM\Column(type="integer")
 * @Groups({"read", "write"})
 */
private $numero;

/**
 * @ORM\Column(type="integer")
 * @Groups({"read"})
 */
private $code_insee;


public function getNumero(): ?int
{
    return $this->numero;
}

public function setNumero(int $numero): self
{
    $this->numero = $numero;

    return $this;
}
public function getCodeInsee(): ?int
{
    return $this->code_insee;
}

public function setCodeInsee(int $code_insee): self
{
    $this->code_insee = $code_insee;

    return $this;
   }
}

and

services:
    # ...

resource.Address:
    parent:    "api.resource"
    arguments: [ "AppBundle\Entity\Address" ]
    calls:
        -      method:    "initNormalizationContext"
               arguments: [ { groups: [ "read" ] } ]
        -      method:    "initDenormalizationContext"
               arguments: [ { groups: [ "write" ] } ]
    tags:      [ { name: "api.resource" } ]

Upvotes: 0

Related Questions