Phase 2 sub-commit 1: Doctrine ORM 3 + Migrations + SQLite
Some checks failed
CI / Quality (push) Has been cancelled
Some checks failed
CI / Quality (push) Has been cancelled
Skeleton gains Doctrine ORM 3.6 (with DoctrineBundle 3.x and Migrations 4.x), pointed at a SQLite file under var/data.sqlite. Apps move to Postgres/MySQL by overriding DATABASE_URL in .env.local. config/packages/doctrine.yaml registers the symfony/uid UuidType so Phase 2 sub-commit 4's UUIDv7 default works without per-app config, and pre-wires the App\Entity attribute mapping under src/Entity/ for the maker to drop entities into. Bundle gains an optional doctrine/dbal Connection via Autowire; when present, bridge:doctor adds a "Database reachable" SELECT-1 probe. The bundle still installs cleanly without doctrine/dbal — apps that opt out get a doctor table without the database row. Verified: `bin/console bridge:doctor` is all green against a fresh SQLite. composer quality (PHPStan + cs-fixer + PHPUnit) stays green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,12 +14,17 @@
|
|||||||
"symfony/dependency-injection": "^8.0",
|
"symfony/dependency-injection": "^8.0",
|
||||||
"symfony/config": "^8.0"
|
"symfony/config": "^8.0"
|
||||||
},
|
},
|
||||||
|
"suggest": {
|
||||||
|
"doctrine/dbal": "Required for the bridge:doctor database-reachable check and for ModelPublisher (Phase 2 sub-commit 2).",
|
||||||
|
"doctrine/orm": "Required for #[BridgeResource]-based reactive models (Phase 2 sub-commit 2)."
|
||||||
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "^11",
|
"phpunit/phpunit": "^11",
|
||||||
"phpstan/phpstan": "^2",
|
"phpstan/phpstan": "^2",
|
||||||
"phpstan/phpstan-symfony": "^2",
|
"phpstan/phpstan-symfony": "^2",
|
||||||
"friendsofphp/php-cs-fixer": "^3",
|
"friendsofphp/php-cs-fixer": "^3",
|
||||||
"symfony/phpunit-bridge": "^8.0"
|
"symfony/phpunit-bridge": "^8.0",
|
||||||
|
"doctrine/dbal": "^4.0"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace PhpQml\Bridge\Command;
|
namespace PhpQml\Bridge\Command;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Connection;
|
||||||
use Symfony\Component\Console\Attribute\AsCommand;
|
use Symfony\Component\Console\Attribute\AsCommand;
|
||||||
use Symfony\Component\Console\Command\Command;
|
use Symfony\Component\Console\Command\Command;
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
@@ -30,6 +31,9 @@ final class BridgeDoctorCommand extends Command
|
|||||||
private readonly string $mercurePublisherKey,
|
private readonly string $mercurePublisherKey,
|
||||||
#[Autowire('%env(default::MERCURE_SUBSCRIBER_JWT_KEY)%')]
|
#[Autowire('%env(default::MERCURE_SUBSCRIBER_JWT_KEY)%')]
|
||||||
private readonly string $mercureSubscriberKey,
|
private readonly string $mercureSubscriberKey,
|
||||||
|
// Optional: present only if the application installs doctrine/dbal.
|
||||||
|
#[Autowire(service: 'doctrine.dbal.default_connection')]
|
||||||
|
private readonly ?Connection $dbConnection = null,
|
||||||
) {
|
) {
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
@@ -68,6 +72,15 @@ final class BridgeDoctorCommand extends Command
|
|||||||
'Set MERCURE_SUBSCRIBER_JWT_KEY in .env.local; or rely on the Caddy `anonymous` directive in dev mode.'],
|
'Set MERCURE_SUBSCRIBER_JWT_KEY in .env.local; or rely on the Caddy `anonymous` directive in dev mode.'],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (null !== $this->dbConnection) {
|
||||||
|
try {
|
||||||
|
$this->dbConnection->fetchOne('SELECT 1');
|
||||||
|
$checks[] = ['Database reachable', true, ''];
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$checks[] = ['Database reachable', false, 'Connection failed: '.$e->getMessage()];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$rows = [];
|
$rows = [];
|
||||||
$allPass = true;
|
$allPass = true;
|
||||||
foreach ($checks as [$label, $ok, $hint]) {
|
foreach ($checks as [$label, $ok, $hint]) {
|
||||||
|
|||||||
@@ -15,3 +15,7 @@ MERCURE_SUBSCRIBER_JWT_KEY=dev_php_qml_bridge_jwt_secret_at_least_256_bits_long_
|
|||||||
|
|
||||||
# Bearer token the Qt host sends on /api/* requests.
|
# Bearer token the Qt host sends on /api/* requests.
|
||||||
BRIDGE_TOKEN=devtoken
|
BRIDGE_TOKEN=devtoken
|
||||||
|
|
||||||
|
# SQLite database for dev. Apps move to Postgres / MySQL by overriding
|
||||||
|
# DATABASE_URL in .env.local once they outgrow it.
|
||||||
|
DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.sqlite"
|
||||||
|
|||||||
@@ -10,6 +10,10 @@
|
|||||||
"symfony/yaml": "^8.0",
|
"symfony/yaml": "^8.0",
|
||||||
"symfony/security-bundle": "^8.0",
|
"symfony/security-bundle": "^8.0",
|
||||||
"symfony/mercure-bundle": "^0.4",
|
"symfony/mercure-bundle": "^0.4",
|
||||||
|
"symfony/uid": "^8.0",
|
||||||
|
"doctrine/orm": "^3.0",
|
||||||
|
"doctrine/doctrine-bundle": "^3.0",
|
||||||
|
"doctrine/doctrine-migrations-bundle": "^4.0",
|
||||||
"php-qml/bridge": "@dev"
|
"php-qml/bridge": "@dev"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
|||||||
1546
framework/skeleton/symfony/composer.lock
generated
1546
framework/skeleton/symfony/composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -4,5 +4,7 @@ return [
|
|||||||
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
|
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
|
||||||
Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
|
Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
|
||||||
Symfony\Bundle\MercureBundle\MercureBundle::class => ['all' => true],
|
Symfony\Bundle\MercureBundle\MercureBundle::class => ['all' => true],
|
||||||
|
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
|
||||||
|
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
|
||||||
PhpQml\Bridge\BridgeBundle::class => ['all' => true],
|
PhpQml\Bridge\BridgeBundle::class => ['all' => true],
|
||||||
];
|
];
|
||||||
|
|||||||
38
framework/skeleton/symfony/config/packages/doctrine.yaml
Normal file
38
framework/skeleton/symfony/config/packages/doctrine.yaml
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
doctrine:
|
||||||
|
dbal:
|
||||||
|
url: '%env(resolve:DATABASE_URL)%'
|
||||||
|
# SQLite default for dev — see .env.
|
||||||
|
# Apps swap this to Postgres / MySQL when they outgrow it.
|
||||||
|
types:
|
||||||
|
uuid: Symfony\Bridge\Doctrine\Types\UuidType
|
||||||
|
orm:
|
||||||
|
validate_xml_mapping: true
|
||||||
|
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
|
||||||
|
identity_generation_preferences:
|
||||||
|
Doctrine\DBAL\Platforms\PostgreSQLPlatform: identity
|
||||||
|
auto_mapping: true
|
||||||
|
mappings:
|
||||||
|
App:
|
||||||
|
type: attribute
|
||||||
|
is_bundle: false
|
||||||
|
dir: '%kernel.project_dir%/src/Entity'
|
||||||
|
prefix: 'App\Entity'
|
||||||
|
alias: App
|
||||||
|
|
||||||
|
when@prod:
|
||||||
|
doctrine:
|
||||||
|
orm:
|
||||||
|
query_cache_driver:
|
||||||
|
type: pool
|
||||||
|
pool: doctrine.system_cache_pool
|
||||||
|
result_cache_driver:
|
||||||
|
type: pool
|
||||||
|
pool: doctrine.result_cache_pool
|
||||||
|
|
||||||
|
framework:
|
||||||
|
cache:
|
||||||
|
pools:
|
||||||
|
doctrine.result_cache_pool:
|
||||||
|
adapter: cache.app
|
||||||
|
doctrine.system_cache_pool:
|
||||||
|
adapter: cache.system
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
doctrine_migrations:
|
||||||
|
migrations_paths:
|
||||||
|
'DoctrineMigrations': '%kernel.project_dir%/migrations'
|
||||||
|
enable_profiler: false
|
||||||
0
framework/skeleton/symfony/migrations/.gitkeep
Normal file
0
framework/skeleton/symfony/migrations/.gitkeep
Normal file
Reference in New Issue
Block a user