Add Sockets as a Meta Item

This commit is contained in:
Timothy Warren 2022-10-20 11:07:27 -04:00
parent 827ee8a8eb
commit 3777b88800
17 changed files with 449 additions and 51 deletions

24
composer.lock generated
View File

@ -853,23 +853,23 @@
},
{
"name": "doctrine/inflector",
"version": "2.0.5",
"version": "2.0.6",
"source": {
"type": "git",
"url": "https://github.com/doctrine/inflector.git",
"reference": "ade2b3bbfb776f27f0558e26eed43b5d9fe1b392"
"reference": "d9d313a36c872fd6ee06d9a6cbcf713eaa40f024"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/inflector/zipball/ade2b3bbfb776f27f0558e26eed43b5d9fe1b392",
"reference": "ade2b3bbfb776f27f0558e26eed43b5d9fe1b392",
"url": "https://api.github.com/repos/doctrine/inflector/zipball/d9d313a36c872fd6ee06d9a6cbcf713eaa40f024",
"reference": "d9d313a36c872fd6ee06d9a6cbcf713eaa40f024",
"shasum": ""
},
"require": {
"php": "^7.2 || ^8.0"
},
"require-dev": {
"doctrine/coding-standard": "^9",
"doctrine/coding-standard": "^10",
"phpstan/phpstan": "^1.8",
"phpstan/phpstan-phpunit": "^1.1",
"phpstan/phpstan-strict-rules": "^1.3",
@ -924,7 +924,7 @@
],
"support": {
"issues": "https://github.com/doctrine/inflector/issues",
"source": "https://github.com/doctrine/inflector/tree/2.0.5"
"source": "https://github.com/doctrine/inflector/tree/2.0.6"
},
"funding": [
{
@ -940,7 +940,7 @@
"type": "tidelift"
}
],
"time": "2022-09-07T09:01:28+00:00"
"time": "2022-10-20T09:10:12+00:00"
},
{
"name": "doctrine/instantiator",
@ -6472,12 +6472,12 @@
"source": {
"type": "git",
"url": "https://github.com/Roave/SecurityAdvisories.git",
"reference": "c8e297691dcc30deef58efddd6765c8b7821be56"
"reference": "4ed057f00e70bf1a45434fbc6fe14790684ff3c5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/c8e297691dcc30deef58efddd6765c8b7821be56",
"reference": "c8e297691dcc30deef58efddd6765c8b7821be56",
"url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/4ed057f00e70bf1a45434fbc6fe14790684ff3c5",
"reference": "4ed057f00e70bf1a45434fbc6fe14790684ff3c5",
"shasum": ""
},
"conflict": {
@ -6750,6 +6750,7 @@
"phpmailer/phpmailer": "<6.5",
"phpmussel/phpmussel": ">=1,<1.6",
"phpmyadmin/phpmyadmin": "<5.1.3",
"phpmyfaq/phpmyfaq": "<=3.1.7",
"phpoffice/phpexcel": "<1.8",
"phpoffice/phpspreadsheet": "<1.16",
"phpseclib/phpseclib": "<2.0.31|>=3,<3.0.7",
@ -6880,6 +6881,7 @@
"thelia/thelia": ">=2.1-beta.1,<2.1.3",
"theonedemon/phpwhois": "<=4.2.5",
"thinkcmf/thinkcmf": "<=5.1.7",
"thorsten/phpmyfaq": "<=3.1.7",
"tinymce/tinymce": "<5.10",
"titon/framework": ">=0,<9.9.99",
"topthink/framework": "<=6.0.13",
@ -6991,7 +6993,7 @@
"type": "tidelift"
}
],
"time": "2022-10-18T22:04:50+00:00"
"time": "2022-10-19T23:05:04+00:00"
},
{
"name": "sebastian/cli-parser",

View File

@ -0,0 +1,84 @@
<?php
namespace App\Controller;
use App\Entity\Socket;
use App\Form\SocketType;
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('/socket')]
class SocketController extends AbstractController
{
#[Route('/', name: 'socket_index', methods: ['GET'])]
public function index(EntityManagerInterface $entityManager): Response
{
$sockets = $entityManager
->getRepository(Socket::class)
->findAll();
return $this->render('socket/index.html.twig', [
'sockets' => $sockets,
]);
}
#[Route('/new', name: 'socket_new', methods: ['GET', 'POST'])]
public function new(Request $request, EntityManagerInterface $entityManager): Response
{
$socket = new Socket();
$form = $this->createForm(SocketType::class, $socket);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$entityManager->persist($socket);
$entityManager->flush();
return $this->redirectToRoute('socket_index', [], Response::HTTP_SEE_OTHER);
}
return $this->renderForm('socket/new.html.twig', [
'socket' => $socket,
'form' => $form,
]);
}
#[Route('/{id}', name: 'socket_show', methods: ['GET'])]
public function show(Socket $socket): Response
{
return $this->render('socket/show.html.twig', [
'socket' => $socket,
]);
}
#[Route('/{id}/edit', name: 'socket_edit', methods: ['GET', 'POST'])]
public function edit(Request $request, Socket $socket, EntityManagerInterface $entityManager): Response
{
$form = $this->createForm(SocketType::class, $socket);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$entityManager->flush();
return $this->redirectToRoute('socket_index', [], Response::HTTP_SEE_OTHER);
}
return $this->renderForm('socket/edit.html.twig', [
'socket' => $socket,
'form' => $form,
]);
}
#[Route('/{id}', name: 'socket_delete', methods: ['POST'])]
public function delete(Request $request, Socket $socket, EntityManagerInterface $entityManager): Response
{
if ($this->isCsrfTokenValid('delete'.$socket->getId(), $request->request->get('_token'))) {
$entityManager->remove($socket);
$entityManager->flush();
}
return $this->redirectToRoute('socket_index', [], Response::HTTP_SEE_OTHER);
}
}

