Doctrine many-to-many with extra field (update and delete specific items)

Good morning.
I have a problem with update and delete specific items. I searched the entire internet for the answer but didn’t find it, nowhere.

This is what I have

1. Action concept:
Adding a sales invoice to a specific budget to calculate how much is left for settle. Each invoice can be settled up to several project steps.
Example:

action concept

2. Database relation

database relation

3. Example JSON Request which is needed to POST and PATCH.

{"number": "4/2020", "netValue": 4000.00, "budgetsDetails": [{"id": 2, "value": 500.00}, {"id": 3, "value": 3500.00}]}

4. Entities

SalesInvoice.php


namespace AppEntity;

use DoctrineCommonCollectionsArrayCollection;
use DoctrineCommonCollectionsCollection;
use DoctrineORMMapping as ORM;

/**
 * @ORMTable(name="SalesInvoices")
 * @ORMEntity(repositoryClass="AppRepositorySalesInvoiceRepository")
 * @ORMHasLifecycleCallbacks()
 */
class SalesInvoice
{
    use Timestamps;

    /**
     * @ORMId()
     * @ORMGeneratedValue()
     * @ORMColumn(type="integer")
     */
    private $id;

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

    /**
     * @ORMColumn(type="decimal", precision=10, scale=2)
     */
    private $netValue;

    /**
     * @ORMOneToMany(targetEntity="SalesInvoiceBudget", mappedBy="salesInvoice", cascade={"persist", "remove"}, fetch="EAGER")
     */
    private $salesInvoiceBudgets;


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

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

    public function getNumber(): ?string
    {
        return $this->number;
    }

    public function setNumber(string $number): self
    {
        $this->number = $number;

        return $this;
    }

    public function getNetValue(): ?string
    {
        return $this->netValue;
    }

    public function setNetValue(string $netValue): self
    {
        $this->netValue = $netValue;

        return $this;
    }

    /**
     * @return Collection|SalesInvoiceBudget[]
     */
    public function getSalesInvoiceBudgets(): Collection
    {
        return $this->salesInvoiceBudgets;
    }

    public function addSalesInvoiceBudget(SalesInvoiceBudget $salesInvoiceBudget): self
    {
        if (!$this->salesInvoiceBudgets->contains($salesInvoiceBudget)) {
            $this->salesInvoiceBudgets[] = $salesInvoiceBudget;
            $salesInvoiceBudget->setSalesInvoice($this);
        }

        return $this;
    }

    public function removeSalesInvoiceBudget(SalesInvoiceBudget $salesInvoiceBudget): self
    {
        if ($this->salesInvoiceBudgets->contains($salesInvoiceBudget)) {
            $this->salesInvoiceBudgets->removeElement($salesInvoiceBudget);
            // set the owning side to null (unless already changed)
            if ($salesInvoiceBudget->getSalesInvoice() === $this) {
                $salesInvoiceBudget->setSalesInvoice(null);
            }
        }

        return $this;
    }
}

SalesInvoiceBudget.php


namespace AppEntity;

use DoctrineORMMapping as ORM;

/**
 * @ORMTable(name="SalesInvoices_ProjectBudgets")
 * @ORMEntity(repositoryClass="AppRepositorySalesInvoiceBudgetRepository")
 */
class SalesInvoiceBudget
{
    /**
     * @ORMId()
     * @ORMGeneratedValue()
     * @ORMColumn(type="integer")
     */
    private $id;

    /**
     * @ORMManyToOne(targetEntity="SalesInvoice", inversedBy="salesInvoiceBudgets", cascade={"persist"})
     * @ORMJoinColumn(referencedColumnName="id", nullable=false, onDelete="CASCADE")
     */
    private $salesInvoice;

    /**
     * @ORMManyToOne(targetEntity="ProjectBudget", inversedBy="salesInvoiceBudgets", cascade={"persist"})
     * @ORMJoinColumn(referencedColumnName="id", nullable=false, onDelete="CASCADE")
     */
    private $budget;

    /**
     * @ORMColumn(type="decimal", precision=10, scale=2)
     */
    private $value;


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

    public function getValue(): ?string
    {
        return $this->value;
    }

    public function setValue(string $value): self
    {
        $this->value = $value;

        return $this;
    }

    public function getSalesInvoice(): ?SalesInvoice
    {
        return $this->salesInvoice;
    }

    public function setSalesInvoice(?SalesInvoice $salesInvoice): self
    {
        $this->salesInvoice = $salesInvoice;

        return $this;
    }

    public function getBudget(): ?ProjectBudget
    {
        return $this->budget;
    }

    public function setBudget(?ProjectBudget $budget): self
    {
        $this->budget = $budget;

        return $this;
    }
}
<?php

namespace AppEntity;

use DoctrineORMMapping as ORM;

/**
 * @ORMTable(name="SalesInvoices_ProjectBudgets")
 * @ORMEntity(repositoryClass="AppRepositorySalesInvoiceBudgetRepository")
 */
class SalesInvoiceBudget
{
    /**
     * @ORMId()
     * @ORMGeneratedValue()
     * @ORMColumn(type="integer")
     */
    private $id;

