Doctrine — проверка с помощью связанных объектов

#symfony #doctrine

#symfony #доктрина

Вопрос:

У меня есть следующие ассоциации с Doctrine:

 Charge:
  id
  amount
  adjustmentItems

Adjustment
  id
  date
  adjustmentItems

AdjustmentItem
  id
  adjustment
  charge
  amount
 

Есть обвинения, и есть корректировки. Каждая корректировка состоит из элементов adjustmentItems, которые являются корректировками одного или нескольких сборов.

При добавлении новой корректировки я добавляю корректировки и связанные с ними элементы с помощью десериализации. Т. е.:

 $adjustment = 
   ["date" => "2020-12-14", 
    "items" => [
       ["charge" => 84, "amount" => 600],
       ["charge" => 85, "amount" => 200],
     ]
  ];
 

Все работает хорошо, за исключением того, что я проверяю обвинения, используя Assert /Valid для AdjustmentItem::charge .

При проверке начисления я проверяю, чтобы убедиться, что сумма всех корректировок не превышает сумму начисления.

Однако Charge::getAdjustmentItems() не показывает только что созданные adjustmentItems (хотя adjustmentItem показывает, что он заряжается, и при сохранении все работает, как ожидалось).

 $adjustment->getAdjustmentItems()->first()->getCharge()->getAdjustmentItems()->toArray()
 

является:

 []
 

Как я могу получить плату за «просмотр» элементов из десериализации перед сохранением для проверки?

Источник:

 <?php

namespace AppEntityTestFin;

use AppRepositoryTestFinChargeRepository;
use DoctrineCommonCollectionsArrayCollection;
use DoctrineCommonCollectionsCollection;
use DoctrineORMMapping as ORM;
use SymfonyComponentValidatorConstraints as Assert;
use SymfonyComponentValidatorContextExecutionContextInterface;

/**
 * @ORMEntity(repositoryClass=ChargeRepository::class)
 * @ORMTable(name="`test_financials_charge`")
 */
class Charge
{
    /**
     * @ORMId()
     * @ORMGeneratedValue()
     * @ORMColumn(type="integer")
     */
    private $id;

    /**
     * @ORMColumn(type="integer")
     */
    private $amount;

    /**
     * @ORMOneToMany(targetEntity=AdjustmentItem::class, mappedBy="charge")
     */
    private $adjustmentItems;

    public function __construct()
    {
        $this->adjustmentItems = new ArrayCollection();
    }

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getAmount(): ?int
    {
        return $this->amount;
    }

    public function setAmount(int $amount): self
    {
        $this->amount = $amount;

        return $this;
    }

    /**
     * @return Collection|AdjustmentItem[]
     */
    public function getAdjustmentItems(): Collection
    {
        return $this->adjustmentItems;
    }

    public function addAdjustmentItem(AdjustmentItem $adjustmentItem): self
    {
        if (!$this->adjustmentItems->contains($adjustmentItem)) {
            $this->adjustmentItems[] = $adjustmentItem;
            $adjustmentItem->setCharge($this);
        }

        return $this;
    }

    public function removeAdjustmentItem(AdjustmentItem $adjustmentItem): self
    {
        if ($this->adjustmentItems->contains($adjustmentItem)) {
            $this->adjustmentItems->removeElement($adjustmentItem);
            // set the owning side to null (unless already changed)
            if ($adjustmentItem->getCharge() === $this) {
                $adjustmentItem->setCharge(null);
            }
        }

        return $this;
    }
    
    /**
     * @AssertCallback
     */
    public function validateBalance(ExecutionContextInterface $context, $payload)
    {
        dd(count($this->adjustmentItems->toArray()));
        if (1) {
            $context->buildViolation('Charge balance (after adjustments) must be greater or equal to $0.')
                ->atPath('invoice')
                ->addViolation();
        }
    }
}

 
 <?php

namespace AppEntityTestFin;

use AppRepositoryTestFinAdjustmentRepository;
use DoctrineCommonCollectionsArrayCollection;
use DoctrineCommonCollectionsCollection;
use DoctrineORMMapping as ORM;
use SymfonyComponentValidatorConstraints as Assert;

