Как получить сумму значений дочерних объектов в основной сущности по умолчанию

#symfony #doctrine-orm

#симфония #доктрина-orm #symfony

Вопрос:

У меня есть объект PurchaseOrder и у меня есть объект Payments. Внутри объекта PurchaseOrder я пытаюсь получить сумму Payments.amountPaid, однако это работает не так, как ожидалось. В идеале $ allPaid должен содержать сумму всех платежей, выплаченных за каждый PurchaseOrder. Я следовал этому руководству: введите описание ссылки здесь

Вот моя сущность PurchaseOrder:

 class PurchaseOrder
{
    /**
     * @var int
     *
     * @ORMColumn(name="id", type="integer")
     * @ORMId
     * @ORMGeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @ORMOneToOne(targetEntity="RequestForEstimate", fetch="EAGER")
     * @ORMJoinColumn(name="request_id", referencedColumnName="request_id")
     */
    private $request;

    /**
     * @ORMOneToMany(targetEntity="Payment", mappedBy="purchaseOrder", orphanRemoval=true, cascade={"persist"}, fetch="EAGER")
     */
    private $payments;

    /**
     * @var DateTime
     *
     * @ORMColumn(name="create_time", type="datetime")
     */
    private $createTime;

    /**
     * @var DateTime
     *
     * @ORMColumn(name="update_time", type="datetime")
     */
    private $updateTime;

    /**
     * @ORMManyToOne(targetEntity="PurchaseOrderStatus", cascade={"persist"})
     */
    private $status;

    /**
     * @var DateTime
     *
     * @ORMColumn(name="ship_date",type="datetime")
     */
    private $shipDate;

    private $allPaid = 0;


    public function getAllPaid()
    {
        foreach ($this->payments as $payment) {
            $this->allPaid  = $payment->amountPaid();
        }
        return $this->allPaid;
    }
    /**
     * Get id
     *
     * @return int
     */
    public function getId()
    {
        return $this->id;
    }
    /**
     * Set createTime
     *
     * @param DateTime $createTime
     *
     * @return PurchaseOrder
     */
    public function setCreateTime($createTime)
    {
        $this->createTime = $createTime;

        return $this;
    }

    /**
     * Get createTime
     *
     * @return DateTime
     */
    public function getCreateTime()
    {
        return $this->createTime;
    }

    /**
     * Set updateTime
     *
     * @param DateTime $updateTime
     *
     * @return PurchaseOrder
     */
    public function setUpdateTime($updateTime)
    {
        $this->updateTime = $updateTime;

        return $this;
    }

    /**
     * Get updateTime
     *
     * @return DateTime
     */
    public function getUpdateTime()
    {
        return $this->updateTime;
    }

    /**
     * Set status
     *
     * @param integer $status
     *
     * @return PurchaseOrder
     */
    public function setStatus($status)
    {
        $this->status = $status;

        return $this;
    }

    /**
     * Get status
     *
     * @return int
     */
    public function getStatus()
    {
        return $this->status;
    }

    /**
     * Set shipDate
     *
     * @param DateTime $shipDate
     *
     * @return PurchaseOrder
     */
    public function setShipDate($shipDate)
    {
        $this->shipDate = $shipDate;

        return $this;
    }

    /**
     * Get shipDate
     *
     * @return DateTime
     */
    public function getShipDate()
    {
        return $this->shipDate;
    }

    /**
     * Set requestForEstimate
     *
     * @param InboundBundleEntityRequestForEstimate $requestForEstimate
     *
     * @return PurchaseOrder
     */
    public function setRequestForEstimate(InboundBundleEntityRequestForEstimate $requestForEstimate = null)
    {
        $this->requestForEstimate = $requestForEstimate;

        return $this;
    }

    /**
     * Get requestForEstimate
     *
     * @return InboundBundleEntityRequestForEstimate
     */
    public function getRequestForEstimate()
    {
        return $this->requestForEstimate;
    }

    /**
     * Set requestId
     *
     * @param InboundBundleEntityRequestForEstimate $requestId
     *
     * @return PurchaseOrder
     */
    // public function setRequest(InboundBundleEntityRequestForEstimate $request = null)
    // {
    //     $this->request = $request;
    //     $request->setRequestId($this);
    //     return $this;
    // }
    public function setPayments(Payment $payments = null)
    {
        $this->payments = $payments;

        return $this;
    }


    /**
     * Get requestId
     *
     * @return InboundBundleEntityRequestForEstimate
     */
    public function getRequest()
    {
        return $this->request;
    }

    /**
     * Set request
     *
     * @param InboundBundleEntityRequestForEstimate $request
     *
     * @return PurchaseOrder
     */


