Ensure doctrine-level unicity in entities

master
Julien Rosset 2 months ago
parent 625f9fceb8
commit c30a8735b8

@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20250603101350 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->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);
}
}

@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20250606142337 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->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);
}
}

@ -4,6 +4,7 @@ namespace App\Entity;
use App\Repository\InputRecipeMaterialRepository; use App\Repository\InputRecipeMaterialRepository;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Stringable;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Validator\Constraints as Assert; use Symfony\Component\Validator\Constraints as Assert;
@ -11,14 +12,15 @@ use Symfony\Component\Validator\Constraints as Assert;
* A material consumed by a recipe * A material consumed by a recipe
*/ */
#[ORM\Entity(repositoryClass: InputRecipeMaterialRepository::class)] #[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')] #[UniqueEntity(fields: ['recipe', 'material'], message: 'Ce matériau est déjà consommé par cette recette')]
class InputRecipeMaterial { class InputRecipeMaterial implements Stringable {
use TBaseEntity; use TBaseEntity;
/** /**
* @var Recipe|null The recipe * @var Recipe|null The recipe
*/ */
#[ORM\ManyToOne(inversedBy: 'inputMaterials')] #[ORM\ManyToOne(inversedBy: 'consumedMaterials')]
#[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')] #[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
#[Assert\NotNull(message: 'Veuillez sélectionner une recette')] #[Assert\NotNull(message: 'Veuillez sélectionner une recette')]
#[Assert\Valid] #[Assert\Valid]
@ -41,6 +43,13 @@ class InputRecipeMaterial {
#[Assert\Positive(message: 'La quantité consommée doit être strictement positive')] #[Assert\Positive(message: 'La quantité consommée doit être strictement positive')]
private ?int $consumedQuantity = null; private ?int $consumedQuantity = null;
/**
* @inheritDoc
*/
public function __toString (): string {
return $this->getMaterial()->getName() . ' x' . $this->getConsumedQuantity();
}
/** /**
* The recipe * The recipe
* *

@ -33,7 +33,6 @@ class Material implements Stringable {
*/ */
#[ORM\Column] #[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\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; private ?bool $isCraftableByDefault = true;
/** /**

@ -4,6 +4,7 @@ namespace App\Entity;
use App\Repository\OutputRecipeMaterialRepository; use App\Repository\OutputRecipeMaterialRepository;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Stringable;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Validator\Constraints as Assert; use Symfony\Component\Validator\Constraints as Assert;
@ -11,8 +12,9 @@ use Symfony\Component\Validator\Constraints as Assert;
* A material produced by a recipe * A material produced by a recipe
*/ */
#[ORM\Entity(repositoryClass: OutputRecipeMaterialRepository::class)] #[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')] #[UniqueEntity(fields: ['recipe', 'material'], message: 'Ce matériau est déjà produite par cette recette')]
class OutputRecipeMaterial { class OutputRecipeMaterial implements Stringable {
use TBaseEntity; use TBaseEntity;
/** /**
@ -41,6 +43,13 @@ class OutputRecipeMaterial {
#[Assert\Positive(message: 'La quantité produite doit être strictement positive')] #[Assert\Positive(message: 'La quantité produite doit être strictement positive')]
private ?float $producedQuantity = null; private ?float $producedQuantity = null;
/**
* @inheritDoc
*/
public function __toString (): string {
return $this->getMaterial()->getName() . ' x' . $this->getProducedQuantity();
}
/** /**
* The recipe * The recipe
* *

@ -13,9 +13,8 @@ use Symfony\Component\Validator\Constraints as Assert;
* A crafting recipe * A crafting recipe
*/ */
#[ORM\Entity(repositoryClass: RecipeRepository::class)] #[ORM\Entity(repositoryClass: RecipeRepository::class)]
class Recipe implements Stringable { class Recipe {
use TBaseEntity; use TBaseEntity;
use TNamedEntity;
/** /**
* @var Machine|null The crafting machine * @var Machine|null The crafting machine
@ -49,14 +48,14 @@ class Recipe implements Stringable {
/** /**
* @var Collection<int, InputRecipeMaterial> The consumed materials * @var Collection<int, InputRecipeMaterial> 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\Count(min: 1, minMessage: 'Veuillez ajouter au moins un matériau consommé')]
#[Assert\Valid] #[Assert\Valid]
private Collection $consumedMaterials; private Collection $consumedMaterials;
/** /**
* @var Collection<int, OutputRecipeMaterial> The produced materials * @var Collection<int, OutputRecipeMaterial> 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\Count(min: 1, minMessage: 'Veuillez ajouter au moins un matériau produit')]
#[Assert\Valid] #[Assert\Valid]
private Collection $producedMaterials; private Collection $producedMaterials;
@ -109,6 +108,7 @@ class Recipe implements Stringable {
$this->machineExtraInfo1 = $machineExtraInfo1; $this->machineExtraInfo1 = $machineExtraInfo1;
return $this; return $this;
} }
/** /**
* The machine's second extra information * The machine's second extra information
* *

Loading…
Cancel
Save