Skip to content

Commit f139ada

Browse files
committed
feature #178 API Platform support: add an --api-resource flag for the make:entity command (dunglas)
This PR was squashed before being merged into the 1.0-dev branch (closes #178). Discussion ---------- API Platform support: add an --api-resource flag for the make:entity command This PR adds a new `--api-resource` flag to the `make:entity` command. It allows to automatically a REST endpoint with CRUD operations for the given resource class using API Platform: ![screencast](https://user-images.githubusercontent.com/57224/39909149-c6c386e6-54f1-11e8-83a2-bfcd10f5ed39.gif) I need some guidance about how to add test for this (actually, a few tests are red locally on my Mac even on master). Fixes #175. Commits ------- 1c8cf9a Make the Generator class a service 3d2918a Add a functional test e6ad344 Check if the class exists ae7c296 Add an interactive question 37b3b9c API Platform support: add an --api-resource flag for the make:entity command
2 parents 2591067 + 1c8cf9a commit f139ada

File tree

10 files changed

+112
-11
lines changed

10 files changed

+112
-11
lines changed

src/Command/MakerCommand.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,14 @@ final class MakerCommand extends Command
3838
/** @var ConsoleStyle */
3939
private $io;
4040
private $checkDependencies = true;
41+
private $generator;
4142

42-
public function __construct(MakerInterface $maker, FileManager $fileManager)
43+
public function __construct(MakerInterface $maker, FileManager $fileManager, Generator $generator)
4344
{
4445
$this->maker = $maker;
4546
$this->fileManager = $fileManager;
4647
$this->inputConfig = new InputConfiguration();
48+
$this->generator = $generator;
4749

4850
parent::__construct();
4951
}
@@ -60,7 +62,7 @@ protected function initialize(InputInterface $input, OutputInterface $output)
6062

6163
if ($this->checkDependencies) {
6264
$dependencies = new DependencyBuilder();
63-
$this->maker->configureDependencies($dependencies);
65+
$this->maker->configureDependencies($dependencies, $input);
6466

6567
if ($missingPackagesMessage = $dependencies->getMissingPackagesMessage($this->getName())) {
6668
throw new RuntimeCommandException($missingPackagesMessage);
@@ -88,12 +90,10 @@ protected function interact(InputInterface $input, OutputInterface $output)
8890

8991
protected function execute(InputInterface $input, OutputInterface $output)
9092
{
91-
$generator = new Generator($this->fileManager, 'App\\');
92-
93-
$this->maker->generate($input, $this->io, $generator);
93+
$this->maker->generate($input, $this->io, $this->generator);
9494

9595
// sanity check for custom makers
96-
if ($generator->hasPendingOperations()) {
96+
if ($this->generator->hasPendingOperations()) {
9797
throw new \LogicException('Make sure to call the writeChanges() method on the generator.');
9898
}
9999
}

src/DependencyInjection/CompilerPass/MakeCommandRegistrationPass.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public function process(ContainerBuilder $container)
3838
)->setArguments([
3939
new Reference($id),
4040
new Reference('maker.file_manager'),
41+
new Reference('maker.generator'),
4142
])->addTag('console.command', ['command' => $class::getCommandName()]);
4243
}
4344
}

src/InputAwareMakerInterface.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony MakerBundle package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\MakerBundle;
13+
14+
use Symfony\Component\Console\Input\InputInterface;
15+
16+
/**
17+
* Lets the configureDependencies method access to the command's input.
18+
*
19+
* @author Kévin Dunglas <[email protected]>
20+
*/
21+
interface InputAwareMakerInterface extends MakerInterface
22+
{
23+
/**
24+
* {@inheritdoc}
25+
*/
26+
public function configureDependencies(DependencyBuilder $dependencies, InputInterface $input = null);
27+
}

src/Maker/MakeEntity.php

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Bundle\MakerBundle\Maker;
1313

14+
use ApiPlatform\Core\Annotation\ApiResource;
1415
use Doctrine\Bundle\DoctrineBundle\DoctrineBundle;
1516
use Doctrine\DBAL\Types\Type;
1617
use Doctrine\ORM\Mapping\Column;
@@ -20,6 +21,7 @@
2021
use Symfony\Bundle\MakerBundle\Doctrine\DoctrineHelper;
2122
use Symfony\Bundle\MakerBundle\Exception\RuntimeCommandException;
2223
use Symfony\Bundle\MakerBundle\Generator;
24+
use Symfony\Bundle\MakerBundle\InputAwareMakerInterface;
2325
use Symfony\Bundle\MakerBundle\InputConfiguration;
2426
use Symfony\Bundle\MakerBundle\Str;
2527
use Symfony\Bundle\MakerBundle\Doctrine\EntityRegenerator;
@@ -31,24 +33,34 @@
3133
use Symfony\Component\Console\Input\InputArgument;
3234
use Symfony\Component\Console\Input\InputInterface;
3335
use Symfony\Component\Console\Input\InputOption;
36+
use Symfony\Component\Console\Question\ConfirmationQuestion;
3437
use Symfony\Component\Console\Question\Question;
3538
use Symfony\Component\Finder\SplFileInfo;
3639

3740
/**
3841
* @author Javier Eguiluz <[email protected]>
3942
* @author Ryan Weaver <[email protected]>
43+
* @author Kévin Dunglas <[email protected]>
4044
*/
41-
final class MakeEntity extends AbstractMaker
45+
final class MakeEntity extends AbstractMaker implements InputAwareMakerInterface
4246
{
4347
private $fileManager;
4448
private $doctrineHelper;
4549
private $projectDirectory;
50+
private $generator;
4651

47-
public function __construct(FileManager $fileManager, string $projectDirectory, DoctrineHelper $doctrineHelper)
52+
public function __construct(FileManager $fileManager, string $projectDirectory, DoctrineHelper $doctrineHelper, Generator $generator = null)
4853
{
4954
$this->fileManager = $fileManager;
5055
$this->projectDirectory = $projectDirectory;
5156
$this->doctrineHelper = $doctrineHelper;
57+
58+
if (null === $generator) {
59+
@trigger_error(sprintf('Passing a "%s" instance as 4th argument is mandatory since version 1.5.', Generator::class), E_USER_DEPRECATED);
60+
$this->generator = new Generator($fileManager, 'App\\');
61+
} else {
62+
$this->generator = $generator;
63+
}
5264
}
5365

5466
public static function getCommandName(): string
@@ -59,8 +71,9 @@ public static function getCommandName(): string
5971
public function configureCommand(Command $command, InputConfiguration $inputConf)
6072
{
6173
$command
62-
->setDescription('Creates or updates a Doctrine entity class')
74+
->setDescription('Creates or updates a Doctrine entity class, and optionally an API Platform resource')
6375
->addArgument('name', InputArgument::OPTIONAL, sprintf('Class name of the entity to create or update (e.g. <fg=yellow>%s</>)', Str::asClassName(Str::getRandomTerm())))
76+
->addOption('api-resource', 'a', InputOption::VALUE_NONE, 'Mark this class as an API Platform resource (expose a CRUD API for it)')
6477
->addOption('regenerate', null, InputOption::VALUE_NONE, 'Instead of adding new fields, simply generate the methods (e.g. getter/setter) for existing fields')
6578
->addOption('overwrite', null, InputOption::VALUE_NONE, 'Overwrite any existing getter/setter methods')
6679
->setHelp(file_get_contents(__DIR__.'/../Resources/help/MakeEntity.txt'))
@@ -106,6 +119,18 @@ public function interact(InputInterface $input, ConsoleStyle $io, Command $comma
106119
$value = $io->askQuestion($question);
107120

108121
$input->setArgument('name', $value);
122+
123+
if (
124+
!$input->getOption('api-resource') &&
125+
class_exists(ApiResource::class) &&
126+
!class_exists($this->generator->createClassNameDetails($value, 'Entity\\')->getFullName())
127+
) {
128+
$description = $command->getDefinition()->getOption('api-resource')->getDescription();
129+
$question = new ConfirmationQuestion($description, false);
130+
$value = $io->askQuestion($question);
131+
132+
$input->setOption('api-resource', $value);
133+
}
109134
}
110135

111136
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator)
@@ -142,6 +167,7 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen
142167
'doctrine/Entity.tpl.php',
143168
[
144169
'repository_full_class_name' => $repositoryClassDetails->getFullName(),
170+
'api_resource' => $input->getOption('api-resource'),
145171
]
146172
);
147173

@@ -269,8 +295,15 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen
269295
]);
270296
}
271297

