Skip to content

Commit

Permalink
Merge pull request #6 from ThomasBerranger/feature/product
Browse files Browse the repository at this point in the history
Product implementation
  • Loading branch information
ThomasBerranger authored Oct 14, 2024
2 parents 3603a97 + 673bd7f commit 958b7fa
Show file tree
Hide file tree
Showing 10 changed files with 460 additions and 0 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,17 @@ Secret for now.
## Tech Highlights

- GitHub CI/CD

Mise en place d'une intégration et d'un déploiement continue via les actions GitHub.

- Authentification via Token

Intégration d'un système d'authentification via le AccessTokenHandler de Symfony et une gestion des Tokens.

- Doctrine Discriminator

Todo

## Roadmap

<details>
Expand Down
33 changes: 33 additions & 0 deletions migrations/Version20241013161706.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?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 Version20241013161706 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 product (id BINARY(16) NOT NULL COMMENT \'(DC2Type:uuid)\', owner_id BINARY(16) NOT NULL COMMENT \'(DC2Type:uuid)\', name VARCHAR(255) NOT NULL, description VARCHAR(255) DEFAULT NULL, image VARCHAR(255) DEFAULT NULL, finished_at DATETIME DEFAULT NULL, added_to_list_at DATETIME DEFAULT NULL, type VARCHAR(255) NOT NULL, barcode VARCHAR(255) DEFAULT NULL, nutriscore VARCHAR(1) DEFAULT NULL, novagroup SMALLINT DEFAULT NULL, ecoscore VARCHAR(1) DEFAULT NULL, INDEX IDX_D34A04AD7E3C61F9 (owner_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
$this->addSql('ALTER TABLE product ADD CONSTRAINT FK_D34A04AD7E3C61F9 FOREIGN KEY (owner_id) REFERENCES user (id)');
}

public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE product DROP FOREIGN KEY FK_D34A04AD7E3C61F9');
$this->addSql('DROP TABLE product');
}
}
3 changes: 3 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
parameters:
ignoreErrors:
- '#Class App\\Security\\.* extends generic class Symfony\\Component\\Security\\Core\\Authorization\\Voter\\Voter but does not specify its types: TAttribute, TSubject#'
72 changes: 72 additions & 0 deletions src/Controller/ProductController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

namespace App\Controller;

use App\Entity\Product\CustomProduct;
use App\Entity\Product\Product;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Attribute\MapRequestPayload;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\SerializerInterface;