    /**
     * Constructor
     */
    public function __construct()
    {
        $this->payments = new DoctrineCommonCollectionsArrayCollection();
    }

    /**
     * Add payment
     *
     * @param InboundBundleEntityPayment $payment
     *
     * @return PurchaseOrder
     */
    public function addPayment(InboundBundleEntityPayment $payment)
    {
        $this->payments[] = $payment;

        return $this;
    }

    /**
     * Remove payment
     *
     * @param InboundBundleEntityPayment $payment
     */
    public function removePayment(InboundBundleEntityPayment $payment)
    {
        $this->payments->removeElement($payment);
    }

    /**
     * Get payments
     *
     * @return DoctrineCommonCollectionsCollection
     */
    public function getPayments()
    {
        return $this->payments;
    }

    /**
     * Set request
     *
     * @param InboundBundleEntityRequestForEstimate $request
     *
     * @return PurchaseOrder
     */
    public function setRequest(InboundBundleEntityRequestForEstimate $request = null)
    {
        $this->request = $request;

        return $this;
    }
}
  

Объект оплаты:

 class Payment
{
    /**
     * @var int
     *
     * @ORMColumn(name="id", type="integer")
     * @ORMId
     * @ORMGeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @ORMManyToOne(targetEntity="PurchaseOrder", inversedBy="payments", cascade={"persist", "detach"})
     * @ORMJoinColumn(name="purchase_order", referencedColumnName="id")
     */
    private $purchaseOrder;
    /**
     * @var DateTime
     *
     * @ORMColumn(name="create_time", type="datetime")
     */
    private $createTime;

    /**
     * @var DateTime
     *
     * @ORMColumn(name="update_time", type="datetime")
     */
    private $updateTime;
    /**
     * @var int
     *
     * @ORMColumn(name="creator", type="integer")
     */
    private $creator;
    /**
     * @var string
     *
     * @ORMColumn(name="amount_paid", type="decimal", precision=10, scale=2)
     */
    private $amountPaid;

    /**
     * @ORMManyToOne(targetEntity="PaymentType", cascade={"persist"})
     * @ORMJoinColumn(name="payment_type", referencedColumnName="id")
     */
    private $paymentType;
    /**
     * @var string
     *
     * @ORMColumn(name="external_transaction_id", type="string", length=255)
     */
    private $externalTransactionId;

    /**
     * @var string
     *
     * @ORMColumn(name="including_fees", type="decimal", precision=10, scale=2)
     */
    private $includingFees;





    /**
     * Get id
     *
     * @return integer
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set createTime
     *
     * @param DateTime $createTime
     *
     * @return Payment
     */
    public function setCreateTime($createTime)
    {
        $this->createTime = $createTime;

        return $this;
    }

    /**
     * Get createTime
     *
     * @return DateTime
     */
    public function getCreateTime()
    {
        return $this->createTime;
    }

    /**
     * Set updateTime
     *
     * @param DateTime $updateTime
     *
     * @return Payment
     */
    public function setUpdateTime($updateTime)
    {
        $this->updateTime = $updateTime;

        return $this;
    }

    /**
     * Get updateTime
     *
     * @return DateTime
     */
    public function getUpdateTime()
    {
        return $this->updateTime;
    }

    /**
     * Set creator
     *
     * @param integer $creator
     *
     * @return Payment
     */
    public function setCreator($creator)
    {
        $this->creator = $creator;

        return $this;
    }

    /**
     * Get creator
     *
     * @return integer
     */
    public function getCreator()
    {
        return $this->creator;
    }

    /**
     * Set amountPaid
     *
     * @param string $amountPaid
     *
     * @return Payment
     */
    public function setAmountPaid($amountPaid)
    {
        $this->amountPaid = $amountPaid;

        return $this;
    }

    /**
     * Get amountPaid
     *
     * @return string
     */
    public function getAmountPaid()
    {
        return $this->amountPaid;
    }

    /**
     * Set paymentType
     *
     * @param string $paymentType
     *
     * @return Payment
     */
    public function setPaymentType($paymentType)
    {
        $this->paymentType = $paymentType;

        return $this;
    }

    /**
     * Get paymentType
     *
     * @return string
     */
    public function getPaymentType()
    {
        return $this->paymentType;
    }

    /**
     * Set externalTransactionId
     *
     * @param string $externalTransactionId
     *
     * @return Payment
     */
    public function setExternalTransactionId($externalTransactionId)
    {
        $this->externalTransactionId = $externalTransactionId;

        return $this;
    }

    /**
     * Get externalTransactionId
     *
     * @return string
     */
    public function getExternalTransactionId()
    {
        return $this->externalTransactionId;
    }

