src/Entity/ProjectOrderTasks.php line 16

Open in your IDE?
  1. <?php
  2. namespace App\Entity;
  3. use App\Repository\ProjectOrderTasksRepository;
  4. use Doctrine\Common\Collections\ArrayCollection;
  5. use Doctrine\Common\Collections\Collection;
  6. use Doctrine\Common\Collections\Selectable;
  7. use Doctrine\ORM\Mapping as ORM;
  8. use Symfony\Component\Serializer\Annotation\Groups;
  9. use Symfony\Component\Serializer\Annotation\SerializedName;
  10. /**
  11.  * @ORM\Entity(repositoryClass=ProjectOrderTasksRepository::class)
  12.  */
  13. class ProjectOrderTasks
  14. {
  15.     /**
  16.      * @ORM\Id
  17.      * @ORM\GeneratedValue
  18.      * @ORM\Column(type="integer")
  19.      * @Groups({
  20.      *     "project_order_task@base",
  21.      *     "project_order_task@core"
  22.      * })
  23.      */
  24.     private $id;
  25.     /**
  26.      * @ORM\Column(type="integer")
  27.      * @Groups({
  28.      *      "project_order_task@base",
  29.      *      "project_order_task@core"
  30.      *  })
  31.      */
  32.     private $task_index;
  33.     /**
  34.      * @ORM\Column(type="string", length=255)
  35.      * @Groups({
  36.      *      "project_order_task@base",
  37.      *      "project_order_task@core"
  38.      *  })
  39.      */
  40.     private $title;
  41.     /**
  42.      * @ORM\Column(type="text", nullable=true)
  43.      * @Groups({
  44.      *      "project_order_task@base",
  45.      *      "project_order_task@core"
  46.      *  })
  47.      */
  48.     private $description;
  49.     /**
  50.      * @ORM\Column(type="float")
  51.      * @Groups({
  52.      *      "project_order_task@core"
  53.      *  })
  54.      */
  55.     private $progress_weight;
  56.     /**
  57.      * @ORM\Column(type="integer", nullable=true)
  58.      * @Groups({
  59.      *      "project_order_task@core"
  60.      *  })
  61.      */
  62.     private $duration_days;
  63.     /**
  64.      * @deprecated never use this controlling over job
  65.      * @ORM\Column(type="date_immutable", nullable=true, options={"comment": "deprecated -> Redundant field; value is dynamically calculated based on completed job weights."})
  66.      * @Groups({
  67.      *      "project_order_task@core"
  68.      *  })
  69.      */
  70.     private $started_at;
  71.     /**
  72.      * @deprecated never use this controlling over job
  73.      * @ORM\Column(type="date_immutable", nullable=true, options={"comment": "deprecated -> Redundant field; value is dynamically calculated based on completed job weights."})
  74.      * @Groups({
  75.      *      "project_order_task@core"
  76.      *  })
  77.      */
  78.     private $completed_at;
  79.     /**
  80.      * @ORM\ManyToOne(targetEntity=ProjectOrders::class, inversedBy="project_order_tasks")
  81.      * @ORM\JoinColumn(nullable=false, onDelete="CASCADE")
  82.      */
  83.     private $project_order;
  84.     /**
  85.      * @ORM\OneToMany(targetEntity=ProjectOrderTaskJobs::class, mappedBy="order_task", cascade={"persist"}, orphanRemoval=true )
  86.      * @Groups({
  87.      *       "project_order_task@task_jobs"
  88.      *   })
  89.      */
  90.     private $task_jobs;
  91.     /**
  92.      * @ORM\OneToOne(targetEntity=ProjectOrderTaskFulfillment::class, mappedBy="task", cascade={"persist", "remove"})
  93.      * @Groups({
  94.      *     "project_order_task@fulfillment"
  95.      * })
  96.      */
  97.     private $task_fulfillment;
  98.     /**
  99.      * @ORM\ManyToOne(targetEntity=WorkerActivities::class, inversedBy="project_order_tasks")
  100.      * @Groups({
  101.      *         "project_order_task@responsible_person"
  102.      *  })
  103.      */
  104.     private $responsible_person;
  105.     public function __construct()
  106.     {
  107.         $this->task_jobs = new ArrayCollection();
  108.     }
  109.     public function getId(): ?int
  110.     {
  111.         return $this->id;
  112.     }
  113.     public function getTaskIndex(): ?int
  114.     {
  115.         return $this->task_index;
  116.     }
  117.     public function setTaskIndex(int $task_index): self
  118.     {
  119.         $this->task_index $task_index;
  120.         return $this;
  121.     }
  122.     public function getTitle(): ?string
  123.     {
  124.         return $this->title;
  125.     }
  126.     public function setTitle(string $title): self
  127.     {
  128.         $this->title $title;
  129.         return $this;
  130.     }
  131.     public function getDescription(): ?string
  132.     {
  133.         return $this->description;
  134.     }
  135.     public function setDescription(?string $description): self
  136.     {
  137.         $this->description $description;
  138.         return $this;
  139.     }
  140.     public function getProgressWeight(): ?float
  141.     {
  142.         return $this->progress_weight;
  143.     }
  144.     public function setProgressWeight(float $progress_weight): self
  145.     {
  146.         $this->progress_weight $progress_weight;
  147.         return $this;
  148.     }
  149.     public function getDurationDays(): ?int
  150.     {
  151.         return $this->duration_days;
  152.     }
  153.     public function setDurationDays(?int $duration_days): self
  154.     {
  155.         $this->duration_days $duration_days;
  156.         return $this;
  157.     }
  158.     public function getStartedAt(): ?\DateTimeImmutable
  159.     {
  160.         return $this->started_at;
  161.     }
  162.     public function setStartedAt(?\DateTimeImmutable $started_at): self
  163.     {
  164.         $this->started_at $started_at;
  165.         return $this;
  166.     }
  167.     public function getCompletedAt(): ?\DateTimeImmutable
  168.     {
  169.         return $this->completed_at;
  170.     }
  171.     public function setCompletedAt(?\DateTimeImmutable $completed_at): self
  172.     {
  173.         $this->completed_at $completed_at;
  174.         return $this;
  175.     }
  176.     public function getProjectOrder(): ?ProjectOrders
  177.     {
  178.         return $this->project_order;
  179.     }
  180.     public function setProjectOrder(?ProjectOrders $project_order): self
  181.     {
  182.         $this->project_order $project_order;
  183.         return $this;
  184.     }
  185.     /**
  186.      * TaskExecution servisi (ağ içi `internal/executions`) + owner FE için sanal alan — task'ın
  187.      * bağlı olduğu siparişin sipariş numarası. DB kolonu DEĞİL, `project_order->order_nr`'dan türer.
  188.      * `project_order` nullable=false olduğu için normalde her zaman dolu döner; savunma amaçlı
  189.      * null-safe. T55: çok-kelimeli sanal getter camelCase serialize olur → `@SerializedName` ZORUNLU.
  190.      *
  191.      * @Groups({
  192.      *     "project_order_task@core"
  193.      * })
  194.      * @SerializedName("order_nr")
  195.      */
  196.     public function getOrderNr(): ?string
  197.     {
  198.         return $this->project_order $this->project_order->getOrderNr() : null;
  199.     }
  200.     /**
  201.      * /**
  202.      *  ⚠️ IMPORTANT – Collection index / Serializer issue
  203.      *
  204.      *  Problem:
  205.      *  --------
  206.      *  ProjectOrderTaskJobs koleksiyonu (Doctrine ArrayCollection) üzerinde
  207.      *  BİRDEN FAZLA remove işlemi yapıldığında, Doctrine elemanları silerken
  208.      *  array indexlerini yeniden sıralamaz.
  209.      *
  210.      *  Örnek:
  211.      *    [0 => JobA, 1 => JobB, 2 => JobC]
  212.      *    JobB silinirse →
  213.      *    [0 => JobA, 2 => JobC]   // index boşluğu oluşur ❌
  214.      *
  215.      *  Symfony Serializer bu yapıyı:
  216.      *    - indexler ardışık değilse → associative object `{}` olarak,
  217.      *    - indexler 0..n ise → JSON array `[]` olarak serialize eder.
  218.      *
  219.      *  Bu nedenle çoklu silme sonrası FE tarafında:
  220.      *    task_jobs: { "1": {...} }
  221.      *  gibi HATALI bir çıktı oluşur.
  222.      *
  223.      *  Çözüm:
  224.      *  ------
  225.      *  Serializer'a gönderilmeden önce collection MUTLAKA reindex edilmelidir.
  226.      *  Bu da getter seviyesinde yapılır:
  227.      *
  228.      *    return new ArrayCollection(array_values($this->task_jobs->toArray()));
  229.      *
  230.      *  Böylece:
  231.      *    - Doctrine iç davranışı bozulmaz
  232.      *    - Serializer her zaman `[...]` JSON array üretir
  233.      *    - FE tarafında index problemi yaşanmaz
  234.      *
  235.      *  Not:
  236.      *  ----
  237.      *  Bu problem serializer flag'leri (preserve_empty_objects vb.)
  238.      *  ile çözülemez; sorun serialization değil, collection index yapısıdır.
  239.      * /
  240.      * @return Collection<int, ProjectOrderTaskJobs>&Selectable
  241.      */
  242.     public function getTaskJobs(): Collection
  243.     {
  244.         return new ArrayCollection(array_values($this->task_jobs->toArray()));
  245.     }
  246.     public function addProjectOrderTaskJob(ProjectOrderTaskJobs $projectOrderTaskJob): self
  247.     {
  248.         if (!$this->task_jobs->contains($projectOrderTaskJob)) {
  249.             $this->task_jobs[] = $projectOrderTaskJob;
  250.             $projectOrderTaskJob->setOrderTask($this);
  251.         }
  252.         return $this;
  253.     }
  254.     /**
  255.      * @see ProjectOrderTasks::getTaskJobs()
  256.     */
  257.     public function removeProjectOrderTaskJob(ProjectOrderTaskJobs $projectOrderTaskJob): self
  258.     {
  259.         if ($this->task_jobs->removeElement($projectOrderTaskJob)) {
  260.             // set the owning side to null (unless already changed)
  261.             if ($projectOrderTaskJob->getOrderTask() === $this) {
  262.                 $projectOrderTaskJob->setOrderTask(null);
  263.             }
  264.         }
  265.         return $this;
  266.     }
  267.     public function getTaskFulfillment(): ?ProjectOrderTaskFulfillment
  268.     {
  269.         return $this->task_fulfillment;
  270.     }
  271.     public function setTaskFulfillment(?ProjectOrderTaskFulfillment $task_fulfillment): self
  272.     {
  273.         // set the owning side of the relation if necessary
  274.         if ($task_fulfillment->getTask() !== $this) {
  275.             $task_fulfillment->setTask($this);
  276.         }
  277.         $this->task_fulfillment $task_fulfillment;
  278.         return $this;
  279.     }
  280.     public function getResponsiblePerson(): ?WorkerActivities
  281.     {
  282.         return $this->responsible_person;
  283.     }
  284.     public function setResponsiblePerson(?WorkerActivities $responsible_person): self
  285.     {
  286.         $this->responsible_person $responsible_person;
  287.         return $this;
  288.     }
  289. }