Ver a proveniência

Merge branch 'master' of ssh://gitea.spawntree.de:1122/spawntree/matsen-tool-be

master
Florian Eisenmenger há 1 ano
ascendente
cometimento
a9eb64a582
30 ficheiros alterados com 6094 adições e 6 eliminações
  1. +1
    -0
      .gitignore
  2. +3
    -3
      config/packages/vich_uploader.yaml
  3. +35
    -0
      migrations/Version20240321094704.php
  4. +35
    -0
      migrations/Version20240322113223.php
  5. +68
    -0
      src/ApiResource/PartnerFollowApi.php
  6. +1
    -0
      src/ApiResource/PostingApi.php
  7. +59
    -0
      src/Controller/CreateDocumentObjectAction.php
  8. +4
    -0
      src/DataFixtures/AppFixtures.php
  9. BIN
      mple
  10. BIN
      mple
  11. BIN
      src/DataFixtures/documents/file-sample_100kB.doc
  12. BIN
      src/DataFixtures/documents/file-sample_100kB.odt
  13. BIN
      src/DataFixtures/documents/file_example_AVI_480_750kB.avi
  14. +5001
    -0
      src/DataFixtures/documents/file_example_CSV_5000.csv
  15. BIN
      src/DataFixtures/documents/file_example_XLS_10.xls
  16. +152
    -0
      src/Entity/DocumentObject.php
  17. +68
    -0
      src/Entity/Partner.php
  18. +74
    -0
      src/Entity/PartnerFollow.php
  19. +34
    -0
      src/Entity/Product.php
  20. +34
    -0
      src/Entity/User.php
  21. +84
    -0
      src/Factory/DocumentObjectFactory.php
  22. +70
    -0
      src/Factory/PartnerFollowFactory.php
  23. +0
    -1
      src/Mapper/CommentEntityToApiMapper.php
  24. +62
    -0
      src/Mapper/PartnerFollowApiToEntityMapper.php
  25. +54
    -0
      src/Mapper/PartnerFollowEntityToApiMapper.php
  26. +48
    -0
      src/Repository/PartnerFollowRepository.php
  27. +2
    -1
      src/Serializer/MediaObjectNormalizer.php
  28. +121
    -0
      tests/Functional/DocumentObjectResourceTest.php
  29. +1
    -1
      tests/Functional/MediaObjectResourceTest.php
  30. +83
    -0
      tests/Functional/PartnerFollowResourceTest.php

+ 1
- 0
.gitignore Ver ficheiro

@@ -6,6 +6,7 @@
/config/secrets/prod/prod.decrypt.private.php
/public/bundles/
/public/media/
/public/document/
/var/
/vendor/
###< symfony/framework-bundle ###


+ 3
- 3
config/packages/vich_uploader.yaml Ver ficheiro

@@ -11,8 +11,8 @@ vich_uploader:
inject_on_load: false
delete_on_update: true
delete_on_remove: true
download:
uri_prefix: /download
upload_destination: '%kernel.project_dir%/public/download'
document_object:
uri_prefix: /document
upload_destination: '%kernel.project_dir%/public/document'
# Will rename uploaded files using a uniqueid as a prefix.
namer: Vich\UploaderBundle\Naming\SmartUniqueNamer

+ 35
- 0
migrations/Version20240321094704.php Ver ficheiro

@@ -0,0 +1,35 @@
<?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 Version20240321094704 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 document_object (id INT AUTO_INCREMENT NOT NULL, partner_id INT DEFAULT NULL, product_id INT DEFAULT NULL, file_path VARCHAR(255) DEFAULT NULL, created_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', INDEX IDX_16CF1A8A9393F8FE (partner_id), INDEX IDX_16CF1A8A4584665A (product_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
$this->addSql('ALTER TABLE document_object ADD CONSTRAINT FK_16CF1A8A9393F8FE FOREIGN KEY (partner_id) REFERENCES partner (id)');
$this->addSql('ALTER TABLE document_object ADD CONSTRAINT FK_16CF1A8A4584665A FOREIGN KEY (product_id) REFERENCES product (id)');
}

public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE document_object DROP FOREIGN KEY FK_16CF1A8A9393F8FE');
$this->addSql('ALTER TABLE document_object DROP FOREIGN KEY FK_16CF1A8A4584665A');
$this->addSql('DROP TABLE document_object');
}
}

+ 35
- 0
migrations/Version20240322113223.php Ver ficheiro

