ApiPlatform PUT relational for multiple entities (annotations)

120 views Asked by At

I have a problem with ApiPlatform that I would like to be able to understand and solve easily.

Context: I want to update an address fields with this instruction:

curl --request PUT \
  --url http://localhost/api/users/25 \
  --header 'authorization: Bearer xxx' \
  --header 'content-type: application/json' \
  --data '{
    "lastName" : "Bernard",
    "firstName" : "Jean",
    "dateBirth" : "1996-12-12",
    "phone" : "0769397070",
    "address" : {
        "street" : "150 rue des paquerettes",
        "pos": [ 66.344133 , 73.049472 ],
        "city": "Paris",
        "postcode": "75001"
    }
}

Problem: Each PUT instruction creates a new Address instance when "address" is defined in the request.

Entities :
App/Entity/User

/**
 * @ApiResource(
 *  attributes={
 *     "security"="is_granted('ROLE_ADMIN') or object == user",
 *     "security_message"="User does not match the authenticated user"
 *     },
 *  itemOperations={
 *     "get"={
 *       "normalization_context"={
 *          "groups"={"read:full:user","read:order"},
 *          }
 *      },
 *     "put"={
 *        "normalization_context"={"groups" ={ "read:full:user"}},
 *        "denormalization_context"={"groups" ={ "write:user:customer", "write:user:lawyer"}},
 *        "security"="object == user"
 *     }
 *     },
 *  collectionOperations={}
 *)
 * @UniqueEntity(
 *     fields={"email"},
 *     message="This user is already registered",
 *     groups={"create:customer", "create:lawyer"}
 * )
 * @ORM\HasLifecycleCallbacks
 * @ORM\Entity
 */
class User implements UserInterface
{

    use Traits\Timestampable;

    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    // ...

    /**
     * @ORM\Column(type="string", length=255, nullable=false)
     * @Assert\NotBlank
     * @Groups({"create:customer","create:lawyer","read:full:user", "write:user:customer", "write:user:lawyer"})
     */
    private $phone;

    /**
     * @ORM\OneToOne(targetEntity=Address::class, cascade={"persist"})
     * @ORM\JoinColumn(nullable=true)
     * @Groups({"create:customer", "create:lawyer", "read:full:user", "write:user:customer", "write:user:lawyer"})
     */
    private $address;

    // ...

}

App/Entity/Address

/**
 * @ApiResource(
 *   denormalizationContext = {
 *      "groups" = {"create:lawyer", "create:customer", "write:user:customer", "write:user:lawyer"}
 *   },
 *    itemOperations={
 *     "get"={
 *          "normalization_context"={"groups"={"read:lawyer","create:lawyer","read:full:lawyer"}}
 *      },
 *     }
 * )
 * @ORM\Entity(repositoryClass=AddressRepository::class)
 */
class Address
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;


    /**
     * @ORM\Column(type="string", length=255, nullable=true)
     * @Groups({"read:full:lawyer", "create:customer", "create:lawyer", "read:full:user", "write:user:customer", "write:user:lawyer"})
     */
    private $street;
}

I want to modify in a single instruction entities whose logical dependency is already specified (OneToOne). Do you have any idea on the best way to go about it?

Thank you!

1

There are 1 answers

1
Jeremka On

One alternative solution is add hydra attribute "@id" for the data provider to associate the object. It's natural for multiple relationships, but it's not elegant for OneToOne relationships.

curl --request PUT \
  --url http://localhost/api/users/25 \
  --header 'authorization: Bearer XXX' \
  --header 'content-type: application/ld+json' \
  --data '{
    "lastName" : "Bernard",
    "firstName" : "Jean",
    "dateBirth" : "1996-12-12",
    "phone" : "0769397070",
    "address" : {
        "@id" : "api/addresses/43",
        "street" : "160 rue des paquerettes",
        "pos": [ 66.44133 , 73.049472 ],
        "city": "Paris",
        "postcode": "75001"
    }
}'