diff --git a/README.md b/README.md index af64c7c..a0f1227 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ public function registerBundles() ## Configuration -This bundle depends on a number of other symfony bundles, so they need to be configured in order for the generator to work properly +This bundle depends on a number of other Symfony bundles, so they need to be configured in order for the generator to work properly ```yaml framework: @@ -77,11 +77,6 @@ sensio_framework_extra: ## Generating the Controller -Right now, the generator uses forms created by the doctrine generator. You must create these to use the generated controller. (If you don't create them prior to running the voryx:generate:rest command, you will get an error, but it will still work if you just create them afterwards) - -```bash -$ php app/console doctrine:generate:form AcmeDemoBundle:Post -``` Generate the REST controller @@ -91,15 +86,62 @@ $ php app/console voryx:generate:rest This will guide you through the generator which will generate a RESTful controller for an entity. -You will still need to Add a route for each generated entity: (Hopefully this will be added to the generator soon) -```yaml -api_posts: - type: rest - resource: "@AcmeDemoBundle/Controller/PostController.php" - prefix: /api +## Example + +Create a new entity called 'Post': + +```bash +$ php app/console doctrine:generate:entity --entity=AppBundle:Post --format=annotation --fields="name:string(255) description:string(255)" --no-interaction +``` + +Update the database schema: + +```bash +$ php app/console doctrine:schema:update --force +``` + +Generate the API controller: + +```bash +$ php app/console voryx:generate:rest --entity="AppBundle:Post" +``` + +### Using the API +If you selected the default options you'll be able to start using the API like this: + +Creating a new post (`POST`) + +```bash +$ curl -i -H "Content-Type: application/json" -X POST -d '{"name" : "Test Post", "description" : "This is a test post"}' http://localhost/app_dev.php/api/posts +``` + +Updating (`PUT`) + +```bash +$ curl -i -H "Content-Type: application/json" -X PUT -d '{"name" : "Test Post 1", "description" : "This is an updated test post"}' http://localhost/app_dev.php/api/posts/1 ``` +Get all posts (`GET`) + +```bash +$ curl http://localhost/app_dev.php/api/posts +``` + +Get one post (`GET`) + +```bash +$ curl http://localhost/app_dev.php/api/posts/1 +``` + + +Delete (`DELETE`) + +```bash +$ curl -X DELETE http://localhost/app_dev.php/api/posts/1 +``` + + ## Related Entities If you want the form to be able to convert related entities into the correct entity id on POST, PUT or PATCH, use the voryx_entity form type diff --git a/composer.json b/composer.json index 1aab61a..fc35c2e 100644 --- a/composer.json +++ b/composer.json @@ -6,14 +6,17 @@ "authors": [ { "name": "Matt Bonneau" + }, + { + "name": "David Dan" } ], - "minimum-stability": "dev", "require": { "php": ">=5.3.0", - "friendsofsymfony/rest-bundle": "1.4.*", + "sensio/generator-bundle": "~2.5", + "friendsofsymfony/rest-bundle": "~1.4", "jms/serializer-bundle": "0.13.*", - "nelmio/cors-bundle": "1.3.*" + "nelmio/cors-bundle": "~1.3" }, "autoload": { "psr-0": { diff --git a/src/Voryx/RESTGeneratorBundle/Command/GenerateDoctrineCommand.php b/src/Voryx/RESTGeneratorBundle/Command/GenerateDoctrineCommand.php deleted file mode 100644 index 0d11b6f..0000000 --- a/src/Voryx/RESTGeneratorBundle/Command/GenerateDoctrineCommand.php +++ /dev/null @@ -1,40 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Voryx\RESTGeneratorBundle\Command; - -use Doctrine\Bundle\DoctrineBundle\Mapping\DisconnectedMetadataFactory; - -abstract class GenerateDoctrineCommand extends GeneratorCommand -{ - public function isEnabled() - { - return class_exists('Doctrine\\Bundle\\DoctrineBundle\\DoctrineBundle'); - } - - protected function parseShortcutNotation($shortcut) - { - $entity = str_replace('/', '\\', $shortcut); - - if (false === $pos = strpos($entity, ':')) { - throw new \InvalidArgumentException(sprintf('The entity name must contain a : ("%s" given, expecting something like AcmeBlogBundle:Blog/Post)', $entity)); - } - - return array(substr($entity, 0, $pos), substr($entity, $pos + 1)); - } - - protected function getEntityMetadata($entity) - { - $factory = new DisconnectedMetadataFactory($this->getContainer()->get('doctrine')); - - return $factory->getClassMetadata($entity)->getMetadata(); - } -} diff --git a/src/Voryx/RESTGeneratorBundle/Command/GenerateDoctrineRESTCommand.php b/src/Voryx/RESTGeneratorBundle/Command/GenerateDoctrineRESTCommand.php index 15e2d81..bb4f790 100644 --- a/src/Voryx/RESTGeneratorBundle/Command/GenerateDoctrineRESTCommand.php +++ b/src/Voryx/RESTGeneratorBundle/Command/GenerateDoctrineRESTCommand.php @@ -5,27 +5,29 @@ namespace Voryx\RESTGeneratorBundle\Command; + +use Sensio\Bundle\GeneratorBundle\Command\GenerateDoctrineCrudCommand; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Output\Output; -use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Question\ConfirmationQuestion; use Symfony\Component\Console\Question\Question; +use Symfony\Component\DependencyInjection\Container; use Symfony\Component\HttpKernel\Bundle\BundleInterface; -use Sensio\Bundle\GeneratorBundle\Command\Helper\DialogHelper; +use Sensio\Bundle\GeneratorBundle\Command\Helper\QuestionHelper; use Voryx\RESTGeneratorBundle\Generator\DoctrineRESTGenerator; -use Sensio\Bundle\GeneratorBundle\Generator\DoctrineFormGenerator; -use Sensio\Bundle\GeneratorBundle\Manipulator\RoutingManipulator; - use Sensio\Bundle\GeneratorBundle\Command\Validators; +use Voryx\RESTGeneratorBundle\Manipulator\RoutingManipulator; /** * Generates a REST api for a Doctrine entity. * */ -class GenerateDoctrineRESTCommand extends GenerateDoctrineCommand +class GenerateDoctrineRESTCommand extends GenerateDoctrineCrudCommand { + /** + * @var + */ private $formGenerator; /** @@ -33,15 +35,19 @@ class GenerateDoctrineRESTCommand extends GenerateDoctrineCommand */ protected function configure() { - $this - ->setDefinition(array( + $this->setDefinition( + array( new InputOption('entity', '', InputOption::VALUE_REQUIRED, 'The entity class name to initialize (shortcut notation)'), new InputOption('route-prefix', '', InputOption::VALUE_REQUIRED, 'The route prefix'), new InputOption('overwrite', '', InputOption::VALUE_NONE, 'Do not stop the generation if rest api controller already exist, thus overwriting all generated files'), - )) + new InputOption('resource', '', InputOption::VALUE_NONE, 'The object will return with the resource name'), + new InputOption('document', '', InputOption::VALUE_NONE, 'Use NelmioApiDocBundle to document the controller'), + ) + ) ->setDescription('Generates a REST api based on a Doctrine entity') - ->setHelp(<<voryx:generate:rest command generates a REST api based on a Doctrine entity. + ->setHelp( + <<voryx:generate:rest command generates a REST api based on a Doctrine entity. php app/console voryx:generate:rest --entity=AcmeBlogBundle:Post --route-prefix=post_admin @@ -60,8 +66,7 @@ protected function configure() EOT ) ->setName('voryx:generate:rest') - ->setAliases(array('generate:voryx:rest')) - ; + ->setAliases(array('generate:voryx:rest')); } /** @@ -69,10 +74,11 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - $dialog = $this->getDialogHelper(); + $questionHelper = $this->getQuestionHelper(); if ($input->isInteractive()) { - if (!$dialog->ask($input, $output, new ConfirmationQuestion('Do you confirm generation', 'yes', '?'), true)) { + $question = new ConfirmationQuestion($questionHelper->getQuestion('Do you confirm generation', 'yes', '?'), true); + if (!$questionHelper->ask($input, $output, $question)) { $output->writeln('Command aborted'); return 1; @@ -82,151 +88,167 @@ protected function execute(InputInterface $input, OutputInterface $output) $entity = Validators::validateEntityName($input->getOption('entity')); list($bundle, $entity) = $this->parseShortcutNotation($entity); - $prefix = $this->getRoutePrefix($input, $entity); + $format = "rest"; + $prefix = $this->getRoutePrefix($input, $entity); $forceOverwrite = $input->getOption('overwrite'); - $dialog->writeSection($output, 'REST api generation'); + $questionHelper->writeSection($output, 'REST api generation'); - $entityClass = $this->getContainer()->get('doctrine')->getAliasNamespace($bundle).'\\'.$entity; + $entityClass = $this->getContainer()->get('doctrine')->getAliasNamespace($bundle) . '\\' . $entity; $metadata = $this->getEntityMetadata($entityClass); $bundle = $this->getContainer()->get('kernel')->getBundle($bundle); + $resource = $input->getOption('resource'); + $document = $input->getOption('document'); $generator = $this->getGenerator($bundle); - $generator->generate($bundle, $entity, $metadata[0], $prefix, $forceOverwrite); + $generator->generate($bundle, $entity, $metadata[0], $prefix, $forceOverwrite, $resource, $document); $output->writeln('Generating the REST api code: OK'); $errors = array(); - $runner = $dialog->getRunner($output, $errors); + $runner = $questionHelper->getRunner($output, $errors); // form $this->generateForm($bundle, $entity, $metadata); $output->writeln('Generating the Form code: OK'); - // TODO: create routing automatically - if (1 == 2) { - $runner($this->updateRouting($dialog, $input, $output, $bundle, $entity, $prefix)); - } + // create route + $runner($this->updateRouting($questionHelper, $input, $output, $bundle, $format, $entity, $prefix)); - $dialog->writeGeneratorSummary($output, $errors); + $questionHelper->writeGeneratorSummary($output, $errors); } + /** + * @param InputInterface $input + * @param OutputInterface $output + */ protected function interact(InputInterface $input, OutputInterface $output) { - $dialog = $this->getDialogHelper(); - $dialog->writeSection($output, 'Welcome to the Doctrine2 REST api generator'); + $questionHelper = $this->getQuestionHelper(); + $questionHelper->writeSection($output, 'Welcome to the Doctrine2 REST api generator'); // namespace - $output->writeln(array( - '', - 'This command helps you generate a REST api controller.', - '', - 'First, you need to give the entity for which you want to generate a REST api.', - 'You can give an entity that does not exist yet and the wizard will help', - 'you defining it.', - '', - 'You must use the shortcut notation like AcmeBlogBundle:Post.', - '', - )); - - $entity = $dialog->ask($input, $output, new Question('The Entity shortcut name: ', $input->getOption('entity')), array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateEntityName'), false, $input->getOption('entity')); + $output->writeln( + array( + '', + 'This command helps you generate a REST api controller.', + '', + 'First, you need to give the entity for which you want to generate a REST api.', + 'You can give an entity that does not exist yet and the wizard will help', + 'you defining it.', + '', + 'You must use the shortcut notation like AcmeBlogBundle:Post.', + '', + ) + ); + + $question = new Question($questionHelper->getQuestion('The Entity shortcut name', $input->getOption('entity')), $input->getOption('entity')); + $question->setValidator(array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateEntityName')); + $entity = $questionHelper->ask($input, $output, $question); + $input->setOption('entity', $entity); list($bundle, $entity) = $this->parseShortcutNotation($entity); // route prefix - $prefix = $this->getRoutePrefix($input, $entity); - $output->writeln(array( - '', - 'Determine the routes prefix (all the routes will be "mounted" under this', - 'prefix: /prefix/, /prefix/new, ...).', - '', - )); - $prefix = $dialog->ask($input, $output, new Question('Routes prefix: ', '/'.$prefix), '/'.$prefix); + $prefix = 'api'; + $output->writeln( + array( + '', + 'Determine the routes prefix (all the API routes will be "mounted" under this', + 'prefix: /prefix/, /prefix/posts, ...).', + '', + ) + ); + + $prefix = $questionHelper->ask($input, $output, new Question($questionHelper->getQuestion('Routes prefix', '/' . $prefix), '/' . $prefix)); $input->setOption('route-prefix', $prefix); // summary - $output->writeln(array( - '', - $this->getHelper('formatter')->formatBlock('Summary before generation', 'bg=blue;fg=white', true), - '', - sprintf("You are going to generate a REST api controller for \"%s:%s\"", $bundle, $entity), - '', - )); + $output->writeln( + array( + '', + $this->getHelper('formatter')->formatBlock('Summary before generation', 'bg=blue;fg=white', true), + '', + sprintf("You are going to generate a REST api controller for \"%s:%s\"", $bundle, $entity), + '', + ) + ); } + /** - * Tries to generate forms if they don't exist yet and if we need write operations on entities. + * @param QuestionHelper $questionHelper + * @param InputInterface $input + * @param OutputInterface $output + * @param BundleInterface $bundle + * @param $entity + * @param $prefix + * @return array */ - protected function generateForm($bundle, $entity, $metadata) - { - try { - $this->getFormGenerator($bundle)->generate($bundle, $entity, $metadata[0]); - } catch (\RuntimeException $e ) { - // form already exists - } - } - - protected function updateRouting(DialogHelper $dialog, InputInterface $input, OutputInterface $output, BundleInterface $bundle, $entity, $prefix) + protected function updateRouting(QuestionHelper $questionHelper, InputInterface $input, OutputInterface $output, BundleInterface $bundle, $format, $entity, $prefix) { $auto = true; if ($input->isInteractive()) { - $auto = $dialog->ask($output, new ConfirmationQuestion('Confirm automatic update of the Routing: ', 'yes', '?'), true); + $question = new ConfirmationQuestion($questionHelper->getQuestion('Confirm automatic update of the Routing', 'yes', '?'), true); + $auto = $questionHelper->ask($input, $output, $question); } $output->write('Importing the REST api routes: '); - $this->getContainer()->get('filesystem')->mkdir($bundle->getPath().'/Resources/config/'); - $routing = new RoutingManipulator($bundle->getPath().'/Resources/config/routing.yml'); + $this->getContainer()->get('filesystem')->mkdir($bundle->getPath() . '/Resources/config/'); + $routing = new RoutingManipulator($this->getContainer()->getParameter('kernel.root_dir') . '/config/routing.yml'); try { - // TODO: fix the format parameter - leaving it for now - $format = "annotation"; - $ret = $auto ? $routing->addResource($bundle->getName(), $format, '/'.$prefix, 'routing/'.strtolower(str_replace('\\', '_', $entity))) : false; + $ret = $auto ? $routing->addResource($bundle->getName(), '/' . $prefix, $entity) : false; } catch (\RuntimeException $exc) { $ret = false; } if (!$ret) { - $help = sprintf(" resource: \"@%s/Resources/config/routing/%s.%s\"\n", $bundle->getName(), strtolower(str_replace('\\', '_', $entity)), $format); + $help = sprintf( + " resource: \"@%s/Controller/%sRESTController.php\"\n", + $bundle->getName(), + $entity + ); + $help .= sprintf(" type: %s\n", 'rest'); $help .= sprintf(" prefix: /%s\n", $prefix); return array( - '- Import the bundle\'s routing resource in the bundle routing file', - sprintf(' (%s).', $bundle->getPath().'/Resources/config/routing.yml'), + '- Import this resource into the Apps routing file', + sprintf(' (%s).', $this->getContainer()->getParameter('kernel.root_dir') . '/config/routing.yml'), '', - sprintf(' %s:', $bundle->getName().('' !== $prefix ? '_'.str_replace('/', '_', $prefix) : '')), + sprintf( + ' %s:', + substr($bundle->getName(), 0, -6) . '_' . $entity . ('' !== $prefix ? '_' . str_replace('/', '_', $prefix) : '') + ), $help, '', ); } } - protected function getRoutePrefix(InputInterface $input, $entity) - { - $prefix = $input->getOption('route-prefix') ?: strtolower(str_replace(array('\\', '/'), '_', $entity)); - - if ($prefix && '/' === $prefix[0]) { - $prefix = substr($prefix, 1); - } - - return $prefix; - } + /** + * @param null $bundle + * @return DoctrineRESTGenerator + */ protected function createGenerator($bundle = null) { return new DoctrineRESTGenerator($this->getContainer()->get('filesystem')); } - protected function getFormGenerator($bundle = null) + /** + * @param BundleInterface $bundle + * @return array + */ + protected function getSkeletonDirs(BundleInterface $bundle = null) { - if (null === $this->formGenerator) { - $this->formGenerator = new DoctrineFormGenerator($this->getContainer()->get('filesystem')); - $this->formGenerator->setSkeletonDirs($this->getSkeletonDirs($bundle)); - } + $skeletonDirs = parent::getSkeletonDirs($bundle); - return $this->formGenerator; - } + $reflClass = new \ReflectionClass(get_class($this)); - public function setFormGenerator(DoctrineFormGenerator $formGenerator) - { - $this->formGenerator = $formGenerator; + $skeletonDirs[] = dirname($reflClass->getFileName()) . '/../Resources/skeleton'; + $skeletonDirs[] = dirname($reflClass->getFileName()) . '/../Resources'; + + return $skeletonDirs; } + } diff --git a/src/Voryx/RESTGeneratorBundle/Command/GeneratorCommand.php b/src/Voryx/RESTGeneratorBundle/Command/GeneratorCommand.php deleted file mode 100644 index 05e8efd..0000000 --- a/src/Voryx/RESTGeneratorBundle/Command/GeneratorCommand.php +++ /dev/null @@ -1,75 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Voryx\RESTGeneratorBundle\Command; - -use Symfony\Component\HttpKernel\Bundle\BundleInterface; -use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; -use Sensio\Bundle\GeneratorBundle\Command\Helper\QuestionHelper; -use Voryx\RESTGeneratorBundle\Generator\Generator; - -/** - * Base class for generator commands. - * - * @author Fabien Potencier - */ -abstract class GeneratorCommand extends ContainerAwareCommand -{ - private $generator; - - // only useful for unit tests - public function setGenerator(Generator $generator) - { - $this->generator = $generator; - } - - abstract protected function createGenerator(); - - protected function getGenerator(BundleInterface $bundle = null) - { - if (null === $this->generator) { - $this->generator = $this->createGenerator(); - $this->generator->setSkeletonDirs($this->getSkeletonDirs($bundle)); - } - - return $this->generator; - } - - protected function getSkeletonDirs(BundleInterface $bundle = null) - { - $skeletonDirs = array(); - - if (isset($bundle) && is_dir($dir = $bundle->getPath().'/Resources/SensioGeneratorBundle/skeleton')) { - $skeletonDirs[] = $dir; - } - - if (is_dir($dir = $this->getContainer()->get('kernel')->getRootdir().'/Resources/SensioGeneratorBundle/skeleton')) { - $skeletonDirs[] = $dir; - } - - $reflClass = new \ReflectionClass(get_class($this)); - - $skeletonDirs[] = dirname($reflClass->getFileName()).'/../Resources/skeleton'; - $skeletonDirs[] = dirname($reflClass->getFileName()).'/../Resources'; - - return $skeletonDirs; - } - - protected function getDialogHelper() - { - $dialog = $this->getHelperSet()->get('dialog'); - if (!$dialog || get_class($dialog) !== 'Sensio\Bundle\GeneratorBundle\Command\Helper\QuestionHelper') { - $this->getHelperSet()->set($dialog = new QuestionHelper()); - } - - return $dialog; - } -} diff --git a/src/Voryx/RESTGeneratorBundle/Controller/VoryxController.php b/src/Voryx/RESTGeneratorBundle/Controller/VoryxController.php index 51cca5c..ce6587d 100644 --- a/src/Voryx/RESTGeneratorBundle/Controller/VoryxController.php +++ b/src/Voryx/RESTGeneratorBundle/Controller/VoryxController.php @@ -13,8 +13,8 @@ class VoryxController extends Controller /** * Create a form without a name * - * @param null $type - * @param null $data + * @param null $type + * @param null $data * @param array $options * * @return Form|FormInterface @@ -35,13 +35,13 @@ public function createForm($type = null, $data = null, array $options = array()) * Get rid on any fields that don't appear in the form * * @param Request $request - * @param Form $form + * @param Form $form */ protected function removeExtraFields(Request $request, Form $form) { - $data = $request->request->all(); + $data = $request->request->all(); $children = $form->all(); - $data = array_intersect_key($data, $children); + $data = array_intersect_key($data, $children); $request->request->replace($data); } } diff --git a/src/Voryx/RESTGeneratorBundle/Form/DataTransformer/ArrayToIdTransformer.php b/src/Voryx/RESTGeneratorBundle/Form/DataTransformer/ArrayToIdTransformer.php index 8a00082..4772da5 100644 --- a/src/Voryx/RESTGeneratorBundle/Form/DataTransformer/ArrayToIdTransformer.php +++ b/src/Voryx/RESTGeneratorBundle/Form/DataTransformer/ArrayToIdTransformer.php @@ -59,4 +59,36 @@ public function reverseTransform($data) return null; } + + /** + * @return EntityManager + */ + public function getEm() + { + return $this->em; + } + + /** + * @param EntityManager $em + */ + public function setEm($em) + { + $this->em = $em; + } + + /** + * @return string + */ + public function getClass() + { + return $this->class; + } + + /** + * @param string $class + */ + public function setClass($class) + { + $this->class = $class; + } } diff --git a/src/Voryx/RESTGeneratorBundle/Form/Type/VoryxEntityType.php b/src/Voryx/RESTGeneratorBundle/Form/Type/VoryxEntityType.php index f8fdd3d..5fe1011 100644 --- a/src/Voryx/RESTGeneratorBundle/Form/Type/VoryxEntityType.php +++ b/src/Voryx/RESTGeneratorBundle/Form/Type/VoryxEntityType.php @@ -1,10 +1,5 @@ em = $em; } + /** + * @param FormBuilderInterface $builder + * @param array $options + */ public function buildForm(FormBuilderInterface $builder, array $options) { $view_transformer = new ArrayToIdTransformer($this->em, null); @@ -38,6 +41,9 @@ public function buildForm(FormBuilderInterface $builder, array $options) // $model_transformer = new UserToUsernameTransformer() } + /** + * @param OptionsResolverInterface $resolver + */ public function setDefaultOptions(OptionsResolverInterface $resolver) { $resolver->setDefaults( @@ -47,11 +53,17 @@ public function setDefaultOptions(OptionsResolverInterface $resolver) ); } + /** + * @return string + */ public function getParent() { return 'entity'; } + /** + * @return string + */ public function getName() { return 'voryx_entity'; diff --git a/src/Voryx/RESTGeneratorBundle/Generator/DoctrineRESTGenerator.php b/src/Voryx/RESTGeneratorBundle/Generator/DoctrineRESTGenerator.php index d664e2e..f90caaf 100644 --- a/src/Voryx/RESTGeneratorBundle/Generator/DoctrineRESTGenerator.php +++ b/src/Voryx/RESTGeneratorBundle/Generator/DoctrineRESTGenerator.php @@ -11,6 +11,7 @@ namespace Voryx\RESTGeneratorBundle\Generator; +use Sensio\Bundle\GeneratorBundle\Generator\Generator; use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\HttpKernel\Bundle\BundleInterface; use Doctrine\ORM\Mapping\ClassMetadataInfo; @@ -38,25 +39,25 @@ class DoctrineRESTGenerator extends Generator */ public function __construct(Filesystem $filesystem) { - $this->filesystem = $filesystem; + $this->filesystem = $filesystem; } /** * Generate the REST controller. * - * @param BundleInterface $bundle A bundle object - * @param string $entity The entity relative class name - * @param ClassMetadataInfo $metadata The entity class metadata - * @param string $routePrefix The route name prefix - * @param array $forceOverwrite Whether or not to overwrite an existing controller + * @param BundleInterface $bundle A bundle object + * @param string $entity The entity relative class name + * @param ClassMetadataInfo $metadata The entity class metadata + * @param string $routePrefix The route name prefix + * @param array $forceOverwrite Whether or not to overwrite an existing controller * * @throws \RuntimeException */ public function generate(BundleInterface $bundle, $entity, ClassMetadataInfo $metadata, $routePrefix, $forceOverwrite) { - $this->routePrefix = $routePrefix; + $this->routePrefix = $routePrefix; $this->routeNamePrefix = str_replace('/', '_', $routePrefix); - $this->actions = array('getById', 'getAll', 'post', 'put', 'delete'); + $this->actions = array('getById', 'getAll', 'post', 'put', 'delete'); if (count($metadata->identifier) > 1) { throw new \RuntimeException('The REST api generator does not support entity classes with multiple primary keys.'); @@ -112,13 +113,17 @@ protected function generateConfiguration() $this->format ); - $this->renderFile('rest/config/routing.'.$this->format.'.twig', $target, array( - 'actions' => $this->actions, - 'route_prefix' => $this->routePrefix, - 'route_name_prefix' => $this->routeNamePrefix, - 'bundle' => $this->bundle->getName(), - 'entity' => $this->entity, - )); + $this->renderFile( + 'rest/config/routing.' . $this->format . '.twig', + $target, + array( + 'actions' => $this->actions, + 'route_prefix' => $this->routePrefix, + 'route_name_prefix' => $this->routeNamePrefix, + 'bundle' => $this->bundle->getName(), + 'entity' => $this->entity, + ) + ); } /** @@ -129,8 +134,8 @@ protected function generateControllerClass($forceOverwrite) { $dir = $this->bundle->getPath(); - $parts = explode('\\', $this->entity); - $entityClass = array_pop($parts); + $parts = explode('\\', $this->entity); + $entityClass = array_pop($parts); $entityNamespace = implode('\\', $parts); $target = sprintf( @@ -144,17 +149,21 @@ protected function generateControllerClass($forceOverwrite) throw new \RuntimeException('Unable to generate the controller as it already exists.'); } - $this->renderFile('rest/controller.php.twig', $target, array( - 'actions' => $this->actions, - 'route_prefix' => $this->routePrefix, - 'route_name_prefix' => $this->routeNamePrefix, - 'bundle' => $this->bundle->getName(), - 'entity' => $this->entity, - 'entity_class' => $entityClass, - 'namespace' => $this->bundle->getNamespace(), - 'entity_namespace' => $entityNamespace, - 'format' => $this->format, - )); + $this->renderFile( + 'rest/controller.php.twig', + $target, + array( + 'actions' => $this->actions, + 'route_prefix' => $this->routePrefix, + 'route_name_prefix' => $this->routeNamePrefix, + 'bundle' => $this->bundle->getName(), + 'entity' => $this->entity, + 'entity_class' => $entityClass, + 'namespace' => $this->bundle->getNamespace(), + 'entity_namespace' => $entityNamespace, + 'format' => $this->format, + ) + ); } /** @@ -163,24 +172,28 @@ protected function generateControllerClass($forceOverwrite) */ protected function generateTestClass() { - $parts = explode('\\', $this->entity); - $entityClass = array_pop($parts); + $parts = explode('\\', $this->entity); + $entityClass = array_pop($parts); $entityNamespace = implode('\\', $parts); - $dir = $this->bundle->getPath() .'/Tests/Controller'; - $target = $dir .'/'. str_replace('\\', '/', $entityNamespace).'/'. $entityClass .'RESTControllerTest.php'; - - $this->renderFile('rest/tests/test.php.twig', $target, array( - 'route_prefix' => $this->routePrefix, - 'route_name_prefix' => $this->routeNamePrefix, - 'entity' => $this->entity, - 'bundle' => $this->bundle->getName(), - 'entity_class' => $entityClass, - 'namespace' => $this->bundle->getNamespace(), - 'entity_namespace' => $entityNamespace, - 'actions' => $this->actions, - 'form_type_name' => strtolower(str_replace('\\', '_', $this->bundle->getNamespace()).($parts ? '_' : '').implode('_', $parts).'_'.$entityClass.'Type'), - )); + $dir = $this->bundle->getPath() . '/Tests/Controller'; + $target = $dir . '/' . str_replace('\\', '/', $entityNamespace) . '/' . $entityClass . 'RESTControllerTest.php'; + + $this->renderFile( + 'rest/tests/test.php.twig', + $target, + array( + 'route_prefix' => $this->routePrefix, + 'route_name_prefix' => $this->routeNamePrefix, + 'entity' => $this->entity, + 'bundle' => $this->bundle->getName(), + 'entity_class' => $entityClass, + 'namespace' => $this->bundle->getNamespace(), + 'entity_namespace' => $entityNamespace, + 'actions' => $this->actions, + 'form_type_name' => strtolower(str_replace('\\', '_', $this->bundle->getNamespace()) . ($parts ? '_' : '') . implode('_', $parts) . '_' . $entityClass . 'Type'), + ) + ); } } diff --git a/src/Voryx/RESTGeneratorBundle/Generator/Generator.php b/src/Voryx/RESTGeneratorBundle/Generator/Generator.php deleted file mode 100644 index 5cf10ad..0000000 --- a/src/Voryx/RESTGeneratorBundle/Generator/Generator.php +++ /dev/null @@ -1,56 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Voryx\RESTGeneratorBundle\Generator; - -/** - * Generator is the base class for all generators. - * - * @author Fabien Potencier - */ -class Generator -{ - private $skeletonDirs; - - /** - * Sets an array of directories to look for templates. - * - * The directories must be sorted from the most specific to the most - * directory. - * - * @param array $skeletonDirs An array of skeleton dirs - */ - public function setSkeletonDirs($skeletonDirs) - { - $this->skeletonDirs = is_array($skeletonDirs) ? $skeletonDirs : array($skeletonDirs); - } - - protected function render($template, $parameters) - { - $twig = new \Twig_Environment(new \Twig_Loader_Filesystem($this->skeletonDirs), array( - 'debug' => true, - 'cache' => false, - 'strict_variables' => true, - 'autoescape' => false, - )); - - return $twig->render($template, $parameters); - } - - protected function renderFile($template, $target, $parameters) - { - if (!is_dir(dirname($target))) { - mkdir(dirname($target), 0777, true); - } - - return file_put_contents($target, $this->render($template, $parameters)); - } -} diff --git a/src/Voryx/RESTGeneratorBundle/Manipulator/RoutingManipulator.php b/src/Voryx/RESTGeneratorBundle/Manipulator/RoutingManipulator.php new file mode 100644 index 0000000..757ef53 --- /dev/null +++ b/src/Voryx/RESTGeneratorBundle/Manipulator/RoutingManipulator.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Voryx\RESTGeneratorBundle\Manipulator; + + +use Sensio\Bundle\GeneratorBundle\Manipulator\Manipulator; +use Symfony\Component\DependencyInjection\Container; + +/** + * Changes the PHP code of a YAML routing file. + * + * @author Fabien Potencier + */ +class RoutingManipulator extends Manipulator +{ + private $file; + + /** + * Constructor. + * + * @param string $file The YAML routing file path + */ + public function __construct($file) + { + $this->file = $file; + } + + /** + * Adds a routing resource at the top of the existing ones. + * + * @param string $bundle + * @param string $prefix + * @param $entity + * @return bool true if it worked, false otherwise + * + */ + public function addResource($bundle, $prefix = '/', $entity) + { + $current = ''; + $code = sprintf("%s:\n", Container::underscore(substr($bundle, 0, -6)) . '_' . Container::underscore($entity) . ('/' !== $prefix ? '_' . str_replace('/', '_', substr($prefix, 1)) : '')); + if (file_exists($this->file)) { + $current = file_get_contents($this->file); + + // Don't add same bundle twice + if (false !== strpos($current, $code)) { + throw new \RuntimeException(sprintf('Bundle "%s" is already imported.', $bundle)); + } + } elseif (!is_dir($dir = dirname($this->file))) { + mkdir($dir, 0777, true); + } + + $code .= sprintf(" resource: \"@%s/Controller/%sRESTController.php\"\n", $bundle, $entity); + $code .= sprintf(" type: %s\n", "rest"); + $code .= sprintf(" prefix: %s\n", $prefix); + $code .= "\n"; + $code .= $current; + + if (false === file_put_contents($this->file, $code)) { + return false; + } + + return true; + } +}