@@ -0,0 +1,35 @@
<?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 Version20240322113223 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 partner_follow (id INT AUTO_INCREMENT NOT NULL, user_id INT NOT NULL, partner_id INT NOT NULL, created_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', INDEX IDX_55FFED0BA76ED395 (user_id), INDEX IDX_55FFED0B9393F8FE (partner_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
$this->addSql('ALTER TABLE partner_follow ADD CONSTRAINT FK_55FFED0BA76ED395 FOREIGN KEY (user_id) REFERENCES `user` (id) ON DELETE CASCADE');
$this->addSql('ALTER TABLE partner_follow ADD CONSTRAINT FK_55FFED0B9393F8FE FOREIGN KEY (partner_id) REFERENCES partner (id) ON DELETE CASCADE');
}

public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE partner_follow DROP FOREIGN KEY FK_55FFED0BA76ED395');
$this->addSql('ALTER TABLE partner_follow DROP FOREIGN KEY FK_55FFED0B9393F8FE');
$this->addSql('DROP TABLE partner_follow');
}
}

+ 68
- 0
src/ApiResource/PartnerFollowApi.php Ver ficheiro

@@ -0,0 +1,68 @@
<?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\Partner;
use App\Entity\PartnerFollow;
use App\Entity\User;
use App\State\EntityClassDtoStateProcessor;
use App\State\EntityToDtoStateProvider;
use ApiPlatform\Metadata\Delete;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Post;
use Symfony\Component\Serializer\Attribute\Groups;
use Symfony\Component\Validator\Constraints\NotBlank;

#[ApiResource(
shortName: 'PartnerFollow',
operations: [
new Get(
security: 'is_granted("ROLE_USER")'
),
new GetCollection(
security: 'is_granted("ROLE_USER")',
),
new Post(
security: 'is_granted("ROLE_USER")',
),
new Delete(
security: 'is_granted("ROLE_USER")',
)
],
security: 'is_granted("ROLE_USER")',
provider: EntityToDtoStateProvider::class,
processor: EntityClassDtoStateProcessor::class,
stateOptions: new Options(entityClass: PartnerFollow::class),
)]
#[ApiFilter(SearchFilter::class, properties: ['partner' => 'exact', 'contact' => 'exact'])]
class PartnerFollowApi
{
#[ApiProperty(readable: false, writable: false, identifier: true)]
public ?int $id = null;

#[ApiProperty(writable: false)]
public ?UserApi $user = null;

#[ApiProperty(writable: false)]
public ?string $userName = null;

#[NotBlank]
public ?PartnerApi $partner = null;

#[ApiProperty(writable: false)]
public ?string $partnerName = null;

#[ApiProperty(writable: false)]
public ?\DateTimeImmutable $createdAt = null;
}

+ 1
- 0
src/ApiResource/PostingApi.php Ver ficheiro

@@ -90,6 +90,7 @@ class PostingApi implements OwnerInterface
* @var $comments array<int, CommentApi>
*/
#[ApiProperty(
writable: false,
readableLink: true,
writableLink: true,
builtinTypes: [


+ 59
- 0
src/Controller/CreateDocumentObjectAction.php Ver ficheiro

@@ -0,0 +1,59 @@
<?php
/**
* @author Daniel Knudsen <d.knudsen@spawntree.de>
* @date 25.01.24
*/


namespace App\Controller;


use ApiPlatform\Api\IriConverterInterface;
use App\Entity\DocumentObject;
use App\Entity\Partner;
use App\Entity\Product;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Attribute\AsController;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfonycasts\MicroMapper\MicroMapperInterface;

#[AsController]
final class CreateDocumentObjectAction extends AbstractController
{
public function __invoke(
Request $request,
IriConverterInterface $iriConverter,
MicroMapperInterface $microMapper
): DocumentObject {
$uploadedFile = $request->files->get('file');
if (!$uploadedFile) {
throw new BadRequestHttpException('"file" is required');
}

$documentObject = new DocumentObject();
$documentObject->file = $uploadedFile;

$partnerIri = $request->request->get('partner');
if ($partnerIri !== null) {
try {
$partnerApi = $iriConverter->getResourceFromIri($partnerIri);
} catch (\Exception $exception) {
throw new BadRequestHttpException('invalid partner iri');
}
$documentObject->setPartner($microMapper->map($partnerApi, Partner::class));
}

$productIri = $request->request->get('product');
if ($productIri !== null) {
try {
$productApi = $iriConverter->getResourceFromIri($productIri);
} catch (\Exception $exception) {
throw new BadRequestHttpException('invalid product iri');
}
$documentObject->setProduct($microMapper->map($productApi, Product::class));
}

return $documentObject;
}
}

+ 4
- 0
src/DataFixtures/AppFixtures.php Ver ficheiro

@@ -4,11 +4,13 @@ namespace App\DataFixtures;

use App\Factory\CommentFactory;
use App\Factory\ContactFactory;
use App\Factory\DocumentObjectFactory;
use App\Factory\MediaObjectLogoFactory;
use App\Factory\MediaObjectProductFactory;
use App\Factory\MediaObjectContactFactory;
use App\Factory\MediaObjectUserFactory;
use App\Factory\PartnerFactory;
use App\Factory\PartnerFollowFactory;
use App\Factory\PostingFactory;
use App\Factory\ProductFactory;
use App\Factory\SaleFactory;
@@ -73,5 +75,7 @@ class AppFixtures extends Fixture
MediaObjectProductFactory::createMany(50);
TaskFactory::createMany(50);
TaskNoteFactory::createMany(100);
DocumentObjectFactory::createMany(50);
PartnerFollowFactory::createMany(100);
}
}

