feat: group resources into rooms on the floor plan

This commit is contained in:
fromme committed 2026-09-14 08:25:05 +02:00
1 parent faef4d70a8
commit 8caf618950
14 files changed
+653 -34

No files matched your search

+18
View File
@@ -34,6 +34,14 @@ class Resource
#[ORM\Column(length: 50, nullable: true)]
private ?string $svgElementId = null;
/**
* Where it physically is. Nullable because a group is optional: a kind of resource that is
* not split across rooms simply has none, and its plan draws its own single frame.
*/
#[ORM\ManyToOne]
#[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')]
private ?ResourceGroup $group = null;
/**
* Retired resources stay in the table so historical bookings keep resolving; they are just
* not offered any more.
@@ -78,6 +86,16 @@ class Resource
$this->svgElementId = $svgElementId;
}
public function getGroup(): ?ResourceGroup
{
return $this->group;
}
public function setGroup(?ResourceGroup $group): void
{
$this->group = $group;
}
public function isActive(): bool
{
return $this->active;
+96
View File
@@ -0,0 +1,96 @@
<?php
declare(strict_types=1);
namespace App\Entity;
use App\Repository\ResourceGroupRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Where a bookable thing physically is: a room of the office, an area of the car park.
*
* A group belongs to exactly one ResourceType, because "Raum Nord" means nothing for parking.
* It is a description of the building, so it is configuration seeded alongside the resources
* rather than something a user creates.
*
* Note there is deliberately no method named `group`, `label` or `position` other than the plain
* accessors: Twig resolves `x.label` to a method of that literal name before trying getLabel(),
* and static or not it would win. See handover.md.
*/
#[ORM\Entity(repositoryClass: ResourceGroupRepository::class)]
#[ORM\UniqueConstraint(name: 'uniq_resource_group_label', columns: ['type', 'label'])]
class ResourceGroup
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(enumType: ResourceType::class)]
private ResourceType $type;
#[ORM\Column(length: 50)]
#[Assert\NotBlank]
private string $label;
/** Draw and list order within the type. */
#[ORM\Column]
private int $position = 0;
/**
* Id of the matching frame in the hand-authored floor plan SVG -- the same role svgElementId
* plays on Resource. Null for a group that is not drawn.
*/
#[ORM\Column(length: 50, nullable: true)]
private ?string $svgElementId = null;
public function __construct(ResourceType $type, string $label, int $position = 0, ?string $svgElementId = null)
{
$this->type = $type;
$this->label = $label;
$this->position = $position;
$this->svgElementId = $svgElementId;
}
public function getId(): ?int
{
return $this->id;
}
public function getType(): ResourceType
{
return $this->type;
}
public function getLabel(): string
{
return $this->label;
}
public function setLabel(string $label): void
{
$this->label = $label;
}
public function getPosition(): int
{
return $this->position;
}
public function setPosition(int $position): void
{
$this->position = $position;
}
public function getSvgElementId(): ?string
{
return $this->svgElementId;
}
public function setSvgElementId(?string $svgElementId): void
{
$this->svgElementId = $svgElementId;
}
}