#20_save-username-to-cookie #24
15 changed files with 188 additions and 18 deletions
44
migrations/Version20240627212849.php
Normal file
44
migrations/Version20240627212849.php
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
<?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 Version20240627212849 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('ALTER TABLE food_order ADD COLUMN created_by VARCHAR(255) DEFAULT \'nobody\' NOT NULL');
|
||||||
|
$this->addSql('ALTER TABLE order_item ADD COLUMN created_by VARCHAR(255) DEFAULT \'nobody\' NOT NULL');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
// this down() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->addSql('CREATE TEMPORARY TABLE __temp__food_order AS SELECT id, closed_at, food_vendor_id FROM food_order');
|
||||||
|
$this->addSql('DROP TABLE food_order');
|
||||||
|
$this->addSql('CREATE TABLE food_order (id BLOB NOT NULL, closed_at DATETIME DEFAULT NULL, food_vendor_id BLOB NOT NULL, PRIMARY KEY(id), CONSTRAINT FK_44856726EF983E8 FOREIGN KEY (food_vendor_id) REFERENCES food_vendor (id) NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||||
|
$this->addSql('INSERT INTO food_order (id, closed_at, food_vendor_id) SELECT id, closed_at, food_vendor_id FROM __temp__food_order');
|
||||||
|
$this->addSql('DROP TABLE __temp__food_order');
|
||||||
|
$this->addSql('CREATE INDEX IDX_44856726EF983E8 ON food_order (food_vendor_id)');
|
||||||
|
$this->addSql('CREATE TEMPORARY TABLE __temp__order_item AS SELECT id, name, extras, food_order_id, menu_item_id FROM order_item');
|
||||||
|
$this->addSql('DROP TABLE order_item');
|
||||||
|
$this->addSql('CREATE TABLE order_item (id BLOB NOT NULL, name VARCHAR(255) NOT NULL, extras VARCHAR(255) DEFAULT 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)');
|
||||||
|
$this->addSql('INSERT INTO order_item (id, name, extras, food_order_id, menu_item_id) SELECT id, name, extras, food_order_id, menu_item_id FROM __temp__order_item');
|
||||||
|
$this->addSql('DROP TABLE __temp__order_item');
|
||||||
|
$this->addSql('CREATE INDEX IDX_52EA1F09A5D24A7A ON order_item (food_order_id)');
|
||||||
|
$this->addSql('CREATE INDEX IDX_52EA1F099AB44FE0 ON order_item (menu_item_id)');
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,6 +26,8 @@ final class FoodOrderController extends AbstractController
|
||||||
public function new(Request $request, EntityManagerInterface $entityManager): Response
|
public function new(Request $request, EntityManagerInterface $entityManager): Response
|
||||||
{
|
{
|
||||||
$foodOrder = new FoodOrder;
|
$foodOrder = new FoodOrder;
|
||||||
|
$username = $request->cookies->get('username', 'nobody');
|
||||||
|
$foodOrder->setCreatedBy($username);
|
||||||
$form = $this->createForm(FoodOrderType::class, $foodOrder, [
|
$form = $this->createForm(FoodOrderType::class, $foodOrder, [
|
||||||
'action' => $this->generateUrl('app_food_order_new'),
|
'action' => $this->generateUrl('app_food_order_new'),
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -2,16 +2,46 @@
|
||||||
|
|
||||||
namespace App\Controller;
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use App\Form\UserNameFormType;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
|
use Symfony\Component\HttpFoundation\Cookie;
|
||||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\Routing\Attribute\Route;
|
use Symfony\Component\Routing\Attribute\Route;
|
||||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||||
|
|
||||||
final class HomeController
|
final class HomeController extends AbstractController
|
||||||
{
|
{
|
||||||
|
public const string DEFAULT_USERNAME = 'nobody';
|
||||||
|
|
||||||
#[Route('/', name: 'home')]
|
#[Route('/', name: 'home')]
|
||||||
public function home(UrlGeneratorInterface $router): Response
|
public function home(UrlGeneratorInterface $router): Response
|
||||||
{
|
{
|
||||||
return new RedirectResponse($router->generate('app_food_order_index'));
|
return new RedirectResponse($router->generate('app_food_order_index'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[Route('/username', name: 'username')]
|
||||||
|
public function usernameForm(Request $request, UrlGeneratorInterface $router): Response
|
||||||
|
{
|
||||||
|
$form = $this->createForm(UsernameFormType::class);
|
||||||
|
$form->handleRequest($request);
|
||||||
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
|
$username = $form->getData()['username'] ?? self::DEFAULT_USERNAME;
|
||||||
|
$response = new RedirectResponse($router->generate('app_food_order_index'));
|
||||||
|
if ($username === self::DEFAULT_USERNAME || $username === '') {
|
||||||
|
$response->headers->clearCookie('username');
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
$response->headers->setCookie(new Cookie('username', $username));
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
$username = $request->cookies->get('username', self::DEFAULT_USERNAME);
|
||||||
|
$form->setData([
|
||||||
|
'username' => $username,
|
||||||
|
]);
|
||||||
|
return $this->render('username.html.twig', [
|
||||||
|
'form' => $form,
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,9 @@ final class OrderItemController extends AbstractController
|
||||||
}
|
}
|
||||||
|
|
||||||
$orderItem = new OrderItem;
|
$orderItem = new OrderItem;
|
||||||
|
$username = $request->cookies->get('username', 'nobody');
|
||||||
|
$orderItem->setCreatedBy($username);
|
||||||
|
|
||||||
$form = $this->createForm(OrderItemType::class, $orderItem);
|
$form = $this->createForm(OrderItemType::class, $orderItem);
|
||||||
$form->handleRequest($request);
|
$form->handleRequest($request);
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,11 @@ class FoodOrder
|
||||||
#[ORM\OneToMany(targetEntity: OrderItem::class, mappedBy: 'foodOrder', orphanRemoval: true)]
|
#[ORM\OneToMany(targetEntity: OrderItem::class, mappedBy: 'foodOrder', orphanRemoval: true)]
|
||||||
private Collection $orderItems;
|
private Collection $orderItems;
|
||||||
|
|
||||||
|
#[ORM\Column(length: 255, options: [
|
||||||
|
'default' => 'nobody',
|
||||||
|
])]
|
||||||
|
private string|null $createdBy = 'nobody';
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->orderItems = new ArrayCollection;
|
$this->orderItems = new ArrayCollection;
|
||||||
|
@ -117,4 +122,16 @@ class FoodOrder
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getCreatedBy(): string|null
|
||||||
|
{
|
||||||
|
return $this->createdBy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCreatedBy(string $createdBy): static
|
||||||
|
{
|
||||||
|
$this->createdBy = $createdBy;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,11 @@ class OrderItem
|
||||||
#[ORM\JoinColumn(nullable: false)]
|
#[ORM\JoinColumn(nullable: false)]
|
||||||
private MenuItem|null $menuItem = null;
|
private MenuItem|null $menuItem = null;
|
||||||
|
|
||||||
|
#[ORM\Column(length: 255, options: [
|
||||||
|
'default' => 'nobody',
|
||||||
|
])]
|
||||||
|
private string|null $createdBy = 'nobody';
|
||||||
|
|
||||||
public function getId(): Ulid|null
|
public function getId(): Ulid|null
|
||||||
{
|
{
|
||||||
return $this->id;
|
return $this->id;
|
||||||
|
@ -83,4 +88,16 @@ class OrderItem
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getCreatedBy(): string|null
|
||||||
|
{
|
||||||
|
return $this->createdBy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCreatedBy(string $createdBy): static
|
||||||
|
{
|
||||||
|
$this->createdBy = $createdBy;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ final class FoodOrderType extends AbstractType
|
||||||
->add(child: 'closedAt', options: [
|
->add(child: 'closedAt', options: [
|
||||||
'label' => 'closes at',
|
'label' => 'closes at',
|
||||||
])
|
])
|
||||||
|
->add(child: 'createdBy')
|
||||||
;
|
;
|
||||||
if ($action !== null) {
|
if ($action !== null) {
|
||||||
$builder->setAction($action);
|
$builder->setAction($action);
|
||||||
|
|
|
@ -14,8 +14,11 @@ final class OrderItemType extends AbstractType
|
||||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||||
{
|
{
|
||||||
$builder
|
$builder
|
||||||
->add('name')
|
->add(child: 'name', options: [
|
||||||
|
'data' => $options['name'] ?? '',
|
||||||
|
])
|
||||||
->add('extras')
|
->add('extras')
|
||||||
|
->add('createdBy')
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
29
src/Form/UserNameFormType.php
Normal file
29
src/Form/UserNameFormType.php
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Form;
|
||||||
|
|
||||||
|
use Override;
|
||||||
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
|
final class UserNameFormType extends AbstractType
|
||||||
|
{
|
||||||
|
#[Override]
|
||||||
|
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||||
|
{
|
||||||
|
$builder
|
||||||
|
->add(child: 'username', options: [
|
||||||
|
'required' => false,
|
||||||
|
])
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Override]
|
||||||
|
public function configureOptions(OptionsResolver $resolver): void
|
||||||
|
{
|
||||||
|
$resolver->setDefaults([
|
||||||
|
// Configure your form options here
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,23 +4,29 @@
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>{% block title %}Welcome!{% endblock %}</title>
|
<title>{% block title %}Welcome!{% endblock %}</title>
|
||||||
<link rel="icon" type="image/png" href="/static/img/slice-of-pizza.png" />
|
<link rel="icon" type="image/png" href="/static/img/slice-of-pizza.png" />
|
||||||
<link rel="stylesheet" href="/static/css/simple.min.css">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@exampledev/new.css@1.1.2/new.min.css">
|
||||||
|
<style>
|
||||||
|
label{
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
<script src="/static/js/htmx.min.js"></script>
|
<script src="/static/js/htmx.min.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
|
<p>Hello {{ app.request.cookies.get('username', 'nobody') }} - <a href="{{ path('username') }}">change name</a></p>
|
||||||
<nav>
|
<nav>
|
||||||
<a href="{{ path('app_food_order_index') }}">Orders</a>
|
<a href="{{ path('app_food_order_index') }}">Orders</a> /
|
||||||
<a href="{{ path('app_food_vendor_index') }}">Vendors</a>
|
<a href="{{ path('app_food_vendor_index') }}">Vendors</a> /
|
||||||
<a
|
<a
|
||||||
href="https://hannover.ccc.de/gitlab/lubiana/futtern/issues/new"
|
href="https://hannover.ccc.de/gitlab/lubiana/futtern/issues/new"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>Create Issue</a>
|
>Create Issue</a>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
</header>
|
</header>
|
||||||
<main>
|
<main>
|
||||||
{% block body %}{% endblock %}
|
{% block body %}{% endblock %}
|
||||||
</main>
|
</main>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
<th>CreatedBy</th>
|
||||||
<th>Vendor</th>
|
<th>Vendor</th>
|
||||||
<th>CreatedAt</th>
|
<th>CreatedAt</th>
|
||||||
<th>ClosedAt</th>
|
<th>ClosedAt</th>
|
||||||
|
|
|
@ -11,6 +11,10 @@
|
||||||
<th>Vendor</th>
|
<th>Vendor</th>
|
||||||
<td>{{ food_order.foodVendor.name }}</td>
|
<td>{{ food_order.foodVendor.name }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Created By</th>
|
||||||
|
<td>{{ food_order.createdBy }}</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>CreatedAt</th>
|
<th>CreatedAt</th>
|
||||||
<td>{{ food_order.createdAt ? food_order.createdAt|date('Y-m-d H:i:s') : '' }}</td>
|
<td>{{ food_order.createdAt ? food_order.createdAt|date('Y-m-d H:i:s') : '' }}</td>
|
||||||
|
@ -32,6 +36,7 @@
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
<th>username</th>
|
||||||
<th>name</th>
|
<th>name</th>
|
||||||
<th>extras</th>
|
<th>extras</th>
|
||||||
<th>actions</th>
|
<th>actions</th>
|
||||||
|
@ -40,6 +45,7 @@
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for item in food_order.orderItems %}
|
{% for item in food_order.orderItems %}
|
||||||
<tr>
|
<tr>
|
||||||
|
<td>{{ item.createdBy }}</td>
|
||||||
<td>{{ item.name }}</td>
|
<td>{{ item.name }}</td>
|
||||||
<td>{{ item.extras }}</td>
|
<td>{{ item.extras }}</td>
|
||||||
<td>
|
<td>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
<tr>
|
<tr>
|
||||||
|
<td>{{ food_order.createdBy }}</td>
|
||||||
<td>{{ food_order.foodVendor.name }}</td>
|
<td>{{ food_order.foodVendor.name }}</td>
|
||||||
<td>{{ food_order.createdAt ? food_order.createdAt|date('Y-m-d H:i:s') : '' }}</td>
|
<td>{{ food_order.createdAt ? food_order.createdAt|date('Y-m-d H:i:s') : '' }}</td>
|
||||||
<td>{{ food_order.closedAt ? food_order.closedAt|date('Y-m-d H:i:s') : '' }}</td>
|
<td>{{ food_order.closedAt ? food_order.closedAt|date('Y-m-d H:i:s') : '' }}</td>
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{% for menuItem in menuItems %}
|
{% for menuItem in menuItems %}
|
||||||
<button data-menu-item>{{ menuItem.name }}</button>
|
<a href="#" data-menu-item>{{ menuItem.name }}</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@
|
||||||
<script>
|
<script>
|
||||||
document.querySelectorAll('[data-menu-item]').forEach(function(element) {
|
document.querySelectorAll('[data-menu-item]').forEach(function(element) {
|
||||||
element.addEventListener('click', function(e) {
|
element.addEventListener('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
document.getElementById('order_item_name').value = e.target.textContent
|
document.getElementById('order_item_name').value = e.target.textContent
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
9
templates/username.html.twig
Normal file
9
templates/username.html.twig
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
{% extends 'base.html.twig' %}
|
||||||
|
|
||||||
|
{% block title %}Tell me your name{% endblock %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<h1>Tell me your name</h1>
|
||||||
|
<p>By submitting the form, you agree that your username will be stored as a cookie.</p>
|
||||||
|
{{ include('_form.html.twig') }}
|
||||||
|
{% endblock %}
|
Loading…
Reference in a new issue