BIN
src/DataFixtures/documents/A → mple Ver ficheiro


BIN
src/DataFixtures/documents/A → mple Ver ficheiro


BIN
src/DataFixtures/documents/file-sample_100kB.doc Ver ficheiro


BIN
src/DataFixtures/documents/file-sample_100kB.odt Ver ficheiro


BIN
src/DataFixtures/documents/file_example_AVI_480_750kB.avi Ver ficheiro


+ 5001
- 0
src/DataFixtures/documents/file_example_CSV_5000.csv
A apresentação das diferenças no ficheiro foi suprimida por ser demasiado grande
Ver ficheiro


BIN
src/DataFixtures/documents/file_example_XLS_10.xls Ver ficheiro


+ 152
- 0
src/Entity/DocumentObject.php Ver ficheiro

@@ -0,0 +1,152 @@
<?php
/**
* @author Daniel Knudsen <d.knudsen@spawntree.de>
* @date 25.01.24
*/

namespace App\Entity;

use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
use ApiPlatform\Metadata\ApiFilter;
use ApiPlatform\Metadata\ApiProperty;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Delete;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Post;
use ApiPlatform\OpenApi\Model;
use App\Controller\CreateDocumentObjectAction;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
use Vich\UploaderBundle\Mapping\Annotation as Vich;

#[Vich\Uploadable]
#[ORM\Entity]
#[ApiResource(
shortName: 'Document',
types: ['https://schema.org/MediaObject'],
operations: [
new Get(),
new GetCollection(),
new Post(
controller: CreateDocumentObjectAction::class,
openapi: new Model\Operation(
requestBody: new Model\RequestBody(
content: new \ArrayObject([
'multipart/form-data' => [
'schema' => [
'type' => 'object',
'properties' => [
'file' => [
'type' => 'string',
'format' => 'binary'
],
'partner' => [
'type' => 'string',
'format' => 'iri_reference',
'example' => 'https://example.com/'
],
'product' => [
'type' => 'string',
'format' => 'iri_reference',
'example' => 'https://example.com/'
]
],
],
],
])
)
),
validationContext: ['groups' => ['Default', 'document_object_create']],
deserialize: false
),
new Delete(
// controller: DeleteMediaObjectAction::class
),
],
normalizationContext: ['groups' => ['document_object:read']],
security: 'is_granted("ROLE_USER")',
)]
#[ApiFilter(SearchFilter::class, properties: ['partner' => 'exact', 'product' => 'exact'])]
class DocumentObject
{
#[ORM\Id, ORM\Column, ORM\GeneratedValue]
private ?int $id = null;

#[ApiProperty(types: ['https://schema.org/contentUrl'])]
#[Groups(['document_object:read'])]
public ?string $contentUrl = null;

#[Vich\UploadableField(mapping: 'document_object', fileNameProperty: 'filePath')]
#[Assert\NotNull(groups: ['document_object_create'])]
public ?File $file = null;

#[ORM\Column(nullable: true)]
public ?string $filePath = null;

#[ORM\Column]
private ?\DateTimeImmutable $createdAt = null;

#[ORM\ManyToOne(inversedBy: 'documentObjects')]
private ?Partner $partner = null;

#[ORM\ManyToOne(inversedBy: 'documentObjects')]
private ?Product $product = null;

public function __construct()
{
$this->createdAt = new \DateTimeImmutable();
}

public function getId(): ?int
{
return $this->id;
}

public function getContentUrl(): ?string
{
return $this->contentUrl;
}

public function getFile(): ?File
{
return $this->file;
}

public function getFilePath(): ?string
{
return $this->filePath;
}

public function getCreatedAt(): ?\DateTimeImmutable
{
return $this->createdAt;
}

public function getPartner(): ?Partner
{
return $this->partner;
}

public function setPartner(?Partner $partner): static
{
$this->partner = $partner;

return $this;
}

public function getProduct(): ?Product
{
return $this->product;
}

public function setProduct(?Product $product): static
{
$this->product = $product;

return $this;
}

}

+ 68
- 0
src/Entity/Partner.php Ver ficheiro

@@ -57,12 +57,20 @@ class Partner
#[ORM\OneToMany(mappedBy: 'partner', targetEntity: Sale::class)]
private Collection $sales;

#[ORM\OneToMany(mappedBy: 'partner', targetEntity: DocumentObject::class)]
private Collection $documentObjects;

#[ORM\OneToMany(mappedBy: 'partner', targetEntity: PartnerFollow::class)]
private Collection $partnerFollows;

public function __construct()
{
$this->createdAt = new \DateTimeImmutable();
$this->contacts = new ArrayCollection();
$this->postings = new ArrayCollection();
$this->sales = new ArrayCollection();
$this->documentObjects = new ArrayCollection();
$this->partnerFollows = new ArrayCollection();
}

public function getId(): ?int
@@ -240,4 +248,64 @@ class Partner
return $this;
}

/**
* @return Collection<int, DocumentObject>
*/
public function getDocumentObjects(): Collection
{
return $this->documentObjects;
}

public function addDocumentObject(DocumentObject $documentObject): static
{
if (!$this->documentObjects->contains($documentObject)) {
$this->documentObjects->add($documentObject);
$documentObject->setPartner($this);
}

return $this;
}

public function removeDocumentObject(DocumentObject $documentObject): static
{
if ($this->documentObjects->removeElement($documentObject)) {
// set the owning side to null (unless already changed)
if ($documentObject->getPartner() === $this) {
$documentObject->setPartner(null);
}
}

return $this;
}

/**
* @return Collection<int, PartnerFollow>
*/
public function getPartnerFollows(): Collection
{
return $this->partnerFollows;
}

public function addPartnerFollow(PartnerFollow $partnerFollow): static
{
if (!$this->partnerFollows->contains($partnerFollow)) {
$this->partnerFollows->add($partnerFollow);
$partnerFollow->setPartner($this);
}

return $this;
}

public function removePartnerFollow(PartnerFollow $partnerFollow): static
{
if ($this->partnerFollows->removeElement($partnerFollow)) {
// set the owning side to null (unless already changed)
if ($partnerFollow->getPartner() === $this) {
$partnerFollow->setPartner(null);
}
}

return $this;
}

}

+ 74
- 0
src/Entity/PartnerFollow.php Ver ficheiro

@@ -0,0 +1,74 @@
<?php

namespace App\Entity;

use App\Repository\PartnerFollowRepository;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity(repositoryClass: PartnerFollowRepository::class)]
class PartnerFollow
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;

#[ORM\ManyToOne(inversedBy: 'partnerFollows')]
#[ORM\JoinColumn(nullable: false, onDelete: "CASCADE")]
private ?User $user = null;

#[ORM\ManyToOne(inversedBy: 'partnerFollows')]
#[ORM\JoinColumn(nullable: false, onDelete: "CASCADE")]
private ?Partner $partner = null;

#[ORM\Column]
private ?\DateTimeImmutable $createdAt = null;

public function __construct(User $user, Partner $partner)
{
$this->user = $user;
$this->partner = $partner;
$this->createdAt = new \DateTimeImmutable();
}

public function getId(): ?int
{
return $this->id;
}

public function getUser(): ?User
{
return $this->user;
}

public function setUser(?User $user): static
{
$this->user = $user;

return $this;
}

public function getPartner(): ?Partner
{
return $this->partner;
}

public function setPartner(?Partner $partner): static
{
$this->partner = $partner;

return $this;
}

public function getCreatedAt(): ?\DateTimeImmutable
{
return $this->createdAt;
}

public function setCreatedAt(\DateTimeImmutable $createdAt): static
{
$this->createdAt = $createdAt;

return $this;
}
}

+ 34
- 0
src/Entity/Product.php Ver ficheiro

@@ -32,10 +32,14 @@ class Product
#[ORM\OneToMany(mappedBy: 'product', targetEntity: Sale::class)]
private Collection $sales;

#[ORM\OneToMany(mappedBy: 'product', targetEntity: DocumentObject::class)]
private Collection $documentObjects;

public function __construct()
{
$this->createdAt = new \DateTimeImmutable();
$this->sales = new ArrayCollection();
$this->documentObjects = new ArrayCollection();
}

public function getId(): ?int
@@ -113,4 +117,34 @@ class Product

return $this;
}

/**
* @return Collection<int, DocumentObject>
*/
public function getDocumentObjects(): Collection
{
return $this->documentObjects;
}

public function addDocumentObject(DocumentObject $documentObject): static
{
if (!$this->documentObjects->contains($documentObject)) {
$this->documentObjects->add($documentObject);
$documentObject->setProduct($this);
}

return $this;
}

public function removeDocumentObject(DocumentObject $documentObject): static
{
if ($this->documentObjects->removeElement($documentObject)) {
// set the owning side to null (unless already changed)
if ($documentObject->getProduct() === $this) {
$documentObject->setProduct(null);
}
}

return $this;
}
}

+ 34
- 0
src/Entity/User.php Ver ficheiro

@@ -55,6 +55,9 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
#[ORM\OneToMany(mappedBy: 'owner', targetEntity: Sale::class)]
private Collection $sales;

#[ORM\OneToMany(mappedBy: 'user', targetEntity: PartnerFollow::class)]
private Collection $partnerFollows;


public function __construct()
{
@@ -63,6 +66,7 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
$this->active = true;
$this->comments = new ArrayCollection();
$this->sales = new ArrayCollection();
$this->partnerFollows = new ArrayCollection();
}

public function getId(): ?int
@@ -280,4 +284,34 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface

return $this;
}

/**
* @return Collection<int, PartnerFollow>
*/
public function getPartnerFollows(): Collection
{
return $this->partnerFollows;
}

public function addPartnerFollow(PartnerFollow $partnerFollow): static
{
if (!$this->partnerFollows->contains($partnerFollow)) {
$this->partnerFollows->add($partnerFollow);
$partnerFollow->setUser($this);
}

return $this;
}

public function removePartnerFollow(PartnerFollow $partnerFollow): static
{
if ($this->partnerFollows->removeElement($partnerFollow)) {
// set the owning side to null (unless already changed)
if ($partnerFollow->getUser() === $this) {
$partnerFollow->setUser(null);
}
}

return $this;
}
}

+ 84
- 0
src/Factory/DocumentObjectFactory.php Ver ficheiro

@@ -0,0 +1,84 @@
<?php

namespace App\Factory;

use App\Entity\DocumentObject;
use Doctrine\ORM\EntityRepository;
use Symfony\Component\HttpKernel\KernelInterface;
use Vich\UploaderBundle\FileAbstraction\ReplacingFile;
use Zenstruck\Foundry\ModelFactory;
use Zenstruck\Foundry\Proxy;
use Zenstruck\Foundry\RepositoryProxy;

/**
* @extends ModelFactory<DocumentObject>
*
* @method DocumentObject|Proxy create(array|callable $attributes = [])
* @method static DocumentObject|Proxy createOne(array $attributes = [])
* @method static DocumentObject|Proxy find(object|array|mixed $criteria)
* @method static DocumentObject|Proxy findOrCreate(array $attributes)
* @method static DocumentObject|Proxy first(string $sortedField = 'id')
* @method static DocumentObject|Proxy last(string $sortedField = 'id')
* @method static DocumentObject|Proxy random(array $attributes = [])
* @method static DocumentObject|Proxy randomOrCreate(array $attributes = [])
* @method static EntityRepository|RepositoryProxy repository()
* @method static DocumentObject[]|Proxy[] all()
* @method static DocumentObject[]|Proxy[] createMany(int $number, array|callable $attributes = [])
* @method static DocumentObject[]|Proxy[] createSequence(iterable|callable $sequence)
* @method static DocumentObject[]|Proxy[] findBy(array $attributes)
* @method static DocumentObject[]|Proxy[] randomRange(int $min, int $max, array $attributes = [])
* @method static DocumentObject[]|Proxy[] randomSet(int $number, array $attributes = [])
*/
final class DocumentObjectFactory extends ModelFactory
{
/**
* @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services
*
* @todo inject services if required
*/
public function __construct(
private KernelInterface $appKernel
)
{
parent::__construct();
}

/**
* @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories
*
* @todo add your default values here
*/
protected function getDefaults(): array
{
$projectRoot = $this->appKernel->getProjectDir();

$folderPath = $projectRoot . '/src/DataFixtures/documents/';
$files = glob($folderPath . '*.*');
$randomFile = null;
if ($files !== false && count($files) > 0) {
$randomFile = $files[array_rand($files)];
}

$randBool = (bool)random_int(0, 1);
return [
'file' => new ReplacingFile($randomFile),
'partner' => $randBool ? PartnerFactory::random() : null,
'product' => !$randBool ? ProductFactory::random() : null
];
}

/**
* @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization
*/
protected function initialize(): self
{
return $this
// ->afterInstantiate(function(DocumentObject $documentObject): void {})
;
}

protected static function getClass(): string
{
return DocumentObject::class;
}
}

