feat: add doctrine-fixtures-bundle & phpstan, restructure entity mapping #2

Merged
jens merged 1 commits from 02_Datenmodel into main 2026-06-17 17:42:13 +00:00
16 changed files with 794 additions and 5 deletions
Showing only changes of commit 98e4abcfb0 - Show all commits
@@ -0,0 +1,58 @@
<?php declare(strict_types=1);
namespace App\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20250617184300_create_saatgut_tables extends AbstractMigration
{
public function getDescription(): string
{
return 'Erstellt die relationalen Tabellen für den Saatgut-Bestand: kategorie, pflanze, saatgut_bestand.';
}
public function up(Schema $schema): void
{
// 1. Kategorie-Tabelle
$this->addSql(<<<SQL
CREATE TABLE kategorie (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL UNIQUE,
farbe VARCHAR(7) DEFAULT NULL
);
SQL);
// 2. Pflanze-Tabelle
$this->addSql(<<<SQL
CREATE TABLE pflanze (
id SERIAL PRIMARY KEY,
art_name VARCHAR(200) NOT NULL,
sorten_name VARCHAR(200) DEFAULT NULL,
kategorie_id INTEGER DEFAULT NULL REFERENCES kategorie(id) ON DELETE SET NULL,
beschreibung TEXT DEFAULT NULL
);
SQL);
// 3. SaatgutBestand-Tabelle mit Unique Constraint (nutzer_id + pflanze_id)
$this->addSql(<<<SQL
CREATE TABLE saatgut_bestand (
id SERIAL PRIMARY KEY,
nutzer_id VARCHAR(255) NOT NULL,
pflanze_id INTEGER NOT NULL REFERENCES pflanze(id) ON DELETE CASCADE,
menge DOUBLE PRECISION NOT NULL,
kaufdatum DATE NOT NULL,
ablaufdatum DATE DEFAULT NULL,
notizen TEXT DEFAULT NULL,
CONSTRAINT unik_nutzer_pflanze UNIQUE (nutzer_id, pflanze_id)
);
SQL);
}
public function down(Schema $schema): void
{
$this->addSql('DROP TABLE IF EXISTS saatgut_bestand');
$this->addSql('DROP TABLE IF EXISTS pflanze');
$this->addSql('DROP TABLE IF EXISTS kategorie');
}
}
+4
View File
@@ -10,6 +10,7 @@
"ext-ctype": "*",
"ext-iconv": "*",
"doctrine/doctrine-bundle": "*",
"doctrine/doctrine-fixtures-bundle": "^4.3",
"doctrine/orm": "^3.6",
"lexik/jwt-authentication-bundle": "^3.2",
"symfony/console": "8.1.*",
@@ -73,5 +74,8 @@
"allow-contrib": false,
"require": "8.1.*"
}
},
"require-dev": {
"phpstan/phpstan": "^2.0"
}
}
+238 -2
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "e6d1737f343b3572fc4b55b50cdc67e2",
"content-hash": "8cfcdf73fe44a155dd3b0835cadb01bd",
"packages": [
{
"name": "doctrine/collections",
@@ -92,6 +92,91 @@
],
"time": "2026-01-15T10:01:58+00:00"
},
{
"name": "doctrine/data-fixtures",
"version": "2.2.1",
"source": {
"type": "git",
"url": "https://github.com/doctrine/data-fixtures.git",
"reference": "bf7ac3a050b54b261cedfb3d0a44733819062275"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/data-fixtures/zipball/bf7ac3a050b54b261cedfb3d0a44733819062275",
"reference": "bf7ac3a050b54b261cedfb3d0a44733819062275",
"shasum": ""
},
"require": {
"doctrine/persistence": "^3.1 || ^4.0",
"php": "^8.1",
"psr/log": "^1.1 || ^2 || ^3"
},
"conflict": {
"doctrine/dbal": "<3.5 || >=5",
"doctrine/orm": "<2.14 || >=4",
"doctrine/phpcr-odm": "<1.3.0"
},
"require-dev": {
"doctrine/coding-standard": "^14",
"doctrine/dbal": "^3.5 || ^4",
"doctrine/mongodb-odm": "^1.3.0 || ^2.0.0",
"doctrine/orm": "^2.14 || ^3",
"doctrine/phpcr-odm": "^1.8 || ^2.0",
"ext-sqlite3": "*",
"fig/log-test": "^1",
"jackalope/jackalope-fs": "*",
"phpstan/phpstan": "2.1.46",
"phpunit/phpunit": "10.5.63 || 12.5.12",
"symfony/cache": "^6.4 || ^7 || ^8",
"symfony/var-exporter": "^6.4 || ^7 || ^8"
},
"suggest": {
"alcaeus/mongo-php-adapter": "For using MongoDB ODM 1.3 with PHP 7 (deprecated)",
"doctrine/mongodb-odm": "For loading MongoDB ODM fixtures",
"doctrine/orm": "For loading ORM fixtures",
"doctrine/phpcr-odm": "For loading PHPCR ODM fixtures"
},
"type": "library",
"autoload": {
"psr-4": {
"Doctrine\\Common\\DataFixtures\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jonathan Wage",
"email": "jonwage@gmail.com"
}
],
"description": "Data Fixtures for all Doctrine Object Managers",
"homepage": "https://www.doctrine-project.org",
"keywords": [
"database"
],
"support": {
"issues": "https://github.com/doctrine/data-fixtures/issues",
"source": "https://github.com/doctrine/data-fixtures/tree/2.2.1"
},
"funding": [
{
"url": "https://www.doctrine-project.org/sponsorship.html",
"type": "custom"
},
{
"url": "https://www.patreon.com/phpdoctrine",
"type": "patreon"
},
{
"url": "https://tidelift.com/funding/github/packagist/doctrine%2Fdata-fixtures",
"type": "tidelift"
}
],
"time": "2026-04-01T13:56:01+00:00"
},
{
"name": "doctrine/dbal",
"version": "4.4.3",
@@ -362,6 +447,92 @@
],
"time": "2026-06-09T19:11:55+00:00"
},
{
"name": "doctrine/doctrine-fixtures-bundle",
"version": "4.3.1",
"source": {
"type": "git",
"url": "https://github.com/doctrine/DoctrineFixturesBundle.git",
"reference": "9e013ed10d49bf7746b07204d336384a7d9b5a4d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/DoctrineFixturesBundle/zipball/9e013ed10d49bf7746b07204d336384a7d9b5a4d",
"reference": "9e013ed10d49bf7746b07204d336384a7d9b5a4d",
"shasum": ""
},
"require": {
"doctrine/data-fixtures": "^2.2",
"doctrine/doctrine-bundle": "^2.2 || ^3.0",
"doctrine/orm": "^2.14.0 || ^3.0",
"doctrine/persistence": "^2.4 || ^3.0 || ^4.0",
"php": "^8.1",
"psr/log": "^2 || ^3",
"symfony/config": "^6.4 || ^7.0 || ^8.0",
"symfony/console": "^6.4 || ^7.0 || ^8.0",
"symfony/dependency-injection": "^6.4 || ^7.0 || ^8.0",
"symfony/deprecation-contracts": "^2.1 || ^3",
"symfony/doctrine-bridge": "^6.4.16 || ^7.1.9 || ^8.0",
"symfony/http-kernel": "^6.4 || ^7.0 || ^8.0"
},
"conflict": {
"doctrine/dbal": "< 3"
},
"require-dev": {
"doctrine/coding-standard": "14.0.0",
"phpstan/phpstan": "2.1.11",
"phpunit/phpunit": "^10.5.38 || 11.4.14"
},
"type": "symfony-bundle",
"autoload": {
"psr-4": {
"Doctrine\\Bundle\\FixturesBundle\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Doctrine Project",
"homepage": "https://www.doctrine-project.org"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony DoctrineFixturesBundle",
"homepage": "https://www.doctrine-project.org",
"keywords": [
"Fixture",
"persistence"
],
"support": {
"issues": "https://github.com/doctrine/DoctrineFixturesBundle/issues",
"source": "https://github.com/doctrine/DoctrineFixturesBundle/tree/4.3.1"
},
"funding": [
{
"url": "https://www.doctrine-project.org/sponsorship.html",
"type": "custom"
},
{
"url": "https://www.patreon.com/phpdoctrine",
"type": "patreon"
},
{
"url": "https://tidelift.com/funding/github/packagist/doctrine%2Fdoctrine-fixtures-bundle",
"type": "tidelift"
}
],
"time": "2025-12-03T16:05:42+00:00"
},
{
"name": "doctrine/event-manager",
"version": "2.1.1",
@@ -4865,7 +5036,72 @@
"time": "2026-05-29T05:06:50+00:00"
}
],
"packages-dev": [],
"packages-dev": [
{
"name": "phpstan/phpstan",
"version": "2.2.2",
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/e5cc34d491a90e79c216d824f60fe21fd4d93bd6",
"reference": "e5cc34d491a90e79c216d824f60fe21fd4d93bd6",
"shasum": ""
},
"require": {
"php": "^7.4|^8.0"
},
"conflict": {
"phpstan/phpstan-shim": "*"
},
"bin": [
"phpstan",
"phpstan.phar"
],
"type": "library",
"autoload": {
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Ondřej Mirtes"
},
{
"name": "Markus Staab"
},
{
"name": "Vincent Langlet"
}
],
"description": "PHPStan - PHP Static Analysis Tool",
"keywords": [
"dev",
"static analysis"
],
"support": {
"docs": "https://phpstan.org/user-guide/getting-started",
"forum": "https://github.com/phpstan/phpstan/discussions",
"issues": "https://github.com/phpstan/phpstan/issues",
"security": "https://github.com/phpstan/phpstan/security/policy",
"source": "https://github.com/phpstan/phpstan-src"
},
"funding": [
{
"url": "https://github.com/ondrejmirtes",
"type": "github"
},
{
"url": "https://github.com/phpstan",
"type": "github"
}
],
"time": "2026-06-05T09:00:01+00:00"
}
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {},
+1
View File
@@ -5,4 +5,5 @@ return [
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle::class => ['all' => true],
Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true],
];
+3 -3
View File
@@ -17,9 +17,9 @@ doctrine:
App:
type: attribute
is_bundle: false
dir: '%kernel.project_dir%/src/Entity'
prefix: 'App\Entity'
alias: App
dir: '%kernel.project_dir%/src/Data/Doctrine/Entity/Saatgut'
prefix: 'App\Data\Doctrine\Entity\Saatgut'
alias: Saatgut
when@test:
doctrine:
+7
View File
@@ -0,0 +1,7 @@
parameters:
level: 8
paths:
- src
ignoreErrors:
# Symfony Kernel has some generic unused methods that are technically framework internals.
- '#Method App\\Kernel::getAllowedEnvs\(\) is unused#'
@@ -0,0 +1,48 @@
<?php declare(strict_types=1);
namespace App\Data\Doctrine\Entity\Saatgut;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
class KategorieEntity
{
#[ORM\Id]
#[ORM\GeneratedValue(strategy: 'IDENTITY')]
#[ORM\Column(type: 'integer')]
public ?int $id = null;
#[ORM\Column(type: 'string', length: 100, unique: true)]
private string $name;
#[ORM\Column(type: 'string', length: 7, nullable: true)]
private ?string $farbe = null;
public function __construct(string $name, ?string $farbe = null)
{
$this->name = $name;
$this->farbe = $farbe;
}
public function getId(): ?int
{
return $this->id;
}
public function getName(): string
{
return $this->name;
}
public function getFarbe(): ?string
{
return $this->farbe;
}
public function setFarbe(?string $farbe): self
{
$this->farbe = $farbe;
return $this;
}
}
@@ -0,0 +1,84 @@
<?php declare(strict_types=1);
namespace App\Data\Doctrine\Entity\Saatgut;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
class PflanzeEntity
{
#[ORM\Id]
#[ORM\GeneratedValue(strategy: 'IDENTITY')]
#[ORM\Column(type: 'integer')]
public ?int $id = null;
#[ORM\Column(type: 'string', length: 200)]
private string $artName;
#[ORM\Column(type: 'string', length: 200, nullable: true)]
private ?string $sortenName = null;
#[ORM\Column(type: 'integer', nullable: true)]
private ?int $kategorieId = null;
#[ORM\Column(type: 'text', nullable: true)]
private ?string $beschreibung = null;
public function __construct(
string $artName,
?string $sortenName = null,
?int $kategorieId = null,
?string $beschreibung = null,
) {
$this->artName = $artName;
$this->sortenName = $sortenName;
$this->kategorieId = $kategorieId;
$this->beschreibung = $beschreibung;
}
public function getId(): ?int
{
return $this->id;
}
public function getArtName(): string
{
return $this->artName;
}
public function getSortenName(): ?string
{
return $this->sortenName;
}
public function setSortenName(?string $sortenName): self
{
$this->sortenName = $sortenName;
return $this;
}
public function getKategorieId(): ?int
{
return $this->kategorieId;
}
public function setKategorieId(?int $kategorieId): self
{
$this->kategorieId = $kategorieId;
return $this;
}
public function getBeschreibung(): ?string
{
return $this->beschreibung;
}
public function setBeschreibung(?string $beschreibung): self
{
$this->beschreibung = $beschreibung;
return $this;
}
}
@@ -0,0 +1,118 @@
<?php declare(strict_types=1);
namespace App\Data\Doctrine\Entity\Saatgut;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
#[ORM\Table(name: 'saatgut_bestand')]
#[ORM\UniqueConstraint(name: 'unik_nutzer_pflanze', columns: ['nutzer_id', 'pflanze_id'])]
class SaatgutBestandEntity
{
#[ORM\Id]
#[ORM\GeneratedValue(strategy: 'IDENTITY')]
#[ORM\Column(type: 'integer')]
public ?int $id = null;
#[ORM\Column(type: 'string', length: 255)]
private string $nutzerId;
#[ORM\Column(type: 'integer')]
private int $pflanzeId;
#[ORM\Column(type: 'float')]
private float $menge;
#[ORM\Column(type: 'date')]
private \DateTimeInterface $kaufdatum;
#[ORM\Column(type: 'date', nullable: true)]
private ?\DateTimeInterface $ablaufdatum = null;
#[ORM\Column(type: 'text', nullable: true)]
private ?string $notizen = null;
public function __construct(
string $nutzerId,
int $pflanzeId,
float $menge,
\DateTimeInterface $kaufdatum,
?\DateTimeInterface $ablaufdatum = null,
?string $notizen = null,
) {
$this->nutzerId = $nutzerId;
$this->pflanzeId = $pflanzeId;
$this->menge = $menge;
$this->kaufdatum = $kaufdatum;
$this->ablaufdatum = $ablaufdatum;
$this->notizen = $notizen;
}
public function getId(): ?int
{
return $this->id;
}
public function getNutzerId(): string
{
return $this->nutzerId;
}
public function getPflanzeId(): int
{
return $this->pflanzeId;
}
public function setPflanzeId(int $pflanzeId): self
{
$this->pflanzeId = $pflanzeId;
return $this;
}
public function menge(): float
{
return $this->menge;
}
public function setMenge(float $menge): self
{
$this->menge = $menge;
return $this;
}
public function kaufdatum(): \DateTimeInterface
{
return $this->kaufdatum;
}
public function setKaufdatum(\DateTimeInterface $kaufdatum): self
{
$this->kaufdatum = $kaufdatum;
return $this;
}
public function ablaufdatum(): ?\DateTimeInterface
{
return $this->ablaufdatum;
}
public function setAblaufdatum(?\DateTimeInterface $ablaufdatum): self
{
$this->ablaufdatum = $ablaufdatum;
return $this;
}
public function notizen(): ?string
{
return $this->notizen;
}
public function setNotizen(?string $notizen): void
{
$this->notizen = $notizen;
}
}
@@ -0,0 +1,29 @@
<?php declare(strict_types=1);
namespace App\Data\Doctrine\Repository\Saatgut;
use App\Data\Doctrine\Entity\Saatgut\KategorieEntity;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<KategorieEntity>
*/
class KategorieRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, KategorieEntity::class);
}
/**
* @return string[]
*/
public function getAllNames(): array
{
return array_map(
fn(KategorieEntity $k) => $k->getName(),
$this->findAll()
);
}
}
@@ -0,0 +1,46 @@
<?php declare(strict_types=1);
namespace App\Data\Doctrine\Repository\Saatgut;
use App\Data\Doctrine\Entity\Saatgut\PflanzeEntity;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<PflanzeEntity>
*/
class PflanzeRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, PflanzeEntity::class);
}
/**
* Find plants by category ID (flat, no joins)
* @return PflanzeEntity[]
*/
public function findByKategorieId(int $kategorieId): array
{
return $this->createQueryBuilder('p')
->andWhere('p.kategorieId = :kategorieId')
->setParameter('kategorieId', $kategorieId)
->orderBy('p.artName', 'ASC')
->getQuery()
->getResult();
}
/**
* Find plant by exact art name and sort name (flat query)
*/
public function findByArtUndSorte(string $artName, string $sortenName): ?PflanzeEntity
{
return $this->createQueryBuilder('p')
->andWhere('p.artName = :artName')
->andWhere('p.sortenName = :sortenName')
->setParameter('artName', $artName)
->setParameter('sortenName', $sortenName)
->getQuery()
->getOneOrNullResult();
}
}
@@ -0,0 +1,47 @@
<?php declare(strict_types=1);
namespace App\Data\Doctrine\Repository\Saatgut;
use App\Data\Doctrine\Entity\Saatgut\SaatgutBestandEntity;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<SaatgutBestandEntity>
*/
class SaatgutBestandRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, SaatgutBestandEntity::class);
}
/**
* Get inventory items for a specific user (flat query via ID reference)
* @return SaatgutBestandEntity[]
*/
public function findByNutzerId(string $nutzerId): array
{
return $this->createQueryBuilder('s')
->andWhere('s.nutzerId = :nutzerId')
->setParameter('nutzerId', $nutzerId)
->orderBy('s.kaufdatum', 'DESC')
->getQuery()
->getResult();
}
/**
* Check if a specific user already has this plant in inventory (to enforce unique constraint semantically)
*/
public function existsByNutzerUndPflanze(string $nutzerId, int $pflanzeId): bool
{
return (bool) $this->createQueryBuilder('s')
->select('COUNT(s.id)')
->andWhere('s.nutzerId = :nutzerId')
->andWhere('s.pflanzeId = :pflanzeId')
->setParameter('nutzerId', $nutzerId)
->setParameter('pflanzeId', $pflanzeId)
->getQuery()
->getSingleScalarResult();
}
}
+17
View File
@@ -0,0 +1,17 @@
<?php
namespace App\DataFixtures;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Persistence\ObjectManager;
class AppFixtures extends Fixture
{
public function load(ObjectManager $manager): void
{
// $product = new Product();
// $manager->persist($product);
$manager->flush();
}
}
@@ -0,0 +1,32 @@
<?php declare(strict_types=1);
namespace App\DataFixtures;
use App\Data\Doctrine\Entity\Saatgut\KategorieEntity;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Persistence\ObjectManager;
class KategorieFixtures extends Fixture
{
private const array KATEGORIEN = [
['name' => 'Gemüse', 'farbe' => '#4CAF50'],
['name' => 'Kräuter', 'farbe' => '#8BC34A'],
['name' => 'Blumen', 'farbe' => '#E91E63'],
['name' => 'Obst', 'farbe' => '#FF9800'],
];
public function load(ObjectManager $manager): void
{
foreach (self::KATEGORIEN as $data) {
$kategorie = new KategorieEntity(
name: $data['name'],
farbe: $data['farbe'],
);
$this->addReference("kategorie-{$data['name']}", $kategorie);
$manager->persist($kategorie);
}
$manager->flush();
}
}
@@ -0,0 +1,47 @@
<?php declare(strict_types=1);
namespace App\DataFixtures;
use App\Data\Doctrine\Entity\Saatgut\KategorieEntity;
use App\Data\Doctrine\Entity\Saatgut\PflanzeEntity;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\DataFixtures\DependentFixtureInterface;
use Doctrine\Persistence\ObjectManager;
class PflanzeFixtures extends Fixture implements DependentFixtureInterface
{
/**
* @return class-string<Fixture>[]
*/
public function getDependencies(): array
{
return [KategorieFixtures::class];
}
private const array PFLANZEN = [
['artName' => 'Tomate', 'sortenName' => 'Stelzer', 'kategorie' => 'Gemüse'],
['artName' => 'Paprika', 'sortenName' => 'Habanero', 'kategorie' => 'Gemüse'],
['artName' => 'Basilikum', 'sortenName' => 'Genovese', 'kategorie' => 'Kräuter'],
['artName' => 'Salbei', 'sortenName' => null, 'kategorie' => 'Kräuter'],
['artName' => 'Sonnenblume', 'sortenName' => 'Riesenblume', 'kategorie' => 'Blumen'],
['artName' => 'Erdbeere', 'sortenName' => 'Senga Sengana', 'kategorie' => 'Obst'],
];
public function load(ObjectManager $manager): void
{
foreach (self::PFLANZEN as $data) {
$kategorie = $this->getReference("kategorie-{$data['kategorie']}", KategorieEntity::class);
$pflanze = new PflanzeEntity(
artName: $data['artName'],
sortenName: $data['sortenName'] ?? null,
kategorieId: $kategorie->getId(),
beschreibung: "Katalogeintrag für {$data['artName']}",
);
$manager->persist($pflanze);
}
$manager->flush();
}
}
+15
View File
@@ -22,6 +22,18 @@
"src/Repository/.gitignore"
]
},
"doctrine/doctrine-fixtures-bundle": {
"version": "4.3",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "3.0",
"ref": "1f5514cfa15b947298df4d771e694e578d4c204d"
},
"files": [
"src/DataFixtures/AppFixtures.php"
]
},
"lexik/jwt-authentication-bundle": {
"version": "3.2",
"recipe": {
@@ -34,6 +46,9 @@
"config/packages/lexik_jwt_authentication.yaml"
]
},
"phpstan/phpstan": {
"version": "2.2.2"
},
"symfony/console": {
"version": "8.1",
"recipe": {