<?php
namespace App\Entity;
use App\Entity\Traits\Timestampable;
use App\Repository\CapsuleRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
// Contenu pédagogique numérique interactif (module Apprentissage) : une suite
// ordonnée de CapsuleBlock consultée par l'élève, avec éventuellement des
// activités auto-corrigées. Rattachée à une Subject (donc à une classe/école/
// année via Subject) et, optionnellement, à un SubjectLesson existant du
// cahier de texte pour "digitaliser" ce point précis du programme — une
// capsule sans subjectLesson est un contenu/exercice libre. Entité neuve et
// isolée : aucun rapport avec Lesson/SubjectLesson/LessonProgress au-delà de
// ce lien optionnel purement additif.
#[ORM\HasLifecycleCallbacks]
#[ORM\Entity(repositoryClass: CapsuleRepository::class)]
class Capsule
{
use Timestampable;
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\ManyToOne(inversedBy: 'capsules')]
#[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
private ?Subject $subject = null;
#[ORM\ManyToOne(inversedBy: 'capsules')]
#[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')]
private ?SubjectLesson $subjectLesson = null;
#[ORM\Column(length: 255)]
private ?string $title = null;
#[ORM\Column(type: 'text', nullable: true)]
private ?string $description = null;
#[ORM\Column]
private int $position = 0;
#[ORM\Column]
private bool $published = false;
#[ORM\Column(type: 'datetime', nullable: true)]
private ?\DateTimeInterface $publishedAt = null;
// Temps limite (en secondes) de consultation de l'ENSEMBLE de la capsule,
// cumulé sur toutes les sessions de l'élève (voir
// StudentCapsuleProgress::timeSpentSeconds). Null = pas de limite. Une
// fois atteint, le mobile verrouille l'écran et soumet automatiquement
// les tentatives d'activité en cours (voir StudentActivityAttempt::autoSubmitted).
#[ORM\Column(nullable: true)]
private ?int $timeLimitSeconds = null;
// Compétence évaluée par TOUTE la capsule (module Apprentissage,
// indépendant du système trimestriel — voir LearningCompetency). Niveau
// le plus large de la chaîne de résolution : une Activity ou une Question
// peuvent avoir leur propre compétence, plus spécifique, qui prime alors
// sur celle-ci (voir Question::getEffectiveLearningCompetency()). Choix
// proposés limités aux compétences de subjectLesson — vide si la capsule
// n'est pas rattachée à une leçon/TD.
#[ORM\ManyToOne]
#[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')]
private ?LearningCompetency $learningCompetency = null;
// Trimestre auquel rattacher la capsule pour permettre à l'élève de
// filtrer les capsules par trimestre + matière côté mobile. Sans rapport
// avec le système de notation (Quarter est simplement réutilisé comme
// référentiel de périodes déjà connu de l'école) — null = visible sur
// tous les trimestres.
#[ORM\ManyToOne]
#[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')]
private ?Quarter $quarter = null;
// Photo de couverture affichée sur la carte de la capsule (web et
// mobile) — chemin relatif sous le même répertoire média que les blocs
// (voir CapsuleBlockController::moveMediaFile). Null = visuel par défaut.
#[ORM\Column(length: 255, nullable: true)]
private ?string $coverPath = null;
#[ORM\ManyToOne]
#[ORM\JoinColumn(nullable: false)]
private ?User $author = null;
#[ORM\ManyToOne]
private ?User $editor = null;
#[ORM\OneToMany(mappedBy: 'capsule', targetEntity: CapsuleBlock::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
#[ORM\OrderBy(['position' => 'ASC'])]
private Collection $blocks;
public function __construct()
{
$this->blocks = new ArrayCollection();
}
public function __toString(): string
{
return $this->title ?? '';
}
public function getId(): ?int
{
return $this->id;
}
public function getSubject(): ?Subject
{
return $this->subject;
}
public function setSubject(?Subject $subject): static
{
$this->subject = $subject;
return $this;
}
public function getSubjectLesson(): ?SubjectLesson
{
return $this->subjectLesson;
}
public function setSubjectLesson(?SubjectLesson $subjectLesson): static
{
$this->subjectLesson = $subjectLesson;
return $this;
}
public function getTitle(): ?string
{
return $this->title;
}
public function setTitle(string $title): static
{
$this->title = $title;
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 isPublished(): bool
{
return $this->published;
}
public function setPublished(bool $published): static
{
$this->published = $published;
return $this;
}
public function getPublishedAt(): ?\DateTimeInterface
{
return $this->publishedAt;
}
public function setPublishedAt(?\DateTimeInterface $publishedAt): static
{
$this->publishedAt = $publishedAt;
return $this;
}
public function getTimeLimitSeconds(): ?int
{
return $this->timeLimitSeconds;
}
public function setTimeLimitSeconds(?int $timeLimitSeconds): static
{
$this->timeLimitSeconds = $timeLimitSeconds;
return $this;
}
public function getLearningCompetency(): ?LearningCompetency
{
return $this->learningCompetency;
}
public function setLearningCompetency(?LearningCompetency $learningCompetency): static
{
$this->learningCompetency = $learningCompetency;
return $this;
}
public function getQuarter(): ?Quarter
{
return $this->quarter;
}
public function setQuarter(?Quarter $quarter): static
{
$this->quarter = $quarter;
return $this;
}
public function getCoverPath(): ?string
{
return $this->coverPath;
}
public function setCoverPath(?string $coverPath): static
{
$this->coverPath = $coverPath;
return $this;
}
public function getAuthor(): ?User
{
return $this->author;
}
public function setAuthor(?User $author): static
{
$this->author = $author;
return $this;
}
public function getEditor(): ?User
{
return $this->editor;
}
public function setEditor(?User $editor): static
{
$this->editor = $editor;
return $this;
}
/** @return Collection<int, CapsuleBlock> */
public function getBlocks(): Collection
{
return $this->blocks;
}
public function addBlock(CapsuleBlock $block): static
{
if (!$this->blocks->contains($block)) {
$this->blocks->add($block);
$block->setCapsule($this);
}
return $this;
}
public function removeBlock(CapsuleBlock $block): static
{
if ($this->blocks->removeElement($block)) {
if ($block->getCapsule() === $this) {
$block->setCapsule(null);
}
}
return $this;
}
}