272-
public function configureDependencies(DependencyBuilder $dependencies)
298+
public function configureDependencies(DependencyBuilder $dependencies, InputInterface $input = null)
273299
{
300+
if (null !== $input && $input->getOption('api-resource')) {
301+
$dependencies->addClassDependency(
302+
ApiResource::class,
303+
'api'
304+
);
305+
}
306+
274307
// guarantee DoctrineBundle
275308
$dependencies->addClassDependency(
276309
DoctrineBundle::class,

src/Resources/config/makers.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
<argument type="service" id="maker.file_manager" />
3030
<argument>%kernel.project_dir%</argument>
3131
<argument type="service" id="maker.doctrine_helper" />
32+
<argument type="service" id="maker.generator" />
3233
<tag name="maker.command" />
3334
</service>
3435

src/Resources/config/services.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,10 @@
2626
<service id="maker.doctrine_helper" class="Symfony\Bundle\MakerBundle\Doctrine\DoctrineHelper">
2727
<argument type="service" id="doctrine" on-invalid="ignore"/>
2828
</service>
29+
30+
<service id="maker.generator" class="Symfony\Bundle\MakerBundle\Generator">
31+
<argument type="service" id="maker.file_manager" />
32+
<argument>App\</argument>
33+
</service>
2934
</services>
3035
</container>

src/Resources/help/MakeEntity.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ The <info>%command.name%</info> command creates or updates an entity and reposit
44

55
If the argument is missing, the command will ask for the entity class name interactively.
66

7+
You can also mark this class as an API Platform resource. A hypermedia CRUD API will
8+
automatically be available for this entity class:
9+
10+
<info>php %command.full_name% --api-resource</info>
11+
712
You can also generate all the getter/setter/adder/remover methods
813
for the properties of existing entities:
914

src/Resources/skeleton/doctrine/Entity.tpl.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@
22

33
namespace <?= $namespace ?>;
44

5+
<?php if ($api_resource): ?>use ApiPlatform\Core\Annotation\ApiResource;
6+
<?php endif ?>
57
use Doctrine\ORM\Mapping as ORM;
68

79
/**
10+
<?php if ($api_resource): ?> * @ApiResource()
11+
<?php endif ?>
812
* @ORM\Entity(repositoryClass="<?= $repository_full_class_name ?>")
913
*/
1014
class <?= $class_name."\n" ?>

tests/Command/MakerCommandTest.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use Symfony\Bundle\MakerBundle\Command\MakerCommand;
77
use Symfony\Bundle\MakerBundle\DependencyBuilder;
88
use Symfony\Bundle\MakerBundle\FileManager;
9+
use Symfony\Bundle\MakerBundle\Generator;
910
use Symfony\Bundle\MakerBundle\MakerInterface;
1011
use Symfony\Component\Console\Tester\CommandTester;
1112

@@ -26,7 +27,7 @@ public function testExceptionOnMissingDependencies()
2627

2728
$fileManager = $this->createMock(FileManager::class);
2829

29-
$command = new MakerCommand($maker, $fileManager);
30+
$command = new MakerCommand($maker, $fileManager, new Generator($fileManager, 'App\\'));
3031
// needed because it's normally set by the Application
3132
$command->setName('make:foo');
3233
$tester = new CommandTester($command);

tests/Maker/FunctionalTest.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,30 @@ public function getCommandEntityTests()
380380
->setRequiredPhpVersion(70100)
381381
];
382382

383+
yield 'entity_new_api_resource' => [MakerTestDetails::createTest(
384+
$this->getMakerInstance(MakeEntity::class),
385+
[
386+
// entity class name
387+
'User',
388+
// Mark the entity as an API Platform resource
389+
'y',
390+
// add not additional fields
391+
'',
392+
])
393+
->addExtraDependencies('api')
394+
->setFixtureFilesPath(__DIR__.'/../fixtures/MakeEntity')
395+
->configureDatabase()
396+
->updateSchemaAfterCommand()
397+
->setRequiredPhpVersion(70100)
398+
->assert(function(string $output, string $directory) {
399+
$this->assertFileExists($directory.'/src/Entity/User.php');
400+
401+
$content = file_get_contents($directory.'/src/Entity/User.php');
402+
$this->assertContains('use ApiPlatform\Core\Annotation\ApiResource;', $content);
403+
$this->assertContains('@ApiResource', $content);
404+
})
405+
];
406+
383407
yield 'entity_with_fields' => [MakerTestDetails::createTest(
384408
$this->getMakerInstance(MakeEntity::class),
385409
[

0 commit comments

Comments
 (0)