    /**
     * Set includingFees
     *
     * @param string $includingFees
     *
     * @return Payment
     */
    public function setIncludingFees($includingFees)
    {
        $this->includingFees = $includingFees;

        return $this;
    }

    /**
     * Get includingFees
     *
     * @return string
     */
    public function getIncludingFees()
    {
        return $this->includingFees;
    }

    /**
     * Set purchaseOrder
     *
     * @param InboundBundleEntityPurchaseOrder $purchaseOrder
     *
     * @return Payment
     */
    public function setPurchaseOrder(InboundBundleEntityPurchaseOrder $purchaseOrder = null)
    {
        $this->purchaseOrder = $purchaseOrder;

        return $this;
    }

    /**
     * Get purchaseOrder
     *
     * @return InboundBundleEntityPurchaseOrder
     */
    public function getPurchaseOrder()
    {
        return $this->purchaseOrder;
    }
}
  

Когда я дамп объекта, он показывает, что allPaid равен 0, как установлено по умолчанию:

введите описание изображения здесь

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

1. private $allPaid = 0; измените на private $allPaid;

2. Ну, теперь это значение равно null.

Ответ №1:

Ответ Матко работает, но ваш вопрос подразумевает, что вы (или, по крайней мере, вы были) упускаете что-то фундаментальное.

В вашем исходном коде внутреннее (частное) allPaid свойство инициализировано нулем. Ваш getAllPaid() метод вычисляет фактическое значение и возвращает его. Как написано, getAllPaid() будет выполнять итерацию по коллекции платежей при каждом ее вызове.

Когда вы создаете дамп своей сущности, $allPaid свойство равно нулю, потому что вы создаете дамп до getAllPaid() вызова. Если вы вызовете getAllPaid() , а затем выполните сброс, вы увидите, что он содержит вычисленное значение. Это потому, что getAllPaid() устанавливает это значение по пути. Или, вместо сброса (и просмотра неинициализированного внутреннего значения), протестируйте путем фактического вызова getAllPaid() и убедитесь, что возвращено правильное значение.

Слабое место решения Matko заключается в том, что оно каждый раз с нетерпением загружает коллекцию. Вероятно, что существуют некоторые сценарии, в которых вам нужно загрузить PurchaseOrder без загрузки всех платежей.

Поскольку $allPaid является закрытым, вы всегда будете использовать getAllPaid() для доступа к значению.

Чтобы улучшить ваш код, я бы хотел, чтобы вы запомнили $allPaid

  1. Инициализировать private $allPaid = null; . Семантика null более подходящая, потому что при инициализации значение не равно нулю. Это неизвестно.

  2. В getAllPaid() добавьте проверку, которая $this->allPaid !== null и, если да, верните досрочно. Таким образом, повторные вызовы getAllPaid() не пересчитывают значение каждый раз.

  3. Обязательно очистите заметку при изменении коллекции платежей. PurchaseOrder::addPayment() и PurchaseOrder::removePayment() оба должны иметь значение $this->allPaid null, что вынуждает пересчитывать значение при следующем вызове getAllPaid() .

  4. Наконец, удалите нетерпеливый режим выборки на PurchaseOrder::payments . Если вы хотите быстро загружать их в случаях, когда вы знаете, что они вам понадобятся, вы можете извлечь их и объединить в своем запросе.

Ответ №2:

Вы должны использовать событие postLoad Doctrine2. Событие postLoad возникает для объекта после того, как объект был загружен в текущий EntityManager из базы данных или после того, как к нему была применена операция обновления.

 ...
use DoctrineORMMapping as ORM;
...

/**
 * ... 
 * @ORMHasLifecycleCallbacks
 * ...
 */
class PurchaseOrder
{
    ...

    private $allPaid = null;

    /**
     * @ORMPostLoad
     */
    public function getAllPaid()
    {
        if (null === $this->allPaid) {
            foreach ($this->payments as $payment) {
                $this->allPaid  = $payment->amountPaid();
            }
        }

        return $this->allPaid;
    }
}
  

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

1. Красивые. Большое спасибо!

2. Похоже, что когда я использую оба: @ORM HasLifecycleCallbacks и @ORM postLoad, я получаю удвоенную сумму. Похоже, мне нужно только одно из них…

3. Вероятно, это потому, что вы вызвали getAllPaid() после полной загрузки объекта. Я обновил пример кода, проверив, отличается ли значение $this-> allPaid от null.

4. Кстати, мой ответ был каким-то быстрым решением 🙂 Вам также следует учитывать советы timdev. Особенно ситуации, когда вы добавляете или удаляете платежи.