View File

@ -2,13 +2,12 @@
namespace App\Entity;
use App\Repository\BrandRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Table(name: 'brand', schema: 'collection')]
#[ORM\Entity]//(repositoryClass: BrandRepository::class)]
#[ORM\Entity]
#[ORM\UniqueConstraint(name: 'brand_unq', columns: ["name"])]
class Brand
{
@ -20,12 +19,15 @@ class Brand
#[ORM\SequenceGenerator(sequenceName: 'brand_id_seq', allocationSize: 1, initialValue: 1)]
private int $id;
/**
* @var Collection<int, BrandCategory>
*/
#[ORM\ManyToMany(targetEntity: BrandCategory::class)]
#[ORM\JoinTable(name: 'collection.brand_category_link')]
#[ORM\JoinColumn(name: 'brand_id', referencedColumnName: 'id')]
#[ORM\InverseJoinColumn(name: 'brand_category', referencedColumnName: 'category_name')]
#[ORM\OrderBy(['name' => 'asc'])]
private $categories;
private Collection $categories;
#[ORM\Column(name: 'name', unique: TRUE, nullable: FALSE)]
private string $name;
@ -40,14 +42,6 @@ class Brand
return $this->name;
}
/**
* @return Collection<int, BrandCategory>
*/
public function getCategories(): Collection
{
return $this->categories;
}
public function addCategory(BrandCategory $category): self
{
if (!$this->categories->contains($category)) {

View File

@ -2,6 +2,7 @@
namespace App\Entity;
use Doctrine\Common\Collections\{Collection, ArrayCollection};
use Doctrine\ORM\Mapping as ORM;
#[ORM\Table(name: 'cpu', schema: 'collection')]
@ -18,4 +19,36 @@ class Cpu {
#[ORM\OrderBy(['name' => 'asc'])]
#[ORM\JoinColumn(name: 'brand_id', referencedColumnName: 'id', nullable: FALSE)]
private Brand $brand;
/**
* @var Collection<int, Socket>
*/
#[ORM\ManyToMany(targetEntity: Socket::class)]
#[ORM\JoinTable(name: 'collection.cpu_socket_link')]
#[ORM\JoinColumn(name: 'socket_id', referencedColumnName: 'id')]
#[ORM\InverseJoinColumn(name: 'cpu_id', referencedColumnName: 'id')]
#[ORM\OrderBy(['name' => 'asc'])]
private Collection $sockets;
public function __construct()
{
$this->sockets = new ArrayCollection();
}
public function addSocket(Socket $socket): self
{
if ( ! $this->sockets->contains($socket))
{
$this->sockets->add($socket);
}
return $this;
}
public function removeSocket(Socket $socket): self
{
$this->sockets->removeElement($socket);
return $this;
}
}

43
src/Entity/Socket.php Normal file
View File

@ -0,0 +1,43 @@
<?php declare(strict_types=1);
namespace App\Entity;
use App\Enum\SocketTypeEnum;
use Doctrine\ORM\Mapping as ORM;
/**
* @see https://en.wikipedia.org/wiki/CPU_socket
*/
#[ORM\Table(name: 'socket', schema: 'collection')]
#[ORM\Entity]
class Socket {
use GetSetTrait;
#[ORM\Column(name: 'id', type: 'integer', nullable: FALSE)]
#[ORM\Id]
#[ORM\GeneratedValue(strategy: 'IDENTITY')]
private int $id;
#[ORM\Column(name: 'name', type: 'string', nullable: FALSE)]
private string $name;
#[ORM\Column(name: 'other_name', type: 'string', nullable: TRUE)]
private ?string $otherName = null;
#[ORM\Column(name: 'pin_count', type: 'integer', nullable: FALSE)]
private int $pinCount;
#[ORM\Column(
name: 'socket_type',
type: 'string',
enumType: SocketTypeEnum::class,
)]
private SocketTypeEnum $type = SocketTypeEnum::PIN_GRID_ARRAY;
public function __toString(): string
{
$name = ( ! empty($this->otherName)) ? "$this->name/$this->otherName" : $this->name;
return "$name ($this->type->value $this->pinCount)";
}
}

