Add Fpu category of items

This commit is contained in:
Timothy Warren 2022-10-25 14:52:58 -04:00
parent 125a739f63
commit 195101ffde
15 changed files with 522 additions and 134 deletions

View File

@ -0,0 +1,57 @@
<?php
namespace App\Controller;
use App\Entity\Fpu;
use App\Form\FpuType;
use App\Traits\FormControllerTrait;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
#[Route('/fpu')]
class FpuController extends AbstractController
{
use FormControllerTrait;
protected const ENTITY = Fpu::class;
protected const TEMPLATE_PATH = 'fpu/';
protected const ROUTE_PREFIX = 'fpu_';
protected const FORM = FpuType::class;
public function __construct(private readonly EntityManagerInterface $entityManager)
{
}
#[Route('/', name: 'fpu_index', methods: ['GET'])]
public function index(): Response
{
return $this->indexView('fpus', []);
}
#[Route('/new', name: 'fpu_new', methods: ['GET', 'POST'])]
public function new(Request $request): Response
{
return $this->itemCreate($request, 'fpu');
}
#[Route('/{id}', name: 'fpu_show', methods: ['GET'])]
public function show(Fpu $fpu): Response
{
return $this->itemView($fpu, 'fpu');
}
#[Route('/{id}/edit', name: 'fpu_edit', methods: ['GET', 'POST'])]
public function edit(Request $request, Fpu $fpu): Response
{
return $this->itemUpdate($request, $fpu, 'fpu');
}
#[Route('/{id}', name: 'fpu_delete', methods: ['POST'])]
public function delete(Request $request, Fpu $fpu): Response
{
return $this->deleteCSRF($request, $fpu);
}
}

View File

@ -9,8 +9,7 @@ use Doctrine\ORM\Mapping as ORM;
#[ORM\Table(name: 'brand', schema: 'collection')] #[ORM\Table(name: 'brand', schema: 'collection')]
#[ORM\Entity] #[ORM\Entity]
#[ORM\UniqueConstraint(name: 'brand_unq', columns: ["name"])] #[ORM\UniqueConstraint(name: 'brand_unq', columns: ["name"])]
class Brand class Brand {
{
use GetSetTrait; use GetSetTrait;
#[ORM\Column(name: 'id', type: 'integer', nullable: FALSE)] #[ORM\Column(name: 'id', type: 'integer', nullable: FALSE)]
@ -22,7 +21,7 @@ class Brand
/** /**
* @var Collection<int, BrandCategory> * @var Collection<int, BrandCategory>
*/ */
#[ORM\ManyToMany(targetEntity: BrandCategory::class)] #[ORM\ManyToMany(targetEntity: BrandCategory::class, fetch: 'EAGER')]
#[ORM\JoinTable(name: 'collection.brand_category_link')] #[ORM\JoinTable(name: 'collection.brand_category_link')]
#[ORM\JoinColumn(name: 'brand_id', referencedColumnName: 'id')] #[ORM\JoinColumn(name: 'brand_id', referencedColumnName: 'id')]
#[ORM\InverseJoinColumn(name: 'brand_category', referencedColumnName: 'category_name')] #[ORM\InverseJoinColumn(name: 'brand_category', referencedColumnName: 'category_name')]
@ -44,7 +43,8 @@ class Brand
public function addCategory(BrandCategory $category): self public function addCategory(BrandCategory $category): self
{ {
if (!$this->categories->contains($category)) { if ( ! $this->categories->contains($category))
{
$this->categories->add($category); $this->categories->add($category);
} }

View File

@ -15,7 +15,7 @@ class Cpu {
#[ORM\GeneratedValue(strategy: 'IDENTITY')] #[ORM\GeneratedValue(strategy: 'IDENTITY')]
private int $id; private int $id;
#[ORM\ManyToOne(targetEntity: 'Brand')] #[ORM\ManyToOne(targetEntity: 'Brand', fetch: 'EAGER')]
#[ORM\OrderBy(['name' => 'asc'])] #[ORM\OrderBy(['name' => 'asc'])]
#[ORM\JoinColumn(name: 'brand_id', referencedColumnName: 'id', nullable: FALSE)] #[ORM\JoinColumn(name: 'brand_id', referencedColumnName: 'id', nullable: FALSE)]
private Brand $brand; private Brand $brand;

View File

@ -0,0 +1,41 @@
<?php declare(strict_types=1);
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Table(name: 'fpu', schema: 'collection')]
#[ORM\Entity]
class Fpu {
use GetSetTrait;
#[ORM\Column(name: 'id', type: 'integer', nullable: FALSE)]
#[ORM\Id]
#[ORM\GeneratedValue(strategy: 'IDENTITY')]
private int $id;
#[ORM\ManyToOne(targetEntity: 'Brand', fetch: 'EAGER')]
#[ORM\OrderBy(['name' => 'asc'])]
#[ORM\JoinColumn(name: 'brand_id', referencedColumnName: 'id', nullable: FALSE)]
private Brand $brand;
#[ORM\ManyToOne(targetEntity: 'Socket', fetch: 'EAGER')]
#[ORM\OrderBy(['name' => 'asc'])]
#[ORM\JoinColumn(name: 'socket_id', referencedColumnName: 'id', nullable: FALSE)]
private Socket $socket;
#[ORM\Column(name: 'series', type: 'string', nullable: TRUE)]
private ?string $series = '';
#[ORM\Column(name: 'model', type: 'string')]
private string $model;
#[ORM\Column(name: 'clock_speed', type: 'integer')]
private int $clockSpeed = 33;
#[ORM\Column(name: 'count', nullable: FALSE, options: array('default' => 1))]
private int $count = 1;
#[ORM\Column(name: 'notes', type: 'text', nullable: TRUE)]
private ?string $notes = '';
}

View File

@ -10,8 +10,7 @@ use Doctrine\ORM\Mapping as ORM;
#[ORM\Table(name: 'gpu', schema: 'collection')] #[ORM\Table(name: 'gpu', schema: 'collection')]
#[ORM\Entity] #[ORM\Entity]
class Gpu class Gpu {
{
use GetSetTrait; use GetSetTrait;
#[ORM\Column(name: 'id', type: 'integer', nullable: FALSE)] #[ORM\Column(name: 'id', type: 'integer', nullable: FALSE)]
@ -19,20 +18,20 @@ class Gpu
#[ORM\GeneratedValue(strategy: 'IDENTITY')] #[ORM\GeneratedValue(strategy: 'IDENTITY')]
private int $id; private int $id;
#[ORM\ManyToOne(targetEntity: 'Brand')] #[ORM\ManyToOne(targetEntity: 'Brand', fetch: 'EAGER')]
#[ORM\OrderBy(['name' => 'asc'])] #[ORM\OrderBy(['name' => 'asc'])]
#[ORM\JoinColumn(name: 'gpu_brand_id', referencedColumnName: 'id', nullable: FALSE)] #[ORM\JoinColumn(name: 'gpu_brand_id', referencedColumnName: 'id', nullable: FALSE)]
private Brand $gpuBrand; private Brand $gpuBrand;
#[ORM\ManyToOne(targetEntity: 'GpuCore')] #[ORM\ManyToOne(targetEntity: 'GpuCore', fetch: 'EAGER')]
#[ORM\OrderBy(['brand' => 'asc', 'name' => 'asc'])] #[ORM\OrderBy(['brand' => 'asc', 'name' => 'asc'])]
#[ORM\JoinColumn(name: 'gpu_core_id', referencedColumnName: 'id', nullable: TRUE)] #[ORM\JoinColumn(name: 'gpu_core_id', referencedColumnName: 'id', nullable: TRUE)]
private ?GpuCore $gpuCore = null; private ?GpuCore $gpuCore = NULL;
#[ORM\Column(name: 'reference_model_name', nullable: FALSE)] #[ORM\Column(name: 'reference_model_name', nullable: FALSE)]
private string $modelName; private string $modelName;
#[ORM\ManyToOne(targetEntity: 'Brand')] #[ORM\ManyToOne(targetEntity: 'Brand', fetch: 'EAGER')]
#[ORM\OrderBy(['name' => 'asc'])] #[ORM\OrderBy(['name' => 'asc'])]
#[ORM\JoinColumn(name: 'board_brand_id', referencedColumnName: 'id', nullable: TRUE)] #[ORM\JoinColumn(name: 'board_brand_id', referencedColumnName: 'id', nullable: TRUE)]
private ?Brand $boardBrand = NULL; private ?Brand $boardBrand = NULL;
@ -162,7 +161,7 @@ class Gpu
#[ORM\Column(name: 'count', nullable: FALSE, options: array('default' => 1))] #[ORM\Column(name: 'count', nullable: FALSE, options: array('default' => 1))]
private int $count = 1; private int $count = 1;
#[ORM\Column(name: 'acquired', nullable: FALSE, options: array('default' => true))] #[ORM\Column(name: 'acquired', nullable: FALSE, options: array('default' => TRUE))]
private bool $acquired; private bool $acquired;
#[ORM\Column(name: 'notes', type: 'text', nullable: TRUE)] #[ORM\Column(name: 'notes', type: 'text', nullable: TRUE)]

View File

@ -7,8 +7,7 @@ use Stringable;
#[ORM\Table(name: 'gpu_core', schema: 'collection')] #[ORM\Table(name: 'gpu_core', schema: 'collection')]
#[ORM\Entity] #[ORM\Entity]
class GpuCore implements Stringable class GpuCore implements Stringable {
{
use GetSetTrait; use GetSetTrait;
#[ORM\Column(name: 'id', type: 'integer', nullable: FALSE)] #[ORM\Column(name: 'id', type: 'integer', nullable: FALSE)]
@ -16,7 +15,7 @@ class GpuCore implements Stringable
#[ORM\GeneratedValue(strategy: 'IDENTITY')] #[ORM\GeneratedValue(strategy: 'IDENTITY')]
private int $id; private int $id;
#[ORM\ManyToOne(targetEntity: 'Brand')] #[ORM\ManyToOne(targetEntity: 'Brand', fetch: 'EAGER')]
#[ORM\OrderBy(['name' => 'asc'])] #[ORM\OrderBy(['name' => 'asc'])]
#[ORM\JoinColumn(name: 'brand_id', referencedColumnName: 'id', nullable: FALSE)] #[ORM\JoinColumn(name: 'brand_id', referencedColumnName: 'id', nullable: FALSE)]
private Brand $brand; private Brand $brand;
@ -45,9 +44,9 @@ class GpuCore implements Stringable
public function __toString(): string public function __toString(): string
{ {
$name = ( ! empty($this->variant)) $name = ( ! empty($this->variant))
? "{$this->name} ({$this->variant})" ? "$this->name ($this->variant)"
: $this->name; : $this->name;
return "{$name} - [{$this->brand}] $this->generationName"; return "$name - [$this->brand] $this->generationName";
} }
} }

View File

@ -22,7 +22,7 @@ class Socket {
private string $name; private string $name;
#[ORM\Column(name: 'other_name', type: 'string', nullable: TRUE)] #[ORM\Column(name: 'other_name', type: 'string', nullable: TRUE)]
private ?string $otherName = null; private ?string $otherName = NULL;
#[ORM\Column(name: 'pin_count', type: 'integer', nullable: FALSE)] #[ORM\Column(name: 'pin_count', type: 'integer', nullable: FALSE)]
private int $pinCount; private int $pinCount;
@ -38,6 +38,6 @@ class Socket {
{ {
$name = ( ! empty($this->otherName)) ? "$this->name/$this->otherName" : $this->name; $name = ( ! empty($this->otherName)) ? "$this->name/$this->otherName" : $this->name;
return "$name ($this->type->value $this->pinCount)"; return "$name ({$this->type->value} $this->pinCount)";
} }
} }

43
src/Form/FpuType.php Normal file
View File

@ -0,0 +1,43 @@
<?php
namespace App\Form;
use App\Entity\Brand;
use App\Entity\Fpu;
use App\Entity\Socket;
use Doctrine\ORM\EntityRepository;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class FpuType extends AbstractType
{
use BrandCategoryTrait;
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('socket', EntityType::class, [
'class' => Socket::class,
'query_builder' => static fn(EntityRepository $e) => $e->createQueryBuilder('s')->orderBy('s.name', 'ASC'),
])
->add('brand', EntityType::class, [
'class' => Brand::class,
'query_builder' => self::filterBrands('fpu'),
])
->add('series')
->add('model')
->add('clockSpeed')
->add('count')
->add('notes')
;
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Fpu::class,
]);
}
}

View File

@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
namespace App\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20221025152435 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 collection.cpu_socket_link (socket_id INT NOT NULL, cpu_id INT NOT NULL, PRIMARY KEY(socket_id, cpu_id))');
$this->addSql('CREATE INDEX IDX_B1ADF47DD20E239C ON collection.cpu_socket_link (socket_id)');
$this->addSql('CREATE INDEX IDX_B1ADF47D3917014 ON collection.cpu_socket_link (cpu_id)');
$this->addSql('CREATE TABLE collection.fpu (id SERIAL NOT NULL, brand_id INT NOT NULL, socket_id INT NOT NULL, model VARCHAR(255) NOT NULL, clock_speed INT NOT NULL, count INT DEFAULT 1 NOT NULL, notes TEXT DEFAULT NULL, PRIMARY KEY(id))');
$this->addSql('CREATE INDEX IDX_C741FA5844F5D008 ON collection.fpu (brand_id)');
$this->addSql('CREATE INDEX IDX_C741FA58D20E239C ON collection.fpu (socket_id)');
$this->addSql('ALTER TABLE collection.cpu_socket_link ADD CONSTRAINT FK_B1ADF47DD20E239C FOREIGN KEY (socket_id) REFERENCES collection.cpu (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
$this->addSql('ALTER TABLE collection.cpu_socket_link ADD CONSTRAINT FK_B1ADF47D3917014 FOREIGN KEY (cpu_id) REFERENCES collection.socket (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
$this->addSql('ALTER TABLE collection.fpu ADD CONSTRAINT FK_C741FA5844F5D008 FOREIGN KEY (brand_id) REFERENCES collection.brand (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
$this->addSql('ALTER TABLE collection.fpu ADD CONSTRAINT FK_C741FA58D20E239C FOREIGN KEY (socket_id) REFERENCES collection.socket (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE SCHEMA public');
$this->addSql('ALTER TABLE collection.cpu_socket_link DROP CONSTRAINT FK_B1ADF47DD20E239C');
$this->addSql('ALTER TABLE collection.cpu_socket_link DROP CONSTRAINT FK_B1ADF47D3917014');
$this->addSql('ALTER TABLE collection.fpu DROP CONSTRAINT FK_C741FA5844F5D008');
$this->addSql('ALTER TABLE collection.fpu DROP CONSTRAINT FK_C741FA58D20E239C');
$this->addSql('DROP TABLE collection.cpu_socket_link');
$this->addSql('DROP TABLE collection.fpu');
}
}

View File

@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace App\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20221025172539 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 collection.fpu ADD series VARCHAR(255) NOT NULL');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE SCHEMA public');
$this->addSql('ALTER TABLE collection.fpu DROP series');
}
}

View File

@ -0,0 +1,31 @@
{% extends 'form.html.twig' %}
{% block title %}Edit Fpu{% endblock %}
{% block form %}
<h1>Edit Fpu</h1>
<div class="small callout">
<ul>
<li>
<a href="{{ path('fpu_index') }}">Back to the list</a>
</li>
</ul>
</div>
<div class="large primary callout">
{{ form_start(edit_form) }}
{{ form_widget(edit_form) }}
<button
type="submit"
class="success button expanded"
>Update</button>
{{ form_end(edit_form) }}
<form method="post" action="{{ path('fpu_delete', {'id': fpu.id}) }}" onsubmit="return confirm('Are you sure you want to delete this item?');">
<input type="hidden" name="_token" value="{{ csrf_token('delete' ~ fpu.id) }}">
<button type="submit" class="alert button expanded">Delete</button>
</form>
</div>
{% endblock %}

View File

@ -0,0 +1,57 @@
{% extends 'base.html.twig' %}
{% block title %}Fpu index{% endblock %}
{% block body %}
<h1>Fpu index</h1>
<div class="small callout primary">
<ul>
<li>
<a href="{{ path('fpu_new') }}">Add an FPU</a>
</li>
</ul>
</div>
<table class="hover scroll sortable stack">
<thead>
<tr>
<th>&nbsp;</th>
<th>Id</th>
<th>Socket</th>
<th>Brand Series</th>
<th>Model</th>
<th>Clock Speed</th>
<th>Count</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
{% for fpu in fpus %}
<tr>
<td>
<ul>
<li>
<a href="{{ path('fpu_show', {'id': fpu.id}) }}">View 👁</a>
</li>
<li>
<a href="{{ path('fpu_edit', {'id': fpu.id}) }}">Edit <span class="edit-icon">&#9998;</span></a>
</li>
</ul>
</td>
<td>{{ fpu.id }}</td>
<td>{{ fpu.socket }}</td>
<td>{{ fpu.brand.name }} {{ fpu.series }}</td>
<td>{{ fpu.model }}</td>
<td>{{ fpu.clockSpeed }} MHz</td>
<td>{{ fpu.count }}</td>
<td>{{ fpu.notes }}</td>
</tr>
{% else %}
<tr>
<td colspan="6">no records found</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

View File

@ -0,0 +1,25 @@
{% extends 'form.html.twig' %}
{% block title %}New Fpu{% endblock %}
{% block form %}
<h2>Add Fpu</h2>
<div class="small callout">
<ul>
<li>
<a href="{{ path('fpu_index') }}">Back to the list</a>
</li>
</ul>
</div>
<div class="large primary callout">
{{ form_start(form) }}
{{ form_widget(form) }}
<button
type="submit"
class="success button expanded"
>Add</button>
{{ form_end(form) }}
</div>
{% endblock %}

View File

@ -0,0 +1,58 @@
{% extends 'form.html.twig' %}
{% block title %}Fpu{% endblock %}
{% block form %}
<h2>Fpu</h2>
<div class="callout">
<ul>
<li>
<a href="{{ path('fpu_index') }}">Back to the list</a>
</li>
<li>
<a href="{{ path('fpu_edit', { 'id': fpu.id }) }}">Edit</a>
</li>
</ul>
<hr />
<form method="post" action="{{ path('fpu_delete', {'id': fpu.id}) }}" onsubmit="return confirm('Are you sure you want to delete this item?');">
<input type="hidden" name="_token" value="{{ csrf_token('delete' ~ fpu.id) }}">
<button type="submit" class="alert button expanded">Delete</button>
</form>
</div>
<div class="large primary callout">
<table class="table">
<tbody>
<tr>
<th>Id</th>
<td>{{ fpu.id }}</td>
</tr>
<tr>
<th>Series</th>
<td>{{ fpu.series }}</td>
</tr>
<tr>
<th>Model</th>
<td>{{ fpu.model }}</td>
</tr>
<tr>
<th>ClockSpeed</th>
<td>{{ fpu.clockSpeed }}</td>
</tr>
<tr>
<th>Count</th>
<td>{{ fpu.count }}</td>
</tr>
<tr>
<th>Notes</th>
<td>{{ fpu.notes }}</td>
</tr>
</tbody>
</table>
</div>
{% endblock %}

View File

@ -9,6 +9,9 @@
<li class="{{ route starts with 'brand-category_' ? 'is-active' }}"> <li class="{{ route starts with 'brand-category_' ? 'is-active' }}">
<a href="{{ path('brand-category_index') }}">💃 Brand Categories</a> <a href="{{ path('brand-category_index') }}">💃 Brand Categories</a>
</li> </li>
<li class="{{ route starts with 'gpu-core' ? 'is-active' }}">
<a href="{{ path('gpu-core_index') }}">🌀 GPU Cores</a>
</li>
<li class="{{ route starts with 'socket_' ? 'is-active' }}"> <li class="{{ route starts with 'socket_' ? 'is-active' }}">
<a href="{{ path('socket_index') }}">📍Sockets</a> <a href="{{ path('socket_index') }}">📍Sockets</a>
</li> </li>
@ -53,19 +56,16 @@
</ul> </ul>
<ul class="menu"> <ul class="menu">
<li class="menu-text">Computer Components</li> <li class="menu-text">Computer Components</li>
<li class="{{ route starts with 'gpu-core' ? 'is-active' }}">
<a href="{{ path('gpu-core_index') }}">🌀 GPU Cores</a>
</li>
<li class="{{ route starts with 'gpu_' ? 'is-active' }}"> <li class="{{ route starts with 'gpu_' ? 'is-active' }}">
<a href="{{ path('gpu_index') }}">🎮 Graphics Cards</a> <a href="{{ path('gpu_index') }}">🎮 Graphics Cards</a>
</li> </li>
<li class="not-implemented"> <li class="not-implemented {{ route starts with 'cpu_' ? 'is-active' }}">
<a href="#">🧠 CPUs</a> <a href="#">🧠 CPUs</a>
</li> </li>
<li class="not-implemented"> <li class="{{ route starts with 'fpu_' ? 'is-active' }}">
<a href="#">🧮 FPUs</a> <a href="{{ path('fpu_index') }}">🧮 FPUs</a>
</li> </li>
<li class="not-implemented"> <li class="not-implemented {{ route starts with 'motherboard_' ? 'is-active' }}">
<a href="#">🤰 Motherboards</a> <a href="#">🤰 Motherboards</a>
</li> </li>
</ul> </ul>