    /**
     * @ORMManyToOne(targetEntity="SalesInvoice", inversedBy="salesInvoiceBudgets", cascade={"persist"})
     * @ORMJoinColumn(referencedColumnName="id", nullable=false, onDelete="CASCADE")
     */
    private $salesInvoice;

    /**
     * @ORMManyToOne(targetEntity="ProjectBudget", inversedBy="salesInvoiceBudgets", cascade={"persist"})
     * @ORMJoinColumn(referencedColumnName="id", nullable=false, onDelete="CASCADE")
     */
    private $budget;

    /**
     * @ORMColumn(type="decimal", precision=10, scale=2)
     */
    private $value;


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

    public function getValue(): ?string
    {
        return $this->value;
    }

    public function setValue(string $value): self
    {
        $this->value = $value;

        return $this;
    }

    public function getSalesInvoice(): ?SalesInvoice
    {
        return $this->salesInvoice;
    }

    public function setSalesInvoice(?SalesInvoice $salesInvoice): self
    {
        $this->salesInvoice = $salesInvoice;

        return $this;
    }

    public function getBudget(): ?ProjectBudget
    {
        return $this->budget;
    }

    public function setBudget(?ProjectBudget $budget): self
    {
        $this->budget = $budget;

        return $this;
    }
}

ProjectBudget.php


namespace AppEntity;

use DoctrineCommonCollectionsArrayCollection;
use DoctrineCommonCollectionsCollection;
use DoctrineORMMapping as ORM;

/**
 * @ORMTable(name="ProjectBudgets")
 * @ORMEntity(repositoryClass="AppRepositoryProjectBudgetRepository")
 * @ORMHasLifecycleCallbacks()
 */
class ProjectBudget
{
    use Timestamps;

    /**
     * @ORMId()
     * @ORMGeneratedValue()
     * @ORMColumn(type="integer")
     */
    private $id;

    /**
     * @ORMColumn(type="decimal", precision=10, scale=2, nullable=true)
     */
    private $budgetAmount;

    /**
     * @ORMOneToMany(targetEntity="SalesInvoiceBudget", mappedBy="budget", fetch="EXTRA_LAZY")
     */
    private $salesInvoiceBudgets;
    

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

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

    public function getBudgetAmount(): ?string
    {
        return $this->budgetAmount;
    }

    public function setBudgetAmount(?string $budgetAmount): self
    {
        $this->budgetAmount = $budgetAmount;

        return $this;
    }

    /**
     * @return Collection|SalesInvoiceBudget[]
     */
    public function getSalesInvoiceBudgets(): Collection
    {
        return $this->salesInvoiceBudgets;
    }

    public function addSalesInvoiceBudget(SalesInvoiceBudget $salesInvoiceBudget): self
    {
        if (!$this->salesInvoiceBudgets->contains($salesInvoiceBudget)) {
            $this->salesInvoiceBudgets[] = $salesInvoiceBudget;
            $salesInvoiceBudget->setBudget($this);
        }

        return $this;
    }

    public function removeSalesInvoiceBudget(SalesInvoiceBudget $salesInvoiceBudget): self
    {
        if ($this->salesInvoiceBudgets->contains($salesInvoiceBudget)) {
            $this->salesInvoiceBudgets->removeElement($salesInvoiceBudget);
            // set the owning side to null (unless already changed)
            if ($salesInvoiceBudget->getBudget() === $this) {
                $salesInvoiceBudget->setBudget(null);
            }
        }

        return $this;
    }
}

5. Part of controller

    /**
     * @RestRequestParam(name="number")
     * @RestRequestParam(name="netValue")
     * @RestRequestParam(name="budgetsDetails", nullable=true)
     * @param ParamFetcher $paramFetcher
     * @return View
     * @throws Exception
     */
    public function postSinvoiceAction(ParamFetcher $paramFetcher)
    {
        $number = $paramFetcher->get('number');
        $netValue = $paramFetcher->get('netValue');
        $budgetsDetails = $paramFetcher->get('budgetsDetails');

        if($number === null) {
            return $this->view(null, Response::HTTP_BAD_REQUEST);
        }

        $contractor = $this->contractorRepository->findOneBy(array("id" => $contractorId));

        $invoice = new SalesInvoice();
        $invoice->setNumber($number);
        $invoice->setNetValue($netValue);

        if ($budgetsDetails && sizeof($budgetsDetails) > 0) {
            foreach ($budgetsDetails as $budgetDetails) {
                $budget = $this->projectBudgetRepository->findOneBy(array('id' => $budgetDetails['id']));

                if ($budget === null) {
                    return $this->view("There is no budget like this.");
                }

                $relation = new SalesInvoiceBudget();
                $relation->setBudget($budget);
                $relation->setValue($budgetDetails['value']);
                $invoice->addSalesInvoiceBudget($relation);
            }
        }

        $this->entityManager->persist($invoice);
        $this->entityManager->flush();

        return $this->view("Added.", Response::HTTP_CREATED);
    }
}

Adding a new invoice with the assignment of the amount to the project stage using the above JSON works, but I have no idea how to edit and delete the assigned budget when someone makes a mistake when entering the amount to be settled or settles the wrong stage of the project.

Source: Symfony Questions

Was this helpful?

0 / 0

Leave a Reply 0

Your email address will not be published. Required fields are marked *