diff --git a/migrations/Version20260309221052.php b/migrations/Version20260309221052.php new file mode 100644 index 0000000..bf2781a --- /dev/null +++ b/migrations/Version20260309221052.php @@ -0,0 +1,70 @@ +addSql('CREATE TABLE task (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, title VARCHAR(255) NOT NULL, description TEXT DEFAULT NULL, status_id INT DEFAULT NULL, effort_id INT DEFAULT NULL, priority_id INT DEFAULT NULL, assignee_id INT DEFAULT NULL, group_id INT DEFAULT NULL, project_id INT NOT NULL, PRIMARY KEY (id))'); + $this->addSql('CREATE INDEX IDX_527EDB256BF700BD ON task (status_id)'); + $this->addSql('CREATE INDEX IDX_527EDB259F2256F ON task (effort_id)'); + $this->addSql('CREATE INDEX IDX_527EDB25497B19F9 ON task (priority_id)'); + $this->addSql('CREATE INDEX IDX_527EDB2559EC7D60 ON task (assignee_id)'); + $this->addSql('CREATE INDEX IDX_527EDB25FE54D947 ON task (group_id)'); + $this->addSql('CREATE INDEX IDX_527EDB25166D1F9C ON task (project_id)'); + $this->addSql('CREATE TABLE task_task_type (task_id INT NOT NULL, task_type_id INT NOT NULL, PRIMARY KEY (task_id, task_type_id))'); + $this->addSql('CREATE INDEX IDX_80470E038DB60186 ON task_task_type (task_id)'); + $this->addSql('CREATE INDEX IDX_80470E03DAADA679 ON task_task_type (task_type_id)'); + $this->addSql('CREATE TABLE task_effort (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, label VARCHAR(50) NOT NULL, PRIMARY KEY (id))'); + $this->addSql('CREATE TABLE task_group (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, title VARCHAR(255) NOT NULL, description TEXT DEFAULT NULL, color VARCHAR(7) NOT NULL, project_id INT NOT NULL, PRIMARY KEY (id))'); + $this->addSql('CREATE INDEX IDX_AA645FE5166D1F9C ON task_group (project_id)'); + $this->addSql('CREATE TABLE task_priority (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, label VARCHAR(255) NOT NULL, color VARCHAR(7) NOT NULL, PRIMARY KEY (id))'); + $this->addSql('CREATE TABLE task_status (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, label VARCHAR(255) NOT NULL, color VARCHAR(7) NOT NULL, position INT NOT NULL, PRIMARY KEY (id))'); + $this->addSql('CREATE TABLE task_type (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, label VARCHAR(255) NOT NULL, color VARCHAR(7) NOT NULL, PRIMARY KEY (id))'); + $this->addSql('ALTER TABLE task ADD CONSTRAINT FK_527EDB256BF700BD FOREIGN KEY (status_id) REFERENCES task_status (id) ON DELETE SET NULL NOT DEFERRABLE'); + $this->addSql('ALTER TABLE task ADD CONSTRAINT FK_527EDB259F2256F FOREIGN KEY (effort_id) REFERENCES task_effort (id) ON DELETE SET NULL NOT DEFERRABLE'); + $this->addSql('ALTER TABLE task ADD CONSTRAINT FK_527EDB25497B19F9 FOREIGN KEY (priority_id) REFERENCES task_priority (id) ON DELETE SET NULL NOT DEFERRABLE'); + $this->addSql('ALTER TABLE task ADD CONSTRAINT FK_527EDB2559EC7D60 FOREIGN KEY (assignee_id) REFERENCES "user" (id) ON DELETE SET NULL NOT DEFERRABLE'); + $this->addSql('ALTER TABLE task ADD CONSTRAINT FK_527EDB25FE54D947 FOREIGN KEY (group_id) REFERENCES task_group (id) ON DELETE SET NULL NOT DEFERRABLE'); + $this->addSql('ALTER TABLE task ADD CONSTRAINT FK_527EDB25166D1F9C FOREIGN KEY (project_id) REFERENCES project (id) ON DELETE CASCADE NOT DEFERRABLE'); + $this->addSql('ALTER TABLE task_task_type ADD CONSTRAINT FK_80470E038DB60186 FOREIGN KEY (task_id) REFERENCES task (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE task_task_type ADD CONSTRAINT FK_80470E03DAADA679 FOREIGN KEY (task_type_id) REFERENCES task_type (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE task_group ADD CONSTRAINT FK_AA645FE5166D1F9C FOREIGN KEY (project_id) REFERENCES project (id) ON DELETE CASCADE NOT DEFERRABLE'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE task DROP CONSTRAINT FK_527EDB256BF700BD'); + $this->addSql('ALTER TABLE task DROP CONSTRAINT FK_527EDB259F2256F'); + $this->addSql('ALTER TABLE task DROP CONSTRAINT FK_527EDB25497B19F9'); + $this->addSql('ALTER TABLE task DROP CONSTRAINT FK_527EDB2559EC7D60'); + $this->addSql('ALTER TABLE task DROP CONSTRAINT FK_527EDB25FE54D947'); + $this->addSql('ALTER TABLE task DROP CONSTRAINT FK_527EDB25166D1F9C'); + $this->addSql('ALTER TABLE task_task_type DROP CONSTRAINT FK_80470E038DB60186'); + $this->addSql('ALTER TABLE task_task_type DROP CONSTRAINT FK_80470E03DAADA679'); + $this->addSql('ALTER TABLE task_group DROP CONSTRAINT FK_AA645FE5166D1F9C'); + $this->addSql('DROP TABLE task'); + $this->addSql('DROP TABLE task_task_type'); + $this->addSql('DROP TABLE task_effort'); + $this->addSql('DROP TABLE task_group'); + $this->addSql('DROP TABLE task_priority'); + $this->addSql('DROP TABLE task_status'); + $this->addSql('DROP TABLE task_type'); + } +} diff --git a/src/Entity/Task.php b/src/Entity/Task.php new file mode 100644 index 0000000..90045c4 --- /dev/null +++ b/src/Entity/Task.php @@ -0,0 +1,214 @@ + ['task:read']], + denormalizationContext: ['groups' => ['task:write']], + order: ['id' => 'DESC'], +)] +#[ApiFilter(SearchFilter::class, properties: ['project' => 'exact', 'group' => 'exact'])] +#[ORM\Entity(repositoryClass: TaskRepository::class)] +class Task +{ + #[ORM\Id] + #[ORM\GeneratedValue] + #[ORM\Column] + #[Groups(['task:read'])] + private ?int $id = null; + + #[ORM\Column(length: 255)] + #[Groups(['task:read', 'task:write'])] + private ?string $title = null; + + #[ORM\Column(type: 'text', nullable: true)] + #[Groups(['task:read', 'task:write'])] + private ?string $description = null; + + #[ORM\ManyToOne(targetEntity: TaskStatus::class)] + #[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')] + #[Groups(['task:read', 'task:write'])] + private ?TaskStatus $status = null; + + #[ORM\ManyToOne(targetEntity: TaskEffort::class)] + #[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')] + #[Groups(['task:read', 'task:write'])] + private ?TaskEffort $effort = null; + + #[ORM\ManyToOne(targetEntity: TaskPriority::class)] + #[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')] + #[Groups(['task:read', 'task:write'])] + private ?TaskPriority $priority = null; + + #[ORM\ManyToOne(targetEntity: User::class)] + #[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')] + #[Groups(['task:read', 'task:write'])] + private ?User $assignee = null; + + #[ORM\ManyToOne(targetEntity: TaskGroup::class)] + #[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')] + #[Groups(['task:read', 'task:write'])] + private ?TaskGroup $group = null; + + #[ORM\ManyToOne(targetEntity: Project::class)] + #[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')] + #[Groups(['task:read', 'task:write'])] + private ?Project $project = null; + + /** @var Collection */ + #[ORM\ManyToMany(targetEntity: TaskType::class)] + #[ORM\JoinTable(name: 'task_task_type')] + #[Groups(['task:read', 'task:write'])] + private Collection $types; + + public function __construct() + { + $this->types = new ArrayCollection(); + } + + 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 getDescription(): ?string + { + return $this->description; + } + + public function setDescription(?string $description): static + { + $this->description = $description; + + return $this; + } + + public function getStatus(): ?TaskStatus + { + return $this->status; + } + + public function setStatus(?TaskStatus $status): static + { + $this->status = $status; + + return $this; + } + + public function getEffort(): ?TaskEffort + { + return $this->effort; + } + + public function setEffort(?TaskEffort $effort): static + { + $this->effort = $effort; + + return $this; + } + + public function getPriority(): ?TaskPriority + { + return $this->priority; + } + + public function setPriority(?TaskPriority $priority): static + { + $this->priority = $priority; + + return $this; + } + + public function getAssignee(): ?User + { + return $this->assignee; + } + + public function setAssignee(?User $assignee): static + { + $this->assignee = $assignee; + + return $this; + } + + public function getGroup(): ?TaskGroup + { + return $this->group; + } + + public function setGroup(?TaskGroup $group): static + { + $this->group = $group; + + return $this; + } + + public function getProject(): ?Project + { + return $this->project; + } + + public function setProject(?Project $project): static + { + $this->project = $project; + + return $this; + } + + /** @return Collection */ + public function getTypes(): Collection + { + return $this->types; + } + + public function addType(TaskType $type): static + { + if (!$this->types->contains($type)) { + $this->types->add($type); + } + + return $this; + } + + public function removeType(TaskType $type): static + { + $this->types->removeElement($type); + + return $this; + } +} diff --git a/src/Entity/TaskEffort.php b/src/Entity/TaskEffort.php new file mode 100644 index 0000000..a9f76da --- /dev/null +++ b/src/Entity/TaskEffort.php @@ -0,0 +1,58 @@ + ['task_effort:read']], + denormalizationContext: ['groups' => ['task_effort:write']], + order: ['label' => 'ASC'], +)] +#[ORM\Entity(repositoryClass: TaskEffortRepository::class)] +class TaskEffort +{ + #[ORM\Id] + #[ORM\GeneratedValue] + #[ORM\Column] + #[Groups(['task_effort:read', 'task:read'])] + private ?int $id = null; + + #[ORM\Column(length: 50)] + #[Groups(['task_effort:read', 'task_effort:write', 'task:read'])] + private ?string $label = null; + + public function getId(): ?int + { + return $this->id; + } + + public function getLabel(): ?string + { + return $this->label; + } + + public function setLabel(string $label): static + { + $this->label = $label; + + return $this; + } +} diff --git a/src/Entity/TaskGroup.php b/src/Entity/TaskGroup.php new file mode 100644 index 0000000..704fc19 --- /dev/null +++ b/src/Entity/TaskGroup.php @@ -0,0 +1,110 @@ + ['task_group:read']], + denormalizationContext: ['groups' => ['task_group:write']], + order: ['title' => 'ASC'], +)] +#[ApiFilter(SearchFilter::class, properties: ['project' => 'exact'])] +#[ORM\Entity(repositoryClass: TaskGroupRepository::class)] +class TaskGroup +{ + #[ORM\Id] + #[ORM\GeneratedValue] + #[ORM\Column] + #[Groups(['task_group:read', 'task:read'])] + private ?int $id = null; + + #[ORM\Column(length: 255)] + #[Groups(['task_group:read', 'task_group:write', 'task:read'])] + private ?string $title = null; + + #[ORM\Column(type: 'text', nullable: true)] + #[Groups(['task_group:read', 'task_group:write'])] + private ?string $description = null; + + #[ORM\Column(length: 7)] + #[Groups(['task_group:read', 'task_group:write', 'task:read'])] + private ?string $color = '#222783'; + + #[ORM\ManyToOne(targetEntity: Project::class)] + #[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')] + #[Groups(['task_group:read', 'task_group:write'])] + private ?Project $project = null; + + 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 getDescription(): ?string + { + return $this->description; + } + + public function setDescription(?string $description): static + { + $this->description = $description; + + return $this; + } + + public function getColor(): ?string + { + return $this->color; + } + + public function setColor(string $color): static + { + $this->color = $color; + + return $this; + } + + public function getProject(): ?Project + { + return $this->project; + } + + public function setProject(?Project $project): static + { + $this->project = $project; + + return $this; + } +} diff --git a/src/Entity/TaskPriority.php b/src/Entity/TaskPriority.php new file mode 100644 index 0000000..c6aa263 --- /dev/null +++ b/src/Entity/TaskPriority.php @@ -0,0 +1,74 @@ + ['task_priority:read']], + denormalizationContext: ['groups' => ['task_priority:write']], + order: ['label' => 'ASC'], +)] +#[ORM\Entity(repositoryClass: TaskPriorityRepository::class)] +class TaskPriority +{ + #[ORM\Id] + #[ORM\GeneratedValue] + #[ORM\Column] + #[Groups(['task_priority:read', 'task:read'])] + private ?int $id = null; + + #[ORM\Column(length: 255)] + #[Groups(['task_priority:read', 'task_priority:write', 'task:read'])] + private ?string $label = null; + + #[ORM\Column(length: 7)] + #[Groups(['task_priority:read', 'task_priority:write', 'task:read'])] + private ?string $color = '#222783'; + + public function getId(): ?int + { + return $this->id; + } + + public function getLabel(): ?string + { + return $this->label; + } + + public function setLabel(string $label): static + { + $this->label = $label; + + return $this; + } + + public function getColor(): ?string + { + return $this->color; + } + + public function setColor(string $color): static + { + $this->color = $color; + + return $this; + } +} diff --git a/src/Entity/TaskStatus.php b/src/Entity/TaskStatus.php new file mode 100644 index 0000000..45a7eaf --- /dev/null +++ b/src/Entity/TaskStatus.php @@ -0,0 +1,90 @@ + ['task_status:read']], + denormalizationContext: ['groups' => ['task_status:write']], + order: ['position' => 'ASC'], +)] +#[ORM\Entity(repositoryClass: TaskStatusRepository::class)] +class TaskStatus +{ + #[ORM\Id] + #[ORM\GeneratedValue] + #[ORM\Column] + #[Groups(['task_status:read', 'task:read'])] + private ?int $id = null; + + #[ORM\Column(length: 255)] + #[Groups(['task_status:read', 'task_status:write', 'task:read'])] + private ?string $label = null; + + #[ORM\Column(length: 7)] + #[Groups(['task_status:read', 'task_status:write', 'task:read'])] + private ?string $color = '#222783'; + + #[ORM\Column] + #[Groups(['task_status:read', 'task_status:write', 'task:read'])] + private ?int $position = 0; + + public function getId(): ?int + { + return $this->id; + } + + public function getLabel(): ?string + { + return $this->label; + } + + public function setLabel(string $label): static + { + $this->label = $label; + + return $this; + } + + public function getColor(): ?string + { + return $this->color; + } + + public function setColor(string $color): static + { + $this->color = $color; + + return $this; + } + + public function getPosition(): ?int + { + return $this->position; + } + + public function setPosition(int $position): static + { + $this->position = $position; + + return $this; + } +} diff --git a/src/Entity/TaskType.php b/src/Entity/TaskType.php new file mode 100644 index 0000000..6e87866 --- /dev/null +++ b/src/Entity/TaskType.php @@ -0,0 +1,74 @@ + ['task_type:read']], + denormalizationContext: ['groups' => ['task_type:write']], + order: ['label' => 'ASC'], +)] +#[ORM\Entity(repositoryClass: TaskTypeRepository::class)] +class TaskType +{ + #[ORM\Id] + #[ORM\GeneratedValue] + #[ORM\Column] + #[Groups(['task_type:read', 'task:read'])] + private ?int $id = null; + + #[ORM\Column(length: 255)] + #[Groups(['task_type:read', 'task_type:write', 'task:read'])] + private ?string $label = null; + + #[ORM\Column(length: 7)] + #[Groups(['task_type:read', 'task_type:write', 'task:read'])] + private ?string $color = '#222783'; + + public function getId(): ?int + { + return $this->id; + } + + public function getLabel(): ?string + { + return $this->label; + } + + public function setLabel(string $label): static + { + $this->label = $label; + + return $this; + } + + public function getColor(): ?string + { + return $this->color; + } + + public function setColor(string $color): static + { + $this->color = $color; + + return $this; + } +} diff --git a/src/Repository/TaskEffortRepository.php b/src/Repository/TaskEffortRepository.php new file mode 100644 index 0000000..22c2c90 --- /dev/null +++ b/src/Repository/TaskEffortRepository.php @@ -0,0 +1,17 @@ +