+ 70
- 0
src/Factory/PartnerFollowFactory.php Ver ficheiro

@@ -0,0 +1,70 @@
<?php

namespace App\Factory;

use App\Entity\PartnerFollow;
use App\Repository\PartnerFollowRepository;
use Zenstruck\Foundry\ModelFactory;
use Zenstruck\Foundry\Proxy;
use Zenstruck\Foundry\RepositoryProxy;

/**
* @extends ModelFactory<PartnerFollow>
*
* @method PartnerFollow|Proxy create(array|callable $attributes = [])
* @method static PartnerFollow|Proxy createOne(array $attributes = [])
* @method static PartnerFollow|Proxy find(object|array|mixed $criteria)
* @method static PartnerFollow|Proxy findOrCreate(array $attributes)
* @method static PartnerFollow|Proxy first(string $sortedField = 'id')
* @method static PartnerFollow|Proxy last(string $sortedField = 'id')
* @method static PartnerFollow|Proxy random(array $attributes = [])
* @method static PartnerFollow|Proxy randomOrCreate(array $attributes = [])
* @method static PartnerFollowRepository|RepositoryProxy repository()
* @method static PartnerFollow[]|Proxy[] all()
* @method static PartnerFollow[]|Proxy[] createMany(int $number, array|callable $attributes = [])
* @method static PartnerFollow[]|Proxy[] createSequence(iterable|callable $sequence)
* @method static PartnerFollow[]|Proxy[] findBy(array $attributes)
* @method static PartnerFollow[]|Proxy[] randomRange(int $min, int $max, array $attributes = [])
* @method static PartnerFollow[]|Proxy[] randomSet(int $number, array $attributes = [])
*/
final class PartnerFollowFactory 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()),
'partner' => PartnerFactory::random(),
'user' => UserFactory::random(),
];
}

/**
* @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization
*/
protected function initialize(): self
{
return $this
// ->afterInstantiate(function(PartnerFollow $partnerFollow): void {})
;
}

protected static function getClass(): string
{
return PartnerFollow::class;
}
}

+ 0
- 1
src/Mapper/CommentEntityToApiMapper.php Ver ficheiro

@@ -6,7 +6,6 @@ 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;


+ 62
- 0
src/Mapper/PartnerFollowApiToEntityMapper.php Ver ficheiro

@@ -0,0 +1,62 @@
<?php

namespace App\Mapper;

use App\ApiResource\PartnerFollowApi;
use App\Entity\Partner;
use App\Entity\PartnerFollow;
use App\Entity\User;
use App\Repository\PartnerFollowRepository;
use Symfony\Bundle\SecurityBundle\Security;
use Symfonycasts\MicroMapper\AsMapper;
use Symfonycasts\MicroMapper\MapperInterface;
use Symfonycasts\MicroMapper\MicroMapperInterface;

#[AsMapper(from: PartnerFollowApi::class, to: PartnerFollow::class)]
class PartnerFollowApiToEntityMapper implements MapperInterface
{
public function __construct(
private PartnerFollowRepository $repository,
private Security $security,
private MicroMapperInterface $microMapper,
)
{

}

public function load(object $from, string $toClass, array $context): object
{
$dto = $from;
assert($dto instanceof PartnerFollowApi);

if ($dto->id) {
$entity = $this->repository->find($dto->id);
} else {
$user = $this->security->getUser();
assert($user instanceof User);
if ($dto->partner === null) {
throw new \Exception('Partner missing');
}
$partner = $this->microMapper->map($dto->partner, Partner::class, [
MicroMapperInterface::MAX_DEPTH => 1,
]);
assert($partner instanceof Partner);
$entity = new PartnerFollow($user, $partner);
}

if (!$entity) {
throw new \Exception('PartnerFollow not found');
}

return $entity;
}

public function populate(object $from, object $to, array $context): object
{
$dto = $from;
assert($dto instanceof PartnerFollowApi);
$entity = $to;
assert($entity instanceof PartnerFollow);
return $entity;
}
}

+ 54
- 0
src/Mapper/PartnerFollowEntityToApiMapper.php Ver ficheiro

@@ -0,0 +1,54 @@
<?php

namespace App\Mapper;

