From cbb7234aa8c997fb1ab6065b742d9e1ab2d1a9c5 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Tue, 4 Mar 2025 13:18:05 +0100 Subject: [PATCH 01/16] Separate project input data to Dto --- .../Project/ProjectApiResource.php | 20 ++---- src/Dto/ProjectEditDto.php | 68 +++++++++++++++++++ src/State/Project/ProjectStateProcessor.php | 5 +- 3 files changed, 78 insertions(+), 15 deletions(-) create mode 100644 src/Dto/ProjectEditDto.php diff --git a/src/ApiResource/Project/ProjectApiResource.php b/src/ApiResource/Project/ProjectApiResource.php index e61d749..6b72e58 100644 --- a/src/ApiResource/Project/ProjectApiResource.php +++ b/src/ApiResource/Project/ProjectApiResource.php @@ -8,16 +8,15 @@ use ApiPlatform\Metadata as API; use App\ApiResource\Accounting\AccountingApiResource; use App\ApiResource\LocalizedApiResourceTrait; use App\ApiResource\User\UserApiResource; +use App\Dto\ProjectEditDto; use App\Entity\Project\Category; use App\Entity\Project\Project; use App\Entity\Project\ProjectStatus; use App\Entity\Project\ProjectVideo; use App\Mapping\Transformer\BudgetMapTransformer; -use App\Mapping\Transformer\ProjectVideoMapTransformer; use App\State\ApiResourceStateProvider; use App\State\Project\ProjectStateProcessor; use AutoMapper\Attribute\MapFrom; -use AutoMapper\Attribute\MapTo; use Symfony\Component\Validator\Constraints as Assert; /** @@ -27,10 +26,13 @@ use Symfony\Component\Validator\Constraints as Assert; shortName: 'Project', stateOptions: new Options(entityClass: Project::class), provider: ApiResourceStateProvider::class, - processor: ProjectStateProcessor::class )] #[API\GetCollection()] -#[API\Post(security: 'is_granted("ROLE_USER")')] +#[API\Post( + security: 'is_granted("ROLE_USER")', + input: ProjectEditDto::class, + processor: ProjectStateProcessor::class +)] #[API\Get()] #[API\Patch(security: 'is_granted("PROJECT_EDIT", previous_object)')] #[API\Delete(security: 'is_granted("PROJECT_EDIT", previous_object)')] @@ -88,20 +90,12 @@ class ProjectApiResource #[Assert\NotBlank()] public string $description; - /** - * A URL to a video showcasing the Project. - */ - #[Assert\Url()] - #[API\ApiProperty(readable: false)] - #[MapTo(target: Project::class, transformer: ProjectVideoMapTransformer::class)] - public string $video; - /** * Extracted embedding data from the Project's video. */ #[API\ApiProperty(writable: false)] #[MapFrom(source: Project::class, property: 'video')] - public ProjectVideo $videoEmbed; + public ProjectVideo $video; /** * The status of a Project represents how far it is in it's life-cycle. diff --git a/src/Dto/ProjectEditDto.php b/src/Dto/ProjectEditDto.php new file mode 100644 index 0000000..2862956 --- /dev/null +++ b/src/Dto/ProjectEditDto.php @@ -0,0 +1,68 @@ +autoMapper->map($project, $data); + return $this->autoMapper->map($project, ProjectApiResource::class); } } From b83cbddee296255b225fd23903f4a739770cef4f Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Wed, 5 Mar 2025 13:36:06 +0100 Subject: [PATCH 02/16] Add project deadline cases --- src/Dto/ProjectDeadline.php | 19 +++++++++++++++++++ src/Dto/ProjectEditDto.php | 7 +++++++ 2 files changed, 26 insertions(+) create mode 100644 src/Dto/ProjectDeadline.php diff --git a/src/Dto/ProjectDeadline.php b/src/Dto/ProjectDeadline.php new file mode 100644 index 0000000..ccc9c05 --- /dev/null +++ b/src/Dto/ProjectDeadline.php @@ -0,0 +1,19 @@ + Date: Wed, 5 Mar 2025 13:36:30 +0100 Subject: [PATCH 03/16] Add ProjectCalendar to handle different deadline cases --- .../Project/ProjectApiResource.php | 7 ++++ src/Entity/Project/Project.php | 15 ++++++++ src/Entity/Project/ProjectCalendar.php | 34 +++++++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 src/Entity/Project/ProjectCalendar.php diff --git a/src/ApiResource/Project/ProjectApiResource.php b/src/ApiResource/Project/ProjectApiResource.php index 6b72e58..49eb669 100644 --- a/src/ApiResource/Project/ProjectApiResource.php +++ b/src/ApiResource/Project/ProjectApiResource.php @@ -11,6 +11,7 @@ use App\ApiResource\User\UserApiResource; use App\Dto\ProjectEditDto; use App\Entity\Project\Category; use App\Entity\Project\Project; +use App\Entity\Project\ProjectCalendar; use App\Entity\Project\ProjectStatus; use App\Entity\Project\ProjectVideo; use App\Mapping\Transformer\BudgetMapTransformer; @@ -69,6 +70,12 @@ class ProjectApiResource #[Assert\NotBlank()] public string $subtitle; + /** + * Deadlines and important Project dates. + */ + #[API\ApiProperty(writable: false)] + public ProjectCalendar $calendar; + /** * One of the available categories. */ diff --git a/src/Entity/Project/Project.php b/src/Entity/Project/Project.php index 0da933b..37a037f 100644 --- a/src/Entity/Project/Project.php +++ b/src/Entity/Project/Project.php @@ -50,6 +50,9 @@ class Project implements UserOwnedInterface, AccountingOwnerInterface, Localized #[Gedmo\Translatable()] private ?string $subtitle = null; + #[ORM\Embedded(class: ProjectTerritory::class)] + private ?ProjectCalendar $calendar = null; + #[ORM\Column(enumType: Category::class)] private ?Category $category = null; @@ -154,6 +157,18 @@ class Project implements UserOwnedInterface, AccountingOwnerInterface, Localized return $this; } + public function getCalendar(): ?ProjectCalendar + { + return $this->calendar; + } + + public function setCalendar(ProjectCalendar $calendar): static + { + $this->calendar = $calendar; + + return $this; + } + public function getCategory(): ?Category { return $this->category; diff --git a/src/Entity/Project/ProjectCalendar.php b/src/Entity/Project/ProjectCalendar.php new file mode 100644 index 0000000..8edad5a --- /dev/null +++ b/src/Entity/Project/ProjectCalendar.php @@ -0,0 +1,34 @@ + Date: Wed, 5 Mar 2025 13:38:35 +0100 Subject: [PATCH 04/16] Remove ProjectApiResource specific attrs from EditDto --- src/Dto/ProjectEditDto.php | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/Dto/ProjectEditDto.php b/src/Dto/ProjectEditDto.php index c817114..14b1627 100644 --- a/src/Dto/ProjectEditDto.php +++ b/src/Dto/ProjectEditDto.php @@ -2,7 +2,6 @@ namespace App\Dto; -use ApiPlatform\Doctrine\Orm\Filter\SearchFilter; use ApiPlatform\Metadata as API; use App\ApiResource\Project\ProjectTerritoryApiResource; use App\Entity\Project\Category; @@ -20,14 +19,12 @@ class ProjectEditDto /** * Main headline for the Project. */ - #[API\ApiFilter(filterClass: SearchFilter::class, strategy: 'partial')] #[Assert\NotBlank()] public string $title; /** * Secondary headline for the Project. */ - #[API\ApiFilter(filterClass: SearchFilter::class, strategy: 'partial')] #[Assert\NotBlank()] public string $subtitle; @@ -35,7 +32,6 @@ class ProjectEditDto * One of the available categories. */ #[Assert\NotBlank()] - #[API\ApiFilter(filterClass: SearchFilter::class, strategy: 'exact')] public Category $category; /** @@ -48,7 +44,6 @@ class ProjectEditDto /** * Free-form rich text description for the Project. */ - #[API\ApiFilter(filterClass: SearchFilter::class, strategy: 'partial')] #[Assert\NotBlank()] public string $description; @@ -69,7 +64,5 @@ class ProjectEditDto /** * The status of a Project represents how far it is in it's life-cycle. */ - #[API\ApiFilter(filterClass: SearchFilter::class, strategy: 'exact')] - #[API\ApiProperty(securityPostDenormalize: 'is_granted("PROJECT_EDIT", previous_object)')] public ProjectStatus $status = ProjectStatus::InEditing; } From 186b4419c533f9dc382f56a1568412f79841d2ab Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Wed, 5 Mar 2025 13:50:37 +0100 Subject: [PATCH 05/16] Move ProjectDeadline enum to project entity namespace --- src/Dto/ProjectEditDto.php | 9 +++++---- src/{Dto => Entity/Project}/ProjectDeadline.php | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) rename src/{Dto => Entity/Project}/ProjectDeadline.php (93%) diff --git a/src/Dto/ProjectEditDto.php b/src/Dto/ProjectEditDto.php index 14b1627..b0f8b0f 100644 --- a/src/Dto/ProjectEditDto.php +++ b/src/Dto/ProjectEditDto.php @@ -6,6 +6,7 @@ use ApiPlatform\Metadata as API; use App\ApiResource\Project\ProjectTerritoryApiResource; use App\Entity\Project\Category; use App\Entity\Project\Project; +use App\Entity\Project\ProjectDeadline; use App\Entity\Project\ProjectStatus; use App\Mapping\Transformer\ProjectVideoMapTransformer; use AutoMapper\Attribute\MapTo; @@ -48,11 +49,11 @@ class ProjectEditDto public string $description; /** - * Every campaign must raise the minimum before the minimum deadline.\ - * Optionally, a Project can choose to remain in campaign for a second deadline - * if it did reach the minimum in the first place. + * On `minimum`, Project will campaign until the minimum deadline.\ + * On `optimum`, Project will campaing until the minimum deadline, + * and then until the optimum deadline if it did raise the minimum. */ - public ProjectDeadline $deadlines = ProjectDeadline::Minimum; + public ProjectDeadline $deadline = ProjectDeadline::Minimum; /** * A URL to a video showcasing the Project. diff --git a/src/Dto/ProjectDeadline.php b/src/Entity/Project/ProjectDeadline.php similarity index 93% rename from src/Dto/ProjectDeadline.php rename to src/Entity/Project/ProjectDeadline.php index ccc9c05..c02a313 100644 --- a/src/Dto/ProjectDeadline.php +++ b/src/Entity/Project/ProjectDeadline.php @@ -1,6 +1,6 @@ Date: Wed, 5 Mar 2025 13:50:52 +0100 Subject: [PATCH 06/16] Store project deadline case --- src/ApiResource/Project/ProjectApiResource.php | 9 +++++++++ src/Entity/Project/Project.php | 15 +++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/ApiResource/Project/ProjectApiResource.php b/src/ApiResource/Project/ProjectApiResource.php index 49eb669..2b29568 100644 --- a/src/ApiResource/Project/ProjectApiResource.php +++ b/src/ApiResource/Project/ProjectApiResource.php @@ -12,6 +12,7 @@ use App\Dto\ProjectEditDto; use App\Entity\Project\Category; use App\Entity\Project\Project; use App\Entity\Project\ProjectCalendar; +use App\Entity\Project\ProjectDeadline; use App\Entity\Project\ProjectStatus; use App\Entity\Project\ProjectVideo; use App\Mapping\Transformer\BudgetMapTransformer; @@ -70,6 +71,14 @@ class ProjectApiResource #[Assert\NotBlank()] public string $subtitle; + /** + * On `minimum`, Project will campaign until the minimum deadline.\ + * On `optimum`, Project will campaing until the minimum deadline, + * and then until the optimum deadline if it did raise the minimum. + */ + #[API\ApiProperty(writable: false)] + public ProjectDeadline $deadline; + /** * Deadlines and important Project dates. */ diff --git a/src/Entity/Project/Project.php b/src/Entity/Project/Project.php index 37a037f..025ae4b 100644 --- a/src/Entity/Project/Project.php +++ b/src/Entity/Project/Project.php @@ -50,6 +50,9 @@ class Project implements UserOwnedInterface, AccountingOwnerInterface, Localized #[Gedmo\Translatable()] private ?string $subtitle = null; + #[ORM\Column(enumType: ProjectDeadline::class)] + private ?ProjectDeadline $deadline = null; + #[ORM\Embedded(class: ProjectTerritory::class)] private ?ProjectCalendar $calendar = null; @@ -157,6 +160,18 @@ class Project implements UserOwnedInterface, AccountingOwnerInterface, Localized return $this; } + public function getDeadline(): ?ProjectDeadline + { + return $this->deadline; + } + + public function setDeadline(ProjectDeadline $deadline): static + { + $this->deadline = $deadline; + + return $this; + } + public function getCalendar(): ?ProjectCalendar { return $this->calendar; From 799b3895ab6d1108098ce3200797facaca47b462 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Wed, 5 Mar 2025 13:55:45 +0100 Subject: [PATCH 07/16] Fix class ref on calendar embedded --- src/Entity/Project/Project.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Entity/Project/Project.php b/src/Entity/Project/Project.php index 025ae4b..6681a70 100644 --- a/src/Entity/Project/Project.php +++ b/src/Entity/Project/Project.php @@ -53,7 +53,7 @@ class Project implements UserOwnedInterface, AccountingOwnerInterface, Localized #[ORM\Column(enumType: ProjectDeadline::class)] private ?ProjectDeadline $deadline = null; - #[ORM\Embedded(class: ProjectTerritory::class)] + #[ORM\Embedded(class: ProjectCalendar::class)] private ?ProjectCalendar $calendar = null; #[ORM\Column(enumType: Category::class)] From e1a0835506f932026bdcb5c637defbaee2228d81 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Wed, 5 Mar 2025 14:54:40 +0100 Subject: [PATCH 08/16] Separate project edit dto for create and update --- src/ApiResource/Project/ProjectApiResource.php | 4 ++-- src/Dto/{ProjectEditDto.php => ProjectCreateDto.php} | 2 +- src/State/Project/ProjectStateProcessor.php | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) rename src/Dto/{ProjectEditDto.php => ProjectCreateDto.php} (98%) diff --git a/src/ApiResource/Project/ProjectApiResource.php b/src/ApiResource/Project/ProjectApiResource.php index 2b29568..c832b78 100644 --- a/src/ApiResource/Project/ProjectApiResource.php +++ b/src/ApiResource/Project/ProjectApiResource.php @@ -8,7 +8,7 @@ use ApiPlatform\Metadata as API; use App\ApiResource\Accounting\AccountingApiResource; use App\ApiResource\LocalizedApiResourceTrait; use App\ApiResource\User\UserApiResource; -use App\Dto\ProjectEditDto; +use App\Dto\ProjectCreateDto; use App\Entity\Project\Category; use App\Entity\Project\Project; use App\Entity\Project\ProjectCalendar; @@ -32,7 +32,7 @@ use Symfony\Component\Validator\Constraints as Assert; #[API\GetCollection()] #[API\Post( security: 'is_granted("ROLE_USER")', - input: ProjectEditDto::class, + input: ProjectCreateDto::class, processor: ProjectStateProcessor::class )] #[API\Get()] diff --git a/src/Dto/ProjectEditDto.php b/src/Dto/ProjectCreateDto.php similarity index 98% rename from src/Dto/ProjectEditDto.php rename to src/Dto/ProjectCreateDto.php index b0f8b0f..aeca3e2 100644 --- a/src/Dto/ProjectEditDto.php +++ b/src/Dto/ProjectCreateDto.php @@ -12,7 +12,7 @@ use App\Mapping\Transformer\ProjectVideoMapTransformer; use AutoMapper\Attribute\MapTo; use Symfony\Component\Validator\Constraints as Assert; -class ProjectEditDto +class ProjectCreateDto { #[API\ApiProperty(identifier: true, writable: false)] public int $id; diff --git a/src/State/Project/ProjectStateProcessor.php b/src/State/Project/ProjectStateProcessor.php index 4855e4d..074962a 100644 --- a/src/State/Project/ProjectStateProcessor.php +++ b/src/State/Project/ProjectStateProcessor.php @@ -5,7 +5,7 @@ namespace App\State\Project; use ApiPlatform\Metadata\Operation; use ApiPlatform\State\ProcessorInterface; use App\ApiResource\Project\ProjectApiResource; -use App\Dto\ProjectEditDto; +use App\Dto\ProjectCreateDto; use App\Entity\Project\Project; use App\Mapping\AutoMapper; use App\Service\Auth\AuthService; @@ -21,7 +21,7 @@ class ProjectStateProcessor implements ProcessorInterface ) {} /** - * @param ProjectEditDto $data + * @param ProjectCreateDto $data * * @return ProjectApiResource|null */ From c8233a7b04141e812b28c9bd97b16823c390d7cb Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Wed, 5 Mar 2025 15:16:15 +0100 Subject: [PATCH 09/16] Separate project create and project update resource handling --- .../Project/ProjectApiResource.php | 11 +++- src/Dto/ProjectCreateDto.php | 10 --- src/Dto/ProjectUpdateDto.php | 64 +++++++++++++++++++ src/State/Project/ProjectStateProcessor.php | 17 +++-- 4 files changed, 85 insertions(+), 17 deletions(-) create mode 100644 src/Dto/ProjectUpdateDto.php diff --git a/src/ApiResource/Project/ProjectApiResource.php b/src/ApiResource/Project/ProjectApiResource.php index c832b78..2ce8ef5 100644 --- a/src/ApiResource/Project/ProjectApiResource.php +++ b/src/ApiResource/Project/ProjectApiResource.php @@ -9,6 +9,7 @@ use App\ApiResource\Accounting\AccountingApiResource; use App\ApiResource\LocalizedApiResourceTrait; use App\ApiResource\User\UserApiResource; use App\Dto\ProjectCreateDto; +use App\Dto\ProjectUpdateDto; use App\Entity\Project\Category; use App\Entity\Project\Project; use App\Entity\Project\ProjectCalendar; @@ -31,12 +32,16 @@ use Symfony\Component\Validator\Constraints as Assert; )] #[API\GetCollection()] #[API\Post( - security: 'is_granted("ROLE_USER")', input: ProjectCreateDto::class, - processor: ProjectStateProcessor::class + processor: ProjectStateProcessor::class, + security: 'is_granted("ROLE_USER")', )] #[API\Get()] -#[API\Patch(security: 'is_granted("PROJECT_EDIT", previous_object)')] +#[API\Patch( + input: ProjectUpdateDto::class, + processor: ProjectStateProcessor::class, + security: 'is_granted("PROJECT_EDIT", previous_object)', +)] #[API\Delete(security: 'is_granted("PROJECT_EDIT", previous_object)')] class ProjectApiResource { diff --git a/src/Dto/ProjectCreateDto.php b/src/Dto/ProjectCreateDto.php index aeca3e2..e2140a0 100644 --- a/src/Dto/ProjectCreateDto.php +++ b/src/Dto/ProjectCreateDto.php @@ -2,21 +2,16 @@ namespace App\Dto; -use ApiPlatform\Metadata as API; use App\ApiResource\Project\ProjectTerritoryApiResource; use App\Entity\Project\Category; use App\Entity\Project\Project; use App\Entity\Project\ProjectDeadline; -use App\Entity\Project\ProjectStatus; use App\Mapping\Transformer\ProjectVideoMapTransformer; use AutoMapper\Attribute\MapTo; use Symfony\Component\Validator\Constraints as Assert; class ProjectCreateDto { - #[API\ApiProperty(identifier: true, writable: false)] - public int $id; - /** * Main headline for the Project. */ @@ -61,9 +56,4 @@ class ProjectCreateDto #[Assert\Url()] #[MapTo(target: Project::class, transformer: ProjectVideoMapTransformer::class)] public string $video; - - /** - * The status of a Project represents how far it is in it's life-cycle. - */ - public ProjectStatus $status = ProjectStatus::InEditing; } diff --git a/src/Dto/ProjectUpdateDto.php b/src/Dto/ProjectUpdateDto.php new file mode 100644 index 0000000..1d7a7f6 --- /dev/null +++ b/src/Dto/ProjectUpdateDto.php @@ -0,0 +1,64 @@ +autoMapper->map($data, Project::class); + if ($data instanceof ProjectCreateDto) { + /** @var Project */ + $project = $this->autoMapper->map($data, Project::class); - if (!$project->getId()) { $owner = $this->authService->getUser(); if (!$owner) { @@ -40,6 +42,13 @@ class ProjectStateProcessor implements ProcessorInterface $project->setOwner($owner); } + if ($data instanceof ProjectUpdateDto) { + $data = $this->autoMapper->map($uriVariables, $data); + + /** @var Project */ + $project = $this->autoMapper->map($data, Project::class); + } + $project = $this->entityStateProcessor->process($project, $operation, $uriVariables, $context); if ($project === null) { From ff6fe1322481920f596e7fc656c67d741ca1d0b6 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Wed, 5 Mar 2025 16:00:40 +0100 Subject: [PATCH 10/16] Project entity default status in_editing --- src/Entity/Project/Project.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Entity/Project/Project.php b/src/Entity/Project/Project.php index 6681a70..201f4ba 100644 --- a/src/Entity/Project/Project.php +++ b/src/Entity/Project/Project.php @@ -96,7 +96,7 @@ class Project implements UserOwnedInterface, AccountingOwnerInterface, Localized * Projects have a start and an end, and in the meantime they go through different phases represented under this status. */ #[ORM\Column(type: 'string', enumType: ProjectStatus::class)] - private ProjectStatus $status; + private ProjectStatus $status = ProjectStatus::InEditing; /** * @var Collection From f05b748c56631dba36f63c0e60c857adc5ab07b2 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Wed, 5 Mar 2025 16:01:01 +0100 Subject: [PATCH 11/16] ProjectCalendar datetimes immutable --- src/Entity/Project/ProjectCalendar.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Entity/Project/ProjectCalendar.php b/src/Entity/Project/ProjectCalendar.php index 8edad5a..0efd88d 100644 --- a/src/Entity/Project/ProjectCalendar.php +++ b/src/Entity/Project/ProjectCalendar.php @@ -11,8 +11,8 @@ class ProjectCalendar /** * The date at which the Project started campaigning. */ - #[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)] - public ?\DateTimeInterface $release = null; + #[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)] + public ?\DateTimeImmutable $release = null; /** * 40 days after the date of release.\ @@ -20,8 +20,8 @@ class ProjectCalendar * The minimum budget must be raised by the end of this date.\ * Failure to do so will move the Project out of status `in_campaign` into status `unfunded`. */ - #[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)] - public ?\DateTimeInterface $minimum = null; + #[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)] + public ?\DateTimeImmutable $minimum = null; /** * 40 days after the minimum deadline. Optional.\ @@ -29,6 +29,6 @@ class ProjectCalendar * If the Project achieved their minimum budget by the minimum deadline, * and this deadline is defined, it may still remain in campaign to raise the optimum. */ - #[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)] - public ?\DateTimeInterface $optimum = null; + #[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)] + public ?\DateTimeImmutable $optimum = null; } From 7c048ca8b0e210d3515d8d3d6d2ae7a64fb18a00 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Wed, 5 Mar 2025 16:06:14 +0100 Subject: [PATCH 12/16] Remove default values from project update dto --- src/Dto/ProjectUpdateDto.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Dto/ProjectUpdateDto.php b/src/Dto/ProjectUpdateDto.php index 1d7a7f6..af09f0a 100644 --- a/src/Dto/ProjectUpdateDto.php +++ b/src/Dto/ProjectUpdateDto.php @@ -48,7 +48,7 @@ class ProjectUpdateDto * On `optimum`, Project will campaing until the minimum deadline, * and then until the optimum deadline if it did raise the minimum. */ - public ProjectDeadline $deadline = ProjectDeadline::Minimum; + public ProjectDeadline $deadline; /** * A URL to a video showcasing the Project. @@ -60,5 +60,5 @@ class ProjectUpdateDto /** * The status of a Project represents how far it is in it's life-cycle. */ - public ProjectStatus $status = ProjectStatus::InEditing; + public ProjectStatus $status; } From ca50bb7edbd5e35e77ce7a5eff18b166b6eca5a1 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Wed, 5 Mar 2025 16:07:05 +0100 Subject: [PATCH 13/16] Update project calendar when it goes in_campaign --- src/EventListener/ProjectsListener.php | 37 +++++++++++++++++++++ src/Service/Project/CalendarService.php | 35 +++++++++++++++++++ src/State/Project/ProjectStateProcessor.php | 2 +- 3 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 src/EventListener/ProjectsListener.php create mode 100644 src/Service/Project/CalendarService.php diff --git a/src/EventListener/ProjectsListener.php b/src/EventListener/ProjectsListener.php new file mode 100644 index 0000000..e44b22c --- /dev/null +++ b/src/EventListener/ProjectsListener.php @@ -0,0 +1,37 @@ +hasChangedField('status')) { + return; + } + + if ($project->getStatus() === ProjectStatus::InCampaign) { + $calendar = $this->calendarService->makeCalendar($project); + + $project->setCalendar($calendar); + } + } +} diff --git a/src/Service/Project/CalendarService.php b/src/Service/Project/CalendarService.php new file mode 100644 index 0000000..b77b655 --- /dev/null +++ b/src/Service/Project/CalendarService.php @@ -0,0 +1,35 @@ +release = $release; + + switch ($project->getDeadline()) { + case ProjectDeadline::Minimum: + $calendar->minimum = $release->add($minimumTtl); + break; + case ProjectDeadline::Optimum: + $calendar->minimum = $release->add($minimumTtl); + $calendar->optimum = $calendar->minimum->add($optimumTtl); + break; + } + + return $calendar; + } +} diff --git a/src/State/Project/ProjectStateProcessor.php b/src/State/Project/ProjectStateProcessor.php index 764e2e2..54a4305 100644 --- a/src/State/Project/ProjectStateProcessor.php +++ b/src/State/Project/ProjectStateProcessor.php @@ -23,7 +23,7 @@ class ProjectStateProcessor implements ProcessorInterface /** * @param ProjectCreateDto|ProjectUpdateDto $data - * @param array{id: int} $uriVariables + * @param array{id: int} $uriVariables * * @return ProjectApiResource|null */ From b2f4207f3194d048e9a8df4f13146e82c19a49da Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Wed, 5 Mar 2025 16:10:37 +0100 Subject: [PATCH 14/16] Fix tests --- tests/Entity/ProjectApiTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/Entity/ProjectApiTest.php b/tests/Entity/ProjectApiTest.php index e92b303..486200a 100644 --- a/tests/Entity/ProjectApiTest.php +++ b/tests/Entity/ProjectApiTest.php @@ -5,6 +5,7 @@ namespace App\Tests\Entity; use ApiPlatform\Symfony\Bundle\Test\ApiTestCase; use App\Entity\Project\Category; use App\Entity\Project\Project; +use App\Entity\Project\ProjectDeadline; use App\Entity\Project\ProjectStatus; use App\Entity\Project\ProjectTerritory; use App\Entity\User\User; @@ -43,6 +44,7 @@ class ProjectApiTest extends ApiTestCase $project = new Project(); $project->setTitle('Test Project'); $project->setSubtitle('Test Project Subtitle'); + $project->setDeadline(ProjectDeadline::Minimum); $project->setCategory(Category::LibreSoftware); $project->setDescription('Test Project Description'); $project->setTerritory(new ProjectTerritory('ES')); From a1d0cfcb9308a5877f407dbcb4e3cc0219e10fd9 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Thu, 6 Mar 2025 10:46:34 +0100 Subject: [PATCH 15/16] Updated CalendarService::makeCalendar fn signature more generic --- src/EventListener/ProjectsListener.php | 2 +- src/Service/Project/CalendarService.php | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/EventListener/ProjectsListener.php b/src/EventListener/ProjectsListener.php index e44b22c..b02dff4 100644 --- a/src/EventListener/ProjectsListener.php +++ b/src/EventListener/ProjectsListener.php @@ -29,7 +29,7 @@ final class ProjectsListener } if ($project->getStatus() === ProjectStatus::InCampaign) { - $calendar = $this->calendarService->makeCalendar($project); + $calendar = $this->calendarService->makeCalendar($project->getDeadline()); $project->setCalendar($calendar); } diff --git a/src/Service/Project/CalendarService.php b/src/Service/Project/CalendarService.php index b77b655..b99063f 100644 --- a/src/Service/Project/CalendarService.php +++ b/src/Service/Project/CalendarService.php @@ -2,30 +2,29 @@ namespace App\Service\Project; -use App\Entity\Project\Project; use App\Entity\Project\ProjectCalendar; use App\Entity\Project\ProjectDeadline; class CalendarService { public function makeCalendar( - Project $project, + ProjectDeadline $deadline, + \DateTimeImmutable $release = new \DateTimeImmutable(), int $minimumTtl = 40, int $optimumTtl = 40, ): ProjectCalendar { - $release = new \DateTimeImmutable(); $minimumTtl = \DateInterval::createFromDateString(\sprintf('%d days', $minimumTtl)); $optimumTtl = \DateInterval::createFromDateString(\sprintf('%d days', $optimumTtl)); $calendar = new ProjectCalendar(); $calendar->release = $release; - switch ($project->getDeadline()) { + switch ($deadline) { case ProjectDeadline::Minimum: - $calendar->minimum = $release->add($minimumTtl); + $calendar->minimum = $calendar->release->add($minimumTtl); break; case ProjectDeadline::Optimum: - $calendar->minimum = $release->add($minimumTtl); + $calendar->minimum = $calendar->release->add($minimumTtl); $calendar->optimum = $calendar->minimum->add($optimumTtl); break; } From 52347ec42776e8faad120988186412d0dc4bfa2c Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Thu, 6 Mar 2025 10:47:32 +0100 Subject: [PATCH 16/16] Pump project deadline case and calendar dates --- src/Benzina/ProjectsPump.php | 43 ++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/Benzina/ProjectsPump.php b/src/Benzina/ProjectsPump.php index 1eb27e9..53fc825 100644 --- a/src/Benzina/ProjectsPump.php +++ b/src/Benzina/ProjectsPump.php @@ -4,6 +4,7 @@ namespace App\Benzina; use App\Entity\Project\Category; use App\Entity\Project\Project; +use App\Entity\Project\ProjectDeadline; use App\Entity\Project\ProjectStatus; use App\Entity\Project\ProjectTerritory; use App\Entity\Project\ProjectVideo; @@ -11,6 +12,7 @@ use App\Entity\Project\Update; use App\Entity\User\User; use App\Repository\User\UserRepository; use App\Service\Embed\EmbedService; +use App\Service\Project\CalendarService; use App\Service\Project\TerritoryService; use Goteo\Benzina\Pump\ArrayPumpTrait; use Goteo\Benzina\Pump\DoctrinePumpTrait; @@ -27,6 +29,7 @@ class ProjectsPump implements PumpInterface private UserRepository $userRepository, private TerritoryService $territoryService, private EmbedService $embedService, + private CalendarService $calendarService, ) {} public function supports(mixed $sample): bool @@ -74,6 +77,16 @@ class ProjectsPump implements PumpInterface $project->addUpdate($update); } + $conf = $this->getProjectConf($project, $context); + + $project->setDeadline($this->getProjectDeadline($conf)); + $project->setCalendar($this->calendarService->makeCalendar( + $project->getDeadline(), + new \DateTimeImmutable($record['published']), + $conf['days_round1'], + $conf['days_round2'], + )); + $this->persist($project, $context); } @@ -235,4 +248,34 @@ class ProjectsPump implements PumpInterface return $query->fetchAll(); } + + private function getProjectConf(Project $project, array $context): array + { + $query = $this->getDbConnection($context)->prepare( + 'SELECT * FROM `project_conf` WHERE `project` = :project' + ); + + $query->execute(['project' => $project->getMigratedId()]); + + $conf = $query->fetch(); + + if (!\is_array($conf)) { + return [ + 'one_round' => 0, + 'days_round1' => 40, + 'days_round2' => 40, + ]; + } + + return $conf; + } + + private function getProjectDeadline(array $conf): ProjectDeadline + { + if ($conf['one_round'] < 1) { + return ProjectDeadline::Optimum; + } + + return ProjectDeadline::Minimum; + } }