From ee4e43a1d58caf945267795fd61940032db87efd Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 14 Feb 2024 16:52:04 +0100 Subject: [PATCH] posts with partners and contacts --- config/packages/api_platform.yaml | 6 ++ ...13115154.php => Version20240214151436.php} | 8 +- openapi.yaml | 75 +++++++++++++++++++ src/ApiResource/ContactApi.php | 3 +- src/ApiResource/PostingApi.php | 7 ++ src/DataFixtures/AppFixtures.php | 19 +++-- src/Entity/Contact.php | 36 +++++++++ src/Entity/Partner.php | 34 +++++++++ src/Entity/Posting.php | 52 ++++++++++++- src/Factory/ContactFactory.php | 1 - 10 files changed, 226 insertions(+), 15 deletions(-) rename migrations/{Version20240213115154.php => Version20240214151436.php} (79%) diff --git a/config/packages/api_platform.yaml b/config/packages/api_platform.yaml index 3606a3f..50c6498 100644 --- a/config/packages/api_platform.yaml +++ b/config/packages/api_platform.yaml @@ -17,6 +17,12 @@ api_platform: extra_properties: standard_put: true rfc_7807_compliant_errors: true + pagination_client_items_per_page: true + pagination_items_per_page: 10 + pagination_maximum_items_per_page: 50 + collection: + pagination: + items_per_page_parameter_name: itemsPerPage event_listeners_backward_compatibility_layer: false keep_legacy_inflector: false diff --git a/migrations/Version20240213115154.php b/migrations/Version20240214151436.php similarity index 79% rename from migrations/Version20240213115154.php rename to migrations/Version20240214151436.php index 2849d9d..50207c5 100644 --- a/migrations/Version20240213115154.php +++ b/migrations/Version20240214151436.php @@ -10,7 +10,7 @@ use Doctrine\Migrations\AbstractMigration; /** * Auto-generated Migration: Please modify to your needs! */ -final class Version20240213115154 extends AbstractMigration +final class Version20240214151436 extends AbstractMigration { public function getDescription(): string { @@ -23,12 +23,14 @@ final class Version20240213115154 extends AbstractMigration $this->addSql('CREATE TABLE contact (id INT AUTO_INCREMENT NOT NULL, partner_id INT NOT NULL, image_id INT DEFAULT NULL, first_name VARCHAR(255) NOT NULL, last_name VARCHAR(255) NOT NULL, birthday DATE DEFAULT NULL, position VARCHAR(255) DEFAULT NULL, phone VARCHAR(255) DEFAULT NULL, email VARCHAR(255) DEFAULT NULL, created_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', INDEX IDX_4C62E6389393F8FE (partner_id), INDEX IDX_4C62E6383DA5256D (image_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); $this->addSql('CREATE TABLE media_object (id INT AUTO_INCREMENT NOT NULL, file_path VARCHAR(255) DEFAULT NULL, created_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); $this->addSql('CREATE TABLE partner (id INT AUTO_INCREMENT NOT NULL, logo_id INT DEFAULT NULL, name VARCHAR(255) NOT NULL, type VARCHAR(255) NOT NULL, street VARCHAR(255) DEFAULT NULL, street_no VARCHAR(255) DEFAULT NULL, zip VARCHAR(255) DEFAULT NULL, city VARCHAR(255) DEFAULT NULL, country VARCHAR(255) DEFAULT NULL, website VARCHAR(255) DEFAULT NULL, created_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', INDEX IDX_312B3E16F98F144A (logo_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); - $this->addSql('CREATE TABLE posting (id INT AUTO_INCREMENT NOT NULL, owner_id INT NOT NULL, message LONGTEXT NOT NULL, created_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', INDEX IDX_BD275D737E3C61F9 (owner_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql('CREATE TABLE posting (id INT AUTO_INCREMENT NOT NULL, owner_id INT NOT NULL, partner_id INT NOT NULL, contact_id INT DEFAULT NULL, headline VARCHAR(255) DEFAULT NULL, message LONGTEXT NOT NULL, created_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', INDEX IDX_BD275D737E3C61F9 (owner_id), INDEX IDX_BD275D739393F8FE (partner_id), INDEX IDX_BD275D73E7A1254A (contact_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); $this->addSql('CREATE TABLE `user` (id INT AUTO_INCREMENT NOT NULL, email VARCHAR(180) NOT NULL, first_name VARCHAR(255) NOT NULL, last_name VARCHAR(255) NOT NULL, roles JSON NOT NULL COMMENT \'(DC2Type:json)\', password VARCHAR(255) NOT NULL, active TINYINT(1) NOT NULL, created_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', UNIQUE INDEX UNIQ_8D93D649E7927C74 (email), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); $this->addSql('ALTER TABLE contact ADD CONSTRAINT FK_4C62E6389393F8FE FOREIGN KEY (partner_id) REFERENCES partner (id)'); $this->addSql('ALTER TABLE contact ADD CONSTRAINT FK_4C62E6383DA5256D FOREIGN KEY (image_id) REFERENCES media_object (id)'); $this->addSql('ALTER TABLE partner ADD CONSTRAINT FK_312B3E16F98F144A FOREIGN KEY (logo_id) REFERENCES media_object (id)'); $this->addSql('ALTER TABLE posting ADD CONSTRAINT FK_BD275D737E3C61F9 FOREIGN KEY (owner_id) REFERENCES `user` (id)'); + $this->addSql('ALTER TABLE posting ADD CONSTRAINT FK_BD275D739393F8FE FOREIGN KEY (partner_id) REFERENCES partner (id)'); + $this->addSql('ALTER TABLE posting ADD CONSTRAINT FK_BD275D73E7A1254A FOREIGN KEY (contact_id) REFERENCES contact (id)'); } public function down(Schema $schema): void @@ -38,6 +40,8 @@ final class Version20240213115154 extends AbstractMigration $this->addSql('ALTER TABLE contact DROP FOREIGN KEY FK_4C62E6383DA5256D'); $this->addSql('ALTER TABLE partner DROP FOREIGN KEY FK_312B3E16F98F144A'); $this->addSql('ALTER TABLE posting DROP FOREIGN KEY FK_BD275D737E3C61F9'); + $this->addSql('ALTER TABLE posting DROP FOREIGN KEY FK_BD275D739393F8FE'); + $this->addSql('ALTER TABLE posting DROP FOREIGN KEY FK_BD275D73E7A1254A'); $this->addSql('DROP TABLE contact'); $this->addSql('DROP TABLE media_object'); $this->addSql('DROP TABLE partner'); diff --git a/openapi.yaml b/openapi.yaml index 3e9679d..8f78e07 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -64,6 +64,21 @@ paths: style: form explode: false allowReserved: false + - + name: itemsPerPage + in: query + description: 'The number of items per page' + required: false + deprecated: false + allowEmptyValue: true + schema: + type: integer + default: 10 + minimum: 0 + maximum: 50 + style: form + explode: false + allowReserved: false - name: partner in: query @@ -308,6 +323,21 @@ paths: style: form explode: false allowReserved: false + - + name: itemsPerPage + in: query + description: 'The number of items per page' + required: false + deprecated: false + allowEmptyValue: true + schema: + type: integer + default: 10 + minimum: 0 + maximum: 50 + style: form + explode: false + allowReserved: false deprecated: false post: operationId: api_media_objects_post @@ -446,6 +476,21 @@ paths: style: form explode: false allowReserved: false + - + name: itemsPerPage + in: query + description: 'The number of items per page' + required: false + deprecated: false + allowEmptyValue: true + schema: + type: integer + default: 10 + minimum: 0 + maximum: 50 + style: form + explode: false + allowReserved: false - name: type in: query @@ -735,6 +780,21 @@ paths: style: form explode: false allowReserved: false + - + name: itemsPerPage + in: query + description: 'The number of items per page' + required: false + deprecated: false + allowEmptyValue: true + schema: + type: integer + default: 10 + minimum: 0 + maximum: 50 + style: form + explode: false + allowReserved: false deprecated: false post: operationId: api_posts_post @@ -953,6 +1013,21 @@ paths: style: form explode: false allowReserved: false + - + name: itemsPerPage + in: query + description: 'The number of items per page' + required: false + deprecated: false + allowEmptyValue: true + schema: + type: integer + default: 10 + minimum: 0 + maximum: 50 + style: form + explode: false + allowReserved: false deprecated: false post: operationId: api_users_post diff --git a/src/ApiResource/ContactApi.php b/src/ApiResource/ContactApi.php index c8582fa..1839d4e 100644 --- a/src/ApiResource/ContactApi.php +++ b/src/ApiResource/ContactApi.php @@ -14,7 +14,6 @@ use ApiPlatform\Metadata\ApiProperty; use ApiPlatform\Metadata\ApiResource; use App\Entity\Contact; use App\Entity\MediaObject; -use App\Entity\Partner; use App\State\EntityClassDtoStateProcessor; use App\State\EntityToDtoStateProvider; use ApiPlatform\Metadata\Delete; @@ -41,7 +40,7 @@ use Symfony\Component\Validator\Constraints\NotBlank; security: 'is_granted("ROLE_ADMIN")', ) ], - paginationItemsPerPage: 6, + order: ['lastName' => 'ASC'], security: 'is_granted("ROLE_USER")', provider: EntityToDtoStateProvider::class, processor: EntityClassDtoStateProcessor::class, diff --git a/src/ApiResource/PostingApi.php b/src/ApiResource/PostingApi.php index e6ac88f..cca13b2 100644 --- a/src/ApiResource/PostingApi.php +++ b/src/ApiResource/PostingApi.php @@ -10,6 +10,8 @@ namespace App\ApiResource; use ApiPlatform\Doctrine\Orm\State\Options; use ApiPlatform\Metadata\ApiProperty; use ApiPlatform\Metadata\ApiResource; +use App\Entity\Contact; +use App\Entity\Partner; use App\Entity\Posting; use App\State\EntityClassDtoStateProcessor; use App\State\EntityToDtoStateProvider; @@ -55,6 +57,11 @@ class PostingApi #[IsValidOwner] public ?UserApi $owner = null; + public ?Partner $partner = null; + + public ?Contact $contact = null; + #[ApiProperty(writable: false)] public ?\DateTimeImmutable $createdAt = null; + } \ No newline at end of file diff --git a/src/DataFixtures/AppFixtures.php b/src/DataFixtures/AppFixtures.php index 2320744..31ce655 100644 --- a/src/DataFixtures/AppFixtures.php +++ b/src/DataFixtures/AppFixtures.php @@ -54,25 +54,30 @@ class AppFixtures extends Fixture $adminF->setRoles(['ROLE_ADMIN']); UserFactory::createMany(10); - PostingFactory::createMany(50, function() { - return [ - 'owner' => UserFactory::random() - ]; - }); MediaObjectLogoFactory::createMany(50); - PartnerFactory::createMany(50, function() { + PartnerFactory::createMany(100, function() { return [ 'logo' => MediaObjectLogoFactory::random() ]; }); MediaObjectProfileFactory::createMany(50); - ContactFactory::createMany(50, function() { + ContactFactory::createMany(200, function() { return [ + 'partner' => PartnerFactory::random(), 'image' => MediaObjectProfileFactory::random() ]; }); + PostingFactory::createMany(200, function() { + $randomBoolean = (bool)rand(0, 1); + return [ + 'owner' => UserFactory::random(), + 'partner' => PartnerFactory::random(), + 'contact' => $randomBoolean ? ContactFactory::random() : null + ]; + }); + } } diff --git a/src/Entity/Contact.php b/src/Entity/Contact.php index 9dde342..162fddc 100644 --- a/src/Entity/Contact.php +++ b/src/Entity/Contact.php @@ -3,6 +3,8 @@ namespace App\Entity; use App\Repository\ContactRepository; +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Collection; use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Mapping as ORM; @@ -43,9 +45,13 @@ class Contact #[ORM\Column] private ?\DateTimeImmutable $createdAt = null; + #[ORM\OneToMany(mappedBy: 'contact', targetEntity: Posting::class)] + private Collection $postings; + public function __construct() { $this->createdAt = new \DateTimeImmutable(); + $this->postings = new ArrayCollection(); } public function getId(): ?int @@ -158,4 +164,34 @@ class Contact return $this; } + + /** + * @return Collection + */ + public function getPostings(): Collection + { + return $this->postings; + } + + public function addPosting(Posting $posting): static + { + if (!$this->postings->contains($posting)) { + $this->postings->add($posting); + $posting->setContact($this); + } + + return $this; + } + + public function removePosting(Posting $posting): static + { + if ($this->postings->removeElement($posting)) { + // set the owning side to null (unless already changed) + if ($posting->getContact() === $this) { + $posting->setContact(null); + } + } + + return $this; + } } diff --git a/src/Entity/Partner.php b/src/Entity/Partner.php index e16878c..2ff54c9 100644 --- a/src/Entity/Partner.php +++ b/src/Entity/Partner.php @@ -50,10 +50,14 @@ class Partner #[ORM\JoinColumn(nullable: true)] private ?MediaObject $logo = null; + #[ORM\OneToMany(mappedBy: 'partner', targetEntity: Posting::class, orphanRemoval: true)] + private Collection $postings; + public function __construct() { $this->createdAt = new \DateTimeImmutable(); $this->contacts = new ArrayCollection(); + $this->postings = new ArrayCollection(); } public function getId(): ?int @@ -171,4 +175,34 @@ class Partner $this->logo = $logo; } + /** + * @return Collection + */ + public function getPostings(): Collection + { + return $this->postings; + } + + public function addPosting(Posting $posting): static + { + if (!$this->postings->contains($posting)) { + $this->postings->add($posting); + $posting->setPartner($this); + } + + return $this; + } + + public function removePosting(Posting $posting): static + { + if ($this->postings->removeElement($posting)) { + // set the owning side to null (unless already changed) + if ($posting->getPartner() === $this) { + $posting->setPartner(null); + } + } + + return $this; + } + } diff --git a/src/Entity/Posting.php b/src/Entity/Posting.php index 5de914c..dde9af3 100644 --- a/src/Entity/Posting.php +++ b/src/Entity/Posting.php @@ -14,16 +14,26 @@ class Posting #[ORM\Column] private ?int $id = null; + #[ORM\Column(length: 255, nullable: true)] + private ?string $headline = null; + #[ORM\Column(type: Types::TEXT)] private ?string $message = null; - #[ORM\Column] - private ?\DateTimeImmutable $createdAt = null; - #[ORM\ManyToOne(inversedBy: 'postings')] #[ORM\JoinColumn(nullable: false)] private ?User $owner = null; + #[ORM\ManyToOne(inversedBy: 'postings')] + #[ORM\JoinColumn(nullable: false)] + private ?Partner $partner = null; + + #[ORM\ManyToOne(inversedBy: 'postings')] + private ?Contact $contact = null; + + #[ORM\Column] + private ?\DateTimeImmutable $createdAt = null; + public function __construct() { $this->createdAt = new \DateTimeImmutable(); @@ -62,4 +72,40 @@ class Posting return $this; } + + public function getHeadline(): ?string + { + return $this->headline; + } + + public function setHeadline(?string $headline): static + { + $this->headline = $headline; + + return $this; + } + + public function getPartner(): ?Partner + { + return $this->partner; + } + + public function setPartner(?Partner $partner): static + { + $this->partner = $partner; + + return $this; + } + + public function getContact(): ?Contact + { + return $this->contact; + } + + public function setContact(?Contact $contact): static + { + $this->contact = $contact; + + return $this; + } } diff --git a/src/Factory/ContactFactory.php b/src/Factory/ContactFactory.php index 4ca1145..61795bc 100644 --- a/src/Factory/ContactFactory.php +++ b/src/Factory/ContactFactory.php @@ -50,7 +50,6 @@ final class ContactFactory extends ModelFactory return [ 'firstName' => self::faker()->firstName(), 'lastName' => self::faker()->lastName(), - 'partner' => PartnerFactory::random(), 'birthday' => \DateTime::createFromFormat('Y-m-d', self::faker()->date()), 'position' => self::faker()->randomElement(FakeValues::POSITIONS), 'phone' => self::faker()->phoneNumber(),