final class ProductController extends BaseController
{
public function __construct(private readonly EntityManagerInterface $entityManager)
{
}

#[Route('/products', name: 'products.create', methods: ['post'])]
#[IsGranted('view', 'product')]
public function create(#[MapRequestPayload] CustomProduct $product): JsonResponse
{
$product->setOwner($this->getCurrentUser());

$this->entityManager->persist($product);
$this->entityManager->flush();

return $this->json($product, Response::HTTP_CREATED, context: ['groups' => 'products.show']);
}

#[Route('/products', name: 'products.index', methods: ['get'])]
#[IsGranted('ROLE_USER')]
public function index(): JsonResponse
{
$products = $this->getCurrentUser()->getProducts();

return $this->json($products, Response::HTTP_OK, context: ['groups' => 'products.show']);
}

#[Route('/products/{id}', name: 'products.show', methods: ['get'])]
#[IsGranted('view', 'product')]
public function show(Product $product): JsonResponse
{
return $this->json($product, Response::HTTP_OK, context: ['groups' => 'products.show']);
}

#[Route('/products/{id}', name: 'products.edit', methods: ['patch'])]
#[IsGranted('edit', 'product')]
public function edit(Request $request, Product $product, SerializerInterface $serializer): JsonResponse
{
$product = $serializer->deserialize($request->getContent(), Product::class, 'json', [
AbstractNormalizer::OBJECT_TO_POPULATE => $product,
'groups' => 'products.edit',
]);

return $this->json($product, Response::HTTP_OK, context: ['groups' => 'products.show']);
}

#[Route('/products/{id}', name: 'products.delete', methods: ['delete'])]
#[IsGranted('edit', 'product')]
public function delete(CustomProduct $product): JsonResponse
{
$this->entityManager->remove($product);
$this->entityManager->flush();

return $this->json([], Response::HTTP_NO_CONTENT);
}
}
10 changes: 10 additions & 0 deletions src/Entity/Product/CustomProduct.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace App\Entity\Product;

use Doctrine\ORM\Mapping\Entity;

#[Entity]
class CustomProduct extends Product
{
}
128 changes: 128 additions & 0 deletions src/Entity/Product/Product.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
<?php

namespace App\Entity\Product;

use App\Entity\User;
use App\Repository\ProductRepository;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Types\UuidType;
use Symfony\Component\Serializer\Attribute\Groups;
use Symfony\Component\Uid\Uuid;

#[ORM\Entity(repositoryClass: ProductRepository::class)]
#[ORM\InheritanceType('SINGLE_TABLE')]
#[ORM\DiscriminatorColumn(name: 'type', type: 'string')]
#[ORM\DiscriminatorMap([
'scanned' => 'ScannedProduct',
'custom' => 'CustomProduct',
])]
class Product
{
#[ORM\Id]
#[ORM\Column(type: UuidType::NAME, unique: true)]
#[ORM\GeneratedValue(strategy: 'CUSTOM')]
#[ORM\CustomIdGenerator(class: 'doctrine.uuid_generator')]
private ?Uuid $id = null;

#[ORM\Column(length: 255)]
#[Groups(['products.show', 'products.edit'])]
private ?string $name = null;

#[ORM\ManyToOne(inversedBy: 'products')]
#[ORM\JoinColumn(nullable: false)]
private ?User $owner = null;

#[ORM\Column(length: 255, nullable: true)]
#[Groups(['products.show'])]
private ?string $description = null;

#[ORM\Column(length: 255, nullable: true)]
#[Groups(['products.show'])]
private ?string $image = null;

#[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)]
#[Groups(['products.show'])]
private ?\DateTimeInterface $finished_at = null;

#[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)]
#[Groups(['products.show'])]
private ?\DateTimeInterface $added_to_list_at = null;

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

public function getName(): ?string
{
return $this->name;
}

public function setName(string $name): static
{
$this->name = $name;

return $this;
}

public function getOwner(): ?User
{
return $this->owner;
}

public function setOwner(?User $owner): static
{
$this->owner = $owner;

return $this;
}

public function getDescription(): ?string
{
return $this->description;
}

public function setDescription(?string $description): static
{
$this->description = $description;

return $this;
}

public function getImage(): ?string
{
return $this->image;
}

public function setImage(?string $image): static
{
$this->image = $image;

return $this;
}

public function getFinishedAt(): ?\DateTimeInterface
{
return $this->finished_at;
}

public function setFinishedAt(?\DateTimeInterface $finished_at): static
{
$this->finished_at = $finished_at;

return $this;
}

public function getAddedToListAt(): ?\DateTimeInterface
{
return $this->added_to_list_at;
}

public function setAddedToListAt(?\DateTimeInterface $added_to_list_at): static
{
$this->added_to_list_at = $added_to_list_at;

return $this;
}
}
73 changes: 73 additions & 0 deletions src/Entity/Product/ScannedProduct.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php

namespace App\Entity\Product;

use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\ORM\Mapping\Entity;
use Symfony\Component\Serializer\Attribute\Groups;

#[Entity]
class ScannedProduct extends Product
{
#[ORM\Column(length: 255)]
private string $barcode;

#[ORM\Column(length: 1, nullable: true)]
#[Groups(['products.show'])]
private ?string $nutriscore = null;

#[ORM\Column(type: Types::SMALLINT, nullable: true)]
#[Groups(['products.show'])]
private ?int $novagroup = null;

#[ORM\Column(length: 1, nullable: true)]
#[Groups(['products.show'])]
private ?string $ecoscore = null;

public function getBarcode(): string
{
return $this->barcode;
}

public function setBarcode(string $barcode): void
{
$this->barcode = $barcode;
}

public function getNutriscore(): ?string
{
return $this->nutriscore;
}

public function setNutriscore(?string $nutriscore): static
{
$this->nutriscore = $nutriscore;

return $this;
}

public function getNovagroup(): ?int
{
return $this->novagroup;
}

public function setNovagroup(?int $novagroup): static
{
$this->novagroup = $novagroup;

return $this;
}

public function getEcoscore(): ?string
{
return $this->ecoscore;
}

public function setEcoscore(?string $ecoscore): static
{
$this->ecoscore = $ecoscore;

return $this;
}
}
Loading

0 comments on commit 958b7fa

Please sign in to comment.