<?php
namespace App\Entity;
use App\Repository\ChapterProgressRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use App\Entity\Traits\Timestampable;
/**
* Progression d'un chapitre LOCAL (SubjectChapter) pour un Subject réel.
*
* Migration : l'ancien champ `chapter` (→ Chapter national) est conservé
* nullable pendant la transition. `subjectChapter` est désormais la
* référence principale. Utiliser getChapterTitle() dans les templates
* pour être compatible avec les deux systèmes.
*
* 1 ChapterProgress = 1 (SubjectChapter, Subject).
*/
#[ORM\HasLifecycleCallbacks]
#[ORM\Entity(repositoryClass: ChapterProgressRepository::class)]
class ChapterProgress
{
use Timestampable;
public const STATUS_NOT_STARTED = 'not_started';
public const STATUS_IN_PROGRESS = 'in_progress';
public const STATUS_DONE = 'done';
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
/**
* @deprecated Remplacé par $subjectChapter.
* Conservé nullable pour assurer la rétrocompatibilité durant la migration.
* À supprimer après migration complète des données existantes.
*/
#[ORM\ManyToOne(inversedBy: 'progresses')]
#[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')]
private ?Chapter $chapter = null;
/**
* Chapitre LOCAL (copie école du programme national, ou chapitre custom).
* Référence principale depuis la migration SubjectChapter.
*/
#[ORM\ManyToOne(inversedBy: 'progresses')]
#[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')]
private ?SubjectChapter $subjectChapter = null;
/**
* Le Subject réel : matière + classe + école + année.
*/
#[ORM\ManyToOne(inversedBy: 'chapterProgresses')]
#[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
private ?Subject $subject = null;
#[ORM\Column(length: 20)]
private string $status = self::STATUS_NOT_STARTED;
#[ORM\Column(type: 'datetime', nullable: true)]
private ?\DateTimeInterface $startedAt = null;
#[ORM\Column(type: 'datetime', nullable: true)]
private ?\DateTimeInterface $completedAt = null;
/** Prof qui a marqué le chapitre comme terminé. */
#[ORM\ManyToOne]
#[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')]
private ?User $completedBy = null;
#[ORM\OneToMany(
mappedBy: 'chapterProgress',
targetEntity: LessonLog::class,
cascade: ['persist', 'remove'],
orphanRemoval: true
)]
#[ORM\OrderBy(['date' => 'DESC'])]
private Collection $lessonLogs;
public function __construct()
{
$this->lessonLogs = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
// ── SubjectChapter (nouveau système) ────────────────────────────────────
public function getSubjectChapter(): ?SubjectChapter
{
return $this->subjectChapter;
}
public function setSubjectChapter(?SubjectChapter $subjectChapter): static
{
$this->subjectChapter = $subjectChapter;
return $this;
}
// ── Chapter (ancien système, deprecated) ─────────────────────────────────
/** @deprecated Utiliser getSubjectChapter() */
public function getChapter(): ?Chapter
{
return $this->chapter;
}
/** @deprecated Utiliser setSubjectChapter() */
public function setChapter(?Chapter $chapter): static
{
$this->chapter = $chapter;
return $this;
}
// ── Helper titre compatible ancien + nouveau système ─────────────────────
/**
* Retourne le titre du chapitre quelle que soit la version du système.
* À utiliser dans les templates à la place de
* chapterProgress.chapter.title.
*/
public function getChapterTitle(): string
{
if ($this->subjectChapter !== null) {
return $this->subjectChapter->getTitle() ?? '';
}
return $this->chapter?->getTitle() ?? '';
}
// ── Subject ──────────────────────────────────────────────────────────────
public function getSubject(): ?Subject
{
return $this->subject;
}
public function setSubject(?Subject $subject): static
{
$this->subject = $subject;
return $this;
}
// ── Statut ───────────────────────────────────────────────────────────────
public function getStatus(): string
{
return $this->status;
}
public function setStatus(string $status): static
{
$this->status = $status;
return $this;
}
public function isNotStarted(): bool
{
return $this->status === self::STATUS_NOT_STARTED;
}
public function isInProgress(): bool
{
return $this->status === self::STATUS_IN_PROGRESS;
}
public function isDone(): bool
{
return $this->status === self::STATUS_DONE;
}
// ── Dates ────────────────────────────────────────────────────────────────
public function getStartedAt(): ?\DateTimeInterface
{
return $this->startedAt;
}
public function setStartedAt(?\DateTimeInterface $startedAt): static
{
$this->startedAt = $startedAt;
return $this;
}
public function getCompletedAt(): ?\DateTimeInterface
{
return $this->completedAt;
}
public function setCompletedAt(?\DateTimeInterface $completedAt): static
{
$this->completedAt = $completedAt;
return $this;
}
public function getCompletedBy(): ?User
{
return $this->completedBy;
}
public function setCompletedBy(?User $completedBy): static
{
$this->completedBy = $completedBy;
return $this;
}
// ── LessonLogs ───────────────────────────────────────────────────────────
/** @return Collection<int, LessonLog> */
public function getLessonLogs(): Collection
{
return $this->lessonLogs;
}
public function addLessonLog(LessonLog $lessonLog): static
{
if (!$this->lessonLogs->contains($lessonLog)) {
$this->lessonLogs->add($lessonLog);
$lessonLog->setChapterProgress($this);
}
return $this;
}
public function removeLessonLog(LessonLog $lessonLog): static
{
if ($this->lessonLogs->removeElement($lessonLog)) {
if ($lessonLog->getChapterProgress() === $this) {
$lessonLog->setChapterProgress(null);
}
}
return $this;
}
/**
* Durée totale (en heures) enregistrée dans le cahier de texte
* pour ce chapitre.
*/
public function getTotalLoggedHours(): float
{
$total = 0.0;
foreach ($this->lessonLogs as $log) {
$total += $log->getDuration() ?? 0.0;
}
return $total;
}
}