View File

@ -0,0 +1,12 @@
<?php declare(strict_types=1);
namespace App\Enum;
enum SocketTypeEnum: string {
case DUAL_INLINE_PACKAGE = 'DIP';
case LEAD_LESS_CHIP_CARRIER = 'LLCC';
case PLASTIC_LEADED_CHIP_CARRIER = 'PLCC';
case PIN_GRID_ARRAY = 'PGA';
case SLOT = 'Slot';
case LAND_GRID_ARRAY = 'LGA';
}

View File

@ -0,0 +1,18 @@
<?php declare(strict_types=1);
namespace App\Form;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\QueryBuilder;
trait BrandCategoryTrait {
public static function filterBrands(string $filter): callable
{
return static fn(EntityRepository $e) =>
$e->createQueryBuilder('b')
->join('b.categories', 'bc')
->where('bc.name=:name')
->orderBy('b.name', 'ASC')
->setParameter('name', $filter);
}
}

View File

@ -10,12 +10,14 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
class GPUCoreType extends AbstractType
{
use BrandCategoryTrait;
public function buildForm(FormBuilderInterface $builder, array $options): void
{
{
$builder
->add('brand', EntityType::class, [
'class' => Brand::class,
'query_builder' => static fn (EntityRepository $e) => $e->createQueryBuilder('b')->orderBy('b.name', 'ASC'),
'query_builder' => self::filterBrands('gpu_core'),
])
->add('name')
->add('variant')
@ -33,3 +35,4 @@ class GPUCoreType extends AbstractType
]);
}
}

View File

@ -12,14 +12,14 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
use UnitEnum;
class GpuType extends AbstractType {
use BrandCategoryTrait;
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$brandQueryBuilder = static fn(EntityRepository $e) => $e->createQueryBuilder('b')->orderBy('b.name', 'ASC');
$builder
->add('gpuBrand', EntityType::class, [
'class' => Brand::class,
'query_builder' => $brandQueryBuilder,
'query_builder' => self::filterBrands('gpu_core'),
])
->add('modelName')
->add('gpuCore', EntityType::class, [
@ -31,7 +31,7 @@ class GpuType extends AbstractType {
])
->add('boardBrand', EntityType::class, [
'class' => Brand::class,
'query_builder' => $brandQueryBuilder,
'query_builder' => self::filterBrands('graphics_card'),
'empty_data' => NULL,
'placeholder' => 'Unknown',
'required' => FALSE,

32
src/Form/SocketType.php Normal file
View File

@ -0,0 +1,32 @@
<?php
namespace App\Form;
use App\Entity\Socket;
use App\Enum\SocketTypeEnum;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\EnumType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class SocketType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('name')
->add('otherName')
->add('pinCount')
->add('type', EnumType::class,[
'class' => SocketTypeEnum::class,
])
;
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Socket::class,
]);
}
}

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 Version20221019183925 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.socket (id SERIAL NOT NULL, name VARCHAR(255) NOT NULL, other_name VARCHAR(255) DEFAULT NULL, pin_count INT NOT NULL, socket_type VARCHAR(255) NOT NULL, PRIMARY KEY(id))');
}
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('DROP TABLE collection.socket');
}
}

View File