/**
 * @ORMEntity(repositoryClass=AdjustmentRepository::class)
 * @ORMTable(name="`test_financials_adjustment`")
 */
 class Adjustment
{
    /**
     * @ORMId()
     * @ORMGeneratedValue()
     * @ORMColumn(type="integer")
     */
    private $id;

    /**
     * @ORMColumn(type="string", length=255, nullable=true)
     */
    private $note;

    /**
     * @ORMOneToMany(targetEntity=AdjustmentItem::class, mappedBy="adjustment", orphanRemoval=true)
     * @AssertValid
     */
    private $adjustmentItems;

    public function __construct()
    {
        $this->adjustmentItems = new ArrayCollection();
    }

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getNote(): ?string
    {
        return $this->note;
    }

    public function setNote(?string $note): self
    {
        $this->note = $note;

        return $this;
    }

    /**
     * @return Collection|AdjustmentItem[]
     */
    public function getAdjustmentItems(): Collection
    {
        return $this->adjustmentItems;
    }

    public function addAdjustmentItem(AdjustmentItem $adjustmentItem): self
    {
        if (!$this->adjustmentItems->contains($adjustmentItem)) {
            $this->adjustmentItems[] = $adjustmentItem;
            $adjustmentItem->setAdjustment($this);
        }

        return $this;
    }

    public function removeAdjustmentItem(AdjustmentItem $adjustmentItem): self
    {
        if ($this->adjustmentItems->contains($adjustmentItem)) {
            $this->adjustmentItems->removeElement($adjustmentItem);
            // set the owning side to null (unless already changed)
            if ($adjustmentItem->getAdjustment() === $this) {
                $adjustmentItem->setAdjustment(null);
            }
        }

        return $this;
    }
}

 
 <?php

namespace AppEntityTestFin;

use AppRepositoryTestFinAdjustmentItemRepository;
use DoctrineORMMapping as ORM;
use SymfonyComponentValidatorConstraints as Assert;

/**
 * @ORMEntity(repositoryClass=AdjustmentItemRepository::class)
 * @ORMTable(name="`test_financials_adjustment_item`")
 */
class AdjustmentItem
{
    /**
     * @ORMId()
     * @ORMGeneratedValue()
     * @ORMColumn(type="integer")
     */
    private $id;

    /**
     * @ORMManyToOne(targetEntity=Adjustment::class, inversedBy="adjustmentItems")
     * @ORMJoinColumn(nullable=false)
     */
    private $adjustment;

    /**
     * @ORMManyToOne(targetEntity=Charge::class, inversedBy="adjustmentItems")
     * @ORMJoinColumn(nullable=false)
     * @AssertValid
     */
    private $charge;

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getAdjustment(): ?Adjustment
    {
        return $this->adjustment;
    }

    public function setAdjustment(?Adjustment $adjustment): self
    {
        $this->adjustment = $adjustment;

        return $this;
    }

    public function getCharge(): ?Charge
    {
        return $this->charge;
    }

    public function setCharge(?Charge $charge): self
    {
        $this->charge = $charge;

        return $this;
    }
}

 
         $json = json_encode([
            "note" => "bla",
            "adjustmentItems" => [
                ["charge" => 1],
            ],
        ]);
        $credit = $this->_deserializeJson($json, Adjustment::class);
        //dd($credit);
        //dd($credit->getAdjustmentItems()->first()->getCharge()->getAdjustmentItems()->toArray());
        $this->_validate($credit);
 

Комментарии:

1. Я думаю, вы должны показать свой код. Потому что причин может быть слишком много — может быть, группа проверки, может быть, в контроллере, может быть, вы что-то забыли..

2. @UrmatZhenaliev Я добавил код

Ответ №1:

Вызов Charge::addAdjustmentItem AdjustmentItem::setCharge решил проблему для меня.

Кто-нибудь знает, зачем это нужно?

     public function setCharge(?Charge $charge): self
    {
        $this->charge = $charge;
        
        // This is needed so that the Assert/Callback in Charge knows about this adjustmentItem.
        $charge->addAdjustmentItem($this);

        return $this;
    }