add menuitem aliases
This commit is contained in:
parent
70b39515ec
commit
9781bd561f
13 changed files with 713 additions and 212 deletions
|
@ -25,6 +25,7 @@
|
||||||
"symfony/yaml": "7.1.*"
|
"symfony/yaml": "7.1.*"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
|
"doctrine/doctrine-fixtures-bundle": "^4.0",
|
||||||
"infection/infection": "^0.29.6",
|
"infection/infection": "^0.29.6",
|
||||||
"lubiana/code-quality": "^1.7.2",
|
"lubiana/code-quality": "^1.7.2",
|
||||||
"phpunit/phpunit": "^9.6.20",
|
"phpunit/phpunit": "^9.6.20",
|
||||||
|
@ -32,6 +33,8 @@
|
||||||
"symfony/css-selector": "7.1.*",
|
"symfony/css-selector": "7.1.*",
|
||||||
"symfony/maker-bundle": "^1.60",
|
"symfony/maker-bundle": "^1.60",
|
||||||
"symfony/phpunit-bridge": "^7.1.3",
|
"symfony/phpunit-bridge": "^7.1.3",
|
||||||
|
"symfony/stopwatch": "7.1.*",
|
||||||
|
"symfony/web-profiler-bundle": "7.1.*",
|
||||||
"symplify/config-transformer": "^12.3.4"
|
"symplify/config-transformer": "^12.3.4"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
|
@ -44,7 +47,7 @@
|
||||||
},
|
},
|
||||||
"sort-packages": true,
|
"sort-packages": true,
|
||||||
"platform": {
|
"platform": {
|
||||||
"php": "8.3"
|
"php": "8.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
@ -77,7 +80,7 @@
|
||||||
],
|
],
|
||||||
"post-update-cmd": [
|
"post-update-cmd": [
|
||||||
"@auto-scripts",
|
"@auto-scripts",
|
||||||
"config-transformer switch-format config"
|
"config-transformer config"
|
||||||
],
|
],
|
||||||
"lint": [
|
"lint": [
|
||||||
"rector",
|
"rector",
|
||||||
|
|
665
composer.lock
generated
665
composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -1,10 +1,12 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
use Doctrine\Bundle\DoctrineBundle\DoctrineBundle;
|
use Doctrine\Bundle\DoctrineBundle\DoctrineBundle;
|
||||||
|
use Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle;
|
||||||
use Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle;
|
use Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle;
|
||||||
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
|
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
|
||||||
use Symfony\Bundle\MakerBundle\MakerBundle;
|
use Symfony\Bundle\MakerBundle\MakerBundle;
|
||||||
use Symfony\Bundle\TwigBundle\TwigBundle;
|
use Symfony\Bundle\TwigBundle\TwigBundle;
|
||||||
|
use Symfony\Bundle\WebProfilerBundle\WebProfilerBundle;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
FrameworkBundle::class => [
|
FrameworkBundle::class => [
|
||||||
|
@ -22,4 +24,12 @@ return [
|
||||||
TwigBundle::class => [
|
TwigBundle::class => [
|
||||||
'all' => true,
|
'all' => true,
|
||||||
],
|
],
|
||||||
|
DoctrineFixturesBundle::class => [
|
||||||
|
'dev' => true,
|
||||||
|
'test' => true,
|
||||||
|
],
|
||||||
|
WebProfilerBundle::class => [
|
||||||
|
'dev' => true,
|
||||||
|
'test' => true,
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
29
config/packages/web_profiler.php
Normal file
29
config/packages/web_profiler.php
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||||
|
|
||||||
|
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||||
|
if ($containerConfigurator->env() === 'dev') {
|
||||||
|
$containerConfigurator->extension('web_profiler', [
|
||||||
|
'toolbar' => true,
|
||||||
|
'intercept_redirects' => false,
|
||||||
|
]);
|
||||||
|
$containerConfigurator->extension('framework', [
|
||||||
|
'profiler' => [
|
||||||
|
'only_exceptions' => false,
|
||||||
|
'collect_serializer_data' => true,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
if ($containerConfigurator->env() === 'test') {
|
||||||
|
$containerConfigurator->extension('web_profiler', [
|
||||||
|
'toolbar' => false,
|
||||||
|
'intercept_redirects' => false,
|
||||||
|
]);
|
||||||
|
$containerConfigurator->extension('framework', [
|
||||||
|
'profiler' => [
|
||||||
|
'collect' => false,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
};
|
12
config/routes/web_profiler.php
Normal file
12
config/routes/web_profiler.php
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
|
||||||
|
|
||||||
|
return static function (RoutingConfigurator $routingConfigurator): void {
|
||||||
|
if ($routingConfigurator->env() === 'dev') {
|
||||||
|
$routingConfigurator->import('@WebProfilerBundle/Resources/config/routing/wdt.xml')
|
||||||
|
->prefix('/_wdt');
|
||||||
|
$routingConfigurator->import('@WebProfilerBundle/Resources/config/routing/profiler.xml')
|
||||||
|
->prefix('/_profiler');
|
||||||
|
}
|
||||||
|
};
|
42
migrations/Version20250124234947.php
Normal file
42
migrations/Version20250124234947.php
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
<?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 Version20250124234947 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 TEMPORARY TABLE __temp__menu_item AS SELECT id, name, food_vendor_id, deleted_at FROM menu_item');
|
||||||
|
$this->addSql('DROP TABLE menu_item');
|
||||||
|
$this->addSql('CREATE TABLE menu_item (id BLOB NOT NULL, name VARCHAR(255) NOT NULL, food_vendor_id BLOB NOT NULL, deleted_at DATETIME DEFAULT NULL, alias_of_id BLOB DEFAULT NULL, PRIMARY KEY(id), CONSTRAINT FK_D754D5506EF983E8 FOREIGN KEY (food_vendor_id) REFERENCES food_vendor (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_D754D55061F0AFC5 FOREIGN KEY (alias_of_id) REFERENCES menu_item (id) NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||||
|
$this->addSql('INSERT INTO menu_item (id, name, food_vendor_id, deleted_at) SELECT id, name, food_vendor_id, deleted_at FROM __temp__menu_item');
|
||||||
|
$this->addSql('DROP TABLE __temp__menu_item');
|
||||||
|
$this->addSql('CREATE INDEX IDX_D754D5506EF983E8 ON menu_item (food_vendor_id)');
|
||||||
|
$this->addSql('CREATE INDEX IDX_D754D55061F0AFC5 ON menu_item (alias_of_id)');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
// this down() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->addSql('CREATE TEMPORARY TABLE __temp__menu_item AS SELECT name, deleted_at, id, food_vendor_id FROM menu_item');
|
||||||
|
$this->addSql('DROP TABLE menu_item');
|
||||||
|
$this->addSql('CREATE TABLE menu_item (name VARCHAR(255) NOT NULL, deleted_at DATETIME DEFAULT NULL, id BLOB NOT NULL, food_vendor_id BLOB NOT NULL, PRIMARY KEY(id), CONSTRAINT FK_D754D5506EF983E8 FOREIGN KEY (food_vendor_id) REFERENCES food_vendor (id) NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||||
|
$this->addSql('INSERT INTO menu_item (name, deleted_at, id, food_vendor_id) SELECT name, deleted_at, id, food_vendor_id FROM __temp__menu_item');
|
||||||
|
$this->addSql('DROP TABLE __temp__menu_item');
|
||||||
|
$this->addSql('CREATE INDEX IDX_D754D5506EF983E8 ON menu_item (food_vendor_id)');
|
||||||
|
}
|
||||||
|
}
|
|
@ -45,6 +45,11 @@ final class OrderItemController extends AbstractController
|
||||||
$entityManager->persist($menuItem);
|
$entityManager->persist($menuItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($menuItem->getAliasOf() !== null) {
|
||||||
|
$menuItem = $menuItem->getAliasOf();
|
||||||
|
$orderItem->setName($menuItem->getName());
|
||||||
|
}
|
||||||
|
|
||||||
$orderItem->setMenuItem($menuItem);
|
$orderItem->setMenuItem($menuItem);
|
||||||
$orderItem->setFoodOrder($foodOrder);
|
$orderItem->setFoodOrder($foodOrder);
|
||||||
$entityManager->persist($orderItem);
|
$entityManager->persist($orderItem);
|
||||||
|
|
50
src/DataFixtures/AppFixtures.php
Normal file
50
src/DataFixtures/AppFixtures.php
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\DataFixtures;
|
||||||
|
|
||||||
|
use App\Entity\FoodVendor;
|
||||||
|
use App\Entity\MenuItem;
|
||||||
|
use Doctrine\Bundle\FixturesBundle\Fixture;
|
||||||
|
use Doctrine\Persistence\ObjectManager;
|
||||||
|
use Override;
|
||||||
|
|
||||||
|
use function range;
|
||||||
|
|
||||||
|
final class AppFixtures extends Fixture
|
||||||
|
{
|
||||||
|
private ObjectManager $manager;
|
||||||
|
|
||||||
|
#[Override]
|
||||||
|
public function load(ObjectManager $manager): void
|
||||||
|
{
|
||||||
|
$this->manager = $manager;
|
||||||
|
$vendorA = $this->createVendor('Vendor A');
|
||||||
|
$this->addMenuItemsToVendor($vendorA);
|
||||||
|
|
||||||
|
$vendorB = $this->createVendor('Vendor B');
|
||||||
|
$this->addMenuItemsToVendor($vendorB);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createVendor(string $name): FoodVendor
|
||||||
|
{
|
||||||
|
$vendorA = new FoodVendor;
|
||||||
|
$vendorA->setName($name);
|
||||||
|
$vendorA->setMenuLink('https://vendora.com');
|
||||||
|
$vendorA->setPhone('1234567890');
|
||||||
|
|
||||||
|
$this->manager->persist($vendorA);
|
||||||
|
$this->manager->flush();
|
||||||
|
return $vendorA;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addMenuItemsToVendor(FoodVendor $vendor): void
|
||||||
|
{
|
||||||
|
foreach (range(1, 10) as $i) {
|
||||||
|
$item = new MenuItem;
|
||||||
|
$item->setName("{$vendor->getName()} Item {$i}");
|
||||||
|
$item->setFoodVendor($vendor);
|
||||||
|
$this->manager->persist($item);
|
||||||
|
$this->manager->flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,8 @@ namespace App\Entity;
|
||||||
|
|
||||||
use App\Repository\MenuItemRepository;
|
use App\Repository\MenuItemRepository;
|
||||||
use DateTimeImmutable;
|
use DateTimeImmutable;
|
||||||
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
|
use Doctrine\Common\Collections\Collection;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
use Symfony\Bridge\Doctrine\IdGenerator\UlidGenerator;
|
use Symfony\Bridge\Doctrine\IdGenerator\UlidGenerator;
|
||||||
use Symfony\Bridge\Doctrine\Types\UlidType;
|
use Symfony\Bridge\Doctrine\Types\UlidType;
|
||||||
|
@ -22,13 +24,24 @@ class MenuItem
|
||||||
#[ORM\Column(nullable: true)]
|
#[ORM\Column(nullable: true)]
|
||||||
private DateTimeImmutable|null $deletedAt = null;
|
private DateTimeImmutable|null $deletedAt = null;
|
||||||
|
|
||||||
|
#[ORM\ManyToOne(targetEntity: self::class, inversedBy: 'aliases')]
|
||||||
|
private self|null $aliasOf = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Collection<int, self>
|
||||||
|
*/
|
||||||
|
#[ORM\OneToMany(targetEntity: self::class, mappedBy: 'aliasOf')]
|
||||||
|
private Collection $aliases;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
#[ORM\Id]
|
#[ORM\Id]
|
||||||
#[ORM\GeneratedValue(strategy: 'CUSTOM')]
|
#[ORM\GeneratedValue(strategy: 'CUSTOM')]
|
||||||
#[ORM\Column(type: UlidType::NAME, unique: true)]
|
#[ORM\Column(type: UlidType::NAME, unique: true)]
|
||||||
#[ORM\CustomIdGenerator(class: UlidGenerator::class)]
|
#[ORM\CustomIdGenerator(class: UlidGenerator::class)]
|
||||||
private Ulid|null $id = new Ulid
|
private Ulid|null $id = new Ulid
|
||||||
) {}
|
) {
|
||||||
|
$this->aliases = new ArrayCollection;
|
||||||
|
}
|
||||||
|
|
||||||
public function getId(): Ulid|null
|
public function getId(): Ulid|null
|
||||||
{
|
{
|
||||||
|
@ -81,4 +94,44 @@ class MenuItem
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getAliasOf(): self|null
|
||||||
|
{
|
||||||
|
return $this->aliasOf;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setAliasOf(self|null $aliasOf): static
|
||||||
|
{
|
||||||
|
$this->aliasOf = $aliasOf;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Collection<int, self>
|
||||||
|
*/
|
||||||
|
public function getAliases(): Collection
|
||||||
|
{
|
||||||
|
return $this->aliases;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addAlias(self $alias): static
|
||||||
|
{
|
||||||
|
if (! $this->aliases->contains($alias)) {
|
||||||
|
$this->aliases->add($alias);
|
||||||
|
$alias->setAliasOf($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function removeAlias(self $alias): static
|
||||||
|
{
|
||||||
|
// set the owning side to null (unless already changed)
|
||||||
|
if ($this->aliases->removeElement($alias) && $alias->getAliasOf() === $this) {
|
||||||
|
$alias->setAliasOf(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,11 @@
|
||||||
namespace App\Form;
|
namespace App\Form;
|
||||||
|
|
||||||
use App\Entity\MenuItem;
|
use App\Entity\MenuItem;
|
||||||
|
use App\Repository\MenuItemRepository;
|
||||||
|
use Doctrine\ORM\QueryBuilder;
|
||||||
use Override;
|
use Override;
|
||||||
|
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||||
|
use Symfony\Bridge\Doctrine\Types\UlidType;
|
||||||
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;
|
||||||
|
@ -13,8 +17,18 @@ final class MenuItemType extends AbstractType
|
||||||
#[Override]
|
#[Override]
|
||||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||||
{
|
{
|
||||||
|
$vendor = $options['data']->getFoodVendor();
|
||||||
|
$vendorId = $vendor->getId();
|
||||||
$builder
|
$builder
|
||||||
->add('name')
|
->add('name')
|
||||||
|
->add('aliasOf', EntityType::class, [
|
||||||
|
'class' => MenuItem::class,
|
||||||
|
'choice_label' => 'name',
|
||||||
|
'query_builder' => static fn(MenuItemRepository $repository): QueryBuilder => $repository
|
||||||
|
->createQueryBuilder('m')
|
||||||
|
->where('m.foodVendor = :vendorId')
|
||||||
|
->setParameter(':vendorId', $vendorId, UlidType::NAME),
|
||||||
|
])
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
25
symfony.lock
25
symfony.lock
|
@ -13,6 +13,18 @@
|
||||||
"src/Repository/.gitignore"
|
"src/Repository/.gitignore"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"doctrine/doctrine-fixtures-bundle": {
|
||||||
|
"version": "4.0",
|
||||||
|
"recipe": {
|
||||||
|
"repo": "github.com/symfony/recipes",
|
||||||
|
"branch": "main",
|
||||||
|
"version": "3.0",
|
||||||
|
"ref": "1f5514cfa15b947298df4d771e694e578d4c204d"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"src/DataFixtures/AppFixtures.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
"doctrine/doctrine-migrations-bundle": {
|
"doctrine/doctrine-migrations-bundle": {
|
||||||
"version": "3.3",
|
"version": "3.3",
|
||||||
"recipe": {
|
"recipe": {
|
||||||
|
@ -171,5 +183,18 @@
|
||||||
"files": [
|
"files": [
|
||||||
"config/packages/validator.yaml"
|
"config/packages/validator.yaml"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"symfony/web-profiler-bundle": {
|
||||||
|
"version": "7.1",
|
||||||
|
"recipe": {
|
||||||
|
"repo": "github.com/symfony/recipes",
|
||||||
|
"branch": "main",
|
||||||
|
"version": "6.1",
|
||||||
|
"ref": "e42b3f0177df239add25373083a564e5ead4e13a"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"config/packages/web_profiler.yaml",
|
||||||
|
"config/routes/web_profiler.yaml"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,9 @@
|
||||||
{% for item in food_vendor.menuItems %}
|
{% for item in food_vendor.menuItems %}
|
||||||
<li>
|
<li>
|
||||||
<a href="{{ path('app_menu_item_show', {'id': item.id}) }}">{{ item.name }}</a>
|
<a href="{{ path('app_menu_item_show', {'id': item.id}) }}">{{ item.name }}</a>
|
||||||
|
{% if(item.aliasOf) %}
|
||||||
|
(alias of: {{ item.aliasOf.name }})
|
||||||
|
{% endif %}
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -15,6 +15,12 @@
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<td>{{ menu_item.name }}</td>
|
<td>{{ menu_item.name }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
{% if(menu_item.aliasOf) %}
|
||||||
|
<tr>
|
||||||
|
<th>Alias of</th>
|
||||||
|
<td>{{ menu_item.aliasOf.name }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue