From c30a8735b801899851c0b020bc0b9d2c425067d0 Mon Sep 17 00:00:00 2001 From: Julien Rosset Date: Fri, 6 Jun 2025 16:28:27 +0200 Subject: [PATCH] Ensure doctrine-level unicity in entities --- migrations/Version20250603101350.php | 41 ++++++++++++++++++++++++++++ migrations/Version20250606142337.php | 41 ++++++++++++++++++++++++++++ src/Entity/InputRecipeMaterial.php | 13 +++++++-- src/Entity/Material.php | 1 - src/Entity/OutputRecipeMaterial.php | 11 +++++++- src/Entity/Recipe.php | 8 +++--- 6 files changed, 107 insertions(+), 8 deletions(-) create mode 100644 migrations/Version20250603101350.php create mode 100644 migrations/Version20250606142337.php diff --git a/migrations/Version20250603101350.php b/migrations/Version20250603101350.php new file mode 100644 index 0000000..a20e5b8 --- /dev/null +++ b/migrations/Version20250603101350.php @@ -0,0 +1,41 @@ +addSql(<<<'SQL' + DROP INDEX UNIQ_DA88B1375E237E06 ON recipe + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE recipe DROP name + SQL); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql(<<<'SQL' + ALTER TABLE recipe ADD name VARCHAR(50) NOT NULL + SQL); + $this->addSql(<<<'SQL' + CREATE UNIQUE INDEX UNIQ_DA88B1375E237E06 ON recipe (name) + SQL); + } +} diff --git a/migrations/Version20250606142337.php b/migrations/Version20250606142337.php new file mode 100644 index 0000000..719852c --- /dev/null +++ b/migrations/Version20250606142337.php @@ -0,0 +1,41 @@ +addSql(<<<'SQL' + CREATE UNIQUE INDEX UNIQ_77575DA759D8A214E308AC6F ON input_recipe_material (recipe_id, material_id) + SQL); + $this->addSql(<<<'SQL' + CREATE UNIQUE INDEX UNIQ_CB0D94B259D8A214E308AC6F ON output_recipe_material (recipe_id, material_id) + SQL); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql(<<<'SQL' + DROP INDEX UNIQ_77575DA759D8A214E308AC6F ON input_recipe_material + SQL); + $this->addSql(<<<'SQL' + DROP INDEX UNIQ_CB0D94B259D8A214E308AC6F ON output_recipe_material + SQL); + } +} diff --git a/src/Entity/InputRecipeMaterial.php b/src/Entity/InputRecipeMaterial.php index 0d5aa76..bfeebec 100644 --- a/src/Entity/InputRecipeMaterial.php +++ b/src/Entity/InputRecipeMaterial.php @@ -4,6 +4,7 @@ namespace App\Entity; use App\Repository\InputRecipeMaterialRepository; use Doctrine\ORM\Mapping as ORM; +use Stringable; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Component\Validator\Constraints as Assert; @@ -11,14 +12,15 @@ use Symfony\Component\Validator\Constraints as Assert; * A material consumed by a recipe */ #[ORM\Entity(repositoryClass: InputRecipeMaterialRepository::class)] +#[ORM\UniqueConstraint(fields: ['recipe', 'material'])] #[UniqueEntity(fields: ['recipe', 'material'], message: 'Ce matériau est déjà consommé par cette recette')] -class InputRecipeMaterial { +class InputRecipeMaterial implements Stringable { use TBaseEntity; /** * @var Recipe|null The recipe */ - #[ORM\ManyToOne(inversedBy: 'inputMaterials')] + #[ORM\ManyToOne(inversedBy: 'consumedMaterials')] #[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')] #[Assert\NotNull(message: 'Veuillez sélectionner une recette')] #[Assert\Valid] @@ -41,6 +43,13 @@ class InputRecipeMaterial { #[Assert\Positive(message: 'La quantité consommée doit être strictement positive')] private ?int $consumedQuantity = null; + /** + * @inheritDoc + */ + public function __toString (): string { + return $this->getMaterial()->getName() . ' x' . $this->getConsumedQuantity(); + } + /** * The recipe * diff --git a/src/Entity/Material.php b/src/Entity/Material.php index 05fc915..3ed8a6d 100644 --- a/src/Entity/Material.php +++ b/src/Entity/Material.php @@ -33,7 +33,6 @@ class Material implements Stringable { */ #[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 = true; /** diff --git a/src/Entity/OutputRecipeMaterial.php b/src/Entity/OutputRecipeMaterial.php index a7c1815..a8026ee 100644 --- a/src/Entity/OutputRecipeMaterial.php +++ b/src/Entity/OutputRecipeMaterial.php @@ -4,6 +4,7 @@ namespace App\Entity; use App\Repository\OutputRecipeMaterialRepository; use Doctrine\ORM\Mapping as ORM; +use Stringable; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Component\Validator\Constraints as Assert; @@ -11,8 +12,9 @@ use Symfony\Component\Validator\Constraints as Assert; * A material produced by a recipe */ #[ORM\Entity(repositoryClass: OutputRecipeMaterialRepository::class)] +#[ORM\UniqueConstraint(fields: ['recipe', 'material'])] #[UniqueEntity(fields: ['recipe', 'material'], message: 'Ce matériau est déjà produite par cette recette')] -class OutputRecipeMaterial { +class OutputRecipeMaterial implements Stringable { use TBaseEntity; /** @@ -41,6 +43,13 @@ class OutputRecipeMaterial { #[Assert\Positive(message: 'La quantité produite doit être strictement positive')] private ?float $producedQuantity = null; + /** + * @inheritDoc + */ + public function __toString (): string { + return $this->getMaterial()->getName() . ' x' . $this->getProducedQuantity(); + } + /** * The recipe * diff --git a/src/Entity/Recipe.php b/src/Entity/Recipe.php index 9ec76a0..0e6d85f 100644 --- a/src/Entity/Recipe.php +++ b/src/Entity/Recipe.php @@ -13,9 +13,8 @@ use Symfony\Component\Validator\Constraints as Assert; * A crafting recipe */ #[ORM\Entity(repositoryClass: RecipeRepository::class)] -class Recipe implements Stringable { +class Recipe { use TBaseEntity; - use TNamedEntity; /** * @var Machine|null The crafting machine @@ -49,14 +48,14 @@ class Recipe implements Stringable { /** * @var Collection The consumed materials */ - #[ORM\OneToMany(targetEntity: InputRecipeMaterial::class, mappedBy: 'recipe', orphanRemoval: true)] + #[ORM\OneToMany(targetEntity: InputRecipeMaterial::class, mappedBy: 'recipe', cascade: ['persist'], orphanRemoval: true)] #[Assert\Count(min: 1, minMessage: 'Veuillez ajouter au moins un matériau consommé')] #[Assert\Valid] private Collection $consumedMaterials; /** * @var Collection The produced materials */ - #[ORM\OneToMany(targetEntity: OutputRecipeMaterial::class, mappedBy: 'recipe', orphanRemoval: true)] + #[ORM\OneToMany(targetEntity: OutputRecipeMaterial::class, mappedBy: 'recipe', cascade: ['persist'], orphanRemoval: true)] #[Assert\Count(min: 1, minMessage: 'Veuillez ajouter au moins un matériau produit')] #[Assert\Valid] private Collection $producedMaterials; @@ -109,6 +108,7 @@ class Recipe implements Stringable { $this->machineExtraInfo1 = $machineExtraInfo1; return $this; } + /** * The machine's second extra information *