<?php
namespace App\Entity;
use App\Repository\SubjectLessonRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use App\Entity\Traits\Timestampable;
/**
* Leçon LOCAL d'une matière dans un établissement.
*
* Créé automatiquement par copie du programme national (Lesson) lors
* de l'affectation d'un Subject à un SubjectReference, ou manuellement
* par l'admin/éditeur de l'école.
*
* Chaque école possède sa propre copie, modifiable indépendamment du
* programme national. `sourceLesson` garde la traçabilité de l'origine
* nationale (nullable : null si leçon 100 % personnalisé).
*
* LessonProgress pointe désormais ici (et non plus sur Lesson).
*/
#[ORM\HasLifecycleCallbacks]
#[ORM\Entity(repositoryClass: SubjectLessonRepository::class)]
class SubjectLesson
{
use Timestampable;
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
private ?string $title = null;
#[ORM\Column(length: 255, nullable: true)]
private ?string $titleEn = null;
#[ORM\Column(type: 'text', nullable: true)]
private ?string $description = null;
/**
* Ordre de la leçon dans la progression locale (1, 2, 3...).
* L'admin peut réordonner librement via drag & drop.
*/
#[ORM\Column]
private ?int $position = null;
/**
* La matière réelle (école + classe + année) à laquelle cette leçon appartient.
*/
#[ORM\ManyToOne(inversedBy: 'subjectLessons')]
#[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
private ?Subject $subject = null;
/**
* Leçon national d'origine.
* NULL si la leçon a été créée manuellement par l'admin (hors programme national).
* Permet au superadmin de tracer la couverture nationale via cette FK.
*/
#[ORM\ManyToOne]
#[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')]
private ?Lesson $sourceLesson = null;
/**
* Séquence avant la fin de laquelle cette leçon doit être terminée.
*/
#[ORM\ManyToOne]
#[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')]
private ?Quarter $deadlineQuarter = null;
/**
* Date calendaire optionnelle de deadline (alternative à deadlineQuarter).
*/
#[ORM\Column(type: 'date', nullable: true)]
private ?\DateTimeInterface $deadlineDate = null;
/**
* Nature de la leçon : Théorique (LT), Digitalisée (LD) ou Pratique (LP).
* Copiée depuis sourceLesson::nature comme valeur par défaut à la
* création (voir SubjectLessonCopier) — c'est ce champ que le prof
* coche/décoche dans le cahier de texte, modifiable indépendamment
* du national ensuite.
*/
#[ORM\Column(length: 2, nullable: true)]
private ?string $nature = null;
/**
* Durée de référence en heures, copiée depuis sourceLesson::duration
* à la création puis modifiable indépendamment par l'école.
*/
#[ORM\Column(nullable: true)]
private ?float $duration = null;
/**
* 1 SubjectLesson → au plus 1 LessonProgress (même Subject fixé).
*/
#[ORM\OneToMany(
mappedBy: 'subjectLesson',
targetEntity: LessonProgress::class,
cascade: ['persist', 'remove'],
orphanRemoval: true
)]
private Collection $progresses;
#[ORM\ManyToOne]
#[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')]
private ?User $author = null;
public function __construct()
{
$this->progresses = new ArrayCollection();
}
public function __toString(): string
{
return $this->title ?? '';
}
public function getId(): ?int
{
return $this->id;
}
public function getTitle(): ?string
{
return $this->title;
}
public function setTitle(string $title): static
{
$this->title = $title;
return $this;
}
public function getTitleEn(): ?string
{
return $this->titleEn;
}
public function setTitleEn(?string $titleEn): static
{
$this->titleEn = $titleEn;
return $this;
}
public function getDescription(): ?string
{
return $this->description;
}
public function setDescription(?string $description): static
{
$this->description = $description;
return $this;
}
public function getPosition(): ?int
{
return $this->position;
}
public function setPosition(int $position): static
{
$this->position = $position;
return $this;
}
public function getSubject(): ?Subject
{
return $this->subject;
}
public function setSubject(?Subject $subject): static
{
$this->subject = $subject;
return $this;
}
public function getSourceLesson(): ?Lesson
{
return $this->sourceLesson;
}
public function setSourceLesson(?Lesson $sourceLesson): static
{
$this->sourceLesson = $sourceLesson;
return $this;
}
public function getDeadlineQuarter(): ?Quarter
{
return $this->deadlineQuarter;
}
public function setDeadlineQuarter(?Quarter $deadlineQuarter): static
{
$this->deadlineQuarter = $deadlineQuarter;
return $this;
}
public function getDeadlineDate(): ?\DateTimeInterface
{
return $this->deadlineDate;
}
public function setDeadlineDate(?\DateTimeInterface $deadlineDate): static
{
$this->deadlineDate = $deadlineDate;
return $this;
}
/** @return Collection<int, LessonProgress> */
public function getProgresses(): Collection
{
return $this->progresses;
}
public function addProgress(LessonProgress $progress): static
{
if (!$this->progresses->contains($progress)) {
$this->progresses->add($progress);
$progress->setSubjectLesson($this);
}
return $this;
}
public function removeProgress(LessonProgress $progress): static
{
if ($this->progresses->removeElement($progress)) {
if ($progress->getSubjectLesson() === $this) {
$progress->setSubjectLesson(null);
}
}
return $this;
}
public function getAuthor(): ?User
{
return $this->author;
}
public function setAuthor(?User $author): static
{
$this->author = $author;
return $this;
}
/**
* true = leçon 100 % custom (hors programme national).
* false = leçon copiée depuis le programme national.
*/
public function isCustom(): bool
{
return $this->sourceLesson === null;
}
public function getNature(): ?string
{
return $this->nature;
}
public function setNature(?string $nature): static
{
$this->nature = $nature;
return $this;
}
public function getDuration(): ?float
{
return $this->duration;
}
public function setDuration(?float $duration): static
{
$this->duration = $duration;
return $this;
}
/**
* Module/UA affichés en lecture seule pour le contrôle admin, lus via
* sourceLesson (pas de copie locale — un SubjectLesson 100% custom
* n'a simplement pas de Module/UA).
*/
public function getModule(): ?Module
{
return $this->sourceLesson?->getModule();
}
public function getLearningUnit(): ?LearningUnit
{
return $this->sourceLesson?->getLearningUnit();
}
public function getCompetenceStatement(): ?string
{
return $this->sourceLesson?->getCompetenceStatement();
}
public function getWeek(): ?ExamWeek
{
return $this->sourceLesson?->getWeek();
}
}