use App\ApiResource\PartnerApi;
use App\ApiResource\PartnerFollowApi;
use App\ApiResource\UserApi;
use App\Entity\PartnerFollow;
use Symfonycasts\MicroMapper\AsMapper;
use Symfonycasts\MicroMapper\MapperInterface;
use Symfonycasts\MicroMapper\MicroMapperInterface;

#[AsMapper(from: PartnerFollow::class, to: PartnerFollowApi::class)]
class PartnerFollowEntityToApiMapper implements MapperInterface
{
public function __construct(
private MicroMapperInterface $microMapper
)
{
}

public function load(object $from, string $toClass, array $context): object
{
$entity = $from;
assert($entity instanceof PartnerFollow);

$dto = new PartnerFollowApi();
$dto->id = $entity->getId();

return $dto;
}

public function populate(object $from, object $to, array $context): object
{
$entity = $from;
$dto = $to;
assert($entity instanceof PartnerFollow);
assert($dto instanceof PartnerFollowApi);

$dto->user = $this->microMapper->map($entity->getUser(), UserApi::class, [
MicroMapperInterface::MAX_DEPTH => 1,
]);
$dto->userName = $entity->getUser()?->getFirstName()." ".$entity->getUser()?->getLastName();

$dto->partner = $this->microMapper->map($entity->getPartner(), PartnerApi::class, [
MicroMapperInterface::MAX_DEPTH => 1,
]);
$dto->partnerName = $entity->getPartner()?->getName();

$dto->createdAt = $entity->getCreatedAt();

return $dto;
}
}

+ 48
- 0
src/Repository/PartnerFollowRepository.php Ver ficheiro

@@ -0,0 +1,48 @@
<?php

namespace App\Repository;

use App\Entity\PartnerFollow;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;

/**
* @extends ServiceEntityRepository<PartnerFollow>
*
* @method PartnerFollow|null find($id, $lockMode = null, $lockVersion = null)
* @method PartnerFollow|null findOneBy(array $criteria, array $orderBy = null)
* @method PartnerFollow[] findAll()
* @method PartnerFollow[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class PartnerFollowRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, PartnerFollow::class);
}

// /**
// * @return PartnerFollow[] Returns an array of PartnerFollow objects
// */
// public function findByExampleField($value): array
// {
// return $this->createQueryBuilder('p')
// ->andWhere('p.exampleField = :val')
// ->setParameter('val', $value)
// ->orderBy('p.id', 'ASC')
// ->setMaxResults(10)
// ->getQuery()
// ->getResult()
// ;
// }

// public function findOneBySomeField($value): ?PartnerFollow
// {
// return $this->createQueryBuilder('p')
// ->andWhere('p.exampleField = :val')
// ->setParameter('val', $value)
// ->getQuery()
// ->getOneOrNullResult()
// ;
// }
}

+ 2
- 1
src/Serializer/MediaObjectNormalizer.php Ver ficheiro

@@ -8,6 +8,7 @@
namespace App\Serializer;


