src/Entity/ChapterProgress.php line 23

Open in your IDE?
  1. <?php
  2. namespace App\Entity;
  3. use App\Repository\ChapterProgressRepository;
  4. use Doctrine\Common\Collections\ArrayCollection;
  5. use Doctrine\Common\Collections\Collection;
  6. use Doctrine\ORM\Mapping as ORM;
  7. use App\Entity\Traits\Timestampable;
  8. /**
  9.  * Progression d'un chapitre LOCAL (SubjectChapter) pour un Subject réel.
  10.  *
  11.  * Migration : l'ancien champ `chapter` (→ Chapter national) est conservé
  12.  * nullable pendant la transition. `subjectChapter` est désormais la
  13.  * référence principale. Utiliser getChapterTitle() dans les templates
  14.  * pour être compatible avec les deux systèmes.
  15.  *
  16.  * 1 ChapterProgress = 1 (SubjectChapter, Subject).
  17.  */
  18. #[ORM\HasLifecycleCallbacks]
  19. #[ORM\Entity(repositoryClassChapterProgressRepository::class)]
  20. class ChapterProgress
  21. {
  22.     use Timestampable;
  23.     public const STATUS_NOT_STARTED 'not_started';
  24.     public const STATUS_IN_PROGRESS 'in_progress';
  25.     public const STATUS_DONE        'done';
  26.     #[ORM\Id]
  27.     #[ORM\GeneratedValue]
  28.     #[ORM\Column]
  29.     private ?int $id null;
  30.     /**
  31.      * @deprecated Remplacé par $subjectChapter.
  32.      * Conservé nullable pour assurer la rétrocompatibilité durant la migration.
  33.      * À supprimer après migration complète des données existantes.
  34.      */
  35.     #[ORM\ManyToOne(inversedBy'progresses')]
  36.     #[ORM\JoinColumn(nullabletrueonDelete'SET NULL')]
  37.     private ?Chapter $chapter null;
  38.     /**
  39.      * Chapitre LOCAL (copie école du programme national, ou chapitre custom).
  40.      * Référence principale depuis la migration SubjectChapter.
  41.      */
  42.     #[ORM\ManyToOne(inversedBy'progresses')]
  43.     #[ORM\JoinColumn(nullabletrueonDelete'SET NULL')]
  44.     private ?SubjectChapter $subjectChapter null;
  45.     /**
  46.      * Le Subject réel : matière + classe + école + année.
  47.      */
  48.     #[ORM\ManyToOne(inversedBy'chapterProgresses')]
  49.     #[ORM\JoinColumn(nullablefalseonDelete'CASCADE')]
  50.     private ?Subject $subject null;
  51.     #[ORM\Column(length20)]
  52.     private string $status self::STATUS_NOT_STARTED;
  53.     #[ORM\Column(type'datetime'nullabletrue)]
  54.     private ?\DateTimeInterface $startedAt null;
  55.     #[ORM\Column(type'datetime'nullabletrue)]
  56.     private ?\DateTimeInterface $completedAt null;
  57.     /** Prof qui a marqué le chapitre comme terminé. */
  58.     #[ORM\ManyToOne]
  59.     #[ORM\JoinColumn(nullabletrueonDelete'SET NULL')]
  60.     private ?User $completedBy null;
  61.     #[ORM\OneToMany(
  62.         mappedBy'chapterProgress',
  63.         targetEntityLessonLog::class,
  64.         cascade: ['persist''remove'],
  65.         orphanRemovaltrue
  66.     )]
  67.     #[ORM\OrderBy(['date' => 'DESC'])]
  68.     private Collection $lessonLogs;
  69.     public function __construct()
  70.     {
  71.         $this->lessonLogs = new ArrayCollection();
  72.     }
  73.     public function getId(): ?int
  74.     {
  75.         return $this->id;
  76.     }
  77.     // ── SubjectChapter (nouveau système) ────────────────────────────────────
  78.     public function getSubjectChapter(): ?SubjectChapter
  79.     {
  80.         return $this->subjectChapter;
  81.     }
  82.     public function setSubjectChapter(?SubjectChapter $subjectChapter): static
  83.     {
  84.         $this->subjectChapter $subjectChapter;
  85.         return $this;
  86.     }
  87.     // ── Chapter (ancien système, deprecated) ─────────────────────────────────
  88.     /** @deprecated Utiliser getSubjectChapter() */
  89.     public function getChapter(): ?Chapter
  90.     {
  91.         return $this->chapter;
  92.     }
  93.     /** @deprecated Utiliser setSubjectChapter() */
  94.     public function setChapter(?Chapter $chapter): static
  95.     {
  96.         $this->chapter $chapter;
  97.         return $this;
  98.     }
  99.     // ── Helper titre compatible ancien + nouveau système ─────────────────────
  100.     /**
  101.      * Retourne le titre du chapitre quelle que soit la version du système.
  102.      * À utiliser dans les templates à la place de
  103.      * chapterProgress.chapter.title.
  104.      */
  105.     public function getChapterTitle(): string
  106.     {
  107.         if ($this->subjectChapter !== null) {
  108.             return $this->subjectChapter->getTitle() ?? '';
  109.         }
  110.         return $this->chapter?->getTitle() ?? '';
  111.     }
  112.     // ── Subject ──────────────────────────────────────────────────────────────
  113.     public function getSubject(): ?Subject
  114.     {
  115.         return $this->subject;
  116.     }
  117.     public function setSubject(?Subject $subject): static
  118.     {
  119.         $this->subject $subject;
  120.         return $this;
  121.     }
  122.     // ── Statut ───────────────────────────────────────────────────────────────
  123.     public function getStatus(): string
  124.     {
  125.         return $this->status;
  126.     }
  127.     public function setStatus(string $status): static
  128.     {
  129.         $this->status $status;
  130.         return $this;
  131.     }
  132.     public function isNotStarted(): bool
  133.     {
  134.         return $this->status === self::STATUS_NOT_STARTED;
  135.     }
  136.     public function isInProgress(): bool
  137.     {
  138.         return $this->status === self::STATUS_IN_PROGRESS;
  139.     }
  140.     public function isDone(): bool
  141.     {
  142.         return $this->status === self::STATUS_DONE;
  143.     }
  144.     // ── Dates ────────────────────────────────────────────────────────────────
  145.     public function getStartedAt(): ?\DateTimeInterface
  146.     {
  147.         return $this->startedAt;
  148.     }
  149.     public function setStartedAt(?\DateTimeInterface $startedAt): static
  150.     {
  151.         $this->startedAt $startedAt;
  152.         return $this;
  153.     }
  154.     public function getCompletedAt(): ?\DateTimeInterface
  155.     {
  156.         return $this->completedAt;
  157.     }
  158.     public function setCompletedAt(?\DateTimeInterface $completedAt): static
  159.     {
  160.         $this->completedAt $completedAt;
  161.         return $this;
  162.     }
  163.     public function getCompletedBy(): ?User
  164.     {
  165.         return $this->completedBy;
  166.     }
  167.     public function setCompletedBy(?User $completedBy): static
  168.     {
  169.         $this->completedBy $completedBy;
  170.         return $this;
  171.     }
  172.     // ── LessonLogs ───────────────────────────────────────────────────────────
  173.     /** @return Collection<int, LessonLog> */
  174.     public function getLessonLogs(): Collection
  175.     {
  176.         return $this->lessonLogs;
  177.     }
  178.     public function addLessonLog(LessonLog $lessonLog): static
  179.     {
  180.         if (!$this->lessonLogs->contains($lessonLog)) {
  181.             $this->lessonLogs->add($lessonLog);
  182.             $lessonLog->setChapterProgress($this);
  183.         }
  184.         return $this;
  185.     }
  186.     public function removeLessonLog(LessonLog $lessonLog): static
  187.     {
  188.         if ($this->lessonLogs->removeElement($lessonLog)) {
  189.             if ($lessonLog->getChapterProgress() === $this) {
  190.                 $lessonLog->setChapterProgress(null);
  191.             }
  192.         }
  193.         return $this;
  194.     }
  195.     /**
  196.      * Durée totale (en heures) enregistrée dans le cahier de texte
  197.      * pour ce chapitre.
  198.      */
  199.     public function getTotalLoggedHours(): float
  200.     {
  201.         $total 0.0;
  202.         foreach ($this->lessonLogs as $log) {
  203.             $total += $log->getDuration() ?? 0.0;
  204.         }
  205.         return $total;
  206.     }
  207. }