From 7e9e4e3b071e728b5a800bb033614c616b3ccdd8 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Wed, 26 Feb 2025 12:46:22 +0100 Subject: [PATCH 1/5] Add ProjectUpdate api resource --- src/ApiResource/Project/UpdateApiResource.php | 71 +++++++++++++++++++ src/OpenApi/RedocExtensionsTrait.php | 27 +++++-- 2 files changed, 94 insertions(+), 4 deletions(-) create mode 100644 src/ApiResource/Project/UpdateApiResource.php diff --git a/src/ApiResource/Project/UpdateApiResource.php b/src/ApiResource/Project/UpdateApiResource.php new file mode 100644 index 0000000..7616efd --- /dev/null +++ b/src/ApiResource/Project/UpdateApiResource.php @@ -0,0 +1,71 @@ + 'Users', - 'tags' => ['User', 'UserToken', 'Person', 'Organization'], + 'tags' => [ + 'User', + 'UserToken', + 'Person', + 'Organization', + ], ], [ 'name' => 'Projects', - 'tags' => ['Project', 'ProjectReward', 'ProjectRewardClaim', 'ProjectBudgetItem'], + 'tags' => [ + 'Project', + 'ProjectReward', + 'ProjectRewardClaim', + 'ProjectBudgetItem', + 'ProjectUpdate', + ], ], [ 'name' => 'Gateways', - 'tags' => ['Gateway', 'GatewayCharge', 'GatewayCheckout'], + 'tags' => [ + 'Gateway', + 'GatewayCharge', + 'GatewayCheckout', + ], ], [ 'name' => 'Accounting', - 'tags' => ['Accounting', 'AccountingBalancePoint', 'AccountingTransaction'], + 'tags' => [ + 'Accounting', + 'AccountingBalancePoint', + 'AccountingTransaction', + ], ], ]; From f49de26dbeb98812f513080b2726f30c52626ea9 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Wed, 26 Feb 2025 12:47:07 +0100 Subject: [PATCH 2/5] Added project related Update entity --- src/Entity/Project/Project.php | 37 ++++++ src/Entity/Project/Update.php | 128 ++++++++++++++++++++ src/Repository/Project/UpdateRepository.php | 43 +++++++ 3 files changed, 208 insertions(+) create mode 100644 src/Entity/Project/Update.php create mode 100644 src/Repository/Project/UpdateRepository.php diff --git a/src/Entity/Project/Project.php b/src/Entity/Project/Project.php index 0e949ac..fd9ec89 100644 --- a/src/Entity/Project/Project.php +++ b/src/Entity/Project/Project.php @@ -101,11 +101,18 @@ class Project implements UserOwnedInterface, AccountingOwnerInterface, Localized #[ORM\OneToMany(targetEntity: BudgetItem::class, mappedBy: 'project')] private Collection $budgetItems; + /** + * @var Collection + */ + #[ORM\OneToMany(targetEntity: Update::class, mappedBy: 'project')] + private Collection $updates; + public function __construct() { $this->accounting = Accounting::of($this); $this->rewards = new ArrayCollection(); $this->budgetItems = new ArrayCollection(); + $this->updates = new ArrayCollection(); } public function getId(): ?int @@ -263,4 +270,34 @@ class Project implements UserOwnedInterface, AccountingOwnerInterface, Localized return $this; } + + /** + * @return Collection + */ + public function getUpdates(): Collection + { + return $this->updates; + } + + public function addUpdate(Update $update): static + { + if (!$this->updates->contains($update)) { + $this->updates->add($update); + $update->setProject($this); + } + + return $this; + } + + public function removeUpdate(Update $update): static + { + if ($this->updates->removeElement($update)) { + // set the owning side to null (unless already changed) + if ($update->getProject() === $this) { + $update->setProject(null); + } + } + + return $this; + } } diff --git a/src/Entity/Project/Update.php b/src/Entity/Project/Update.php new file mode 100644 index 0000000..a493f86 --- /dev/null +++ b/src/Entity/Project/Update.php @@ -0,0 +1,128 @@ +date = $this->date ?? new \DateTime(); + } + + public function getId(): ?int + { + return $this->id; + } + + public function getProject(): ?Project + { + return $this->project; + } + + public function setProject(?Project $project): static + { + $this->project = $project; + + return $this; + } + + public function getTitle(): ?string + { + return $this->title; + } + + public function setTitle(string $title): static + { + $this->title = $title; + + return $this; + } + + public function getSubtitle(): ?string + { + return $this->subtitle; + } + + public function setSubtitle(string $subtitle): static + { + $this->subtitle = $subtitle; + + return $this; + } + + public function getBody(): ?string + { + return $this->body; + } + + public function setBody(string $body): static + { + $this->body = $body; + + return $this; + } + + public function getDate(): ?\DateTimeInterface + { + return $this->date; + } + + public function setDate(\DateTimeInterface $date): static + { + $this->date = $date; + + return $this; + } + + public function getCover(): ?string + { + return $this->cover; + } + + public function setCover(?string $cover): static + { + $this->cover = $cover; + + return $this; + } +} diff --git a/src/Repository/Project/UpdateRepository.php b/src/Repository/Project/UpdateRepository.php new file mode 100644 index 0000000..b796176 --- /dev/null +++ b/src/Repository/Project/UpdateRepository.php @@ -0,0 +1,43 @@ + + */ +class UpdateRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, Update::class); + } + + // /** + // * @return Update[] Returns an array of Update objects + // */ + // public function findByExampleField($value): array + // { + // return $this->createQueryBuilder('u') + // ->andWhere('u.exampleField = :val') + // ->setParameter('val', $value) + // ->orderBy('u.id', 'ASC') + // ->setMaxResults(10) + // ->getQuery() + // ->getResult() + // ; + // } + + // public function findOneBySomeField($value): ?Update + // { + // return $this->createQueryBuilder('u') + // ->andWhere('u.exampleField = :val') + // ->setParameter('val', $value) + // ->getQuery() + // ->getOneOrNullResult() + // ; + // } +} From 17eeaa3f9bc0e19f035ab479a9ee75a10606debb Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Wed, 26 Feb 2025 12:48:15 +0100 Subject: [PATCH 3/5] Update api resource locales to use trait --- src/ApiResource/LocalizedApiResourceTrait.php | 16 ++++++++++++++++ .../Project/BudgetItemApiResource.php | 11 +++-------- src/ApiResource/Project/ProjectApiResource.php | 11 +++-------- 3 files changed, 22 insertions(+), 16 deletions(-) create mode 100644 src/ApiResource/LocalizedApiResourceTrait.php diff --git a/src/ApiResource/LocalizedApiResourceTrait.php b/src/ApiResource/LocalizedApiResourceTrait.php new file mode 100644 index 0000000..0c20d6e --- /dev/null +++ b/src/ApiResource/LocalizedApiResourceTrait.php @@ -0,0 +1,16 @@ + + */ + #[API\ApiProperty(writable: false)] + public array $locales; +} diff --git a/src/ApiResource/Project/BudgetItemApiResource.php b/src/ApiResource/Project/BudgetItemApiResource.php index bfa343c..4f7805f 100644 --- a/src/ApiResource/Project/BudgetItemApiResource.php +++ b/src/ApiResource/Project/BudgetItemApiResource.php @@ -5,6 +5,7 @@ namespace App\ApiResource\Project; use ApiPlatform\Doctrine\Orm\Filter\SearchFilter; use ApiPlatform\Doctrine\Orm\State\Options; use ApiPlatform\Metadata as API; +use App\ApiResource\LocalizedApiResourceTrait; use App\ApiResource\Money; use App\Entity\Project\BudgetItem; use App\Entity\Project\BudgetItemType; @@ -25,6 +26,8 @@ use Symfony\Component\Validator\Constraints as Assert; )] class BudgetItemApiResource { + use LocalizedApiResourceTrait; + #[API\ApiProperty(identifier: true, writable: false)] public int $id; @@ -37,14 +40,6 @@ class BudgetItemApiResource #[Assert\NotBlank()] public BudgetItemType $type; - /** - * List of the available content locales. - * - * @var array - */ - #[API\ApiProperty(writable: false)] - public array $locales; - /** * A short, descriptive string for the item. */ diff --git a/src/ApiResource/Project/ProjectApiResource.php b/src/ApiResource/Project/ProjectApiResource.php index 4b7e6b6..90f715d 100644 --- a/src/ApiResource/Project/ProjectApiResource.php +++ b/src/ApiResource/Project/ProjectApiResource.php @@ -6,6 +6,7 @@ use ApiPlatform\Doctrine\Orm\Filter\SearchFilter; use ApiPlatform\Doctrine\Orm\State\Options; use ApiPlatform\Metadata as API; use App\ApiResource\Accounting\AccountingApiResource; +use App\ApiResource\LocalizedApiResourceTrait; use App\ApiResource\User\UserApiResource; use App\Entity\Project\Project; use App\Entity\Project\ProjectStatus; @@ -34,6 +35,8 @@ use Symfony\Component\Validator\Constraints as Assert; #[API\Delete(security: 'is_granted("PROJECT_EDIT", object)')] class ProjectApiResource { + use LocalizedApiResourceTrait; + #[API\ApiProperty(identifier: true, writable: false)] public int $id; @@ -49,14 +52,6 @@ class ProjectApiResource #[API\ApiProperty(writable: false)] public UserApiResource $owner; - /** - * List of the available content locales. - * - * @var array - */ - #[API\ApiProperty(writable: false)] - public array $locales; - /** * Main headline for the Project. */ From 5400c7cb22902602a7044c0c5fe897209c5fe079 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Wed, 26 Feb 2025 12:59:16 +0100 Subject: [PATCH 4/5] Add updates to project api resource --- src/ApiResource/Project/ProjectApiResource.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/ApiResource/Project/ProjectApiResource.php b/src/ApiResource/Project/ProjectApiResource.php index 90f715d..2d72633 100644 --- a/src/ApiResource/Project/ProjectApiResource.php +++ b/src/ApiResource/Project/ProjectApiResource.php @@ -124,4 +124,12 @@ class ProjectApiResource */ #[API\ApiProperty(writable: false)] public array $budgetItems; + + /** + * A list of the ProjectUpdates this Project has. + * + * @var array + */ + #[API\ApiProperty(writable: false)] + public array $updates; } From 2d6d956bb9bf224a6c0230ff0d78643994d8dd6d Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Wed, 26 Feb 2025 13:03:16 +0100 Subject: [PATCH 5/5] Update ProjectUpdate api resource sort and security --- src/ApiResource/Project/UpdateApiResource.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ApiResource/Project/UpdateApiResource.php b/src/ApiResource/Project/UpdateApiResource.php index 7616efd..070fe4e 100644 --- a/src/ApiResource/Project/UpdateApiResource.php +++ b/src/ApiResource/Project/UpdateApiResource.php @@ -2,6 +2,7 @@ namespace App\ApiResource\Project; +use ApiPlatform\Doctrine\Orm\Filter\OrderFilter; use ApiPlatform\Doctrine\Orm\Filter\SearchFilter; use ApiPlatform\Doctrine\Orm\State\Options; use ApiPlatform\Metadata as API; @@ -19,6 +20,7 @@ use Symfony\Component\Validator\Constraints as Assert; */ #[API\ApiResource( shortName: 'ProjectUpdate', + securityPostDenormalize: 'is_granted("PROJECT_EDIT", object.project)', stateOptions: new Options(entityClass: Update::class), provider: ApiResourceStateProvider::class, processor: ApiResourceStateProcessor::class, @@ -61,6 +63,7 @@ class UpdateApiResource */ #[Assert\DateTime()] #[API\ApiProperty(default: 'now')] + #[API\ApiFilter(OrderFilter::class)] public \DateTimeInterface $date; /**