use App\Entity\DocumentObject;
use App\Entity\MediaObject;
use Symfony\Component\DependencyInjection\Attribute\AsDecorator;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
@@ -31,7 +32,7 @@ final class MediaObjectNormalizer implements NormalizerInterface, SerializerAwar
{
$context[self::ALREADY_CALLED] = true;

if ($object instanceof MediaObject) {
if ($object instanceof MediaObject || $object instanceof DocumentObject) {
// Nur für MediaObject die URI generieren
$object->contentUrl = $this->storage->resolveUri($object, 'file');
}


+ 121
- 0
tests/Functional/DocumentObjectResourceTest.php Ver ficheiro

@@ -0,0 +1,121 @@
<?php
/**
* @author Daniel Knudsen <d.knudsen@spawntree.de>
* @date 01.03.24
*/


namespace App\Tests\Functional;

use App\Factory\DocumentObjectFactory;
use App\Factory\MediaObjectLogoFactory;
use App\Factory\MediaObjectProductFactory;
use App\Factory\PartnerFactory;
use App\Factory\ProductFactory;
use App\Factory\UserFactory;
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Zenstruck\Browser\Test\HasBrowser;
use Zenstruck\Foundry\Test\Factories;
use Zenstruck\Foundry\Test\ResetDatabase;

class DocumentObjectResourceTest extends KernelTestCase
{
use HasBrowser;
use ResetDatabase;
use Factories;

private JWTTokenManagerInterface $JWTManager;
private string $projectDir;

protected function setUp(): void
{
parent::setUp();
$this->JWTManager = self::getContainer()->get('lexik_jwt_authentication.jwt_manager');
$this->projectDir = self::getContainer()->get('kernel')->getProjectDir();
}

public function testCreateDocumentObject(): void
{
$path = $this->projectDir . '/tests/fixtures/';
$srcFile = $path . '1176.png';
$dstFile = $path . '1176_upload.png';
copy($srcFile, $dstFile);

$file = new UploadedFile($dstFile, 'image.png');

$user = UserFactory::createOne(
[
'firstName' => 'Peter',
'lastName' => 'Test',
'password' => 'test',
'email' => 'peter@test.de',
]
);
MediaObjectProductFactory::createOne();
$partner = PartnerFactory::createOne();
$product = ProductFactory::createOne();

$token = $this->JWTManager->create($user->object());

$this->browser()
->get('/api/documents', [
'headers' => [
'Authorization' => 'Bearer ' . $token,
],
])
->assertSuccessful()
;

$this->browser()
->post('/api/documents', [
'headers' => [
'Authorization' => 'Bearer ' . $token,
'Content-Type' => 'multipart/form-data'
],
'files' => [
'file' => $file,
],
'partner' => '/api/partners/' . $partner->getId(),
'product' => '/api/products/' . $product->getId(),
])
->assertSuccessful()
;

$this->browser()
->delete('/api/documents/1',
[
'headers' => [
'Authorization' => 'Bearer ' . $token,
],
])
->assertSuccessful()
;
}

public function testDeleteDocumentObject()
{
MediaObjectProductFactory::createOne();
$partner = PartnerFactory::createOne();
$product = ProductFactory::createOne();
$documentsObject = DocumentObjectFactory::createOne();
$user = UserFactory::createOne(
[
'firstName' => 'Peter',
'lastName' => 'Test',
'password' => 'test',
'email' => 'peter@test.de',
]
);
$token = $this->JWTManager->create($user->object());
$this->browser()
->delete('/api/documents/' . $documentsObject->getId(), [
'headers' => [
'Authorization' => 'Bearer ' . $token,
],
])
->assertSuccessful()
;
}
}

+ 1
- 1
tests/Functional/MediaObjectResourceTest.php Ver ficheiro

@@ -33,7 +33,7 @@ class MediaObjectResourceTest extends KernelTestCase
$this->projectDir = self::getContainer()->get('kernel')->getProjectDir();
}

public function testCreateAMediaObject(): void
public function testCreateMediaObject(): void
{
$path = $this->projectDir . '/tests/fixtures/';
$srcFile = $path . '1176.png';


+ 83
- 0
tests/Functional/PartnerFollowResourceTest.php Ver ficheiro

@@ -0,0 +1,83 @@
<?php
/**
* @author Daniel Knudsen <d.knudsen@spawntree.de>
* @date 12.12.23
*/


namespace App\Tests\Functional;

use App\Factory\MediaObjectLogoFactory;
use App\Factory\PartnerFactory;
use App\Factory\PartnerFollowFactory;
use App\Factory\UserFactory;
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Zenstruck\Browser\Test\HasBrowser;
use Zenstruck\Foundry\Test\Factories;
use Zenstruck\Foundry\Test\ResetDatabase;

class PartnerFollowResourceTest extends KernelTestCase
{
use HasBrowser;
use ResetDatabase;
use Factories;

private JWTTokenManagerInterface $JWTManager;

protected function setUp(): void
{
parent::setUp();
$this->JWTManager = self::getContainer()->get('lexik_jwt_authentication.jwt_manager');
}

public function testPostPartnerFollow(): void
{
$user = UserFactory::createOne(
[
'firstName' => 'Peter',
'lastName' => 'Test',
'password' => 'test',
'email' => 'peter@test.de',
]
);
MediaObjectLogoFactory::createOne();
$partner = PartnerFactory::createOne();
PartnerFollowFactory::createOne();

$token = $this->JWTManager->create($user->object());

$this->browser()
->get('/api/partner_follows', [
'headers' => [
'Authorization' => 'Bearer ' . $token,
],
])
->assertSuccessful()
->assertJsonMatches('"hydra:totalItems"', 1)
;


$this->browser()
->post('/api/partner_follows' , [
'json' => [
'partner' => '/api/partners/' . $partner->getId(),
],
'headers' => [
'Authorization' => 'Bearer ' . $token,
]
])
->assertSuccessful()
;

$this->browser()
->get('/api/partner_follows', [
'headers' => [
'Authorization' => 'Bearer ' . $token,
],
])
->assertSuccessful()
->assertJsonMatches('"hydra:totalItems"', 2)
;
}
}

Carregando…
Cancelar
Guardar