| @@ -10,7 +10,7 @@ use Doctrine\Migrations\AbstractMigration; | |||||
| /** | /** | ||||
| * Auto-generated Migration: Please modify to your needs! | * Auto-generated Migration: Please modify to your needs! | ||||
| */ | */ | ||||
| final class Version20240308161707 extends AbstractMigration | |||||
| final class Version20240312084330 extends AbstractMigration | |||||
| { | { | ||||
| public function getDescription(): string | public function getDescription(): string | ||||
| { | { | ||||
| @@ -26,6 +26,7 @@ final class Version20240308161707 extends AbstractMigration | |||||
| $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 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) 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 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 product (id INT AUTO_INCREMENT NOT NULL, image_id INT DEFAULT NULL, name VARCHAR(255) NOT NULL, description LONGTEXT DEFAULT NULL, created_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', INDEX IDX_D34A04AD3DA5256D (image_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); | $this->addSql('CREATE TABLE product (id INT AUTO_INCREMENT NOT NULL, image_id INT DEFAULT NULL, name VARCHAR(255) NOT NULL, description LONGTEXT DEFAULT NULL, created_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', INDEX IDX_D34A04AD3DA5256D (image_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); | ||||
| $this->addSql('CREATE TABLE task (id INT AUTO_INCREMENT NOT NULL, created_by_id INT NOT NULL, assigned_to_id INT NOT NULL, partner_id INT DEFAULT NULL, contact_id INT DEFAULT NULL, headline VARCHAR(255) NOT NULL, description LONGTEXT DEFAULT NULL, due_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', prio VARCHAR(255) NOT NULL, completed TINYINT(1) NOT NULL, created_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', INDEX IDX_527EDB25B03A8386 (created_by_id), INDEX IDX_527EDB25F4BD7827 (assigned_to_id), INDEX IDX_527EDB259393F8FE (partner_id), INDEX IDX_527EDB25E7A1254A (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, image_id INT DEFAULT 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), INDEX IDX_8D93D6493DA5256D (image_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, image_id INT DEFAULT 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), INDEX IDX_8D93D6493DA5256D (image_id), 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_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 comment ADD CONSTRAINT FK_9474526C9AE985F6 FOREIGN KEY (posting_id) REFERENCES posting (id)'); | ||||
| @@ -36,6 +37,10 @@ final class Version20240308161707 extends AbstractMigration | |||||
| $this->addSql('ALTER TABLE posting ADD CONSTRAINT FK_BD275D739393F8FE FOREIGN KEY (partner_id) REFERENCES partner (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)'); | $this->addSql('ALTER TABLE posting ADD CONSTRAINT FK_BD275D73E7A1254A FOREIGN KEY (contact_id) REFERENCES contact (id)'); | ||||
| $this->addSql('ALTER TABLE product ADD CONSTRAINT FK_D34A04AD3DA5256D FOREIGN KEY (image_id) REFERENCES media_object (id) ON DELETE SET NULL'); | $this->addSql('ALTER TABLE product ADD CONSTRAINT FK_D34A04AD3DA5256D FOREIGN KEY (image_id) REFERENCES media_object (id) ON DELETE SET NULL'); | ||||
| $this->addSql('ALTER TABLE task ADD CONSTRAINT FK_527EDB25B03A8386 FOREIGN KEY (created_by_id) REFERENCES `user` (id)'); | |||||
| $this->addSql('ALTER TABLE task ADD CONSTRAINT FK_527EDB25F4BD7827 FOREIGN KEY (assigned_to_id) REFERENCES `user` (id)'); | |||||
| $this->addSql('ALTER TABLE task ADD CONSTRAINT FK_527EDB259393F8FE FOREIGN KEY (partner_id) REFERENCES partner (id) ON DELETE SET NULL'); | |||||
| $this->addSql('ALTER TABLE task ADD CONSTRAINT FK_527EDB25E7A1254A FOREIGN KEY (contact_id) REFERENCES contact (id) ON DELETE SET NULL'); | |||||
| $this->addSql('ALTER TABLE `user` ADD CONSTRAINT FK_8D93D6493DA5256D FOREIGN KEY (image_id) REFERENCES media_object (id) ON DELETE SET NULL'); | $this->addSql('ALTER TABLE `user` ADD CONSTRAINT FK_8D93D6493DA5256D FOREIGN KEY (image_id) REFERENCES media_object (id) ON DELETE SET NULL'); | ||||
| } | } | ||||
| @@ -51,6 +56,10 @@ final class Version20240308161707 extends AbstractMigration | |||||
| $this->addSql('ALTER TABLE posting DROP FOREIGN KEY FK_BD275D739393F8FE'); | $this->addSql('ALTER TABLE posting DROP FOREIGN KEY FK_BD275D739393F8FE'); | ||||
| $this->addSql('ALTER TABLE posting DROP FOREIGN KEY FK_BD275D73E7A1254A'); | $this->addSql('ALTER TABLE posting DROP FOREIGN KEY FK_BD275D73E7A1254A'); | ||||
| $this->addSql('ALTER TABLE product DROP FOREIGN KEY FK_D34A04AD3DA5256D'); | $this->addSql('ALTER TABLE product DROP FOREIGN KEY FK_D34A04AD3DA5256D'); | ||||
| $this->addSql('ALTER TABLE task DROP FOREIGN KEY FK_527EDB25B03A8386'); | |||||
| $this->addSql('ALTER TABLE task DROP FOREIGN KEY FK_527EDB25F4BD7827'); | |||||
| $this->addSql('ALTER TABLE task DROP FOREIGN KEY FK_527EDB259393F8FE'); | |||||
| $this->addSql('ALTER TABLE task DROP FOREIGN KEY FK_527EDB25E7A1254A'); | |||||
| $this->addSql('ALTER TABLE `user` DROP FOREIGN KEY FK_8D93D6493DA5256D'); | $this->addSql('ALTER TABLE `user` DROP FOREIGN KEY FK_8D93D6493DA5256D'); | ||||
| $this->addSql('DROP TABLE comment'); | $this->addSql('DROP TABLE comment'); | ||||
| $this->addSql('DROP TABLE contact'); | $this->addSql('DROP TABLE contact'); | ||||
| @@ -58,6 +67,7 @@ final class Version20240308161707 extends AbstractMigration | |||||
| $this->addSql('DROP TABLE partner'); | $this->addSql('DROP TABLE partner'); | ||||
| $this->addSql('DROP TABLE posting'); | $this->addSql('DROP TABLE posting'); | ||||
| $this->addSql('DROP TABLE product'); | $this->addSql('DROP TABLE product'); | ||||
| $this->addSql('DROP TABLE task'); | |||||
| $this->addSql('DROP TABLE `user`'); | $this->addSql('DROP TABLE `user`'); | ||||
| } | } | ||||
| } | } | ||||
| @@ -0,0 +1,82 @@ | |||||
| <?php | |||||
| /** | |||||
| * @author Daniel Knudsen <d.knudsen@spawntree.de> | |||||
| * @date 12.12.23 | |||||
| */ | |||||
| namespace App\ApiResource; | |||||
| use ApiPlatform\Doctrine\Orm\State\Options; | |||||
| use ApiPlatform\Metadata\ApiProperty; | |||||
| use ApiPlatform\Metadata\ApiResource; | |||||
| use ApiPlatform\Metadata\Delete; | |||||
| use ApiPlatform\Metadata\Get; | |||||
| use ApiPlatform\Metadata\GetCollection; | |||||
| use ApiPlatform\Metadata\Patch; | |||||
| use ApiPlatform\Metadata\Post; | |||||
| use App\Entity\Task; | |||||
| use App\Enum\PrioType; | |||||
| use App\State\EntityClassDtoStateProcessor; | |||||
| use App\State\EntityToDtoStateProvider; | |||||
| use Symfony\Component\Validator\Constraints as Assert; | |||||
| #[ApiResource( | |||||
| shortName: 'Task', | |||||
| operations: [ | |||||
| new Get( | |||||
| security: 'is_granted("ROLE_USER")' | |||||
| ), | |||||
| new GetCollection( | |||||
| security: 'is_granted("ROLE_USER")' | |||||
| ), | |||||
| new Post( | |||||
| security: 'is_granted("ROLE_USER")', | |||||
| validationContext: ['groups' => ['Default', 'postValidation']] | |||||
| ), | |||||
| new Patch( | |||||
| security: 'is_granted("is_granted("EDIT", object)")' | |||||
| ), | |||||
| new Delete( | |||||
| security: 'is_granted("ROLE_USER")', | |||||
| ) | |||||
| ], | |||||
| security: 'is_granted("ROLE_USER")', | |||||
| provider: EntityToDtoStateProvider::class, | |||||
| processor: EntityClassDtoStateProcessor::class, | |||||
| stateOptions: new Options(entityClass: Task::class), | |||||
| )] | |||||
| class TaskApi | |||||
| { | |||||
| #[ApiProperty(readable: false, writable: false, identifier: true)] | |||||
| public ?int $id = null; | |||||
| #[Assert\NotBlank] | |||||
| public ?string $headline = null; | |||||
| #[Assert\NotBlank] | |||||
| public ?string $description = null; | |||||
| #[ApiProperty(writable: false)] | |||||
| public ?UserApi $createdBy = null; | |||||
| #[Assert\NotBlank] | |||||
| public ?UserApi $assignedTo = null; | |||||
| #[Assert\NotBlank] | |||||
| public ?\DateTimeImmutable $dueAt = null; | |||||
| public ?PartnerApi $partner = null; | |||||
| public ?ContactApi $contact = null; | |||||
| #[Assert\NotBlank] | |||||
| public PrioType $prio; | |||||
| public ?bool $completed = null; | |||||
| #[ApiProperty(writable: false)] | |||||
| public ?\DateTimeImmutable $createdAt = null; | |||||
| } | |||||
| @@ -11,6 +11,7 @@ use App\Factory\MediaObjectUserFactory; | |||||
| use App\Factory\PartnerFactory; | use App\Factory\PartnerFactory; | ||||
| use App\Factory\PostingFactory; | use App\Factory\PostingFactory; | ||||
| use App\Factory\ProductFactory; | use App\Factory\ProductFactory; | ||||
| use App\Factory\TaskFactory; | |||||
| use App\Factory\UserFactory; | use App\Factory\UserFactory; | ||||
| use Doctrine\Bundle\FixturesBundle\Fixture; | use Doctrine\Bundle\FixturesBundle\Fixture; | ||||
| use Doctrine\Persistence\ObjectManager; | use Doctrine\Persistence\ObjectManager; | ||||
| @@ -67,5 +68,6 @@ class AppFixtures extends Fixture | |||||
| CommentFactory::createMany(300); | CommentFactory::createMany(300); | ||||
| MediaObjectProductFactory::createMany(50); | MediaObjectProductFactory::createMany(50); | ||||
| ProductFactory::createMany(100); | ProductFactory::createMany(100); | ||||
| TaskFactory::createMany(50); | |||||
| } | } | ||||
| } | } | ||||
| @@ -204,4 +204,21 @@ class FakeValues | |||||
| 'CryoVortex Acid', 'NanoRadiance Blend', 'QuantumFusion Serum', 'SynthoFlare-X', 'PlasmaQuell Acid', 'MolecularGlow Blend', 'Luminex Catalyst-X', | 'CryoVortex Acid', 'NanoRadiance Blend', 'QuantumFusion Serum', 'SynthoFlare-X', 'PlasmaQuell Acid', 'MolecularGlow Blend', 'Luminex Catalyst-X', | ||||
| 'ElectroWave Serum', 'CryoMyst Acid', 'NeuroFusion Elixir', 'NanoSizzle Blend-X', 'QuantumPulse Acid', 'RadiantFlare Serum', 'BioQuanta Catalyst', | 'ElectroWave Serum', 'CryoMyst Acid', 'NeuroFusion Elixir', 'NanoSizzle Blend-X', 'QuantumPulse Acid', 'RadiantFlare Serum', 'BioQuanta Catalyst', | ||||
| ]; | ]; | ||||
| const TASKS = [ | |||||
| 'Teller abwaschen', 'Staubsaugen', 'Wäsche falten', 'Müll rausbringen', | |||||
| 'Fenster putzen', 'Boden wischen', 'Einkaufen gehen', 'Betten machen', | |||||
| 'Geschirrspüler ausräumen', 'Küche aufräumen', 'Blumen gießen', 'Bad reinigen', | |||||
| 'Briefkasten leeren', 'Rasen mähen', 'Regale abstauben', 'Post sortieren', | |||||
| 'Auto waschen', 'Kühlschrank sauber machen', 'Toilette putzen', 'Kleiderschrank aufräumen', | |||||
| 'Abendessen kochen', 'Mittagessen vorbereiten', 'Frühstück zubereiten', 'Tisch decken', | |||||
| 'Schuhe putzen', 'Bücher sortieren', 'Schreibtisch aufräumen', 'Pflanzen gießen', | |||||
| 'Handtücher wechseln', 'Kinderzimmer aufräumen', 'Fahrzeuge auftanken', 'Keller entrümpeln', | |||||
| 'Lampen abstauben', 'Kerzen anzünden', 'Bügelwäsche erledigen', 'Spiegel putzen', | |||||
| 'Mülleimer säubern', 'Arbeitsflächen desinfizieren', 'Kamin reinigen', 'Gardinen waschen', | |||||
| 'Kabel ordnen', 'Geschenke einpacken', 'Fahrrad pflegen', 'Teppiche saugen', | |||||
| 'Dachboden organisieren', 'Zimmer lüften', 'Küchengeräte reinigen', 'Garderobe aufräumen', | |||||
| 'Hausaufgaben machen', 'Papierkram sortieren', | |||||
| ]; | |||||
| } | } | ||||
| @@ -30,7 +30,7 @@ class Contact | |||||
| #[ORM\Column(type: Types::DATE_MUTABLE, nullable: true)] | #[ORM\Column(type: Types::DATE_MUTABLE, nullable: true)] | ||||
| private ?\DateTimeInterface $birthday = null; | private ?\DateTimeInterface $birthday = null; | ||||
| #[ORM\ManyToOne(targetEntity: MediaObject::class)] | |||||
| #[ORM\ManyToOne] | |||||
| #[ORM\JoinColumn(nullable: true, onDelete: "SET NULL")] | #[ORM\JoinColumn(nullable: true, onDelete: "SET NULL")] | ||||
| private ?MediaObject $image = null; | private ?MediaObject $image = null; | ||||
| @@ -0,0 +1,159 @@ | |||||
| <?php | |||||
| namespace App\Entity; | |||||
| use App\Enum\PrioType; | |||||
| use App\Repository\TaskRepository; | |||||
| use Doctrine\DBAL\Types\Types; | |||||
| use Doctrine\ORM\Mapping as ORM; | |||||
| #[ORM\Entity(repositoryClass: TaskRepository::class)] | |||||
| class Task | |||||
| { | |||||
| #[ORM\Id] | |||||
| #[ORM\GeneratedValue] | |||||
| #[ORM\Column] | |||||
| private ?int $id = null; | |||||
| #[ORM\Column(length: 255)] | |||||
| private ?string $headline = null; | |||||
| #[ORM\Column(type: Types::TEXT, nullable: true)] | |||||
| private ?string $description = null; | |||||
| #[ORM\ManyToOne] | |||||
| #[ORM\JoinColumn(nullable: false)] | |||||
| private ?User $createdBy = null; | |||||
| #[ORM\ManyToOne] | |||||
| #[ORM\JoinColumn(nullable: false)] | |||||
| private ?User $assignedTo = null; | |||||
| #[ORM\Column] | |||||
| private ?\DateTimeImmutable $dueAt = null; | |||||
| #[ORM\ManyToOne] | |||||
| #[ORM\JoinColumn(nullable: true, onDelete: "SET NULL")] | |||||
| private ?Partner $partner = null; | |||||
| #[ORM\ManyToOne] | |||||
| #[ORM\JoinColumn(nullable: true, onDelete: "SET NULL")] | |||||
| private ?Contact $contact = null; | |||||
| #[ORM\Column(type: 'string', enumType: PrioType::class)] | |||||
| private PrioType $prio; | |||||
| #[ORM\Column] | |||||
| private ?bool $completed = null; | |||||
| #[ORM\Column] | |||||
| private ?\DateTimeImmutable $createdAt = null; | |||||
| public function __construct(User $createdBy, User $assignedTo) | |||||
| { | |||||
| $this->createdBy = $createdBy; | |||||
| $this->assignedTo = $assignedTo; | |||||
| $this->createdAt = new \DateTimeImmutable(); | |||||
| } | |||||
| public function getId(): ?int | |||||
| { | |||||
| return $this->id; | |||||
| } | |||||
| public function getHeadline(): ?string | |||||
| { | |||||
| return $this->headline; | |||||
| } | |||||
| public function setHeadline(?string $headline): void | |||||
| { | |||||
| $this->headline = $headline; | |||||
| } | |||||
| public function getDescription(): ?string | |||||
| { | |||||
| return $this->description; | |||||
| } | |||||
| public function setDescription(?string $description): void | |||||
| { | |||||
| $this->description = $description; | |||||
| } | |||||
| public function getCreatedBy(): ?User | |||||
| { | |||||
| return $this->createdBy; | |||||
| } | |||||
| public function setCreatedBy(?User $createdBy): void | |||||
| { | |||||
| $this->createdBy = $createdBy; | |||||
| } | |||||
| public function getAssignedTo(): ?User | |||||
| { | |||||
| return $this->assignedTo; | |||||
| } | |||||
| public function setAssignedTo(?User $assignedTo): void | |||||
| { | |||||
| $this->assignedTo = $assignedTo; | |||||
| } | |||||
| public function getDueAt(): ?\DateTimeImmutable | |||||
| { | |||||
| return $this->dueAt; | |||||
| } | |||||
| public function setDueAt(?\DateTimeImmutable $dueAt): void | |||||
| { | |||||
| $this->dueAt = $dueAt; | |||||
| } | |||||
| public function getPartner(): ?Partner | |||||
| { | |||||
| return $this->partner; | |||||
| } | |||||
| public function setPartner(?Partner $partner): void | |||||
| { | |||||
| $this->partner = $partner; | |||||
| } | |||||
| public function getContact(): ?Contact | |||||
| { | |||||
| return $this->contact; | |||||
| } | |||||
| public function setContact(?Contact $contact): void | |||||
| { | |||||
| $this->contact = $contact; | |||||
| } | |||||
| public function getPrio(): PrioType | |||||
| { | |||||
| return $this->prio; | |||||
| } | |||||
| public function setPrio(PrioType $prio): void | |||||
| { | |||||
| $this->prio = $prio; | |||||
| } | |||||
| public function getCompleted(): ?bool | |||||
| { | |||||
| return $this->completed; | |||||
| } | |||||
| public function setCompleted(?bool $completed): void | |||||
| { | |||||
| $this->completed = $completed; | |||||
| } | |||||
| public function getCreatedAt(): ?\DateTimeImmutable | |||||
| { | |||||
| return $this->createdAt; | |||||
| } | |||||
| } | |||||
| @@ -27,7 +27,7 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface | |||||
| #[ORM\Column(length: 255)] | #[ORM\Column(length: 255)] | ||||
| private ?string $lastName = null; | private ?string $lastName = null; | ||||
| #[ORM\ManyToOne(targetEntity: MediaObject::class)] | |||||
| #[ORM\ManyToOne] | |||||
| #[ORM\JoinColumn(nullable: true, onDelete: "SET NULL")] | #[ORM\JoinColumn(nullable: true, onDelete: "SET NULL")] | ||||
| private ?MediaObject $image = null; | private ?MediaObject $image = null; | ||||
| @@ -0,0 +1,15 @@ | |||||
| <?php | |||||
| /** | |||||
| * @author Daniel Knudsen <d.knudsen@spawntree.de> | |||||
| * @date 20.12.23 | |||||
| */ | |||||
| namespace App\Enum; | |||||
| enum PrioType: string { | |||||
| case Low = 'low'; | |||||
| case Medium = 'medium'; | |||||
| case High = 'high'; | |||||
| } | |||||
| @@ -0,0 +1,79 @@ | |||||
| <?php | |||||
| namespace App\Factory; | |||||
| use App\DataFixtures\FakeValues; | |||||
| use App\Entity\Task; | |||||
| use App\Enum\PrioType; | |||||
| use App\Repository\TaskRepository; | |||||
| use Zenstruck\Foundry\ModelFactory; | |||||
| use Zenstruck\Foundry\Proxy; | |||||
| use Zenstruck\Foundry\RepositoryProxy; | |||||
| /** | |||||
| * @extends ModelFactory<Task> | |||||
| * | |||||
| * @method Task|Proxy create(array|callable $attributes = []) | |||||
| * @method static Task|Proxy createOne(array $attributes = []) | |||||
| * @method static Task|Proxy find(object|array|mixed $criteria) | |||||
| * @method static Task|Proxy findOrCreate(array $attributes) | |||||
| * @method static Task|Proxy first(string $sortedField = 'id') | |||||
| * @method static Task|Proxy last(string $sortedField = 'id') | |||||
| * @method static Task|Proxy random(array $attributes = []) | |||||
| * @method static Task|Proxy randomOrCreate(array $attributes = []) | |||||
| * @method static TaskRepository|RepositoryProxy repository() | |||||
| * @method static Task[]|Proxy[] all() | |||||
| * @method static Task[]|Proxy[] createMany(int $number, array|callable $attributes = []) | |||||
| * @method static Task[]|Proxy[] createSequence(iterable|callable $sequence) | |||||
| * @method static Task[]|Proxy[] findBy(array $attributes) | |||||
| * @method static Task[]|Proxy[] randomRange(int $min, int $max, array $attributes = []) | |||||
| * @method static Task[]|Proxy[] randomSet(int $number, array $attributes = []) | |||||
| */ | |||||
| final class TaskFactory 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 | |||||
| { | |||||
| $randomBoolean = ContactFactory::count() > 0 && (bool)random_int(0, 1); | |||||
| return [ | |||||
| 'headline' => self::faker()->randomElement(FakeValues::TASKS), | |||||
| 'description' => self::faker()->sentence(), | |||||
| 'createdBy' => UserFactory::new(), | |||||
| 'assignedTo' => UserFactory::random(), | |||||
| 'partner' => PartnerFactory::random(), | |||||
| 'contact' => $randomBoolean ? ContactFactory::random() : null, | |||||
| 'dueAt' => \DateTimeImmutable::createFromMutable(self::faker()->dateTime()), | |||||
| 'completed' => self::faker()->boolean(), | |||||
| 'prio' => self::faker()->randomElement(PrioType::cases()), | |||||
| ]; | |||||
| } | |||||
| /** | |||||
| * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization | |||||
| */ | |||||
| protected function initialize(): self | |||||
| { | |||||
| return $this | |||||
| // ->afterInstantiate(function(Task $task): void {}) | |||||
| ; | |||||
| } | |||||
| protected static function getClass(): string | |||||
| { | |||||
| return Task::class; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,76 @@ | |||||
| <?php | |||||
| namespace App\Mapper; | |||||
| use App\ApiResource\TaskApi; | |||||
| use App\Entity\Contact; | |||||
| use App\Entity\Partner; | |||||
| use App\Entity\Task; | |||||
| use App\Entity\User; | |||||
| use App\Repository\TaskRepository; | |||||
| use Symfony\Bundle\SecurityBundle\Security; | |||||
| use Symfonycasts\MicroMapper\AsMapper; | |||||
| use Symfonycasts\MicroMapper\MapperInterface; | |||||
| use Symfonycasts\MicroMapper\MicroMapperInterface; | |||||
| #[AsMapper(from: TaskApi::class, to: Task::class)] | |||||
| class TaskApiToEntityMapper implements MapperInterface | |||||
| { | |||||
| public function __construct( | |||||
| private TaskRepository $repository, | |||||
| private Security $security, | |||||
| private MicroMapperInterface $microMapper, | |||||
| ) | |||||
| { | |||||
| } | |||||
| public function load(object $from, string $toClass, array $context): object | |||||
| { | |||||
| $dto = $from; | |||||
| assert($dto instanceof TaskApi); | |||||
| $assignedTo = $this->microMapper->map($dto->assignedTo, User::class, [ | |||||
| MicroMapperInterface::MAX_DEPTH => 1, | |||||
| ]); | |||||
| if ($dto->id) { | |||||
| $entity = $this->repository->find($dto->id); | |||||
| if (!$entity) { | |||||
| throw new \Exception('Task not found'); | |||||
| } | |||||
| $entity->setAssignedTo($assignedTo); | |||||
| } else { | |||||
| $user = $this->security->getUser(); | |||||
| assert($user instanceof User); | |||||
| assert($assignedTo instanceof User); | |||||
| $entity = new Task($user, $assignedTo); | |||||
| } | |||||
| if (!$entity) { | |||||
| throw new \Exception('Task not found'); | |||||
| } | |||||
| return $entity; | |||||
| } | |||||
| public function populate(object $from, object $to, array $context): object | |||||
| { | |||||
| $dto = $from; | |||||
| assert($dto instanceof TaskApi); | |||||
| $entity = $to; | |||||
| assert($entity instanceof Task); | |||||
| $entity->setHeadline($dto->headline); | |||||
| $entity->setDescription($dto->description); | |||||
| $entity->setDueAt($dto->dueAt); | |||||
| $entity->setPartner($this->microMapper->map($dto->partner, Partner::class, [ | |||||
| MicroMapperInterface::MAX_DEPTH => 1, | |||||
| ])); | |||||
| $entity->setContact($this->microMapper->map($dto->contact, Contact::class, [ | |||||
| MicroMapperInterface::MAX_DEPTH => 1, | |||||
| ])); | |||||
| $entity->setPrio($dto->prio); | |||||
| $entity->setCompleted($dto->completed); | |||||
| return $entity; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,73 @@ | |||||
| <?php | |||||
| namespace App\Mapper; | |||||
| use App\ApiResource\CommentApi; | |||||
| use App\ApiResource\ContactApi; | |||||
| use App\ApiResource\PartnerApi; | |||||
| use App\ApiResource\TaskApi; | |||||
| use App\ApiResource\UserApi; | |||||
| use App\ApiResource\PostingApi; | |||||
| use App\Entity\Comment; | |||||
| use App\Entity\Task; | |||||
| use Symfony\Bundle\SecurityBundle\Security; | |||||
| use Symfonycasts\MicroMapper\AsMapper; | |||||
| use Symfonycasts\MicroMapper\MapperInterface; | |||||
| use Symfonycasts\MicroMapper\MicroMapperInterface; | |||||
| #[AsMapper(from: Task::class, to: TaskApi::class)] | |||||
| class TaskEntityToApiMapper implements MapperInterface | |||||
| { | |||||
| public function __construct( | |||||
| private MicroMapperInterface $microMapper | |||||
| ) | |||||
| { | |||||
| } | |||||
| public function load(object $from, string $toClass, array $context): object | |||||
| { | |||||
| $entity = $from; | |||||
| assert($entity instanceof Task); | |||||
| $dto = new TaskApi(); | |||||
| $dto->id = $entity->getId(); | |||||
| return $dto; | |||||
| } | |||||
| public function populate(object $from, object $to, array $context): object | |||||
| { | |||||
| $entity = $from; | |||||
| $dto = $to; | |||||
| assert($entity instanceof Task); | |||||
| assert($dto instanceof TaskApi); | |||||
| $dto->headline = $entity->getHeadline(); | |||||
| $dto->description = $entity->getDescription(); | |||||
| $dto->createdBy = $this->microMapper->map($entity->getCreatedBy(), UserApi::class, [ | |||||
| MicroMapperInterface::MAX_DEPTH => 1, | |||||
| ]); | |||||
| $dto->assignedTo = $this->microMapper->map($entity->getAssignedTo(), UserApi::class, [ | |||||
| MicroMapperInterface::MAX_DEPTH => 1, | |||||
| ]); | |||||
| $dto->dueAt = $entity->getDueAt(); | |||||
| if ($entity->getPartner()) { | |||||
| $dto->partner = $this->microMapper->map($entity->getPartner(), PartnerApi::class, [ | |||||
| MicroMapperInterface::MAX_DEPTH => 1, | |||||
| ]); | |||||
| } | |||||
| if ($entity->getContact() !== null) { | |||||
| $dto->contact = $this->microMapper->map($entity->getContact(), ContactApi::class, [ | |||||
| MicroMapperInterface::MAX_DEPTH => 1, | |||||
| ]); | |||||
| } | |||||
| $dto->prio = $entity->getPrio(); | |||||
| $dto->completed = $entity->getCompleted(); | |||||
| $dto->createdAt = $entity->getCreatedAt(); | |||||
| return $dto; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,48 @@ | |||||
| <?php | |||||
| namespace App\Repository; | |||||
| use App\Entity\Task; | |||||
| use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; | |||||
| use Doctrine\Persistence\ManagerRegistry; | |||||
| /** | |||||
| * @extends ServiceEntityRepository<Task> | |||||
| * | |||||
| * @method Task|null find($id, $lockMode = null, $lockVersion = null) | |||||
| * @method Task|null findOneBy(array $criteria, array $orderBy = null) | |||||
| * @method Task[] findAll() | |||||
| * @method Task[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) | |||||
| */ | |||||
| class TaskRepository extends ServiceEntityRepository | |||||
| { | |||||
| public function __construct(ManagerRegistry $registry) | |||||
| { | |||||
| parent::__construct($registry, Task::class); | |||||
| } | |||||
| // /** | |||||
| // * @return Task[] Returns an array of Task objects | |||||
| // */ | |||||
| // public function findByExampleField($value): array | |||||
| // { | |||||
| // return $this->createQueryBuilder('t') | |||||
| // ->andWhere('t.exampleField = :val') | |||||
| // ->setParameter('val', $value) | |||||
| // ->orderBy('t.id', 'ASC') | |||||
| // ->setMaxResults(10) | |||||
| // ->getQuery() | |||||
| // ->getResult() | |||||
| // ; | |||||
| // } | |||||
| // public function findOneBySomeField($value): ?Task | |||||
| // { | |||||
| // return $this->createQueryBuilder('t') | |||||
| // ->andWhere('t.exampleField = :val') | |||||
| // ->setParameter('val', $value) | |||||
| // ->getQuery() | |||||
| // ->getOneOrNullResult() | |||||
| // ; | |||||
| // } | |||||
| } | |||||
| @@ -0,0 +1,84 @@ | |||||
| <?php | |||||
| /** | |||||
| * @author Daniel Knudsen <d.knudsen@spawntree.de> | |||||
| * @date 12.12.23 | |||||
| */ | |||||
| namespace App\Tests\Functional; | |||||
| use App\Enum\PartnerType; | |||||
| use App\Factory\CommentFactory; | |||||
| use App\Factory\ContactFactory; | |||||
| use App\Factory\MediaObjectLogoFactory; | |||||
| use App\Factory\MediaObjectContactFactory; | |||||
| use App\Factory\PartnerFactory; | |||||
| use App\Factory\PostingFactory; | |||||
| 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 TaskResourceTest 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 testPostTask(): void | |||||
| { | |||||
| $user = UserFactory::createOne( | |||||
| [ | |||||
| 'firstName' => 'Peter', | |||||
| 'lastName' => 'Test', | |||||
| 'password' => 'test', | |||||
| 'email' => 'peter@test.de', | |||||
| ] | |||||
| ); | |||||
| $partner = PartnerFactory::createOne(); | |||||
| $contact = ContactFactory::createOne(); | |||||
| $token = $this->JWTManager->create($user->object()); | |||||
| $this->browser() | |||||
| ->post('/api/tasks' , [ | |||||
| 'json' => [ | |||||
| 'headline' => 'new task', | |||||
| 'description' => 'this is what is to do...', | |||||
| 'assignedTo' => '/api/users/' . $user->getId(), | |||||
| 'dueAt'=> "2024-03-12T08:57:39.892Z", | |||||
| 'partner'=> "/api/partners/" . $partner->getId(), | |||||
| 'contact'=> "/api/contacts/" . $contact->getId(), | |||||
| 'prio'=> "low", | |||||
| 'completed'=> false, | |||||
| ], | |||||
| 'headers' => [ | |||||
| 'Authorization' => 'Bearer ' . $token, | |||||
| ] | |||||
| ]) | |||||
| ->assertSuccessful() | |||||
| ; | |||||
| $this->browser() | |||||
| ->get('/api/tasks', [ | |||||
| 'headers' => [ | |||||
| 'Authorization' => 'Bearer ' . $token, | |||||
| ], | |||||
| ]) | |||||
| ->assertSuccessful() | |||||
| ->assertJsonMatches('"hydra:totalItems"', 1) | |||||
| ->assertJsonMatches('"hydra:member"[0].headline', 'new task') | |||||
| ; | |||||
| } | |||||
| } | |||||