php - DataPersister 未在“PUT”操作时累加数量
问题描述
我是 API 平台的新手。目前,我正面临来自 API 平台的“PUT”操作问题。
我有两个实体。一个是用户实体,另一个是信用实体。他们的关系是一对多的关系。我有一个 DataPersister,我参考并遵循本文档(https://api-platform.com/docs/core/data-persisters/)。所以,坚持,我想做一些业务逻辑。所以现在的问题是当我更新信用时。它应该添加当前值+请求的信用额度,而不是直接添加请求的信用额度。例如,假设我在贷方表中有 100 美元的金额。当我尝试再添加 50 美元时,它应该是 150,但相反,它只存储为 50 美元。
下面是我的用户实体
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Serializer\Annotation\SerializedName;
use Gedmo\Mapping\Annotation as Gedmo;
/**
* @ApiResource(
* collectionOperations={
* "get" ,
* "post",
* "app_login"={
* "route_name"="app_login",
* "method"="POST",
* "swagger_context" = {
* "parameters" = {
* {
* "name" = "User Login",
* "in" = "body",
* "type" = "object",
* "schema"= {
* "email" = {"type": "string"},
* "password" = {"type" : "string"},
* "example" ={
* "email" = "string",
* "password" ="string"
* }
* }
* }
* },
* "responses" = {
* "200" = {
* "description" = "You will get User IRI and PHPSESSID",
* "schema" = {
* "type" = "object",
* "required" = {
* "email",
* "password"
* },
* "properties" = {
* "user" = {
* "type" = "string"
* },
* "PHPSESSID" = {
* "type" = "string"
* },
*
* }
* }
* },
* "400" = {
* "description" = "Bad Requests"
* }
* },
* "summary" = "User Login",
* "description" = "Set User session to api platform by email and password",
* "consumes" = {
* "application/json",
* "text/html",
* },
* "produces" = {
* "application/json",
* "application/ld+json"
* }
* }
* }
* },
* itemOperations={
* "get" ={
* "normalization_context"={"groups"={"user:item:get"}}
* },
* "put" = {
* "swagger_context" ={
* "summary" = "Update username details and credit items"
* }
*
* }
* },
* normalizationContext={"groups"={"user:read"}},
* denormalizationContext={"groups"={"user:write"}},
* shortName="User"
*
* )
* @UniqueEntity(fields={"email"})
* @UniqueEntity(fields={"contact"})
* @ORM\Entity(repositoryClass="App\Repository\UserRepository")
*/
class User implements UserInterface
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @Groups({"user:read", "user:write"})
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=180, unique=true)
* @Groups({"user:read", "user:write", "user:item:get", "credit:item:get"})
* @Assert\Email()
* @Assert\NotBlank()
*/
private $email;
/**
* @ORM\Column(type="json")
*/
private $roles = [];
/**
* @var string The hashed password
* @ORM\Column(type="string")
*/
private $password;
/**
* @Groups("user:write")
* @SerializedName("password")
* @Assert\NotBlank(groups={"create"})
*/
private $plainPassword;
/**
* @ORM\Column(type="string", length=255)
* @Groups({"user:read", "user:write"})
* @Assert\NotBlank()
*/
private $firstName;
/**
* @ORM\Column(type="string", length=255)
* @Groups({"user:read", "user:write"})
* @Assert\NotBlank()
*/
private $lastName;
/**
* @var string provide in YYYY-MM-DD (neglect Time)
* @ORM\Column(type="date")
* @Groups({"user:read", "user:write"})
* @Assert\NotBlank()
*/
private $dob;
/**
* @ORM\Column(type="text")
* @Groups({"user:read", "user:write"})
* @Assert\NotBlank()
*/
private $address;
/**
* @ORM\Column(type="string", length=255)
* @Groups({"user:read", "user:write"})
* @Assert\NotBlank()
* @Assert\Length(
* min=8,
* max=8,
* maxMessage="contact number must have 8 character",
* minMessage="contact number must have 8 character"
* )
*/
private $contact;
/**
* @ORM\OneToMany(targetEntity="App\Entity\Credit", mappedBy="user", cascade={"persist"}, orphanRemoval=true)
* @Groups({"user:read", "user:write"})
* @Assert\Valid()
*/
private $credits;
/**
* @var \DateTime $created
*
* @Gedmo\Timestampable(on="create")
* @ORM\Column(type="datetime")
*/
private $created_at;
/**
* @var \DateTime $updated
*
* @Gedmo\Timestampable(on="update")
* @ORM\Column(type="datetime")
*/
private $updated_at;
public function __construct()
{
$this->credits = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): self
{
$this->email = $email;
return $this;
}
/**
* A visual identifier that represents this user.
*
* @see UserInterface
*/
public function getUsername(): string
{
return (string) $this->email;
}
/**
* @see UserInterface
*/
public function getRoles(): array
{
$roles = $this->roles;
// guarantee every user at least has ROLE_USER
$roles[] = 'ROLE_USER';
return array_unique($roles);
}
public function setRoles(array $roles): self
{
$this->roles = $roles;
return $this;
}
/**
* @see UserInterface
*/
public function getPassword(): string
{
return (string) $this->password;
}
public function setPassword(string $password): self
{
$this->password = $password;
return $this;
}
/**
* @see UserInterface
*/
public function getSalt()
{
// not needed when using the "bcrypt" algorithm in security.yaml
}
/**
* @see UserInterface
*/
public function eraseCredentials()
{
// If you store any temporary, sensitive data on the user, clear it here
// $this->plainPassword = null;
}
public function getFirstName(): ?string
{
return $this->firstName;
}
public function setFirstName(string $firstName): self
{
$this->firstName = $firstName;
return $this;
}
public function getLastName(): ?string
{
return $this->lastName;
}
public function setLastName(string $lastName): self
{
$this->lastName = $lastName;
return $this;
}
public function getDob(): ?\DateTimeInterface
{
return $this->dob;
}
public function setDob(\DateTimeInterface $dob): self
{
$this->dob = $dob;
return $this;
}
public function getAddress(): ?string
{
return $this->address;
}
public function setAddress(string $address): self
{
$this->address = $address;
return $this;
}
public function getContact(): ?string
{
return $this->contact;
}
public function setContact(string $contact): self
{
$this->contact = $contact;
return $this;
}
/**
* @return Collection|Credit[]
*/
public function getCredits(): Collection
{
return $this->credits;
}
public function addCredit(Credit $credit): self
{
if (!$this->credits->contains($credit)) {
$this->credits[] = $credit;
$credit->setUser($this);
}
return $this;
}
public function removeCredit(Credit $credit): self
{
if ($this->credits->contains($credit)) {
$this->credits->removeElement($credit);
// set the owning side to null (unless already changed)
if ($credit->getUser() === $this) {
$credit->setUser(null);
}
}
return $this;
}
public function getPlainPassword(): ?string
{
return $this->plainPassword;
}
public function setPlainPassword(string $plainPassword): self
{
$this->plainPassword = $plainPassword;
return $this;
}
public function getCreated()
{
return $this->created_at;
}
public function getUpdated()
{
return $this->updated_at;
}
}
这是我的信用实体
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
/**
* @ApiResource(
* collectionOperations={
* "get" ={
* "normalization_context"={"groups"={"credit:read", "credit:item:get"}},
* }
* },
* itemOperations={
* "get"
* },
* shortName="credits",
* normalizationContext={"groups"={"credit:read"}, "swagger_definition_name"="Read"},
* denormalizationContext={"groups"={"credit:write"}, "swagger_definition_name"="Write"},
* )
* @ORM\Entity(repositoryClass="App\Repository\CreditRepository")
*/
class Credit
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="credits")
* @ORM\JoinColumn(nullable=false)
* @Groups({"credit:read", "credit:write"})
* @Assert\Valid()
*/
private $user;
/**
* @ORM\Column(type="string", length=255)
* @Groups({ "user:read", "user:write", "credit:read", "credit:write"})
*/
private $amount;
/**
* @var \DateTime $created
*
* @Gedmo\Timestampable(on="create")
* @ORM\Column(type="datetime")
*/
private $created_at;
/**
* @var \DateTime $updated
*
* @Gedmo\Timestampable(on="update")
* @ORM\Column(type="datetime")
*/
private $updated_at;
public function getId(): ?int
{
return $this->id;
}
public function getAmount(): ?string
{
return $this->amount;
}
public function setAmount(string $amount): self
{
$this->amount = $amount;
return $this;
}
public function getUser(): ?User
{
return $this->user;
}
public function setUser(?User $user): self
{
$this->user = $user;
return $this;
}
public function getCreated()
{
return $this->created_at;
}
public function getUpdated()
{
return $this->updated_at;
}
}
这是我的 UserDataPersister
<?php
namespace App\DataPersister;
use ApiPlatform\Core\DataPersister\DataPersisterInterface;
use App\Entity\Credit;
use App\Entity\User;
use App\Managers\EmailManager;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
class UserDataPersister implements DataPersisterInterface
{
private $entityManager;
private $userPasswordEncoder;
private $emailManager;
public function __construct(EntityManagerInterface $entityManager, UserPasswordEncoderInterface $userPasswordEncoder, EmailManager $emailManager )
{
$this->entityManager = $entityManager;
$this->userPasswordEncoder = $userPasswordEncoder;
$this->emailManager = $emailManager;
}
public function supports($data): bool
{
return $data instanceof User;
}
/**
* @param User $data
*/
public function persist($data)
{
//encrypt password
if ($data->getPlainPassword()) {
$data->setPassword(
$this->userPasswordEncoder->encodePassword($data, $data->getPlainPassword())
);
$data->eraseCredentials();
}
$creditArray = $data->getCredits();
$creditAmt = 0;
foreach ( $creditArray as $credit ) {
$creditAmt = $credit->getAmount();
}
//credit top up detection
if($creditAmt !==0){
$creditRepo = $this->entityManager->getRepository(Credit::class);
$currentCredit = $creditRepo->findAmountByUserId($data->getId());
$currentCreditAmt = $currentCredit->getAmount();
$topupAmt = $currentCreditAmt + $creditAmt;
$currentCredit -> setAmount($topupAmt);
//update entity
$this->entityManager->persist($currentCredit);
//send email
//$this->emailManager->sendCreditTopupNotification($data);
}else{
//set credit to zero
$creditEntity = new Credit();
$creditEntity ->setAmount(0);
$data->addCredit($creditEntity);
//send registration email
//$this->emailManager->sendRegisterNotification($data);
}
$this->entityManager->persist($data);
$this->entityManager->flush();
}
public function remove($data)
{
$this->entityManager->remove($data);
$this->entityManager->flush();
}
}
我相信问题来自 UserDataPersister 的这个会话
//credit top up detection
if($creditAmt !==0){
$creditRepo = $this->entityManager->getRepository(Credit::class);
$currentCredit = $creditRepo->findAmountByUserId($data->getId());
$currentCreditAmt = $currentCredit->getAmount();
$topupAmt = $currentCreditAmt + $creditAmt;
$currentCredit -> setAmount($topupAmt);
//update entity
$this->entityManager->persist($currentCredit);
//send email
//$this->emailManager->sendCreditTopupNotification($data);
}else{
//set credit to zero
$creditEntity = new Credit();
$creditEntity ->setAmount(0);
$data->addCredit($creditEntity);
//send registration email
//$this->emailManager->sendRegisterNotification($data);
}
$this->entityManager->persist($data);
$this->entityManager->flush();
当 API 被称为“PUT”操作时,我该如何实现,其中包含积分的金额(已实现)并添加当前金额 + API 请求的金额?提前致谢。
解决方案
我很确定您正在尝试对数组中的所有学分求和,但您只是在$creditAmt
这里使用最后一个元素的值:
$creditAmt = 0;
foreach ( $creditArray as $credit ) {
$creditAmt = $credit->getAmount();
}
这应该是:
$creditAmt = 0;
foreach ( $creditArray as $credit ) {
$creditAmt = $creditAmt + $credit->getAmount();
}
此外,如果您$creditArray
是空的,您将有$creditAmt = 0;
这解释为什么您没有看到任何添加的信用。确保$data->getCredits()
确实拥有您要总结的“功劳”!
推荐阅读
- google-sheets - 如何计算具有范围的 2 列
- angular - 使用 Ionic 从 firebase 获取对象数据
- javascript - 要求输入,然后在新窗口中打开
- scala - 为什么使用模式查询时所有字段都为空?
- swift - 从委托类调用视图控制器函数
- javascript - 如何在颤动中将一个图标更改为两个不同的图标?
- python - 如何在不使用 pywin 包或 excel 模板的情况下在 python 中创建类似 excel 的表?
- algorithm - 二维字母数组中的最长路径
- regex - 使用正则表达式删除匹配的字符串
- python - 如何在 Pytorch 中实现 dropout,以及在哪里应用它