-->

API Platform model attributes are readOnly

2019-08-01 19:32发布

问题:

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...)

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

回答1:

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" } ]


回答2:

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"}}