<?php
namespace App\Entity;
use App\Repository\ProjectOrderTasksRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\Selectable;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;
/**
* @ORM\Entity(repositoryClass=ProjectOrderTasksRepository::class)
*/
class ProjectOrderTasks
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
* @Groups({
* "project_order_task@base",
* "project_order_task@core"
* })
*/
private $id;
/**
* @ORM\Column(type="integer")
* @Groups({
* "project_order_task@base",
* "project_order_task@core"
* })
*/
private $task_index;
/**
* @ORM\Column(type="string", length=255)
* @Groups({
* "project_order_task@base",
* "project_order_task@core"
* })
*/
private $title;
/**
* @ORM\Column(type="text", nullable=true)
* @Groups({
* "project_order_task@base",
* "project_order_task@core"
* })
*/
private $description;
/**
* @ORM\Column(type="float")
* @Groups({
* "project_order_task@core"
* })
*/
private $progress_weight;
/**
* @ORM\Column(type="integer", nullable=true)
* @Groups({
* "project_order_task@core"
* })
*/
private $duration_days;
/**
* @deprecated never use this controlling over job
* @ORM\Column(type="date_immutable", nullable=true, options={"comment": "deprecated -> Redundant field; value is dynamically calculated based on completed job weights."})
* @Groups({
* "project_order_task@core"
* })
*/
private $started_at;
/**
* @deprecated never use this controlling over job
* @ORM\Column(type="date_immutable", nullable=true, options={"comment": "deprecated -> Redundant field; value is dynamically calculated based on completed job weights."})
* @Groups({
* "project_order_task@core"
* })
*/
private $completed_at;
/**
* @ORM\ManyToOne(targetEntity=ProjectOrders::class, inversedBy="project_order_tasks")
* @ORM\JoinColumn(nullable=false, onDelete="CASCADE")
*/
private $project_order;
/**
* @ORM\OneToMany(targetEntity=ProjectOrderTaskJobs::class, mappedBy="order_task", cascade={"persist"}, orphanRemoval=true )
* @Groups({
* "project_order_task@task_jobs"
* })
*/
private $task_jobs;
/**
* @ORM\OneToOne(targetEntity=ProjectOrderTaskFulfillment::class, mappedBy="task", cascade={"persist", "remove"})
* @Groups({
* "project_order_task@fulfillment"
* })
*/
private $task_fulfillment;
/**
* @ORM\ManyToOne(targetEntity=WorkerActivities::class, inversedBy="project_order_tasks")
* @Groups({
* "project_order_task@responsible_person"
* })
*/
private $responsible_person;
public function __construct()
{
$this->task_jobs = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getTaskIndex(): ?int
{
return $this->task_index;
}
public function setTaskIndex(int $task_index): self
{
$this->task_index = $task_index;
return $this;
}
public function getTitle(): ?string
{
return $this->title;
}
public function setTitle(string $title): self
{
$this->title = $title;
return $this;
}
public function getDescription(): ?string
{
return $this->description;
}
public function setDescription(?string $description): self
{
$this->description = $description;
return $this;
}
public function getProgressWeight(): ?float
{
return $this->progress_weight;
}
public function setProgressWeight(float $progress_weight): self
{
$this->progress_weight = $progress_weight;
return $this;
}
public function getDurationDays(): ?int
{
return $this->duration_days;
}
public function setDurationDays(?int $duration_days): self
{
$this->duration_days = $duration_days;
return $this;
}
public function getStartedAt(): ?\DateTimeImmutable
{
return $this->started_at;
}
public function setStartedAt(?\DateTimeImmutable $started_at): self
{
$this->started_at = $started_at;
return $this;
}
public function getCompletedAt(): ?\DateTimeImmutable
{
return $this->completed_at;
}
public function setCompletedAt(?\DateTimeImmutable $completed_at): self
{
$this->completed_at = $completed_at;
return $this;
}
public function getProjectOrder(): ?ProjectOrders
{
return $this->project_order;
}
public function setProjectOrder(?ProjectOrders $project_order): self
{
$this->project_order = $project_order;
return $this;
}
/**
* /**
* ⚠️ IMPORTANT – Collection index / Serializer issue
*
* Problem:
* --------
* ProjectOrderTaskJobs koleksiyonu (Doctrine ArrayCollection) üzerinde
* BİRDEN FAZLA remove işlemi yapıldığında, Doctrine elemanları silerken
* array indexlerini yeniden sıralamaz.
*
* Örnek:
* [0 => JobA, 1 => JobB, 2 => JobC]
* JobB silinirse →
* [0 => JobA, 2 => JobC] // index boşluğu oluşur ❌
*
* Symfony Serializer bu yapıyı:
* - indexler ardışık değilse → associative object `{}` olarak,
* - indexler 0..n ise → JSON array `[]` olarak serialize eder.
*
* Bu nedenle çoklu silme sonrası FE tarafında:
* task_jobs: { "1": {...} }
* gibi HATALI bir çıktı oluşur.
*
* Çözüm:
* ------
* Serializer'a gönderilmeden önce collection MUTLAKA reindex edilmelidir.
* Bu da getter seviyesinde yapılır:
*
* return new ArrayCollection(array_values($this->task_jobs->toArray()));
*
* Böylece:
* - Doctrine iç davranışı bozulmaz
* - Serializer her zaman `[...]` JSON array üretir
* - FE tarafında index problemi yaşanmaz
*
* Not:
* ----
* Bu problem serializer flag'leri (preserve_empty_objects vb.)
* ile çözülemez; sorun serialization değil, collection index yapısıdır.
* /
* @return Collection<int, ProjectOrderTaskJobs>&Selectable
*/
public function getTaskJobs(): Collection
{
return new ArrayCollection(array_values($this->task_jobs->toArray()));
}
public function addProjectOrderTaskJob(ProjectOrderTaskJobs $projectOrderTaskJob): self
{
if (!$this->task_jobs->contains($projectOrderTaskJob)) {
$this->task_jobs[] = $projectOrderTaskJob;
$projectOrderTaskJob->setOrderTask($this);
}
return $this;
}
/**
* @see ProjectOrderTasks::getTaskJobs()
*/
public function removeProjectOrderTaskJob(ProjectOrderTaskJobs $projectOrderTaskJob): self
{
if ($this->task_jobs->removeElement($projectOrderTaskJob)) {
// set the owning side to null (unless already changed)
if ($projectOrderTaskJob->getOrderTask() === $this) {
$projectOrderTaskJob->setOrderTask(null);
}
}
return $this;
}
public function getTaskFulfillment(): ?ProjectOrderTaskFulfillment
{
return $this->task_fulfillment;
}
public function setTaskFulfillment(?ProjectOrderTaskFulfillment $task_fulfillment): self
{
// set the owning side of the relation if necessary
if ($task_fulfillment->getTask() !== $this) {
$task_fulfillment->setTask($this);
}
$this->task_fulfillment = $task_fulfillment;
return $this;
}
public function getResponsiblePerson(): ?WorkerActivities
{
return $this->responsible_person;
}
public function setResponsiblePerson(?WorkerActivities $responsible_person): self
{
$this->responsible_person = $responsible_person;
return $this;
}
}