| @@ -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 | |||
| @@ -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'); | |||
| @@ -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 | |||
| @@ -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, | |||
| @@ -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; | |||
| } | |||
| @@ -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 | |||
| ]; | |||
| }); | |||
| } | |||
| } | |||
| @@ -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<int, Posting> | |||
| */ | |||
| 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; | |||
| } | |||
| } | |||
| @@ -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<int, Posting> | |||
| */ | |||
| 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; | |||
| } | |||
| } | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -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(), | |||