#20_save-username-to-cookie #24

Merged
lubiana merged 3 commits from #20_save-username-to-cookie into main 2024-06-30 07:58:09 +00:00
12 changed files with 147 additions and 24 deletions
Showing only changes of commit 511f12f10f - Show all commits

View 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)');
}
}

View file

@ -4,6 +4,7 @@ namespace App\Controller;
use App\Form\UserNameFormType; use App\Form\UserNameFormType;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; 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\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
@ -13,21 +14,34 @@ use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
final class HomeController extends AbstractController final class HomeController extends AbstractController
{ {
public const string DEFAULT_USERNAME = 'nobody'; 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'));
} }
public function usernameForm(Request $request): Response #[Route('/username', name: 'username')]
public function usernameForm(Request $request, UrlGeneratorInterface $router): Response
{ {
$username = $request->cookies->get('username', self::DEFAULT_USERNAME);
$form = $this->createForm(UsernameFormType::class); $form = $this->createForm(UsernameFormType::class);
$form->handleRequest($request); $form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) { if ($form->isSubmitted() && $form->isValid()) {
$username = $form->getData()['username'] ?? self::DEFAULT_USERNAME; $username = $form->getData()['username'] ?? self::DEFAULT_USERNAME;
if ($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,
]);
} }
} }

View file

@ -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;
}
} }

View file

@ -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;
}
} }

View file

@ -14,7 +14,9 @@ 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')
; ;
} }

View file

@ -1,20 +1,25 @@
<?php <?php declare(strict_types=1);
namespace App\Form; namespace App\Form;
use Override;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\OptionsResolver;
class UserNameFormType extends AbstractType final class UserNameFormType extends AbstractType
{ {
#[Override]
public function buildForm(FormBuilderInterface $builder, array $options): void public function buildForm(FormBuilderInterface $builder, array $options): void
{ {
$builder $builder
->add('username') ->add(child: 'username', options: [
'required' => false,
])
; ;
} }
#[Override]
public function configureOptions(OptionsResolver $resolver): void public function configureOptions(OptionsResolver $resolver): void
{ {
$resolver->setDefaults([ $resolver->setDefaults([

View file

@ -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>
<nav> <p>Hello {{ app.request.cookies.get('username', 'nobody') }} - <a href="{{ path('username') }}">change name</a></p>
<a href="{{ path('app_food_order_index') }}">Orders</a> <nav>
<a href="{{ path('app_food_vendor_index') }}">Vendors</a> <a href="{{ path('app_food_order_index') }}">Orders</a> /
<a <a href="{{ path('app_food_vendor_index') }}">Vendors</a> /
href="https://hannover.ccc.de/gitlab/lubiana/futtern/issues/new" <a
target="_blank" href="https://hannover.ccc.de/gitlab/lubiana/futtern/issues/new"
>Create Issue</a> target="_blank"
</nav> >Create Issue</a>
</nav>
</header> </header>
<main> <main>
{% block body %}{% endblock %} {% block body %}{% endblock %}
</main> </main>
</body>
</html> </html>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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
}); });
}); });

View 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 %}