@ -1,21 +0,0 @@
<?php declare(strict_types=1);
namespace App\Repository;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query;
class BrandRepository extends EntityRepository {
public function filterByCategory(string $category): Query
{
$em = $this->getEntityManager();
$query = $em->createQuery("
SELECT b FROM App\Entity\Brand b
INNER JOIN b.categories c WHERE c.name = ?1
ORDER BY b.name ASC
");
$query->setParameter(1, $category);
return $query->execute();
}
}

View File

@ -9,7 +9,9 @@
<li class="{{ route starts with 'brand-category_' ? 'is-active' }}">
<a href="{{ path('brand-category_index') }}">💃 Brand Categories</a>
</li>
<li class="{{ route starts with 'socket_' ? 'is-active' }}">
<a href="{{ path('socket_index') }}">📍Sockets</a>
</li>
<li class="not-implemented">
<a href="#">🐏 Ram Types</a>
</li>
@ -58,7 +60,13 @@
<a href="{{ path('gpu_index') }}">🎮 Graphics Cards</a>
</li>
<li class="not-implemented">
<a href="#">🧮 CPUs</a>
<a href="#">🧠 CPUs</a>
</li>
<li class="not-implemented">
<a href="#">🧮 FPUs</a>
</li>
<li class="not-implemented">
<a href="#">🤰 Motherboards</a>
</li>
</ul>
</div>

View File

@ -0,0 +1,27 @@
{% extends 'form.html.twig' %}
{% block title %}Edit Socket{% endblock %}
{% block form %}
<h1>Edit Socket</h1>
<div class="small callout">
<ul>
<li>
<a href="{{ path('socket_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">Update</button>
{{ form_end(form) }}
<form method="post" action="{{ path('socket_delete', {'id': socket.id}) }}" onsubmit="return confirm('Are you sure you want to delete this item?');">
<input type="hidden" name="_token" value="{{ csrf_token('delete' ~ socket.id) }}">
<button type="submit" class="alert button expanded">Delete</button>
</form>
</div>
{% endblock %}

View File

@ -0,0 +1,54 @@
{% extends 'form.html.twig' %}
{% block title %}Socket{% endblock %}
{% block form %}
<h2>Sockets</h2>
<div class="small callout primary">
<ul>
<li>
<a href="{{ path('socket_new') }}">Add a Socket</a>
</li>
</ul>
</div>
<table class="hover scroll sortable stack">
<thead>
<tr>
<th>&nbsp;</th>
<th>Id</th>
<th>Name</th>
<th>OtherName</th>
<th>PinCount</th>
<th>Type</th>
</tr>
</thead>
<tbody>
{% for socket in sockets %}
<tr>
<td>
<ul>
<li>
<a href="{{ path('socket_show', {'id': socket.id}) }}">View 👁</a>
</li>
<li>
<a href="{{ path('socket_edit', {'id': socket.id}) }}">Edit <span class="edit-icon">&#9998;</span></a>
</li>
</ul>
</td>
<td>{{ socket.id }}</td>
<td>{{ socket.name }}</td>
<td>{{ socket.otherName }}</td>
<td>{{ socket.pinCount }}</td>
<td>{{ socket.type.value }}</td>
</tr>
{% else %}
<tr>
<td colspan="6">no records found</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

View File

@ -0,0 +1,22 @@
{% extends 'form.html.twig' %}
{% block title %}New Socket{% endblock %}
{% block form %}
<h2>Add a Socket</h2>
<div class="small callout">
<ul>
<li>
<a href="{{ path('socket_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,55 @@
{% extends 'form.html.twig' %}
{% block title %}Socket{% endblock %}
{% block form %}
<h2>Socket</h2>
<div class="callout">
<ul>
<li>
<a href="{{ path('socket_index') }}">Back to the list</a>
</li>
<li>
<a href="{{ path('socket_edit', { 'id': socket.id }) }}">Edit</a>
</li>
</ul>
<hr />
<form method="post" action="{{ path('socket_delete', {'id': socket.id}) }}" onsubmit="return confirm('Are you sure you want to delete this item?');">
<input type="hidden" name="_token" value="{{ csrf_token('delete' ~ socket.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>{{ socket.id }}</td>
</tr>
<tr>
<th>Name</th>
<td>{{ socket.name }}</td>
</tr>
<tr>
<th>OtherName</th>
<td>{{ socket.otherName }}</td>
</tr>
<tr>
<th>PinCount</th>
<td>{{ socket.pinCount }}</td>
</tr>
<tr>
<th>Type</th>
<td>{{ socket.type.value }}</td>
</tr>
</tbody>
</table>
</div>
{% endblock %}