| @@ -30,4 +30,6 @@ api_platform: | |||||
| api_keys: | api_keys: | ||||
| JWT: | JWT: | ||||
| name: Authorization | name: Authorization | ||||
| type: header | |||||
| type: header | |||||
| swagger_ui_extra_configuration: | |||||
| persistAuthorization: true | |||||
| @@ -1,51 +0,0 @@ | |||||
| <?php | |||||
| declare(strict_types=1); | |||||
| namespace DoctrineMigrations; | |||||
| use Doctrine\DBAL\Schema\Schema; | |||||
| use Doctrine\Migrations\AbstractMigration; | |||||
| /** | |||||
| * Auto-generated Migration: Please modify to your needs! | |||||
| */ | |||||
| final class Version20240214151436 extends AbstractMigration | |||||
| { | |||||
| public function getDescription(): string | |||||
| { | |||||
| return ''; | |||||
| } | |||||
| public function up(Schema $schema): void | |||||
| { | |||||
| // this up() migration is auto-generated, please modify it to your needs | |||||
| $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, 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 | |||||
| { | |||||
| // this down() migration is auto-generated, please modify it to your needs | |||||
| $this->addSql('ALTER TABLE contact DROP FOREIGN KEY FK_4C62E6389393F8FE'); | |||||
| $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'); | |||||
| $this->addSql('DROP TABLE posting'); | |||||
| $this->addSql('DROP TABLE `user`'); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,45 @@ | |||||
| <?php | |||||
| declare(strict_types=1); | |||||
| namespace DoctrineMigrations; | |||||
| use Doctrine\DBAL\Schema\Schema; | |||||
| use Doctrine\Migrations\AbstractMigration; | |||||
| /** | |||||
| * Auto-generated Migration: Please modify to your needs! | |||||
| */ | |||||
| final class Version20240215093709 extends AbstractMigration | |||||
| { | |||||
| public function getDescription(): string | |||||
| { | |||||
| return ''; | |||||
| } | |||||
| public function up(Schema $schema): void | |||||
| { | |||||
| // this up() migration is auto-generated, please modify it to your needs | |||||
| $this->addSql('CREATE TABLE comment (id INT AUTO_INCREMENT NOT NULL, owner_id INT NOT NULL, posting_id INT NOT NULL, message LONGTEXT NOT NULL, created_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', INDEX IDX_9474526C7E3C61F9 (owner_id), INDEX IDX_9474526C9AE985F6 (posting_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) NOT 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 comment ADD CONSTRAINT FK_9474526C7E3C61F9 FOREIGN KEY (owner_id) REFERENCES `user` (id)'); | |||||
| $this->addSql('ALTER TABLE comment ADD CONSTRAINT FK_9474526C9AE985F6 FOREIGN KEY (posting_id) REFERENCES posting (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 | |||||
| { | |||||
| // this down() migration is auto-generated, please modify it to your needs | |||||
| $this->addSql('ALTER TABLE comment DROP FOREIGN KEY FK_9474526C7E3C61F9'); | |||||
| $this->addSql('ALTER TABLE comment DROP FOREIGN KEY FK_9474526C9AE985F6'); | |||||
| $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 comment'); | |||||
| $this->addSql('DROP TABLE posting'); | |||||
| $this->addSql('DROP TABLE `user`'); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,65 @@ | |||||
| <?php | |||||
| /** | |||||
| * @author Daniel Knudsen <d.knudsen@spawntree.de> | |||||
| * @date 12.12.23 | |||||
| */ | |||||
| namespace App\ApiResource; | |||||
| use ApiPlatform\Doctrine\Orm\Filter\SearchFilter; | |||||
| use ApiPlatform\Doctrine\Orm\State\Options; | |||||
| use ApiPlatform\Metadata\ApiFilter; | |||||
| use ApiPlatform\Metadata\ApiProperty; | |||||
| use ApiPlatform\Metadata\ApiResource; | |||||
| use App\Entity\Comment; | |||||
| use App\State\EntityClassDtoStateProcessor; | |||||
| use App\State\EntityToDtoStateProvider; | |||||
| use ApiPlatform\Metadata\Delete; | |||||
| use ApiPlatform\Metadata\Get; | |||||
| use ApiPlatform\Metadata\GetCollection; | |||||
| use ApiPlatform\Metadata\Patch; | |||||
| use ApiPlatform\Metadata\Post; | |||||
| use App\Validator\IsValidOwner; | |||||
| use Symfony\Component\Validator\Constraints\NotBlank; | |||||
| #[ApiResource( | |||||
| shortName: 'Comment', | |||||
| operations: [ | |||||
| new Get( | |||||
| security: 'is_granted("ROLE_USER")' | |||||
| ), | |||||
| new GetCollection(), | |||||
| new Post( | |||||
| security: 'is_granted("ROLE_USER")', | |||||
| ), | |||||
| new Patch( | |||||
| security: 'is_granted("EDIT", object)', | |||||
| ), | |||||
| new Delete( | |||||
| security: 'is_granted("ROLE_ADMIN")', | |||||
| ) | |||||
| ], | |||||
| security: 'is_granted("ROLE_USER")', | |||||
| provider: EntityToDtoStateProvider::class, | |||||
| processor: EntityClassDtoStateProcessor::class, | |||||
| stateOptions: new Options(entityClass: Comment::class), | |||||
| )] | |||||
| #[ApiFilter(SearchFilter::class, properties: ['partner' => 'exact', 'contact' => 'exact'])] | |||||
| class CommentApi | |||||
| { | |||||
| #[ApiProperty(readable: false, writable: false, identifier: true)] | |||||
| public ?int $id = null; | |||||
| #[NotBlank] | |||||
| public ?string $message = null; | |||||
| #[IsValidOwner] | |||||
| public ?UserApi $owner = null; | |||||
| public ?PostingApi $posting = null; | |||||
| #[ApiProperty(writable: false)] | |||||
| public ?\DateTimeImmutable $createdAt = null; | |||||
| } | |||||
| @@ -73,5 +73,10 @@ class ContactApi | |||||
| public ?string $email = null; | public ?string $email = null; | ||||
| /** | |||||
| * @var array<int, PostingApi> | |||||
| */ | |||||
| public array $postings = []; | |||||
| public ?\DateTimeImmutable $createdAt = null; | public ?\DateTimeImmutable $createdAt = null; | ||||
| } | } | ||||
| @@ -44,7 +44,6 @@ use Symfony\Component\Validator\Constraints\NotBlank; | |||||
| ) | ) | ||||
| ], | ], | ||||
| order: ['name' => 'ASC'], | order: ['name' => 'ASC'], | ||||
| paginationItemsPerPage: 10, | |||||
| security: 'is_granted("ROLE_USER")', | security: 'is_granted("ROLE_USER")', | ||||
| provider: EntityToDtoStateProvider::class, | provider: EntityToDtoStateProvider::class, | ||||
| processor: EntityClassDtoStateProcessor::class, | processor: EntityClassDtoStateProcessor::class, | ||||
| @@ -21,15 +21,18 @@ use ApiPlatform\Metadata\GetCollection; | |||||
| use ApiPlatform\Metadata\Patch; | use ApiPlatform\Metadata\Patch; | ||||
| use ApiPlatform\Metadata\Post; | use ApiPlatform\Metadata\Post; | ||||
| use App\Validator\IsValidOwner; | use App\Validator\IsValidOwner; | ||||
| use Symfony\Component\PropertyInfo\Type; | |||||
| use Symfony\Component\Validator\Constraints\NotBlank; | use Symfony\Component\Validator\Constraints\NotBlank; | ||||
| #[ApiResource( | #[ApiResource( | ||||
| shortName: 'Post', | shortName: 'Post', | ||||
| operations: [ | operations: [ | ||||
| new Get( | new Get( | ||||
| security: 'is_granted("ROLE_USER")' | |||||
| security: 'is_granted("ROLE_USER")', | |||||
| ), | |||||
| new GetCollection( | |||||
| security: 'is_granted("ROLE_USER")', | |||||
| ), | ), | ||||
| new GetCollection(), | |||||
| new Post( | new Post( | ||||
| security: 'is_granted("ROLE_USER")', | security: 'is_granted("ROLE_USER")', | ||||
| ), | ), | ||||
| @@ -40,11 +43,10 @@ use Symfony\Component\Validator\Constraints\NotBlank; | |||||
| security: 'is_granted("ROLE_ADMIN")', | security: 'is_granted("ROLE_ADMIN")', | ||||
| ) | ) | ||||
| ], | ], | ||||
| paginationItemsPerPage: 10, | |||||
| security: 'is_granted("ROLE_USER")', | security: 'is_granted("ROLE_USER")', | ||||
| provider: EntityToDtoStateProvider::class, | provider: EntityToDtoStateProvider::class, | ||||
| processor: EntityClassDtoStateProcessor::class, | processor: EntityClassDtoStateProcessor::class, | ||||
| stateOptions: new Options(entityClass: Posting::class), | |||||
| stateOptions: new Options(entityClass: Posting::class) | |||||
| )] | )] | ||||
| #[ApiFilter(SearchFilter::class, properties: ['partner' => 'exact', 'contact' => 'exact'])] | #[ApiFilter(SearchFilter::class, properties: ['partner' => 'exact', 'contact' => 'exact'])] | ||||
| class PostingApi | class PostingApi | ||||
| @@ -68,6 +70,24 @@ class PostingApi | |||||
| public ?ContactApi $contact = null; | public ?ContactApi $contact = null; | ||||
| /** | |||||
| * @var array<int, CommentApi> | |||||
| */ | |||||
| #[ApiProperty( | |||||
| readableLink: true, | |||||
| writableLink: true, | |||||
| fetchEager: true, | |||||
| builtinTypes: [ | |||||
| new Type( | |||||
| 'object', | |||||
| collection: true, | |||||
| collectionKeyType: [new Type('int')], | |||||
| collectionValueType: new Type('object', class: CommentApi::class) | |||||
| ) | |||||
| ] | |||||
| )] | |||||
| public array $comments = []; | |||||
| #[ApiProperty(writable: false)] | #[ApiProperty(writable: false)] | ||||
| public ?\DateTimeImmutable $createdAt = null; | public ?\DateTimeImmutable $createdAt = null; | ||||
| @@ -36,7 +36,6 @@ use Symfony\Component\Validator\Constraints as Assert; | |||||
| security: 'is_granted("ROLE_USER")' | security: 'is_granted("ROLE_USER")' | ||||
| ), | ), | ||||
| ], | ], | ||||
| paginationItemsPerPage: 10, | |||||
| security: 'is_granted("ROLE_USER")', | security: 'is_granted("ROLE_USER")', | ||||
| provider: EntityToDtoStateProvider::class, | provider: EntityToDtoStateProvider::class, | ||||
| processor: EntityClassDtoStateProcessor::class, | processor: EntityClassDtoStateProcessor::class, | ||||
| @@ -72,11 +71,11 @@ class UserApi | |||||
| #[ApiProperty(security: 'object === null or is_granted("EDIT", object)')] | #[ApiProperty(security: 'object === null or is_granted("EDIT", object)')] | ||||
| public bool $active; | public bool $active; | ||||
| #[ApiProperty(writable: false)] | |||||
| public ?\DateTimeImmutable $createdAt = null; | |||||
| /** | /** | ||||
| * @var array<int, PostingApi> | * @var array<int, PostingApi> | ||||
| */ | */ | ||||
| public array $postings = []; | public array $postings = []; | ||||
| } | |||||
| #[ApiProperty(writable: false)] | |||||
| public ?\DateTimeImmutable $createdAt = null; | |||||
| } | |||||
| @@ -2,6 +2,7 @@ | |||||
| namespace App\DataFixtures; | namespace App\DataFixtures; | ||||
| use App\Factory\CommentFactory; | |||||
| use App\Factory\ContactFactory; | use App\Factory\ContactFactory; | ||||
| use App\Factory\MediaObjectLogoFactory; | use App\Factory\MediaObjectLogoFactory; | ||||
| use App\Factory\MediaObjectProfileFactory; | use App\Factory\MediaObjectProfileFactory; | ||||
| @@ -54,30 +55,11 @@ class AppFixtures extends Fixture | |||||
| $adminF->setRoles(['ROLE_ADMIN']); | $adminF->setRoles(['ROLE_ADMIN']); | ||||
| UserFactory::createMany(10); | UserFactory::createMany(10); | ||||
| MediaObjectLogoFactory::createMany(50); | MediaObjectLogoFactory::createMany(50); | ||||
| PartnerFactory::createMany(100, function() { | |||||
| return [ | |||||
| 'logo' => MediaObjectLogoFactory::random() | |||||
| ]; | |||||
| }); | |||||
| PartnerFactory::createMany(100); | |||||
| MediaObjectProfileFactory::createMany(50); | MediaObjectProfileFactory::createMany(50); | ||||
| ContactFactory::createMany(200, function() { | |||||
| return [ | |||||
| 'partner' => PartnerFactory::random(), | |||||
| 'image' => MediaObjectProfileFactory::random() | |||||
| ]; | |||||
| }); | |||||
| PostingFactory::createMany(200, function() { | |||||
| $randomBoolean = (bool)random_int(0, 1); | |||||
| return [ | |||||
| 'owner' => UserFactory::random(), | |||||
| 'partner' => PartnerFactory::random(), | |||||
| 'contact' => $randomBoolean ? ContactFactory::random() : null | |||||
| ]; | |||||
| }); | |||||
| ContactFactory::createMany(200); | |||||
| PostingFactory::createMany(200); | |||||
| CommentFactory::createMany(300); | |||||
| } | } | ||||
| } | } | ||||
| @@ -0,0 +1,95 @@ | |||||
| <?php | |||||
| namespace App\Entity; | |||||
| use App\Repository\CommentRepository; | |||||
| use Doctrine\DBAL\Types\Types; | |||||
| use Doctrine\ORM\Mapping as ORM; | |||||
| #[ORM\Entity(repositoryClass: CommentRepository::class)] | |||||
| class Comment | |||||
| { | |||||
| #[ORM\Id] | |||||
| #[ORM\GeneratedValue] | |||||
| #[ORM\Column] | |||||
| private ?int $id = null; | |||||
| #[ORM\Column(type: Types::TEXT)] | |||||
| private ?string $message = null; | |||||
| #[ORM\ManyToOne(inversedBy: 'comments')] | |||||
| #[ORM\JoinColumn(nullable: false)] | |||||
| private ?User $owner = null; | |||||
| #[ORM\ManyToOne(inversedBy: 'comments')] | |||||
| #[ORM\JoinColumn(nullable: false)] | |||||
| private ?Posting $posting = null; | |||||
| #[ORM\Column] | |||||
| private ?\DateTimeImmutable $createdAt = null; | |||||
| public function __construct() | |||||
| { | |||||
| $this->createdAt = new \DateTimeImmutable(); | |||||
| } | |||||
| public function getId(): ?int | |||||
| { | |||||
| return $this->id; | |||||
| } | |||||
| public function setId(int $id): static | |||||
| { | |||||
| $this->id = $id; | |||||
| return $this; | |||||
| } | |||||
| public function getMessage(): ?string | |||||
| { | |||||
| return $this->message; | |||||
| } | |||||
| public function setMessage(string $message): static | |||||
| { | |||||
| $this->message = $message; | |||||
| return $this; | |||||
| } | |||||
| public function getOwner(): ?User | |||||
| { | |||||
| return $this->owner; | |||||
| } | |||||
| public function setOwner(?User $owner): static | |||||
| { | |||||
| $this->owner = $owner; | |||||
| return $this; | |||||
| } | |||||
| public function getPosting(): ?Posting | |||||
| { | |||||
| return $this->posting; | |||||
| } | |||||
| public function setPosting(?Posting $posting): static | |||||
| { | |||||
| $this->posting = $posting; | |||||
| return $this; | |||||
| } | |||||
| public function getCreatedAt(): ?\DateTimeImmutable | |||||
| { | |||||
| return $this->createdAt; | |||||
| } | |||||
| public function setCreatedAt(\DateTimeImmutable $createdAt): static | |||||
| { | |||||
| $this->createdAt = $createdAt; | |||||
| return $this; | |||||
| } | |||||
| } | |||||
| @@ -45,7 +45,7 @@ class Contact | |||||
| #[ORM\Column] | #[ORM\Column] | ||||
| private ?\DateTimeImmutable $createdAt = null; | private ?\DateTimeImmutable $createdAt = null; | ||||
| #[ORM\OneToMany(mappedBy: 'contact', targetEntity: Posting::class)] | |||||
| #[ORM\OneToMany(mappedBy: 'contact', targetEntity: Posting::class, orphanRemoval: true)] | |||||
| private Collection $postings; | private Collection $postings; | ||||
| public function __construct() | public function __construct() | ||||
| @@ -3,6 +3,8 @@ | |||||
| namespace App\Entity; | namespace App\Entity; | ||||
| use App\Repository\PostingRepository; | use App\Repository\PostingRepository; | ||||
| use Doctrine\Common\Collections\ArrayCollection; | |||||
| use Doctrine\Common\Collections\Collection; | |||||
| use Doctrine\DBAL\Types\Types; | use Doctrine\DBAL\Types\Types; | ||||
| use Doctrine\ORM\Mapping as ORM; | use Doctrine\ORM\Mapping as ORM; | ||||
| @@ -34,9 +36,13 @@ class Posting | |||||
| #[ORM\Column] | #[ORM\Column] | ||||
| private ?\DateTimeImmutable $createdAt = null; | private ?\DateTimeImmutable $createdAt = null; | ||||
| #[ORM\OneToMany(mappedBy: 'posting', targetEntity: Comment::class, orphanRemoval: true)] | |||||
| private Collection $comments; | |||||
| public function __construct() | public function __construct() | ||||
| { | { | ||||
| $this->createdAt = new \DateTimeImmutable(); | $this->createdAt = new \DateTimeImmutable(); | ||||
| $this->comments = new ArrayCollection(); | |||||
| } | } | ||||
| public function getId(): ?int | public function getId(): ?int | ||||
| @@ -108,4 +114,34 @@ class Posting | |||||
| return $this; | return $this; | ||||
| } | } | ||||
| /** | |||||
| * @return Collection<int, Comment> | |||||
| */ | |||||
| public function getComments(): Collection | |||||
| { | |||||
| return $this->comments; | |||||
| } | |||||
| public function addComment(Comment $comment): static | |||||
| { | |||||
| if (!$this->comments->contains($comment)) { | |||||
| $this->comments->add($comment); | |||||
| $comment->setPosting($this); | |||||
| } | |||||
| return $this; | |||||
| } | |||||
| public function removeComment(Comment $comment): static | |||||
| { | |||||
| if ($this->comments->removeElement($comment)) { | |||||
| // set the owning side to null (unless already changed) | |||||
| if ($comment->getPosting() === $this) { | |||||
| $comment->setPosting(null); | |||||
| } | |||||
| } | |||||
| return $this; | |||||
| } | |||||
| } | } | ||||
| @@ -45,12 +45,16 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface | |||||
| #[ORM\OneToMany(mappedBy: 'owner', targetEntity: Posting::class, orphanRemoval: true)] | #[ORM\OneToMany(mappedBy: 'owner', targetEntity: Posting::class, orphanRemoval: true)] | ||||
| private Collection $postings; | private Collection $postings; | ||||
| #[ORM\OneToMany(mappedBy: 'owner', targetEntity: Comment::class, orphanRemoval: true)] | |||||
| private Collection $comments; | |||||
| public function __construct() | public function __construct() | ||||
| { | { | ||||
| $this->createdAt = new \DateTimeImmutable(); | $this->createdAt = new \DateTimeImmutable(); | ||||
| $this->postings = new ArrayCollection(); | $this->postings = new ArrayCollection(); | ||||
| $this->active = true; | $this->active = true; | ||||
| $this->comments = new ArrayCollection(); | |||||
| } | } | ||||
| public function getId(): ?int | public function getId(): ?int | ||||
| @@ -198,4 +202,34 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface | |||||
| return $this; | return $this; | ||||
| } | } | ||||
| /** | |||||
| * @return Collection<int, Comment> | |||||
| */ | |||||
| public function getComments(): Collection | |||||
| { | |||||
| return $this->comments; | |||||
| } | |||||
| public function addComment(Comment $comment): static | |||||
| { | |||||
| if (!$this->comments->contains($comment)) { | |||||
| $this->comments->add($comment); | |||||
| $comment->setOwner($this); | |||||
| } | |||||
| return $this; | |||||
| } | |||||
| public function removeComment(Comment $comment): static | |||||
| { | |||||
| if ($this->comments->removeElement($comment)) { | |||||
| // set the owning side to null (unless already changed) | |||||
| if ($comment->getOwner() === $this) { | |||||
| $comment->setOwner(null); | |||||
| } | |||||
| } | |||||
| return $this; | |||||
| } | |||||
| } | } | ||||
| @@ -0,0 +1,71 @@ | |||||
| <?php | |||||
| namespace App\Factory; | |||||
| use App\Entity\Comment; | |||||
| use App\Repository\CommentRepository; | |||||
| use Zenstruck\Foundry\ModelFactory; | |||||
| use Zenstruck\Foundry\Proxy; | |||||
| use Zenstruck\Foundry\RepositoryProxy; | |||||
| /** | |||||
| * @extends ModelFactory<Comment> | |||||
| * | |||||
| * @method Comment|Proxy create(array|callable $attributes = []) | |||||
| * @method static Comment|Proxy createOne(array $attributes = []) | |||||
| * @method static Comment|Proxy find(object|array|mixed $criteria) | |||||
| * @method static Comment|Proxy findOrCreate(array $attributes) | |||||
| * @method static Comment|Proxy first(string $sortedField = 'id') | |||||
| * @method static Comment|Proxy last(string $sortedField = 'id') | |||||
| * @method static Comment|Proxy random(array $attributes = []) | |||||
| * @method static Comment|Proxy randomOrCreate(array $attributes = []) | |||||
| * @method static CommentRepository|RepositoryProxy repository() | |||||
| * @method static Comment[]|Proxy[] all() | |||||
| * @method static Comment[]|Proxy[] createMany(int $number, array|callable $attributes = []) | |||||
| * @method static Comment[]|Proxy[] createSequence(iterable|callable $sequence) | |||||
| * @method static Comment[]|Proxy[] findBy(array $attributes) | |||||
| * @method static Comment[]|Proxy[] randomRange(int $min, int $max, array $attributes = []) | |||||
| * @method static Comment[]|Proxy[] randomSet(int $number, array $attributes = []) | |||||
| */ | |||||
| final class CommentFactory extends ModelFactory | |||||
| { | |||||
| /** | |||||
| * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services | |||||
| * | |||||
| * @todo inject services if required | |||||
| */ | |||||
| public function __construct() | |||||
| { | |||||
| parent::__construct(); | |||||
| } | |||||
| /** | |||||
| * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories | |||||
| * | |||||
| * @todo add your default values here | |||||
| */ | |||||
| protected function getDefaults(): array | |||||
| { | |||||
| return [ | |||||
| 'createdAt' => \DateTimeImmutable::createFromMutable(self::faker()->dateTime()), | |||||
| 'message' => self::faker()->text(), | |||||
| 'owner' => UserFactory::random(), | |||||
| 'posting' => PostingFactory::random(), | |||||
| ]; | |||||
| } | |||||
| /** | |||||
| * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization | |||||
| */ | |||||
| protected function initialize(): self | |||||
| { | |||||
| return $this | |||||
| // ->afterInstantiate(function(Comment $comment): void {}) | |||||
| ; | |||||
| } | |||||
| protected static function getClass(): string | |||||
| { | |||||
| return Comment::class; | |||||
| } | |||||
| } | |||||
| @@ -53,7 +53,9 @@ final class ContactFactory extends ModelFactory | |||||
| 'birthday' => \DateTime::createFromFormat('Y-m-d', self::faker()->date()), | 'birthday' => \DateTime::createFromFormat('Y-m-d', self::faker()->date()), | ||||
| 'position' => self::faker()->randomElement(FakeValues::POSITIONS), | 'position' => self::faker()->randomElement(FakeValues::POSITIONS), | ||||
| 'phone' => self::faker()->phoneNumber(), | 'phone' => self::faker()->phoneNumber(), | ||||
| 'email' => self::faker()->email() | |||||
| 'email' => self::faker()->email(), | |||||
| 'partner' => PartnerFactory::random(), | |||||
| 'image' => MediaObjectProfileFactory::random() | |||||
| ]; | ]; | ||||
| } | } | ||||
| @@ -57,6 +57,7 @@ final class PartnerFactory extends ModelFactory | |||||
| 'zip' => self::faker()->numberBetween(1000,10000), | 'zip' => self::faker()->numberBetween(1000,10000), | ||||
| 'city' => self::faker()->randomElement(FakeValues::CITIES), | 'city' => self::faker()->randomElement(FakeValues::CITIES), | ||||
| 'country' => self::faker()->randomElement(FakeValues::COUNTRIES), | 'country' => self::faker()->randomElement(FakeValues::COUNTRIES), | ||||
| 'logo' => MediaObjectLogoFactory::random(), | |||||
| 'website' => 'https://spawntree.de/', | 'website' => 'https://spawntree.de/', | ||||
| ]; | ]; | ||||
| } | } | ||||
| @@ -50,6 +50,9 @@ final class PostingFactory extends ModelFactory | |||||
| return [ | return [ | ||||
| 'headline' => self::faker()->words(random_int(1, 5), true), | 'headline' => self::faker()->words(random_int(1, 5), true), | ||||
| 'message' => $randomBoolean ? self::faker()->sentence() : self::faker()->sentences(random_int(1, 3), true), | 'message' => $randomBoolean ? self::faker()->sentence() : self::faker()->sentences(random_int(1, 3), true), | ||||
| 'owner' => UserFactory::random(), | |||||
| 'partner' => PartnerFactory::random(), | |||||
| 'contact' => $randomBoolean ? ContactFactory::random() : null, | |||||
| ]; | ]; | ||||
| } | } | ||||
| @@ -0,0 +1,58 @@ | |||||
| <?php | |||||
| namespace App\Mapper; | |||||
| use App\ApiResource\CommentApi; | |||||
| use App\Entity\Comment; | |||||
| use App\Entity\User; | |||||
| use App\Repository\CommentRepository; | |||||
| use Symfony\Bundle\SecurityBundle\Security; | |||||
| use Symfonycasts\MicroMapper\AsMapper; | |||||
| use Symfonycasts\MicroMapper\MapperInterface; | |||||
| use Symfonycasts\MicroMapper\MicroMapperInterface; | |||||
| #[AsMapper(from: CommentApi::class, to: Comment::class)] | |||||
| class CommentApiToEntityMapper implements MapperInterface | |||||
| { | |||||
| public function __construct( | |||||
| private CommentRepository $repository, | |||||
| private Security $security, | |||||
| private MicroMapperInterface $microMapper, | |||||
| ) | |||||
| { | |||||
| } | |||||
| public function load(object $from, string $toClass, array $context): object | |||||
| { | |||||
| $dto = $from; | |||||
| assert($dto instanceof CommentApi); | |||||
| $entity = $dto->id ? $this->repository->find($dto->id) : new Comment(); | |||||
| if (!$entity) { | |||||
| throw new \Exception('Comment not found'); | |||||
| } | |||||
| return $entity; | |||||
| } | |||||
| public function populate(object $from, object $to, array $context): object | |||||
| { | |||||
| $dto = $from; | |||||
| assert($dto instanceof CommentApi); | |||||
| $entity = $to; | |||||
| assert($entity instanceof Comment); | |||||
| if ($dto->owner) { | |||||
| $entity->setOwner($this->microMapper->map($dto->owner, User::class, [ | |||||
| MicroMapperInterface::MAX_DEPTH => 1, | |||||
| ])); | |||||
| } else { | |||||
| $entity->setOwner($this->security->getUser()); | |||||
| } | |||||
| $entity->setPosting($dto->posting); | |||||
| $entity->setMessage($dto->message); | |||||
| return $entity; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,55 @@ | |||||
| <?php | |||||
| namespace App\Mapper; | |||||
| use App\ApiResource\CommentApi; | |||||
| use App\ApiResource\UserApi; | |||||
| use App\ApiResource\PostingApi; | |||||
| use App\Entity\Comment; | |||||
| use Symfony\Bundle\SecurityBundle\Security; | |||||
| use Symfonycasts\MicroMapper\AsMapper; | |||||
| use Symfonycasts\MicroMapper\MapperInterface; | |||||
| use Symfonycasts\MicroMapper\MicroMapperInterface; | |||||
| #[AsMapper(from: Comment::class, to: CommentApi::class)] | |||||
| class CommentEntityToApiMapper implements MapperInterface | |||||
| { | |||||
| public function __construct( | |||||
| private MicroMapperInterface $microMapper, | |||||
| private Security $security, | |||||
| ) | |||||
| { | |||||
| } | |||||
| public function load(object $from, string $toClass, array $context): object | |||||
| { | |||||
| $entity = $from; | |||||
| assert($entity instanceof Comment); | |||||
| $dto = new CommentApi(); | |||||
| $dto->id = $entity->getId(); | |||||
| return $dto; | |||||
| } | |||||
| public function populate(object $from, object $to, array $context): object | |||||
| { | |||||
| $entity = $from; | |||||
| $dto = $to; | |||||
| assert($entity instanceof Comment); | |||||
| assert($dto instanceof CommentApi); | |||||
| $dto->message = $entity->getMessage(); | |||||
| $dto->owner = $this->microMapper->map($entity->getOwner(), UserApi::class, [ | |||||
| MicroMapperInterface::MAX_DEPTH => 0, | |||||
| ]); | |||||
| $dto->posting = $this->microMapper->map($entity->getPosting(), PostingApi::class, [ | |||||
| MicroMapperInterface::MAX_DEPTH => 0, | |||||
| ]); | |||||
| $dto->createdAt = $entity->getCreatedAt(); | |||||
| return $dto; | |||||
| } | |||||
| } | |||||
| @@ -4,8 +4,10 @@ namespace App\Mapper; | |||||
| use App\ApiResource\ContactApi; | use App\ApiResource\ContactApi; | ||||
| use App\ApiResource\PartnerApi; | use App\ApiResource\PartnerApi; | ||||
| use App\ApiResource\PostingApi; | |||||
| use App\Entity\Contact; | use App\Entity\Contact; | ||||
| use App\Entity\Partner; | use App\Entity\Partner; | ||||
| use App\Entity\Posting; | |||||
| use Symfonycasts\MicroMapper\AsMapper; | use Symfonycasts\MicroMapper\AsMapper; | ||||
| use Symfonycasts\MicroMapper\MapperInterface; | use Symfonycasts\MicroMapper\MapperInterface; | ||||
| use Symfonycasts\MicroMapper\MicroMapperInterface; | use Symfonycasts\MicroMapper\MicroMapperInterface; | ||||
| @@ -54,6 +56,12 @@ class ContactEntityToApiMapper implements MapperInterface | |||||
| $dto->email = $entity->getEmail(); | $dto->email = $entity->getEmail(); | ||||
| $dto->createdAt = $entity->getCreatedAt(); | $dto->createdAt = $entity->getCreatedAt(); | ||||
| $dto->postings = array_map(function(Posting $posting) { | |||||
| return $this->microMapper->map($posting, PostingApi::class, [ | |||||
| MicroMapperInterface::MAX_DEPTH => 0, | |||||
| ]); | |||||
| }, $entity->getPostings()->getValues()); | |||||
| return $dto; | return $dto; | ||||
| } | } | ||||
| } | } | ||||
| @@ -54,6 +54,8 @@ class PostingApiToEntityMapper implements MapperInterface | |||||
| $entity->setOwner($this->security->getUser()); | $entity->setOwner($this->security->getUser()); | ||||
| } | } | ||||
| $entity->setPartner($dto->partner); | |||||
| $entity->setContact($dto->contact); | |||||
| $entity->setMessage($dto->message); | $entity->setMessage($dto->message); | ||||
| return $entity; | return $entity; | ||||
| @@ -2,12 +2,13 @@ | |||||
| namespace App\Mapper; | namespace App\Mapper; | ||||
| use App\ApiResource\CommentApi; | |||||
| use App\ApiResource\ContactApi; | use App\ApiResource\ContactApi; | ||||
| use App\ApiResource\PartnerApi; | use App\ApiResource\PartnerApi; | ||||
| use App\ApiResource\UserApi; | use App\ApiResource\UserApi; | ||||
| use App\ApiResource\PostingApi; | use App\ApiResource\PostingApi; | ||||
| use App\Entity\Comment; | |||||
| use App\Entity\Posting; | use App\Entity\Posting; | ||||
| use Symfony\Bundle\SecurityBundle\Security; | |||||
| use Symfonycasts\MicroMapper\AsMapper; | use Symfonycasts\MicroMapper\AsMapper; | ||||
| use Symfonycasts\MicroMapper\MapperInterface; | use Symfonycasts\MicroMapper\MapperInterface; | ||||
| use Symfonycasts\MicroMapper\MicroMapperInterface; | use Symfonycasts\MicroMapper\MicroMapperInterface; | ||||
| @@ -17,7 +18,6 @@ class PostingEntityToApiMapper implements MapperInterface | |||||
| { | { | ||||
| public function __construct( | public function __construct( | ||||
| private MicroMapperInterface $microMapper, | private MicroMapperInterface $microMapper, | ||||
| private Security $security, | |||||
| ) | ) | ||||
| { | { | ||||
| } | } | ||||
| @@ -43,18 +43,27 @@ class PostingEntityToApiMapper implements MapperInterface | |||||
| $dto->headline = $entity->getHeadline(); | $dto->headline = $entity->getHeadline(); | ||||
| $dto->message = $entity->getMessage(); | $dto->message = $entity->getMessage(); | ||||
| $dto->owner = $this->microMapper->map($entity->getOwner(), UserApi::class, [ | $dto->owner = $this->microMapper->map($entity->getOwner(), UserApi::class, [ | ||||
| MicroMapperInterface::MAX_DEPTH => 0, | |||||
| MicroMapperInterface::MAX_DEPTH => 1, | |||||
| ]); | ]); | ||||
| $dto->ownerName = $entity->getOwner()?->getFirstName()." ".$entity->getOwner()?->getLastName(); | $dto->ownerName = $entity->getOwner()?->getFirstName()." ".$entity->getOwner()?->getLastName(); | ||||
| $dto->partner = $this->microMapper->map($entity->getPartner(), PartnerApi::class, [ | $dto->partner = $this->microMapper->map($entity->getPartner(), PartnerApi::class, [ | ||||
| MicroMapperInterface::MAX_DEPTH => 0, | |||||
| MicroMapperInterface::MAX_DEPTH => 1, | |||||
| ]); | ]); | ||||
| $dto->contact = $this->microMapper->map($entity->getContact(), ContactApi::class, [ | |||||
| MicroMapperInterface::MAX_DEPTH => 0, | |||||
| ]); | |||||
| $dto->contact = null; | |||||
| if ($entity->getContact() !== null) { | |||||
| $dto->contact = $this->microMapper->map($entity->getContact(), ContactApi::class, [ | |||||
| MicroMapperInterface::MAX_DEPTH => 1, | |||||
| ]); | |||||
| } | |||||
| $dto->comments = array_map(function(Comment $comment) { | |||||
| return $this->microMapper->map($comment, CommentApi::class, [ | |||||
| MicroMapperInterface::MAX_DEPTH => 1, | |||||
| ]); | |||||
| }, $entity->getComments()->getValues()); | |||||
| //dd($dto); | |||||
| $dto->createdAt = $entity->getCreatedAt(); | $dto->createdAt = $entity->getCreatedAt(); | ||||
| return $dto; | return $dto; | ||||
| @@ -40,8 +40,9 @@ class UserEntityToApiMapper implements MapperInterface | |||||
| $dto->email = $entity->getEmail(); | $dto->email = $entity->getEmail(); | ||||
| $dto->firstName = $entity->getFirstName(); | $dto->firstName = $entity->getFirstName(); | ||||
| $dto->lastName = $entity->getLastName(); | $dto->lastName = $entity->getLastName(); | ||||
| $dto->postings = array_map(function(Posting $userPost) { | |||||
| return $this->microMapper->map($userPost, PostingApi::class, [ | |||||
| $dto->postings = array_map(function(Posting $posting) { | |||||
| return $this->microMapper->map($posting, PostingApi::class, [ | |||||
| MicroMapperInterface::MAX_DEPTH => 0, | MicroMapperInterface::MAX_DEPTH => 0, | ||||
| ]); | ]); | ||||
| }, $entity->getPostings()->getValues()); | }, $entity->getPostings()->getValues()); | ||||
| @@ -0,0 +1,48 @@ | |||||
| <?php | |||||
| namespace App\Repository; | |||||
| use App\Entity\Comment; | |||||
| use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; | |||||
| use Doctrine\Persistence\ManagerRegistry; | |||||
| /** | |||||
| * @extends ServiceEntityRepository<Comment> | |||||
| * | |||||
| * @method Comment|null find($id, $lockMode = null, $lockVersion = null) | |||||
| * @method Comment|null findOneBy(array $criteria, array $orderBy = null) | |||||
| * @method Comment[] findAll() | |||||
| * @method Comment[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) | |||||
| */ | |||||
| class CommentRepository extends ServiceEntityRepository | |||||
| { | |||||
| public function __construct(ManagerRegistry $registry) | |||||
| { | |||||
| parent::__construct($registry, Comment::class); | |||||
| } | |||||
| // /** | |||||
| // * @return Comment[] Returns an array of Comment objects | |||||
| // */ | |||||
| // public function findByExampleField($value): array | |||||
| // { | |||||
| // return $this->createQueryBuilder('c') | |||||
| // ->andWhere('c.exampleField = :val') | |||||
| // ->setParameter('val', $value) | |||||
| // ->orderBy('c.id', 'ASC') | |||||
| // ->setMaxResults(10) | |||||
| // ->getQuery() | |||||
| // ->getResult() | |||||
| // ; | |||||
| // } | |||||
| // public function findOneBySomeField($value): ?Comment | |||||
| // { | |||||
| // return $this->createQueryBuilder('c') | |||||
| // ->andWhere('c.exampleField = :val') | |||||
| // ->setParameter('val', $value) | |||||
| // ->getQuery() | |||||
| // ->getOneOrNullResult() | |||||
| // ; | |||||
| // } | |||||
| } | |||||
| @@ -1,66 +0,0 @@ | |||||
| <?php | |||||
| /** | |||||
| * @author Daniel Knudsen <d.knudsen@spawntree.de> | |||||
| * @date 25.01.24 | |||||
| */ | |||||
| namespace App\Serializer; | |||||
| use App\Entity\MediaObject; | |||||
| use Symfony\Component\DependencyInjection\Attribute\AsDecorator; | |||||
| use Symfony\Component\Serializer\Normalizer\NormalizerInterface; | |||||
| use Symfony\Component\Serializer\SerializerAwareInterface; | |||||
| use Symfony\Component\Serializer\SerializerInterface; | |||||
| use Vich\UploaderBundle\Storage\StorageInterface; | |||||
| #[AsDecorator('api_platform.jsonld.normalizer.item')] | |||||
| final class MediaObjectNormalizer implements NormalizerInterface, SerializerAwareInterface | |||||
| { | |||||
| private const ALREADY_CALLED = 'MEDIA_OBJECT_NORMALIZER_ALREADY_CALLED'; | |||||
| public function __construct( | |||||
| private StorageInterface $storage, | |||||
| private NormalizerInterface $normalizer | |||||
| ) | |||||
| { | |||||
| } | |||||
| public function normalize($object, ?string $format = null, array $context = []): array|string|int|float|bool|\ArrayObject|null | |||||
| { | |||||
| $context[self::ALREADY_CALLED] = true; | |||||
| if ($object instanceof MediaObject) { | |||||
| // Nur für MediaObject die URI generieren | |||||
| $object->contentUrl = $this->storage->resolveUri($object, 'file'); | |||||
| } | |||||
| return $this->normalizer->normalize($object, $format, $context); | |||||
| } | |||||
| public function supportsNormalization($data, ?string $format = null, array $context = []): bool | |||||
| { | |||||
| if (isset($context[self::ALREADY_CALLED])) { | |||||
| return false; | |||||
| } | |||||
| return $this->normalizer->supportsNormalization($data, $format, $context); | |||||
| } | |||||
| public function setSerializer(SerializerInterface $serializer): void | |||||
| { | |||||
| if ($this->normalizer instanceof SerializerAwareInterface) { | |||||
| $this->normalizer->setSerializer($serializer); | |||||
| } | |||||
| } | |||||
| public function getSupportedTypes(?string $format): array | |||||
| { | |||||
| if (method_exists($this->normalizer, 'getSupportedTypes')) { | |||||
| return $this->normalizer->getSupportedTypes($format); | |||||
| } | |||||
| return 'jsonld' === $format ? ['*' => true] : []; | |||||
| } | |||||
| } | |||||