Compare commits
3 commits
Author | SHA1 | Date | |
---|---|---|---|
64f5341371 | |||
15f8db46a0 | |||
2bdd5f9ac2 |
19 changed files with 645 additions and 135 deletions
|
@ -36,6 +36,7 @@
|
|||
"symfony/validator": "7.3.*",
|
||||
"symfony/yaml": "7.3.*",
|
||||
"twig/extra-bundle": "^2.12|^3.0",
|
||||
"twig/intl-extra": "^3.21",
|
||||
"twig/twig": "^2.12|^3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
|
|
152
composer.lock
generated
152
composer.lock
generated
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "bc8674fb48687aeee3da991cd7c8d0ad",
|
||||
"content-hash": "b2a06a767859688f71a67ddab0976aea",
|
||||
"packages": [
|
||||
{
|
||||
"name": "api-platform/doctrine-common",
|
||||
|
@ -4918,6 +4918,92 @@
|
|||
],
|
||||
"time": "2025-06-28T08:24:55+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/intl",
|
||||
"version": "v7.3.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/intl.git",
|
||||
"reference": "bd50940329ac1cfc4af0491cc4468f477d967e45"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/intl/zipball/bd50940329ac1cfc4af0491cc4468f477d967e45",
|
||||
"reference": "bd50940329ac1cfc4af0491cc4468f477d967e45",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.2",
|
||||
"symfony/deprecation-contracts": "^2.5|^3"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/string": "<7.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/filesystem": "^6.4|^7.0",
|
||||
"symfony/var-exporter": "^6.4|^7.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\Intl\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/",
|
||||
"/Resources/data/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Bernhard Schussek",
|
||||
"email": "bschussek@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Eriksen Costa",
|
||||
"email": "eriksen.costa@infranology.com.br"
|
||||
},
|
||||
{
|
||||
"name": "Igor Wiedler",
|
||||
"email": "igor@wiedler.ch"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Provides access to the localization data of the ICU library",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"i18n",
|
||||
"icu",
|
||||
"internationalization",
|
||||
"intl",
|
||||
"l10n",
|
||||
"localization"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/intl/tree/v7.3.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-06-06T16:10:07+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/monolog-bridge",
|
||||
"version": "v7.3.0",
|
||||
|
@ -7375,6 +7461,70 @@
|
|||
],
|
||||
"time": "2025-02-19T14:29:33+00:00"
|
||||
},
|
||||
{
|
||||
"name": "twig/intl-extra",
|
||||
"version": "v3.21.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/twigphp/intl-extra.git",
|
||||
"reference": "05bc5d46b9df9e62399eae53e7c0b0633298b146"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/twigphp/intl-extra/zipball/05bc5d46b9df9e62399eae53e7c0b0633298b146",
|
||||
"reference": "05bc5d46b9df9e62399eae53e7c0b0633298b146",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.1.0",
|
||||
"symfony/intl": "^5.4|^6.4|^7.0",
|
||||
"twig/twig": "^3.13|^4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/phpunit-bridge": "^6.4|^7.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Twig\\Extra\\Intl\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com",
|
||||
"homepage": "http://fabien.potencier.org",
|
||||
"role": "Lead Developer"
|
||||
}
|
||||
],
|
||||
"description": "A Twig extension for Intl",
|
||||
"homepage": "https://twig.symfony.com",
|
||||
"keywords": [
|
||||
"intl",
|
||||
"twig"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/twigphp/intl-extra/tree/v3.21.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/twig/twig",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-01-31T20:45:36+00:00"
|
||||
},
|
||||
{
|
||||
"name": "twig/twig",
|
||||
"version": "v3.21.1",
|
||||
|
|
4
ecs.php
4
ecs.php
|
@ -13,5 +13,7 @@ return ECSConfig::configure()
|
|||
__DIR__ . '/tests',
|
||||
])
|
||||
->withRootFiles()
|
||||
->withRules([FinalClassFixer::class])
|
||||
->withRules([
|
||||
FinalClassFixer::class,
|
||||
])
|
||||
->withSets([LubiSetList::ECS]);
|
||||
|
|
56
migrations/Version20250629160123.php
Normal file
56
migrations/Version20250629160123.php
Normal file
|
@ -0,0 +1,56 @@
|
|||
<?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 Version20250629160123 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(<<<'SQL'
|
||||
ALTER TABLE order_item ADD COLUMN is_paid BOOLEAN DEFAULT 0 NOT NULL
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
ALTER TABLE order_item ADD COLUMN price_cents INTEGER DEFAULT 0 NOT NULL
|
||||
SQL);
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql(<<<'SQL'
|
||||
CREATE TEMPORARY TABLE __temp__order_item AS SELECT name, extras, created_by, id, food_order_id, menu_item_id FROM order_item
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
DROP TABLE order_item
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
CREATE TABLE order_item (name VARCHAR(255) NOT NULL, extras VARCHAR(255) DEFAULT NULL, created_by VARCHAR(255) DEFAULT 'nobody' NOT NULL, id BLOB NOT NULL, food_order_id BLOB DEFAULT NULL, menu_item_id BLOB NOT NULL, PRIMARY KEY(id), CONSTRAINT FK_52EA1F09A5D24A7A FOREIGN KEY (food_order_id) REFERENCES food_order (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_52EA1F099AB44FE0 FOREIGN KEY (menu_item_id) REFERENCES menu_item (id) NOT DEFERRABLE INITIALLY IMMEDIATE)
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
INSERT INTO order_item (name, extras, created_by, id, food_order_id, menu_item_id) SELECT name, extras, created_by, id, food_order_id, menu_item_id FROM __temp__order_item
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
DROP TABLE __temp__order_item
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
CREATE INDEX IDX_52EA1F09A5D24A7A ON order_item (food_order_id)
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
CREATE INDEX IDX_52EA1F099AB44FE0 ON order_item (menu_item_id)
|
||||
SQL);
|
||||
}
|
||||
}
|
53
migrations/Version20250629160639.php
Normal file
53
migrations/Version20250629160639.php
Normal file
|
@ -0,0 +1,53 @@
|
|||
<?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 Version20250629160639 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(<<<'SQL'
|
||||
ALTER TABLE menu_item ADD COLUMN price_cents INTEGER DEFAULT 0 NOT NULL
|
||||
SQL);
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql(<<<'SQL'
|
||||
CREATE TEMPORARY TABLE __temp__menu_item AS SELECT name, deleted_at, id, food_vendor_id, alias_of_id FROM menu_item
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
DROP TABLE menu_item
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
CREATE TABLE menu_item (name VARCHAR(255) NOT NULL, deleted_at DATETIME DEFAULT NULL, id BLOB NOT NULL, food_vendor_id BLOB NOT NULL, alias_of_id BLOB DEFAULT NULL, PRIMARY KEY(id), CONSTRAINT FK_D754D5506EF983E8 FOREIGN KEY (food_vendor_id) REFERENCES food_vendor (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_D754D55061F0AFC5 FOREIGN KEY (alias_of_id) REFERENCES menu_item (id) NOT DEFERRABLE INITIALLY IMMEDIATE)
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
INSERT INTO menu_item (name, deleted_at, id, food_vendor_id, alias_of_id) SELECT name, deleted_at, id, food_vendor_id, alias_of_id FROM __temp__menu_item
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
DROP TABLE __temp__menu_item
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
CREATE INDEX IDX_D754D5506EF983E8 ON menu_item (food_vendor_id)
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
CREATE INDEX IDX_D754D55061F0AFC5 ON menu_item (alias_of_id)
|
||||
SQL);
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ namespace App\Controller;
|
|||
|
||||
use App\Entity\FoodOrder;
|
||||
use App\Form\FoodOrderType;
|
||||
use App\Form\OrderFinalize;
|
||||
use App\Repository\FoodOrderRepository;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
|
@ -104,11 +105,21 @@ final class FoodOrderController extends AbstractController
|
|||
], Response::HTTP_SEE_OTHER);
|
||||
}
|
||||
|
||||
#[Route('/{id}', name: 'app_food_order_show', methods: ['GET'])]
|
||||
public function show(FoodOrder $foodOrder): Response
|
||||
#[Route('/{id}', name: 'app_food_order_show', methods: ['GET', 'POST'])]
|
||||
public function show(Request $request, FoodOrder $foodOrder, EntityManagerInterface $entityManager): Response
|
||||
{
|
||||
$form = null;
|
||||
if ($foodOrder->isClosed()) {
|
||||
$form = $this->createForm(OrderFinalize::class, $foodOrder);
|
||||
$form->handleRequest($request);
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$entityManager->persist($foodOrder);
|
||||
$entityManager->flush();
|
||||
}
|
||||
}
|
||||
return $this->render('food_order/show.html.twig', [
|
||||
'food_order' => $foodOrder,
|
||||
'form' => $form,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
namespace App\Controller;
|
||||
|
||||
use App\Entity\FoodOrder;
|
||||
use App\Entity\MenuItem;
|
||||
use App\Entity\OrderItem;
|
||||
use App\Form\OrderItemType;
|
||||
use App\Repository\MenuItemRepository;
|
||||
|
@ -75,19 +74,6 @@ final class OrderItemController extends AbstractController
|
|||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$menuItem = $menuItemRepository->findOneBy([
|
||||
'name' => $orderItem->getName(),
|
||||
'foodVendor' => $foodOrder->getFoodVendor(),
|
||||
]);
|
||||
|
||||
if ($menuItem === null) {
|
||||
$menuItem = new MenuItem;
|
||||
$menuItem->setName($orderItem->getName());
|
||||
$menuItem->setFoodVendor($foodOrder->getFoodVendor());
|
||||
$entityManager->persist($menuItem);
|
||||
}
|
||||
|
||||
$orderItem->setMenuItem($menuItem);
|
||||
$orderItem->setFoodOrder($foodOrder);
|
||||
$entityManager->persist($orderItem);
|
||||
$entityManager->flush();
|
||||
|
@ -121,24 +107,6 @@ final class OrderItemController extends AbstractController
|
|||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$menuItem = $menuItemRepository->findOneBy([
|
||||
'name' => $orderItem->getName(),
|
||||
'foodVendor' => $foodOrder->getFoodVendor(),
|
||||
]);
|
||||
|
||||
if ($menuItem === null) {
|
||||
$menuItem = new MenuItem;
|
||||
$menuItem->setName($orderItem->getName());
|
||||
$menuItem->setFoodVendor($foodOrder->getFoodVendor());
|
||||
$entityManager->persist($menuItem);
|
||||
}
|
||||
|
||||
if ($menuItem->getAliasOf() !== null) {
|
||||
$menuItem = $menuItem->getAliasOf();
|
||||
$orderItem->setName($menuItem->getName());
|
||||
}
|
||||
|
||||
$orderItem->setMenuItem($menuItem);
|
||||
$orderItem->setFoodOrder($foodOrder);
|
||||
$entityManager->persist($orderItem);
|
||||
$entityManager->flush();
|
||||
|
|
|
@ -11,6 +11,7 @@ use Doctrine\ORM\Mapping as ORM;
|
|||
use Symfony\Bridge\Doctrine\IdGenerator\UlidGenerator;
|
||||
use Symfony\Bridge\Doctrine\Types\UlidType;
|
||||
use Symfony\Component\Uid\Ulid;
|
||||
use Symfony\Component\Validator\Constraints\Positive;
|
||||
|
||||
#[ApiResource]
|
||||
#[ORM\Entity(repositoryClass: MenuItemRepository::class)]
|
||||
|
@ -35,6 +36,12 @@ class MenuItem
|
|||
#[ORM\Column(length: 255)]
|
||||
private string|null $name = null;
|
||||
|
||||
#[ORM\Column(type: 'integer', options: [
|
||||
'default' => 0,
|
||||
])]
|
||||
#[Positive]
|
||||
private int $priceCents = 0;
|
||||
|
||||
public function __construct(
|
||||
#[ORM\Column(type: UlidType::NAME, unique: true)]
|
||||
#[ORM\CustomIdGenerator(class: UlidGenerator::class)]
|
||||
|
@ -95,6 +102,11 @@ class MenuItem
|
|||
return $this->name;
|
||||
}
|
||||
|
||||
public function getPriceCents(): int
|
||||
{
|
||||
return $this->priceCents;
|
||||
}
|
||||
|
||||
public function isDeleted(): bool
|
||||
{
|
||||
return $this->getDeletedAt() instanceof DateTimeImmutable;
|
||||
|
@ -137,4 +149,10 @@ class MenuItem
|
|||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setPriceCents(int $priceCents): self
|
||||
{
|
||||
$this->priceCents = $priceCents;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,12 @@ class OrderItem
|
|||
#[ORM\ManyToOne(inversedBy: 'orderItems')]
|
||||
private FoodOrder|null $foodOrder = null;
|
||||
|
||||
#[Groups('food_order:latest')]
|
||||
#[ORM\Column(type: 'boolean', options: [
|
||||
'default' => false,
|
||||
])]
|
||||
private bool $isPaid = false;
|
||||
|
||||
#[Groups(['food_order:latest'])]
|
||||
#[ORM\JoinColumn(nullable: false)]
|
||||
#[ORM\ManyToOne]
|
||||
|
@ -37,6 +43,13 @@ class OrderItem
|
|||
#[ORM\Column(length: 255)]
|
||||
private string|null $name = null;
|
||||
|
||||
#[Groups('food_order:latest')]
|
||||
#[ORM\Column(type: 'integer', options: [
|
||||
'default' => 0,
|
||||
])]
|
||||
#[Positive]
|
||||
private int $priceCents = 0;
|
||||
|
||||
public function __construct(
|
||||
#[Groups(['food_order:latest'])]
|
||||
#[ORM\Column(type: UlidType::NAME, unique: true)]
|
||||
|
@ -78,6 +91,16 @@ class OrderItem
|
|||
return $this->name;
|
||||
}
|
||||
|
||||
public function getPriceCents(): int
|
||||
{
|
||||
return $this->priceCents;
|
||||
}
|
||||
|
||||
public function isPaid(): bool
|
||||
{
|
||||
return $this->isPaid;
|
||||
}
|
||||
|
||||
public function setCreatedBy(string $createdBy): static
|
||||
{
|
||||
$this->createdBy = $createdBy;
|
||||
|
@ -99,6 +122,12 @@ class OrderItem
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function setIsPaid(bool $isPaid): self
|
||||
{
|
||||
$this->isPaid = $isPaid;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setMenuItem(MenuItem|null $menuItem): static
|
||||
{
|
||||
$this->menuItem = $menuItem;
|
||||
|
@ -113,4 +142,10 @@ class OrderItem
|
|||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setPriceCents(int $priceCents): self
|
||||
{
|
||||
$this->priceCents = $priceCents;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
52
src/EventListener/OrderItemPreFlush.php
Normal file
52
src/EventListener/OrderItemPreFlush.php
Normal file
|
@ -0,0 +1,52 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\EventListener;
|
||||
|
||||
use App\Entity\MenuItem;
|
||||
use App\Entity\OrderItem;
|
||||
use App\Repository\MenuItemRepository;
|
||||
use Doctrine\Bundle\DoctrineBundle\Attribute\AsDoctrineListener;
|
||||
use Doctrine\ORM\Event\PreFlushEventArgs;
|
||||
use Doctrine\ORM\Events;
|
||||
use Doctrine\Persistence\ObjectManager;
|
||||
|
||||
#[AsDoctrineListener(event: Events::preFlush)]
|
||||
final readonly class OrderItemPreFlush
|
||||
{
|
||||
public function __construct(
|
||||
private MenuItemRepository $menuItemRepository,
|
||||
) {}
|
||||
|
||||
public function preFlush(PreFlushEventArgs $eventArgs): void
|
||||
{
|
||||
foreach (($eventArgs->getObjectManager()->getUnitOfWork()->getIdentityMap()[OrderItem::class] ?? []) as $orderItem) {
|
||||
$this->checkOrderItem($orderItem, $eventArgs->getObjectManager());
|
||||
}
|
||||
}
|
||||
|
||||
private function checkOrderItem(OrderItem $orderItem, ObjectManager $objectManager): void
|
||||
{
|
||||
$menuItem = $this->menuItemRepository->findOneBy([
|
||||
'name' => $orderItem->getName(),
|
||||
'foodVendor' => $orderItem->getFoodOrder()
|
||||
->getFoodVendor(),
|
||||
]);
|
||||
if ($menuItem === null) {
|
||||
$menuItem = new MenuItem;
|
||||
$menuItem->setName($orderItem->getName());
|
||||
$menuItem->setFoodVendor($orderItem->getFoodOrder()->getFoodVendor());
|
||||
$objectManager->persist($menuItem);
|
||||
}
|
||||
if ($menuItem->getAliasOf() !== null) {
|
||||
$menuItem = $menuItem->getAliasOf();
|
||||
$orderItem->setName($menuItem->getName());
|
||||
}
|
||||
$orderItem->setMenuItem($menuItem);
|
||||
if ($orderItem->getPriceCents() === 0) {
|
||||
$orderItem->setPriceCents($menuItem->getPriceCents());
|
||||
} elseif ($orderItem->getPriceCents() !== $menuItem->getPriceCents()) {
|
||||
$menuItem->setPriceCents($orderItem->getPriceCents());
|
||||
$objectManager->persist($menuItem);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@ use Doctrine\ORM\QueryBuilder;
|
|||
use Override;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
@ -29,6 +30,10 @@ final class MenuItemType extends AbstractType
|
|||
]),
|
||||
],
|
||||
]);
|
||||
$builder->add('priceCents', MoneyType::class, [
|
||||
'label' => 'Price',
|
||||
'divisor' => 100,
|
||||
]);
|
||||
$builder->add('aliases', EntityType::class, [
|
||||
'class' => MenuItem::class,
|
||||
'choice_label' => 'name',
|
||||
|
|
32
src/Form/OrderFinalize.php
Normal file
32
src/Form/OrderFinalize.php
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Form;
|
||||
|
||||
use App\Entity\FoodOrder;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
final class OrderFinalize extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add('orderItems', CollectionType::class, [
|
||||
'entry_type' => OrderItemFinalize::class,
|
||||
'entry_options' => [
|
||||
'label' => false,
|
||||
],
|
||||
])
|
||||
->add('save', SubmitType::class)
|
||||
;
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => FoodOrder::class,
|
||||
]);
|
||||
}
|
||||
}
|
44
src/Form/OrderItemFinalize.php
Normal file
44
src/Form/OrderItemFinalize.php
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Form;
|
||||
|
||||
use App\Entity\OrderItem;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
final class OrderItemFinalize extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder
|
||||
->add(child: 'name', options: [
|
||||
'label' => 'order item',
|
||||
'disabled' => true,
|
||||
])
|
||||
->add(child: 'extras', options: [
|
||||
'disabled' => true,
|
||||
])
|
||||
->add(child: 'createdBy', options: [
|
||||
'disabled' => true,
|
||||
])
|
||||
->add(child: 'priceCents', type: MoneyType::class, options: [
|
||||
'label' => 'price',
|
||||
'divisor' => 100,
|
||||
])
|
||||
->add(child: 'isPaid', type: CheckboxType::class, options: [
|
||||
'required' => false,
|
||||
'label' => 'paid?',
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => OrderItem::class,
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -39,6 +39,38 @@
|
|||
</div>
|
||||
|
||||
<h2 class="mt-5">Items</h2>
|
||||
{% if (food_order.isClosed and form) %}
|
||||
{{ form_start(form) }}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>By</th>
|
||||
<th>item</th>
|
||||
<th>extras</th>
|
||||
<th>price</th>
|
||||
<th>is paid</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for itemForm in form.orderItems %}
|
||||
<tr>
|
||||
<td>{{ field_value(itemForm.createdBy) }}</td>
|
||||
<td>{{ field_value(itemForm.name) }}</td>
|
||||
<td>{{ field_value(itemForm.extras) }}</td>
|
||||
<td>{{ form_widget(itemForm.priceCents) }}</td>
|
||||
<td>{{ form_widget(itemForm.isPaid) }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tr>
|
||||
<td colspan="4"></td>
|
||||
<td>{{ form_row(form.save) }} {{ form_row(form._token) }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{ form_end(form, {render_rest: false}) }}
|
||||
{% else %}
|
||||
<table class="table table-hover">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
|
@ -68,6 +100,7 @@
|
|||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
<a class="btn btn-primary mt-2" href="{{ path('app_order_item_new', {'foodOrder': food_order.id}) }}">New Item</a>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -15,6 +15,10 @@
|
|||
<th>Name</th>
|
||||
<td>{{ menu_item.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Price</th>
|
||||
<td>{{ (menu_item.priceCents / 100)|format_currency('EUR') }}</td>
|
||||
</tr>
|
||||
{% if(menu_item.aliasOf) %}
|
||||
<tr>
|
||||
<th>Alias of</th>
|
||||
|
|
|
@ -13,7 +13,8 @@ use App\Form\OrderItemType;
|
|||
use App\Repository\FoodOrderRepository;
|
||||
use App\Repository\MenuItemRepository;
|
||||
|
||||
use function describe;
|
||||
use function covers;
|
||||
use function it;
|
||||
use function pest;
|
||||
use function sprintf;
|
||||
use function test;
|
||||
|
@ -55,105 +56,105 @@ pest()
|
|||
$this->manager->flush();
|
||||
|
||||
});
|
||||
covers(
|
||||
MenuItemController::class,
|
||||
OrderItemController::class,
|
||||
OrderItemType::class,
|
||||
MenuItemRepository::class,
|
||||
FoodOrder::class,
|
||||
FoodVendor::class,
|
||||
MenuItem::class,
|
||||
OrderItem::class,
|
||||
FoodOrderRepository::class,
|
||||
MenuItemType::class,
|
||||
);
|
||||
|
||||
describe(MenuItemController::class, function (): void {
|
||||
test('show', function (): void {
|
||||
it('show', function (): void {
|
||||
|
||||
$crawler = $this->client->request('GET', "{$this->path}{$this->menuItem->getId()}");
|
||||
$idValue = $crawler->filter(
|
||||
'.table > tbody:nth-child(1) > tr:nth-child(1) > td:nth-child(2)'
|
||||
)->text();
|
||||
$nameValue = $crawler->filter(
|
||||
'.table > tbody:nth-child(1) > tr:nth-child(2) > td:nth-child(2)'
|
||||
)->text();
|
||||
$crawler = $this->client->request('GET', "{$this->path}{$this->menuItem->getId()}");
|
||||
$idValue = $crawler->filter(
|
||||
'.table > tbody:nth-child(1) > tr:nth-child(1) > td:nth-child(2)'
|
||||
)->text();
|
||||
$nameValue = $crawler->filter(
|
||||
'.table > tbody:nth-child(1) > tr:nth-child(2) > td:nth-child(2)'
|
||||
)->text();
|
||||
|
||||
$aliasTwoNameValue = $crawler->filter(
|
||||
'.table > tbody:nth-child(1) > tr:nth-child(3) > td:nth-child(2) > ul:nth-child(1) > li:nth-child(1)'
|
||||
)->text();
|
||||
$aliasOneNameValue = $crawler->filter(
|
||||
'.table > tbody:nth-child(1) > tr:nth-child(3) > td:nth-child(2) > ul:nth-child(1) > li:nth-child(2)'
|
||||
)->text();
|
||||
$this->assertResponseStatusCodeSame(200);
|
||||
$this->assertEquals($idValue, $this->menuItem->getId());
|
||||
$this->assertEquals($nameValue, $this->menuItem->getName());
|
||||
$this->assertEquals($aliasTwoNameValue, $this->aliasOne->getName());
|
||||
$this->assertEquals($aliasOneNameValue, $this->aliasTwo->getName());
|
||||
});
|
||||
$aliasTwoNameValue = $crawler->filter(
|
||||
'.table > tbody:nth-child(1) > tr:nth-child(4) > td:nth-child(2) > ul:nth-child(1) > li:nth-child(1)'
|
||||
)->text();
|
||||
$aliasOneNameValue = $crawler->filter(
|
||||
'.table > tbody:nth-child(1) > tr:nth-child(4) > td:nth-child(2) > ul:nth-child(1) > li:nth-child(2)'
|
||||
)->text();
|
||||
$this->assertResponseStatusCodeSame(200);
|
||||
$this->assertEquals($idValue, $this->menuItem->getId());
|
||||
$this->assertEquals($nameValue, $this->menuItem->getName());
|
||||
$this->assertEquals($aliasTwoNameValue, $this->aliasOne->getName());
|
||||
$this->assertEquals($aliasOneNameValue, $this->aliasTwo->getName());
|
||||
});
|
||||
|
||||
test('edit', function (): void {
|
||||
$crawler = $this->client->request('GET', sprintf('%s%s/edit', $this->path, $this->menuItem->getId()));
|
||||
$nameElem = $crawler->filter('#menu_item_name');
|
||||
$this->assertEquals(
|
||||
'Testing 1 2',
|
||||
$nameElem->attr('value')
|
||||
);
|
||||
|
||||
$form = $crawler->selectButton('Update')
|
||||
->form();
|
||||
$form['menu_item[name]'] = 'Testing-1';
|
||||
$form['menu_item[aliases]'][0]->untick();
|
||||
|
||||
$this->client->submit($form);
|
||||
|
||||
$this->assertResponseRedirects(sprintf('/menu/item/%s', $this->menuItem->getId()));
|
||||
$menuItem = $this->repository->find($this->menuItem->getId());
|
||||
$this->assertEquals('Testing-1', $menuItem->getName());
|
||||
$this->assertEquals(1, $menuItem->getAliases()->count());
|
||||
$aliasOne = $this->repository->find($this->aliasOne->getId());
|
||||
$this->assertNull($aliasOne->getAliasOf());
|
||||
});
|
||||
|
||||
test('edit invalid', function (): void {
|
||||
$crawler = $this->client->request('GET', sprintf('%s%s/edit', $this->path, $this->menuItem->getId()));
|
||||
$nameElem = $crawler->filter('#menu_item_name');
|
||||
$this->assertEquals(
|
||||
'Testing 1 2',
|
||||
$nameElem->attr('value')
|
||||
);
|
||||
|
||||
$form = $crawler->selectButton('Update')
|
||||
->form();
|
||||
$form['menu_item[name]'] = 'a';
|
||||
|
||||
$this->client->submit($form);
|
||||
|
||||
$this->assertResponseStatusCodeSame(422);
|
||||
});
|
||||
|
||||
test('delete', function (): void {
|
||||
$order = new FoodOrder;
|
||||
$order->setFoodVendor($this->vendor);
|
||||
|
||||
$this->manager->persist($order);
|
||||
$this->manager->flush();
|
||||
$this->assertFalse($this->menuItem->isDeleted());
|
||||
|
||||
$this->client->request('GET', "{$this->path}{$this->menuItem->getId()}");
|
||||
$this->client->submitForm('Delete', []);
|
||||
|
||||
$menuItem = $this->repository->find($this->menuItem->getId());
|
||||
|
||||
$this->assertTrue($menuItem->isDeleted());
|
||||
|
||||
$crawler = $this->client->request('GET', '/order/item/new/' . $order->getId());
|
||||
$count = $crawler->filter('body > main:nth-child(2) > div:nth-child(5)')
|
||||
->children()
|
||||
->count();
|
||||
$this->assertSame(2, $count);
|
||||
|
||||
$this->assertResponseIsSuccessful();
|
||||
|
||||
});
|
||||
})
|
||||
->covers(
|
||||
MenuItemController::class,
|
||||
OrderItemController::class,
|
||||
OrderItemType::class,
|
||||
MenuItemRepository::class,
|
||||
FoodOrder::class,
|
||||
FoodVendor::class,
|
||||
MenuItem::class,
|
||||
OrderItem::class,
|
||||
FoodOrderRepository::class,
|
||||
MenuItemType::class,
|
||||
test('edit', function (): void {
|
||||
$crawler = $this->client->request('GET', sprintf('%s%s/edit', $this->path, $this->menuItem->getId()));
|
||||
$nameElem = $crawler->filter('#menu_item_name');
|
||||
$this->assertEquals(
|
||||
'Testing 1 2',
|
||||
$nameElem->attr('value')
|
||||
);
|
||||
|
||||
$form = $crawler->selectButton('Update')
|
||||
->form();
|
||||
$form['menu_item[name]'] = 'Testing-1';
|
||||
$form['menu_item[priceCents]'] = '1.23';
|
||||
$form['menu_item[aliases]'][0]->untick();
|
||||
|
||||
$this->client->submit($form);
|
||||
|
||||
$this->assertResponseRedirects(sprintf('/menu/item/%s', $this->menuItem->getId()));
|
||||
$menuItem = $this->repository->find($this->menuItem->getId());
|
||||
$this->assertEquals('Testing-1', $menuItem->getName());
|
||||
$this->assertEquals(123, $menuItem->getPriceCents());
|
||||
$this->assertEquals(1, $menuItem->getAliases()->count());
|
||||
$aliasOne = $this->repository->find($this->aliasOne->getId());
|
||||
$this->assertNull($aliasOne->getAliasOf());
|
||||
});
|
||||
|
||||
test('edit invalid', function (): void {
|
||||
$crawler = $this->client->request('GET', sprintf('%s%s/edit', $this->path, $this->menuItem->getId()));
|
||||
$nameElem = $crawler->filter('#menu_item_name');
|
||||
$this->assertEquals(
|
||||
'Testing 1 2',
|
||||
$nameElem->attr('value')
|
||||
);
|
||||
|
||||
$form = $crawler->selectButton('Update')
|
||||
->form();
|
||||
$form['menu_item[name]'] = 'a';
|
||||
|
||||
$this->client->submit($form);
|
||||
|
||||
$this->assertResponseStatusCodeSame(422);
|
||||
});
|
||||
|
||||
test('delete', function (): void {
|
||||
$order = new FoodOrder;
|
||||
$order->setFoodVendor($this->vendor);
|
||||
|
||||
$this->manager->persist($order);
|
||||
$this->manager->flush();
|
||||
$this->assertFalse($this->menuItem->isDeleted());
|
||||
|
||||
$this->client->request('GET', "{$this->path}{$this->menuItem->getId()}");
|
||||
$this->client->submitForm('Delete', []);
|
||||
|
||||
$menuItem = $this->repository->find($this->menuItem->getId());
|
||||
|
||||
$this->assertTrue($menuItem->isDeleted());
|
||||
|
||||
$crawler = $this->client->request('GET', '/order/item/new/' . $order->getId());
|
||||
$count = $crawler->filter('body > main:nth-child(2) > div:nth-child(5)')
|
||||
->children()
|
||||
->count();
|
||||
$this->assertSame(2, $count);
|
||||
|
||||
$this->assertResponseIsSuccessful();
|
||||
|
||||
});
|
||||
|
|
45
tests/Feature/EventListener/OrderItemPreFlushTest.php
Normal file
45
tests/Feature/EventListener/OrderItemPreFlushTest.php
Normal file
|
@ -0,0 +1,45 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
use App\Entity\FoodOrder;
|
||||
use App\Entity\FoodVendor;
|
||||
use App\Entity\OrderItem;
|
||||
|
||||
pest()
|
||||
->beforeEach(function (): void {
|
||||
$this->setEntityClass(FoodOrder::class);
|
||||
$this->setPath('/food/order/');
|
||||
$this->repository = $this->manager->getRepository($this->entityClass);
|
||||
$this->vendor = new FoodVendor;
|
||||
$this->vendor->setName('Food Vendor');
|
||||
|
||||
$this->manager->persist($this->vendor);
|
||||
|
||||
$this->order = new FoodOrder;
|
||||
$this->order->setFoodVendor($this->vendor);
|
||||
|
||||
$this->manager->persist($this->order);
|
||||
$this->manager->flush();
|
||||
});
|
||||
|
||||
it('updates the menu item price', function (): void {
|
||||
$orderItem = new OrderItem;
|
||||
$orderItem->setFoodOrder($this->order);
|
||||
$orderItem->setName('Item');
|
||||
$orderItem->setPriceCents(100);
|
||||
|
||||
$this->manager->persist($orderItem);
|
||||
$this->manager->flush();
|
||||
|
||||
expect($orderItem->getMenuItem()->getPriceCents())
|
||||
->toBe(100);
|
||||
|
||||
$orderItem->setPriceCents(200);
|
||||
$this->manager->persist($orderItem);
|
||||
$this->manager->flush();
|
||||
|
||||
$id = $orderItem->getId();
|
||||
|
||||
$orderItem = $this->manager->find(OrderItem::class, $id);
|
||||
expect($orderItem->getMenuItem()->getPriceCents())
|
||||
->toBe(200);
|
||||
});
|
|
@ -14,7 +14,7 @@ use App\Tests\DbWebTest;
|
|||
*/
|
||||
|
||||
pest()
|
||||
->extends(DbWebTest::class)->in('Feature/Controller/*.php');
|
||||
->extends(DbWebTest::class)->in('Feature/Controller/*.php', 'Feature/EventListener/*.php');
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue