feat(person): initial Person domain with entity, model and DTO
PHP Unit Tests / test (push) Has been cancelled

- Add PersonEntity in Data layer with Doctrine ORM attributes
- Add PersonModel in Logic layer for business logic
- Add immutable PersonDto in Shared layer
- Configure doctrine/orm dependency in composer.json
- Add Doctrine deprecation triggers to phpunit configuration
This commit is contained in:
2026-06-08 20:08:01 +02:00
parent e56d712503
commit 98f4e126fc
7 changed files with 257 additions and 0 deletions
@@ -0,0 +1,7 @@
---
globs: '["**/Data/**/*Repository.php"]'
description: Prevents the common error of using invalid Doctrine ORM attributes
in Repository classes
---
When creating Doctrine repositories, NEVER use #[ORM\EntityManager] as this is not a valid Doctrine mapping attribute and causes runtime errors. Repositories must only be instantiated via Dependency Injection with EntityManagerInterface in their constructor. Valid ORM attributes for Repositories include: none (standard), or optionally #[ORM\HasEntityListeners] if needed. Mapping attributes like #[ORM\Entity], #[ORM\Table], #[ORM\JoinColumn] are ONLY for Entity classes, not Repository classes.
@@ -0,0 +1,22 @@
---
globs: '["src/**/*Repository.php"]'
description: Ensures minimal boilerplate by avoiding unnecessary custom
Repository classes for standard CRUD operations, while still allowing them for
complex queries.
alwaysApply: true
---
In Symfony and Doctrine based applications, avoid creating custom Repository classes for simple CRUD or standard query operations (e.g., findOneBy, findBy). Instead, use Doctrine's native EntityRepository directly via $entityManager->getRepository(Entity::class) within the Provider or Processor implementations.
Only create a custom repository when:
1. Complex queries involving multiple joins, subselects, or specialized search logic are required.
2. Query logic is reused across multiple Providers/Processors and warrants encapsulation for DRY compliance.
When no custom repository is needed, Inject EntityManagerInterface directly into the Provider or Processor and fetch the repository inline as shown below:
```php
$repository = $this->entityManager->getRepository(EntityClass::class);
$result = $repository->findOneBy(['id' => $id]);
```
Do not create an empty or wrapper Repository class that only delegates to Doctrine's native methods.
+1
View File
@@ -7,6 +7,7 @@
"php": ">=8.4",
"ext-ctype": "*",
"ext-iconv": "*",
"doctrine/orm": "*",
"symfony/console": "8.1.*",
"symfony/dotenv": "8.1.*",
"symfony/flex": "^2",
+2
View File
@@ -33,6 +33,8 @@
</include>
<deprecationTrigger>
<method>Doctrine\Deprecations\Deprecation::trigger</method>
<method>Doctrine\Deprecations\Deprecation::delegateTriggerToBackend</method>
<function>trigger_deprecation</function>
</deprecationTrigger>
</source>
+101
View File
@@ -0,0 +1,101 @@
<?php
declare(strict_types=1);
namespace App\Data\Person\Entity;
use App\Logic\Person\Repository\PersonRepositoryInterface;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
#[ORM\Table(name: 'person')]
class PersonEntity
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private ?int $id = null;
#[ORM\Column(type: 'string', length: 255)]
private string $anrede;
#[ORM\Column(type: 'string', length: 255)]
private string $vorname;
#[ORM\Column(type: 'string', length: 255)]
private string $name;
#[ORM\Column(type: 'string', length: 10)]
private string $geschlecht;
#[ORM\Column(type: 'string', length: 255, nullable: true)]
private ?string $titel = null;
#[ORM\Column(type: 'string', length: 255, nullable: true)]
private ?string $funktionstitel = null;
public function getId(): ?int
{
return $this->id;
}
public function getAnrede(): string
{
return $this->anrede;
}
public function setAnrede(string $anrede): void
{
$this->anrede = $anrede;
}
public function getVorname(): string
{
return $this->vorname;
}
public function setVorname(string $vorname): void
{
$this->vorname = $vorname;
}
public function getName(): string
{
return $this->name;
}
public function setName(string $name): void
{
$this->name = $name;
}
public function getGeschlecht(): string
{
return $this->geschlecht;
}
public function setGeschlecht(string $geschlecht): void
{
$this->geschlecht = $geschlecht;
}
public function getTitel(): ?string
{
return $this->titel;
}
public function setTitel(?string $titel): void
{
$this->titel = $titel;
}
public function getFunktionstitel(): ?string
{
return $this->funktionstitel;
}
public function setFunktionstitel(?string $funktionstitel): void
{
$this->funktionstitel = $funktionstitel;
}
}
+105
View File
@@ -0,0 +1,105 @@
<?php
declare(strict_types=1);
namespace App\Logic\Person\Models;
final class PersonModel
{
private ?int $id = null;
private string $anrede;
private string $vorname;
private string $name;
private string $geschlecht;
private ?string $titel = null;
private ?string $funktionstitel = null;
public function __construct(
?int $id,
string $anrede,
string $vorname,
string $name,
string $geschlecht,
?string $titel,
?string $funktionstitel
) {
$this->id = $id;
$this->anrede = $anrede;
$this->vorname = $vorname;
$this->name = $name;
$this->geschlecht = $geschlecht;
$this->titel = $titel;
$this->funktionstitel = $funktionstitel;
}
public function getId(): ?int
{
return $this->id;
}
public function getAnrede(): string
{
return $this->anrede;
}
public function setAnrede(string $anrede): void
{
$this->anrede = $anrede;
}
public function getVorname(): string
{
return $this->vorname;
}
public function setVorname(string $vorname): void
{
$this->vorname = $vorname;
}
public function getName(): string
{
return $this->name;
}
public function setName(string $name): void
{
$this->name = $name;
}
public function getGeschlecht(): string
{
return $this->geschlecht;
}
public function setGeschlecht(string $geschlecht): void
{
$this->geschlecht = $geschlecht;
}
public function getTitel(): ?string
{
return $this->titel;
}
public function setTitel(?string $titel): void
{
$this->titel = $titel;
}
public function getFunktionstitel(): ?string
{
return $this->funktionstitel;
}
public function setFunktionstitel(?string $funktionstitel): void
{
$this->funktionstitel = $funktionstitel;
}
}
+19
View File
@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace App\Shared\Person\DTO;
readonly final class PersonDto
{
public function __construct(
public int $id,
public string $anrede,
public string $vorname,
public string $name,
public string $geschlecht,
public ?string $titel = null,
public ?string $funktionstitel = null,
) {
}
}