Phase 3 sub-commit 2: make:bridge:window + make:bridge:command makers
Two new makers complete the trio the todo POC needs:
`make:bridge:window <Name>`:
- emits {qml_path}/<Name>Window.qml — an ApplicationWindow wrapping
AppShell with a content slot to fill in. Apps open it via
Qt.createComponent() / a Component { } block to get extra
instances for the multi-window test (PLAN.md §13 Phase 3).
- pure-QML output, no PHP runtime deps.
`make:bridge:command <Name>`:
- emits src/Controller/<Name>Controller.php mounted at
POST /api/<kebab-name>. The body is a TODO stub that fills in
domain logic and flushes via the injected EntityManager —
Doctrine listeners pick up the changes and publish to Mercure
automatically. Synchronous by design (no Messenger plumbing for
a POC); apps that need async dispatch can add Messenger and
refactor.
Templates excluded from PHPStan / cs-fixer the same way the resource
maker's are. Smoke-tested both makers against `MarkAllDone` and
`AboutDialog` — output is correct PHP / QML and re-running them
reproduces byte-for-byte. composer quality stays green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
98
framework/php/src/Maker/BridgeWindowMaker.php
Normal file
98
framework/php/src/Maker/BridgeWindowMaker.php
Normal file
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpQml\Bridge\Maker;
|
||||
|
||||
use Symfony\Bundle\MakerBundle\ConsoleStyle;
|
||||
use Symfony\Bundle\MakerBundle\DependencyBuilder;
|
||||
use Symfony\Bundle\MakerBundle\Generator;
|
||||
use Symfony\Bundle\MakerBundle\InputConfiguration;
|
||||
use Symfony\Bundle\MakerBundle\Maker\AbstractMaker;
|
||||
use Symfony\Bundle\MakerBundle\Str;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
|
||||
/**
|
||||
* `make:bridge:window <Name>` — emits a top-level QML Window that
|
||||
* wraps `AppShell` and a content slot. Application code opens it via
|
||||
* `Qt.createComponent("<Name>Window.qml")` (or by importing it) for
|
||||
* the first window and as many extra instances as it wants for the
|
||||
* multi-window test from PLAN.md §9 / §13 Phase 3.
|
||||
*
|
||||
* Generated file goes to `qml_path` (default: `../qml/`).
|
||||
*/
|
||||
final class BridgeWindowMaker extends AbstractMaker
|
||||
{
|
||||
public function __construct(
|
||||
private readonly string $qmlPath = '../qml/',
|
||||
) {
|
||||
}
|
||||
|
||||
public static function getCommandName(): string
|
||||
{
|
||||
return 'make:bridge:window';
|
||||
}
|
||||
|
||||
public static function getCommandDescription(): string
|
||||
{
|
||||
return 'Generate a top-level QML Window scaffold (AppShell + content slot).';
|
||||
}
|
||||
|
||||
public function configureCommand(Command $command, InputConfiguration $inputConfig): void
|
||||
{
|
||||
$command
|
||||
->addArgument(
|
||||
'name',
|
||||
InputArgument::OPTIONAL,
|
||||
'Singular name of the window (e.g. Todo, Settings).',
|
||||
)
|
||||
->setHelp(
|
||||
"Creates one file:\n\n"
|
||||
." • <info>{qml_path}/<Name>Window.qml</info> — Window subclass with AppShell\n"
|
||||
);
|
||||
}
|
||||
|
||||
public function interact(InputInterface $input, ConsoleStyle $io, Command $command): void
|
||||
{
|
||||
if (null === $input->getArgument('name')) {
|
||||
$name = $io->ask('Window name?', null, static function (?string $v): string {
|
||||
if (null === $v || '' === trim($v)) {
|
||||
throw new \RuntimeException('Window name cannot be empty.');
|
||||
}
|
||||
|
||||
return ucfirst(trim($v));
|
||||
});
|
||||
$input->setArgument('name', $name);
|
||||
}
|
||||
}
|
||||
|
||||
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void
|
||||
{
|
||||
$rawName = (string) $input->getArgument('name');
|
||||
$singular = ucfirst(Str::asCamelCase($rawName));
|
||||
$resource = strtolower($singular);
|
||||
|
||||
$vars = [
|
||||
'singular' => $singular,
|
||||
'resource' => $resource,
|
||||
];
|
||||
|
||||
$target = rtrim($this->qmlPath, '/').'/'.$singular.'Window.qml';
|
||||
$generator->generateFile(
|
||||
$target,
|
||||
__DIR__.'/templates/Window.tpl.php',
|
||||
$vars,
|
||||
);
|
||||
|
||||
$generator->writeChanges();
|
||||
$this->writeSuccessMessage($io);
|
||||
$io->text("Window scaffold at <info>{$target}</info>.");
|
||||
}
|
||||
|
||||
public function configureDependencies(DependencyBuilder $dependencies): void
|
||||
{
|
||||
// Pure-QML output — no PHP runtime deps.
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user