<?php
namespace App\Entity;
use App\Repository\LessonLogRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use App\Entity\Traits\Timestampable;
/**
* Une entrée du "cahier de texte numérique".
*
* Le prof saisit, après chaque séance, ce qu'il a effectivement vu en
* cours — même si le chapitre n'est pas terminé, et même s'il n'y a PAS
* de chapitre concerné (révisions générales, correction d'épreuve...).
* Permet au censeur / préfet des études de suivre l'avancement réel jour
* par jour.
*
* - chapterProgress est NULLABLE : si null, 'motif' doit être rempli
* (cf. LessonLogController/ProgressController qui imposent cette règle).
* - subject est TOUJOURS rempli (avec ou sans chapitre), pour pouvoir
* lister tout le cahier de texte d'une matière indépendamment du
* programme national.
* - agendaElement + timeRanges permettent de savoir précisément quelle(s)
* tranche(s) horaire(s) du cours ont déjà été documentées, pour empêcher
* les doublons (cf. règle métier : on ne remplit pas deux fois la même
* tranche horaire).
* - startAt/endAt sont calculés depuis agendaElement/timeRanges au moment
* de la création, jamais saisis directement par l'utilisateur.
*/
#[ORM\HasLifecycleCallbacks]
#[ORM\Entity(repositoryClass: LessonLogRepository::class)]
class LessonLog
{
use Timestampable;
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
/**
* Chapitre du programme national concerné. NULL si la séance ne
* correspond à aucun chapitre (révisions, correction d'épreuve...) —
* dans ce cas, 'motif' doit être rempli à la place.
*/
#[ORM\ManyToOne(inversedBy: 'lessonLogs')]
private ?ChapterProgress $chapterProgress = null;
/**
* La matière réelle concernée. Toujours rempli, même sans chapitre,
* pour permettre une liste chronologique du cahier de texte par
* matière indépendamment du programme national.
*/
#[ORM\ManyToOne(inversedBy: 'lessonLogs')]
#[ORM\JoinColumn(nullable: false)]
private ?Subject $subject = null;
/**
* Motif de la séance quand aucun chapitre n'est concerné
* (ex: "Révisions générales avant le contrôle", "Correction de
* l'épreuve du contrôle commun"). NULL si chapterProgress est rempli.
*/
#[ORM\Column(length: 255, nullable: true)]
private ?string $motif = null;
/**
* Date à laquelle le cours a été dispensé.
*/
#[ORM\Column(type: 'date')]
private ?\DateTimeInterface $date = null;
/**
* Heure de début réelle de la séance documentée (copiée depuis
* l'AgendaElement ou la TimeRange choisie — jamais saisie).
*/
#[ORM\Column(type: Types::TIME_MUTABLE, nullable: true)]
private ?\DateTimeInterface $startAt = null;
/**
* Heure de fin réelle de la séance documentée (idem startAt).
*/
#[ORM\Column(type: Types::TIME_MUTABLE, nullable: true)]
private ?\DateTimeInterface $endAt = null;
/**
* Texte libre : ce qui a été vu/fait pendant la séance.
*/
#[ORM\Column(type: 'text')]
private ?string $content = null;
/**
* Durée de la séance en heures (ex: 1, 1.5, 2), calculée à partir du
* nombre de tranches horaires couvertes et de AgendaTimeConvertion.
*/
#[ORM\Column(nullable: true)]
private ?float $duration = null;
#[ORM\ManyToOne]
#[ORM\JoinColumn(nullable: false)]
private ?User $createdBy = null;
/**
* Le cours (AgendaElement) pendant lequel la séance a eu lieu.
* Nécessaire pour la vérification anti-doublon par tranche horaire.
*/
#[ORM\ManyToOne(inversedBy: 'lessonLogs')]
#[ORM\JoinColumn(nullable: false)]
private ?AgendaElement $agendaElement = null;
/**
* Tranche(s) horaire(s) couvertes par cette entrée. Vide si "tout le
* cours" a été choisi sans tranches définies sur l'AgendaElement
* (cas rare, fallback sur agendaElement seul pour l'anti-doublon).
*
* Côté INVERSE de la relation (comme Absence::timeRanges) :
* TimeRange::lessonLogs est le côté propriétaire (owning side) avec
* 'inversedBy' => 'timeRanges' ; ici on déclare 'mappedBy' => 'lessonLogs'.
*
* @var Collection<int, TimeRange>
*/
#[ORM\ManyToMany(targetEntity: TimeRange::class, mappedBy: 'lessonLogs')]
private Collection $timeRanges;
public function __construct()
{
$this->timeRanges = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getChapterProgress(): ?ChapterProgress
{
return $this->chapterProgress;
}
public function setChapterProgress(?ChapterProgress $chapterProgress): static
{
$this->chapterProgress = $chapterProgress;
return $this;
}
public function getSubject(): ?Subject
{
return $this->subject;
}
public function setSubject(?Subject $subject): static
{
$this->subject = $subject;
return $this;
}
public function getMotif(): ?string
{
return $this->motif;
}
public function setMotif(?string $motif): static
{
$this->motif = $motif;
return $this;
}
/**
* Libellé à afficher : le titre du chapitre s'il existe, sinon le motif.
*/
public function getDisplayLabel(): string
{
if ($this->chapterProgress !== null) {
return $this->chapterProgress->getChapterTitle();
}
return $this->motif ?? 'Séance sans chapitre';
}
public function getDate(): ?\DateTimeInterface
{
return $this->date;
}
public function setDate(\DateTimeInterface $date): static
{
$this->date = $date;
return $this;
}
public function getStartAt(): ?\DateTimeInterface
{
return $this->startAt;
}
public function setStartAt(?\DateTimeInterface $startAt): static
{
$this->startAt = $startAt;
return $this;
}
public function getEndAt(): ?\DateTimeInterface
{
return $this->endAt;
}
public function setEndAt(?\DateTimeInterface $endAt): static
{
$this->endAt = $endAt;
return $this;
}
public function getContent(): ?string
{
return $this->content;
}
public function setContent(string $content): static
{
$this->content = $content;
return $this;
}
public function getDuration(): ?float
{
return $this->duration;
}
public function setDuration(?float $duration): static
{
$this->duration = $duration;
return $this;
}
public function getCreatedBy(): ?User
{
return $this->createdBy;
}
public function setCreatedBy(?User $createdBy): static
{
$this->createdBy = $createdBy;
return $this;
}
public function getAgendaElement(): ?AgendaElement
{
return $this->agendaElement;
}
public function setAgendaElement(?AgendaElement $agendaElement): static
{
$this->agendaElement = $agendaElement;
return $this;
}
/**
* @return Collection<int, TimeRange>
*/
public function getTimeRanges(): Collection
{
return $this->timeRanges;
}
public function addTimeRange(TimeRange $timeRange): static
{
if (!$this->timeRanges->contains($timeRange)) {
$this->timeRanges->add($timeRange);
$timeRange->addLessonLog($this);
}
return $this;
}
public function removeTimeRange(TimeRange $timeRange): static
{
if ($this->timeRanges->removeElement($timeRange)) {
$timeRange->removeLessonLog($this);
}
return $this;
}
}