diff --git a/migrations/Version20250526164743.php b/migrations/Version20250526164743.php new file mode 100644 index 0000000..9a69b91 --- /dev/null +++ b/migrations/Version20250526164743.php @@ -0,0 +1,71 @@ +addSql(<<<'SQL' + CREATE TABLE task (id INT AUTO_INCREMENT NOT NULL, user_id INT NOT NULL, produced_material_id INT NOT NULL, recipe_id INT DEFAULT NULL, parent_task_id INT DEFAULT NULL, root_task_id INT DEFAULT NULL, machine_extra_info1 VARCHAR(255) DEFAULT NULL, machine_extra_info2 VARCHAR(255) DEFAULT NULL, quantity_to_produce INT NOT NULL, quantity_provided_at_start INT NOT NULL, INDEX IDX_527EDB25A76ED395 (user_id), INDEX IDX_527EDB252B1CB36C (produced_material_id), INDEX IDX_527EDB2559D8A214 (recipe_id), INDEX IDX_527EDB25FFFE75C0 (parent_task_id), INDEX IDX_527EDB2561F7494C (root_task_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE task ADD CONSTRAINT FK_527EDB25A76ED395 FOREIGN KEY (user_id) REFERENCES user (id) ON DELETE CASCADE + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE task ADD CONSTRAINT FK_527EDB252B1CB36C FOREIGN KEY (produced_material_id) REFERENCES material (id) ON DELETE CASCADE + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE task ADD CONSTRAINT FK_527EDB2559D8A214 FOREIGN KEY (recipe_id) REFERENCES recipe (id) ON DELETE CASCADE + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE task ADD CONSTRAINT FK_527EDB25FFFE75C0 FOREIGN KEY (parent_task_id) REFERENCES task (id) ON DELETE CASCADE + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE task ADD CONSTRAINT FK_527EDB2561F7494C FOREIGN KEY (root_task_id) REFERENCES task (id) ON DELETE CASCADE + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE material_type ADD stack_name VARCHAR(50) NOT NULL + SQL); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql(<<<'SQL' + ALTER TABLE task DROP FOREIGN KEY FK_527EDB25A76ED395 + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE task DROP FOREIGN KEY FK_527EDB252B1CB36C + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE task DROP FOREIGN KEY FK_527EDB2559D8A214 + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE task DROP FOREIGN KEY FK_527EDB25FFFE75C0 + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE task DROP FOREIGN KEY FK_527EDB2561F7494C + SQL); + $this->addSql(<<<'SQL' + DROP TABLE task + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE material_type DROP stack_name + SQL); + } +} diff --git a/src/Entity/InputRecipeMaterial.php b/src/Entity/InputRecipeMaterial.php index 221e44a..0d5aa76 100644 --- a/src/Entity/InputRecipeMaterial.php +++ b/src/Entity/InputRecipeMaterial.php @@ -5,6 +5,7 @@ namespace App\Entity; use App\Repository\InputRecipeMaterialRepository; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Validator\Constraints as Assert; /** * A material consumed by a recipe @@ -19,18 +20,25 @@ class InputRecipeMaterial { */ #[ORM\ManyToOne(inversedBy: 'inputMaterials')] #[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')] + #[Assert\NotNull(message: 'Veuillez sélectionner une recette')] + #[Assert\Valid] private ?Recipe $recipe = null; /** * @var Material|null The material */ #[ORM\ManyToOne] #[ORM\JoinColumn(nullable: false)] + #[Assert\NotNull(message: 'Veuillez sélectionner un matériau')] + #[Assert\Valid] private ?Material $material = null; /** * @var int|null The consumed quantity */ #[ORM\Column] + #[Assert\Type(type: 'integer', message: 'La quantité consommée doit être un nombre entier')] + #[Assert\NotNull(message: 'Veuillez saisir une quantité consommée')] + #[Assert\Positive(message: 'La quantité consommée doit être strictement positive')] private ?int $consumedQuantity = null; /** diff --git a/src/Entity/Machine.php b/src/Entity/Machine.php index 0895b2c..c95c17f 100644 --- a/src/Entity/Machine.php +++ b/src/Entity/Machine.php @@ -7,6 +7,7 @@ use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Validator\Constraints as Assert; /** * A machine @@ -21,19 +22,25 @@ class Machine { * @var string|null The label for the first extra information */ #[ORM\Column(length: 255, nullable: true)] + #[Assert\NotBlank(message: 'Veuillez saisir un nom d\'information complémentaire n° 1', allowNull: true)] private ?string $labelExtraInfo1 = null; /** * @var string|null The label for the second extra information */ #[ORM\Column(length: 255, nullable: true)] + #[Assert\NotBlank(message: 'Veuillez saisir un nom d\'information complémentaire n° 2', allowNull: true)] private ?string $labelExtraInfo2 = null; /** * @var Collection The recipes */ #[ORM\OneToMany(targetEntity: Recipe::class, mappedBy: 'machine', orphanRemoval: true)] + #[Assert\Valid] private Collection $recipes; + /** + * Initialization + */ public function __construct () { $this->recipes = new ArrayCollection(); } diff --git a/src/Entity/Material.php b/src/Entity/Material.php index fe5afcd..fa7a832 100644 --- a/src/Entity/Material.php +++ b/src/Entity/Material.php @@ -7,6 +7,7 @@ use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Validator\Constraints as Assert; /** * A material @@ -22,18 +23,23 @@ class Material { */ #[ORM\ManyToOne] #[ORM\JoinColumn(nullable: false)] + #[Assert\NotNull(message: 'Veuillez sélectionner un type de matériau')] + #[Assert\Valid] private ?MaterialType $type = null; /** * @var bool|null Is the material craftable by default? */ #[ORM\Column] + #[Assert\Type(type: 'bool', message: 'L\'indicateur de si le matériau est craftable par défaut doit être un booléen')] + #[Assert\NotNull(message: 'Veuillez indiquer si le matériau est craftable par défaut')] private ?bool $isCraftableByDefault = null; /** * @var Collection The recipes */ #[ORM\OneToMany(targetEntity: OutputRecipeMaterial::class, mappedBy: 'material', orphanRemoval: true)] + #[Assert\Valid] private Collection $recipes; /** diff --git a/src/Entity/MaterialType.php b/src/Entity/MaterialType.php index 1b4a656..9337525 100644 --- a/src/Entity/MaterialType.php +++ b/src/Entity/MaterialType.php @@ -5,6 +5,7 @@ namespace App\Entity; use App\Repository\MaterialTypeRepository; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Validator\Constraints as Assert; /** * A material type @@ -16,23 +17,53 @@ class MaterialType { use TNamedEntity; /** - * @var int|null The stack size + * @var string|null The “stack” name + */ + #[ORM\Column(length: 50)] + #[Assert\NotBlank(message: 'Veuillez saisir un nom de pile')] + private ?string $stackName = null; + /** + * @var int|null The “stack” size */ #[ORM\Column] + #[Assert\Type(type: 'integer', message: 'La taille de pile doit être un nombre entier')] + #[Assert\NotNull(message: 'Veuillez saisir une taille de pile')] + #[Assert\Positive(message: 'La taille de pile doit être strictement positive')] private ?int $stackSize = null; /** - * The stack size + * The “stack” name + * + * @return string|null The “stack” name + */ + public function getStackName (): ?string { + return $this->stackName; + } + /** + * Set the “stack” name + * + * @param string $stackName The “stack” name + * + * @return $this + */ + public function setStackName (string $stackName): static { + $this->stackName = $stackName; + + return $this; + } + + /** + * The “stack” size * - * @return int|null The stack size + * @return int|null The “stack” size */ public function getStackSize (): ?int { return $this->stackSize; } /** - * Set the stack size + * Set the “stack” size * - * @param int $stackSize The stack size + * @param int $stackSize The “stack” size * * @return $this */ diff --git a/src/Entity/OutputRecipeMaterial.php b/src/Entity/OutputRecipeMaterial.php index e8fd704..8b4dc8a 100644 --- a/src/Entity/OutputRecipeMaterial.php +++ b/src/Entity/OutputRecipeMaterial.php @@ -5,6 +5,7 @@ namespace App\Entity; use App\Repository\OutputRecipeMaterialRepository; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Validator\Constraints as Assert; /** * A material produced by a recipe @@ -19,18 +20,25 @@ class OutputRecipeMaterial { */ #[ORM\ManyToOne(inversedBy: 'outputMaterials')] #[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')] + #[Assert\NotNull(message: 'Veuillez sélectionner une recette')] + #[Assert\Valid] private ?Recipe $recipe = null; /** * @var Material|null The material */ #[ORM\ManyToOne(inversedBy: 'recipes')] #[ORM\JoinColumn(nullable: false)] + #[Assert\NotNull(message: 'Veuillez sélectionner un matériau')] + #[Assert\Valid] private ?Material $material = null; /** * @var float|null The produced quantity */ #[ORM\Column] + #[Assert\Type(type: 'float', message: 'La quantité produite doit être un nombre réel')] + #[Assert\NotNull(message: 'Veuillez saisir une quantité produite')] + #[Assert\Positive(message: 'La quantité produite doit être strictement positive')] private ?float $producedQuantity = null; /** diff --git a/src/Entity/Recipe.php b/src/Entity/Recipe.php index 2c9d974..f91715c 100644 --- a/src/Entity/Recipe.php +++ b/src/Entity/Recipe.php @@ -6,6 +6,7 @@ use App\Repository\RecipeRepository; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; +use Symfony\Component\Validator\Constraints as Assert; /** * A crafting recipe @@ -20,30 +21,39 @@ class Recipe { */ #[ORM\ManyToOne(inversedBy: 'recipes')] #[ORM\JoinColumn(nullable: false)] + #[Assert\NotNull(message: 'Veuillez sélectionner une machine de fabrication')] + #[Assert\Valid] private ?Machine $machine = null; /** * @var float|null The crafting time, in seconds */ #[ORM\Column] + #[Assert\Type(type: 'float', message: 'Le temps de production doit être un nombre réel')] + #[Assert\NotNull(message: 'Veuillez saisir un temps de production')] + #[Assert\Positive(message: 'Le temps de production doit être strictement positive')] private ?float $craftingTime = null; /** - * @var Collection The input materials + * @var Collection The consumed materials */ #[ORM\OneToMany(targetEntity: InputRecipeMaterial::class, mappedBy: 'recipe', orphanRemoval: true)] - private Collection $inputMaterials; + #[Assert\Count(min: 1, minMessage: 'Veuillez ajouter au moins un matériau consommé')] + #[Assert\Valid] + private Collection $consumedMaterials; /** - * @var Collection The output materials + * @var Collection The produced materials */ #[ORM\OneToMany(targetEntity: OutputRecipeMaterial::class, mappedBy: 'recipe', orphanRemoval: true)] - private Collection $outputMaterials; + #[Assert\Count(min: 1, minMessage: 'Veuillez ajouter au moins un matériau produit')] + #[Assert\Valid] + private Collection $producedMaterials; /** * Initialization */ public function __construct () { - $this->inputMaterials = new ArrayCollection(); - $this->outputMaterials = new ArrayCollection(); + $this->consumedMaterials = new ArrayCollection(); + $this->producedMaterials = new ArrayCollection(); } /** @@ -89,37 +99,37 @@ class Recipe { } /** - * The input materials + * The consumed materials * - * @return Collection The input materials + * @return Collection The consumed materials */ - public function getInputMaterials (): Collection { - return $this->inputMaterials; + public function getConsumedMaterials (): Collection { + return $this->consumedMaterials; } /** - * Add an input material + * Add a consumed material * - * @param InputRecipeMaterial $inputMaterial The input material + * @param InputRecipeMaterial $inputMaterial The consumed material * * @return $this */ - public function addInputMaterial (InputRecipeMaterial $inputMaterial): static { - if (!$this->inputMaterials->contains($inputMaterial)) { - $this->inputMaterials->add($inputMaterial); + public function addConsumedMaterial (InputRecipeMaterial $inputMaterial): static { + if (!$this->consumedMaterials->contains($inputMaterial)) { + $this->consumedMaterials->add($inputMaterial); $inputMaterial->setRecipe($this); } return $this; } /** - * Remove an input material + * Remove a consumed material * - * @param InputRecipeMaterial $inputMaterial The input material + * @param InputRecipeMaterial $inputMaterial The consumed material * * @return $this */ - public function removeInputMaterial (InputRecipeMaterial $inputMaterial): static { - if ($this->inputMaterials->removeElement($inputMaterial)) { + public function removeConsumedMaterial (InputRecipeMaterial $inputMaterial): static { + if ($this->consumedMaterials->removeElement($inputMaterial)) { // set the owning side to null (unless already changed) if ($inputMaterial->getRecipe() === $this) { $inputMaterial->setRecipe(null); @@ -130,40 +140,40 @@ class Recipe { } /** - * The output materials + * The produced materials * - * @return Collection The output materials + * @return Collection The produced materials */ - public function getOutputMaterials(): Collection + public function getProducedMaterials(): Collection { - return $this->outputMaterials; + return $this->producedMaterials; } /** - * Add an output material + * Add a produced material * - * @param OutputRecipeMaterial $outputMaterial The output material + * @param OutputRecipeMaterial $outputMaterial The produced material * * @return $this */ - public function addOutputMaterial(OutputRecipeMaterial $outputMaterial): static + public function addProducedMaterial(OutputRecipeMaterial $outputMaterial): static { - if (!$this->outputMaterials->contains($outputMaterial)) { - $this->outputMaterials->add($outputMaterial); + if (!$this->producedMaterials->contains($outputMaterial)) { + $this->producedMaterials->add($outputMaterial); $outputMaterial->setRecipe($this); } return $this; } /** - * Remove an output material + * Remove a produced material * - * @param OutputRecipeMaterial $outputMaterial The output material + * @param OutputRecipeMaterial $outputMaterial The produced material * * @return $this */ - public function removeOutputMaterial(OutputRecipeMaterial $outputMaterial): static + public function removeProducedMaterial(OutputRecipeMaterial $outputMaterial): static { - if ($this->outputMaterials->removeElement($outputMaterial)) { + if ($this->producedMaterials->removeElement($outputMaterial)) { // set the owning side to null (unless already changed) if ($outputMaterial->getRecipe() === $this) { $outputMaterial->setRecipe(null); diff --git a/src/Entity/TNamedEntity.php b/src/Entity/TNamedEntity.php index d95594b..bf4335f 100644 --- a/src/Entity/TNamedEntity.php +++ b/src/Entity/TNamedEntity.php @@ -3,7 +3,7 @@ namespace App\Entity; use Doctrine\ORM\Mapping as ORM; -use JsonSerializable; +use Symfony\Component\Validator\Constraints as Assert; /** * Implementation for an entity with a name @@ -13,6 +13,7 @@ trait TNamedEntity { * @var string|null The name */ #[ORM\Column(length: 50)] + #[Assert\NotBlank(message: 'Veuillez saisir un email')] private ?string $name = null; /** diff --git a/src/Entity/Task.php b/src/Entity/Task.php new file mode 100644 index 0000000..cb7d864 --- /dev/null +++ b/src/Entity/Task.php @@ -0,0 +1,287 @@ + The child tasks + */ + #[ORM\OneToMany(targetEntity: self::class, mappedBy: 'parentTask', orphanRemoval: true)] + #[Assert\Valid] + private Collection $childTasks; + /** + * @var Task|null The root task + */ + #[ORM\ManyToOne(targetEntity: self::class)] + #[ORM\JoinColumn(onDelete: 'CASCADE')] + #[Assert\Valid] + private ?self $rootTask = null; + + /** + * Initialization + */ + public function __construct () { + $this->childTasks = new ArrayCollection(); + } + + /** + * The user + * + * @return User|null The user + */ + public function getUser (): ?User { + return $this->user; + } + /** + * Set the user + * + * @param User|null $user The user + * + * @return $this + */ + public function setUser (?User $user): static { + $this->user = $user; + + return $this; + } + + /** + * The produced material + * + * @return Material|null The produced material + */ + public function getProducedMaterial (): ?Material { + return $this->producedMaterial; + } + /** + * Set the produced material + * + * @param Material|null $producedMaterial The produced material + * + * @return $this + */ + public function setProducedMaterial (?Material $producedMaterial): static { + $this->producedMaterial = $producedMaterial; + + return $this; + } + + /** + * The recipe + * + * @return Recipe|null The recipe + */ + public function getRecipe (): ?Recipe { + return $this->recipe; + } + /** + * The recipe + * + * @param Recipe|null $recipe The recipe + * + * @return $this + */ + public function setRecipe (?Recipe $recipe): static { + $this->recipe = $recipe; + + return $this; + } + + /** + * The quantity asked / to produce + * + * @return int|null The quantity asked / to produce + */ + public function getQuantityToProduce (): ?int { + return $this->quantityToProduce; + } + /** + * Set the quantity asked / to produce + * + * @param int $quantityToProduce The quantity asked / to produce + * + * @return $this + */ + public function setQuantityToProduce (int $quantityToProduce): static { + $this->quantityToProduce = $quantityToProduce; + + return $this; + } + + /** + * The quantity already provided at the start + * + * @return int|null The quantity already provided at the start + */ + public function getQuantityProvidedAtStart (): ?int { + return $this->quantityProvidedAtStart; + } + /** + * Set the quantity already provided at the start + * + * @param int $quantityProvidedAtStart The quantity already provided at the start + * + * @return $this + */ + public function setQuantityProvidedAtStart (int $quantityProvidedAtStart): static { + $this->quantityProvidedAtStart = $quantityProvidedAtStart; + + return $this; + } + + /** + * The parent task + * + * @return self|null The parent task + */ + public function getParentTask (): ?self { + return $this->parentTask; + } + /** + * Set the parent task + * + * @param Task|null $parentTask The parent task + * + * @return $this + */ + public function setParentTask (?self $parentTask): static { + $this->parentTask = $parentTask; + + return $this; + } + + /** + * The child tasks + * + * @return Collection The child tasks + */ + public function getChildTasks (): Collection { + return $this->childTasks; + } + /** + * Add a child task + * + * @param Task $childTask The child task + * + * @return $this + */ + public function addChildTask (self $childTask): static { + if (!$this->childTasks->contains($childTask)) { + $this->childTasks->add($childTask); + $childTask->setParentTask($this); + } + + return $this; + } + /** + * Remove a child task + * + * @param Task $childTask The child task + * + * @return $this + */ + public function removeChildTask (self $childTask): static { + if ($this->childTasks->removeElement($childTask)) { + // set the owning side to null (unless already changed) + if ($childTask->getParentTask() === $this) { + $childTask->setParentTask(null); + } + } + + return $this; + } + + /** + * The root task + * + * @return self|null The root task + */ + public function getRootTask (): ?self { + return $this->rootTask; + } + /** + * Set the root task + * + * @param Task|null $rootTask The root task + * + * @return $this + */ + public function setRootTask (?self $rootTask): static { + $this->rootTask = $rootTask; + + return $this; + } +} diff --git a/src/Entity/User.php b/src/Entity/User.php index 3f0513f..280135f 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -3,10 +3,6 @@ namespace App\Entity; use App\Repository\UserRepository; -use DateTimeImmutable; -use Doctrine\Common\Collections\ArrayCollection; -use Doctrine\Common\Collections\Collection; -use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Mapping as ORM; use Stringable; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; @@ -39,6 +35,7 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface, Stringa * @var string|null The name */ #[ORM\Column(length: 100, nullable: true)] + #[Assert\NotBlank(message: 'Veuillez saisir un nom', allowNull: true)] private ?string $name = null; /** diff --git a/src/Repository/TaskRepository.php b/src/Repository/TaskRepository.php new file mode 100644 index 0000000..97061ee --- /dev/null +++ b/src/Repository/TaskRepository.php @@ -0,0 +1,43 @@ + + */ +class TaskRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, Task::class); + } + + // /** + // * @return Task[] Returns an array of Task objects + // */ + // public function findByExampleField($value): array + // { + // return $this->createQueryBuilder('t') + // ->andWhere('t.exampleField = :val') + // ->setParameter('val', $value) + // ->orderBy('t.id', 'ASC') + // ->setMaxResults(10) + // ->getQuery() + // ->getResult() + // ; + // } + + // public function findOneBySomeField($value): ?Task + // { + // return $this->createQueryBuilder('t') + // ->andWhere('t.exampleField = :val') + // ->setParameter('val', $value) + // ->getQuery() + // ->getOneOrNullResult() + // ; + // } +}