Initial commit with Symfony 2.1+Vendors

Signed-off-by: Gergely POLONKAI (W00d5t0ck) <polesz@w00d5t0ck.info>
This commit is contained in:
Polonkai Gergely
2012-07-01 09:52:20 +02:00
commit 082a0130c2
5381 changed files with 416709 additions and 0 deletions

View File

@@ -0,0 +1,9 @@
language: php
php:
- 5.3
- 5.4
before_script:
- wget http://getcomposer.org/composer.phar
- php composer.phar install

View File

@@ -0,0 +1,316 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\GeneratorBundle\Command;
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\HttpKernel\KernelInterface;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Sensio\Bundle\GeneratorBundle\Generator\BundleGenerator;
use Sensio\Bundle\GeneratorBundle\Manipulator\KernelManipulator;
use Sensio\Bundle\GeneratorBundle\Manipulator\RoutingManipulator;
use Sensio\Bundle\GeneratorBundle\Command\Helper\DialogHelper;
/**
* Generates bundles.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class GenerateBundleCommand extends ContainerAwareCommand
{
private $generator;
/**
* @see Command
*/
protected function configure()
{
$this
->setDefinition(array(
new InputOption('namespace', '', InputOption::VALUE_REQUIRED, 'The namespace of the bundle to create'),
new InputOption('dir', '', InputOption::VALUE_REQUIRED, 'The directory where to create the bundle'),
new InputOption('bundle-name', '', InputOption::VALUE_REQUIRED, 'The optional bundle name'),
new InputOption('format', '', InputOption::VALUE_REQUIRED, 'Use the format for configuration files (php, xml, yml, or annotation)', 'annotation'),
new InputOption('structure', '', InputOption::VALUE_NONE, 'Whether to generate the whole directory structure'),
))
->setDescription('Generates a bundle')
->setHelp(<<<EOT
The <info>generate:bundle</info> command helps you generates new bundles.
By default, the command interacts with the developer to tweak the generation.
Any passed option will be used as a default value for the interaction
(<comment>--namespace</comment> is the only one needed if you follow the
conventions):
<info>php app/console generate:bundle --namespace=Acme/BlogBundle</info>
Note that you can use <comment>/</comment> instead of <comment>\\</comment> for the namespace delimiter to avoid any
problem.
If you want to disable any user interaction, use <comment>--no-interaction</comment> but don't forget to pass all needed options:
<info>php app/console generate:bundle --namespace=Acme/BlogBundle --dir=src [--bundle-name=...] --no-interaction</info>
Note that the bundle namespace must end with "Bundle".
EOT
)
->setName('generate:bundle')
;
}
/**
* @see Command
*
* @throws \InvalidArgumentException When namespace doesn't end with Bundle
* @throws \RuntimeException When bundle can't be executed
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$dialog = $this->getDialogHelper();
if ($input->isInteractive()) {
if (!$dialog->askConfirmation($output, $dialog->getQuestion('Do you confirm generation', 'yes', '?'), true)) {
$output->writeln('<error>Command aborted</error>');
return 1;
}
}
foreach (array('namespace', 'dir') as $option) {
if (null === $input->getOption($option)) {
throw new \RuntimeException(sprintf('The "%s" option must be provided.', $option));
}
}
$namespace = Validators::validateBundleNamespace($input->getOption('namespace'));
if (!$bundle = $input->getOption('bundle-name')) {
$bundle = strtr($namespace, array('\\' => ''));
}
$bundle = Validators::validateBundleName($bundle);
$dir = Validators::validateTargetDir($input->getOption('dir'), $bundle, $namespace);
$format = Validators::validateFormat($input->getOption('format'));
$structure = $input->getOption('structure');
$dialog->writeSection($output, 'Bundle generation');
if (!$this->getContainer()->get('filesystem')->isAbsolutePath($dir)) {
$dir = getcwd().'/'.$dir;
}
$generator = $this->getGenerator();
$generator->generate($namespace, $bundle, $dir, $format, $structure);
$output->writeln('Generating the bundle code: <info>OK</info>');
$errors = array();
$runner = $dialog->getRunner($output, $errors);
// check that the namespace is already autoloaded
$runner($this->checkAutoloader($output, $namespace, $bundle, $dir));
// register the bundle in the Kernel class
$runner($this->updateKernel($dialog, $input, $output, $this->getContainer()->get('kernel'), $namespace, $bundle));
// routing
$runner($this->updateRouting($dialog, $input, $output, $bundle, $format));
$dialog->writeGeneratorSummary($output, $errors);
}
protected function interact(InputInterface $input, OutputInterface $output)
{
$dialog = $this->getDialogHelper();
$dialog->writeSection($output, 'Welcome to the Symfony2 bundle generator');
// namespace
$output->writeln(array(
'',
'Your application code must be written in <comment>bundles</comment>. This command helps',
'you generate them easily.',
'',
'Each bundle is hosted under a namespace (like <comment>Acme/Bundle/BlogBundle</comment>).',
'The namespace should begin with a "vendor" name like your company name, your',
'project name, or your client name, followed by one or more optional category',
'sub-namespaces, and it should end with the bundle name itself',
'(which must have <comment>Bundle</comment> as a suffix).',
'',
'See http://symfony.com/doc/current/cookbook/bundles/best_practices.html#index-1 for more',
'details on bundle naming conventions.',
'',
'Use <comment>/</comment> instead of <comment>\\</comment> for the namespace delimiter to avoid any problem.',
'',
));
$namespace = $dialog->askAndValidate($output, $dialog->getQuestion('Bundle namespace', $input->getOption('namespace')), array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateBundleNamespace'), false, $input->getOption('namespace'));
$input->setOption('namespace', $namespace);
// bundle name
$bundle = $input->getOption('bundle-name') ?: strtr($namespace, array('\\Bundle\\' => '', '\\' => ''));
$output->writeln(array(
'',
'In your code, a bundle is often referenced by its name. It can be the',
'concatenation of all namespace parts but it\'s really up to you to come',
'up with a unique name (a good practice is to start with the vendor name).',
'Based on the namespace, we suggest <comment>'.$bundle.'</comment>.',
'',
));
$bundle = $dialog->askAndValidate($output, $dialog->getQuestion('Bundle name', $bundle), array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateBundleName'), false, $bundle);
$input->setOption('bundle-name', $bundle);
// target dir
$dir = $input->getOption('dir') ?: dirname($this->getContainer()->getParameter('kernel.root_dir')).'/src';
$output->writeln(array(
'',
'The bundle can be generated anywhere. The suggested default directory uses',
'the standard conventions.',
'',
));
$dir = $dialog->askAndValidate($output, $dialog->getQuestion('Target directory', $dir), function ($dir) use ($bundle, $namespace) { return Validators::validateTargetDir($dir, $bundle, $namespace); }, false, $dir);
$input->setOption('dir', $dir);
// format
$output->writeln(array(
'',
'Determine the format to use for the generated configuration.',
'',
));
$format = $dialog->askAndValidate($output, $dialog->getQuestion('Configuration format (yml, xml, php, or annotation)', $input->getOption('format')), array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateFormat'), false, $input->getOption('format'));
$input->setOption('format', $format);
// optional files to generate
$output->writeln(array(
'',
'To help you get started faster, the command can generate some',
'code snippets for you.',
'',
));
$structure = $input->getOption('structure');
if (!$structure && $dialog->askConfirmation($output, $dialog->getQuestion('Do you want to generate the whole directory structure', 'no', '?'), false)) {
$structure = true;
}
$input->setOption('structure', $structure);
// summary
$output->writeln(array(
'',
$this->getHelper('formatter')->formatBlock('Summary before generation', 'bg=blue;fg=white', true),
'',
sprintf("You are going to generate a \"<info>%s\\%s</info>\" bundle\nin \"<info>%s</info>\" using the \"<info>%s</info>\" format.", $namespace, $bundle, $dir, $format),
'',
));
}
protected function checkAutoloader(OutputInterface $output, $namespace, $bundle, $dir)
{
$output->write('Checking that the bundle is autoloaded: ');
if (!class_exists($namespace.'\\'.$bundle)) {
return array(
'- Edit the <comment>composer.json</comment> file and register the bundle',
' namespace in the "autoload" section:',
'',
);
}
}
protected function updateKernel($dialog, InputInterface $input, OutputInterface $output, KernelInterface $kernel, $namespace, $bundle)
{
$auto = true;
if ($input->isInteractive()) {
$auto = $dialog->askConfirmation($output, $dialog->getQuestion('Confirm automatic update of your Kernel', 'yes', '?'), true);
}
$output->write('Enabling the bundle inside the Kernel: ');
$manip = new KernelManipulator($kernel);
try {
$ret = $auto ? $manip->addBundle($namespace.'\\'.$bundle) : false;
if (!$ret) {
$reflected = new \ReflectionObject($kernel);
return array(
sprintf('- Edit <comment>%s</comment>', $reflected->getFilename()),
' and add the following bundle in the <comment>AppKernel::registerBundles()</comment> method:',
'',
sprintf(' <comment>new %s(),</comment>', $namespace.'\\'.$bundle),
'',
);
}
} catch (\RuntimeException $e) {
return array(
sprintf('Bundle <comment>%s</comment> is already defined in <comment>AppKernel::registerBundles()</comment>.', $namespace.'\\'.$bundle),
'',
);
}
}
protected function updateRouting($dialog, InputInterface $input, OutputInterface $output, $bundle, $format)
{
$auto = true;
if ($input->isInteractive()) {
$auto = $dialog->askConfirmation($output, $dialog->getQuestion('Confirm automatic update of the Routing', 'yes', '?'), true);
}
$output->write('Importing the bundle routing resource: ');
$routing = new RoutingManipulator($this->getContainer()->getParameter('kernel.root_dir').'/config/routing.yml');
try {
$ret = $auto ? $routing->addResource($bundle, $format) : false;
if (!$ret) {
if ('annotation' === $format) {
$help = sprintf(" <comment>resource: \"@%s/Controller/\"</comment>\n <comment>type: annotation</comment>\n", $bundle);
} else {
$help = sprintf(" <comment>resource: \"@%s/Resources/config/routing.%s\"</comment>\n", $bundle, $format);
}
$help .= " <comment>prefix: /</comment>\n";
return array(
'- Import the bundle\'s routing resource in the app main routing file:',
'',
sprintf(' <comment>%s:</comment>', $bundle),
$help,
'',
);
}
} catch (\RuntimeException $e) {
return array(
sprintf('Bundle <comment>%s</comment> is already imported.', $bundle),
'',
);
}
}
protected function getGenerator()
{
if (null === $this->generator) {
$this->generator = new BundleGenerator($this->getContainer()->get('filesystem'), __DIR__.'/../Resources/skeleton/bundle');
}
return $this->generator;
}
public function setGenerator(BundleGenerator $generator)
{
$this->generator = $generator;
}
protected function getDialogHelper()
{
$dialog = $this->getHelperSet()->get('dialog');
if (!$dialog || get_class($dialog) !== 'Sensio\Bundle\GeneratorBundle\Command\Helper\DialogHelper') {
$this->getHelperSet()->set($dialog = new DialogHelper());
}
return $dialog;
}
}

View File

@@ -0,0 +1,41 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\GeneratorBundle\Command;
use Doctrine\Bundle\DoctrineBundle\Mapping\MetadataFactory;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
abstract class GenerateDoctrineCommand extends ContainerAwareCommand
{
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 MetadataFactory($this->getContainer()->get('doctrine'));
return $factory->getClassMetadata($entity)->getMetadata();
}
}

View File

@@ -0,0 +1,274 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\GeneratorBundle\Command;
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 Sensio\Bundle\GeneratorBundle\Generator\DoctrineCrudGenerator;
use Sensio\Bundle\GeneratorBundle\Generator\DoctrineFormGenerator;
use Sensio\Bundle\GeneratorBundle\Command\Helper\DialogHelper;
use Sensio\Bundle\GeneratorBundle\Manipulator\RoutingManipulator;
/**
* Generates a CRUD for a Doctrine entity.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class GenerateDoctrineCrudCommand extends GenerateDoctrineCommand
{
private $generator;
private $formGenerator;
/**
* @see Command
*/
protected function configure()
{
$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('with-write', '', InputOption::VALUE_NONE, 'Whether or not to generate create, new and delete actions'),
new InputOption('format', '', InputOption::VALUE_REQUIRED, 'Use the format for configuration files (php, xml, yml, or annotation)', 'annotation'),
))
->setDescription('Generates a CRUD based on a Doctrine entity')
->setHelp(<<<EOT
The <info>doctrine:generate:crud</info> command generates a CRUD based on a Doctrine entity.
The default command only generates the list and show actions.
<info>php app/console doctrine:generate:crud --entity=AcmeBlogBundle:Post --route-prefix=post_admin</info>
Using the --with-write option allows to generate the new, edit and delete actions.
<info>php app/console doctrine:generate:crud --entity=AcmeBlogBundle:Post --route-prefix=post_admin --with-write</info>
EOT
)
->setName('doctrine:generate:crud')
->setAliases(array('generate:doctrine:crud'))
;
}
/**
* @see Command
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$dialog = $this->getDialogHelper();
if ($input->isInteractive()) {
if (!$dialog->askConfirmation($output, $dialog->getQuestion('Do you confirm generation', 'yes', '?'), true)) {
$output->writeln('<error>Command aborted</error>');
return 1;
}
}
$entity = Validators::validateEntityName($input->getOption('entity'));
list($bundle, $entity) = $this->parseShortcutNotation($entity);
$format = Validators::validateFormat($input->getOption('format'));
$prefix = $this->getRoutePrefix($input, $entity);
$withWrite = $input->getOption('with-write');
$dialog->writeSection($output, 'CRUD generation');
$entityClass = $this->getContainer()->get('doctrine')->getEntityNamespace($bundle).'\\'.$entity;
$metadata = $this->getEntityMetadata($entityClass);
$bundle = $this->getContainer()->get('kernel')->getBundle($bundle);
$generator = $this->getGenerator();
$generator->generate($bundle, $entity, $metadata[0], $format, $prefix, $withWrite);
$output->writeln('Generating the CRUD code: <info>OK</info>');
$errors = array();
$runner = $dialog->getRunner($output, $errors);
// form
if ($withWrite) {
$this->generateForm($bundle, $entity, $metadata);
$output->writeln('Generating the Form code: <info>OK</info>');
}
// routing
if ('annotation' != $format) {
$runner($this->updateRouting($dialog, $input, $output, $bundle, $format, $entity, $prefix));
}
$dialog->writeGeneratorSummary($output, $errors);
}
protected function interact(InputInterface $input, OutputInterface $output)
{
$dialog = $this->getDialogHelper();
$dialog->writeSection($output, 'Welcome to the Doctrine2 CRUD generator');
// namespace
$output->writeln(array(
'',
'This command helps you generate CRUD controllers and templates.',
'',
'First, you need to give the entity for which you want to generate a CRUD.',
'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 <comment>AcmeBlogBundle:Post</comment>.',
'',
));
$entity = $dialog->askAndValidate($output, $dialog->getQuestion('The Entity shortcut name', $input->getOption('entity')), array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateEntityName'), false, $input->getOption('entity'));
$input->setOption('entity', $entity);
list($bundle, $entity) = $this->parseShortcutNotation($entity);
// Entity exists?
$entityClass = $this->getContainer()->get('doctrine')->getEntityNamespace($bundle).'\\'.$entity;
$metadata = $this->getEntityMetadata($entityClass);
// write?
$withWrite = $input->getOption('with-write') ?: false;
$output->writeln(array(
'',
'By default, the generator creates two actions: list and show.',
'You can also ask it to generate "write" actions: new, update, and delete.',
'',
));
$withWrite = $dialog->askConfirmation($output, $dialog->getQuestion('Do you want to generate the "write" actions', $withWrite ? 'yes' : 'no', '?'), $withWrite);
$input->setOption('with-write', $withWrite);
// format
$format = $input->getOption('format');
$output->writeln(array(
'',
'Determine the format to use for the generated CRUD.',
'',
));
$format = $dialog->askAndValidate($output, $dialog->getQuestion('Configuration format (yml, xml, php, or annotation)', $format), array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateFormat'), false, $format);
$input->setOption('format', $format);
// 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($output, $dialog->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 CRUD controller for \"<info>%s:%s</info>\"", $bundle, $entity),
sprintf("using the \"<info>%s</info>\" format.", $format),
'',
));
}
/**
* Tries to generate forms if they don't exist yet and if we need write operations on entities.
*/
private function generateForm($bundle, $entity, $metadata)
{
try {
$this->getFormGenerator()->generate($bundle, $entity, $metadata[0]);
} catch (\RuntimeException $e ) {
// form already exists
}
}
private function updateRouting($dialog, InputInterface $input, OutputInterface $output, $bundle, $format, $entity, $prefix)
{
$auto = true;
if ($input->isInteractive()) {
$auto = $dialog->askConfirmation($output, $dialog->getQuestion('Confirm automatic update of the Routing', 'yes', '?'), true);
}
$output->write('Importing the CRUD routes: ');
$this->getContainer()->get('filesystem')->mkdir($bundle->getPath().'/Resources/config/');
$routing = new RoutingManipulator($bundle->getPath().'/Resources/config/routing.yml');
try {
$ret = $auto ? $routing->addResource($bundle->getName(), $format, '/'.$prefix, 'routing/'.strtolower(str_replace('\\', '_', $entity))) : false;
} catch (\RuntimeException $exc) {
$ret = false;
}
if (!$ret) {
$help = sprintf(" <comment>resource: \"@%s/Resources/config/routing/%s.%s\"</comment>\n", $bundle->getName(), strtolower(str_replace('\\', '_', $entity)), $format);
$help .= sprintf(" <comment>prefix: /%s</comment>\n", $prefix);
return array(
'- Import the bundle\'s routing resource in the bundle routing file',
sprintf(' (%s).', $bundle->getPath().'/Resources/config/routing.yml'),
'',
sprintf(' <comment>%s:</comment>', $bundle->getName().('' !== $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;
}
protected function getGenerator()
{
if (null === $this->generator) {
$this->generator = new DoctrineCrudGenerator($this->getContainer()->get('filesystem'), __DIR__.'/../Resources/skeleton/crud');
}
return $this->generator;
}
public function setGenerator(DoctrineCrudGenerator $generator)
{
$this->generator = $generator;
}
protected function getFormGenerator()
{
if (null === $this->formGenerator) {
$this->formGenerator = new DoctrineFormGenerator($this->getContainer()->get('filesystem'), __DIR__.'/../Resources/skeleton/form');
}
return $this->formGenerator;
}
public function setFormGenerator(DoctrineFormGenerator $formGenerator)
{
$this->formGenerator = $formGenerator;
}
protected function getDialogHelper()
{
$dialog = $this->getHelperSet()->get('dialog');
if (!$dialog || get_class($dialog) !== 'Sensio\Bundle\GeneratorBundle\Command\Helper\DialogHelper') {
$this->getHelperSet()->set($dialog = new DialogHelper());
}
return $dialog;
}
}

View File

@@ -0,0 +1,312 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\GeneratorBundle\Command;
use Sensio\Bundle\GeneratorBundle\Generator\DoctrineEntityGenerator;
use Sensio\Bundle\GeneratorBundle\Command\Helper\DialogHelper;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Doctrine\DBAL\Types\Type;
/**
* Initializes a Doctrine entity inside a bundle.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class GenerateDoctrineEntityCommand extends GenerateDoctrineCommand
{
private $generator;
protected function configure()
{
$this
->setName('doctrine:generate:entity')
->setAliases(array('generate:doctrine:entity'))
->setDescription('Generates a new Doctrine entity inside a bundle')
->addOption('entity', null, InputOption::VALUE_REQUIRED, 'The entity class name to initialize (shortcut notation)')
->addOption('fields', null, InputOption::VALUE_REQUIRED, 'The fields to create with the new entity')
->addOption('format', null, InputOption::VALUE_REQUIRED, 'Use the format for configuration files (php, xml, yml, or annotation)', 'annotation')
->addOption('with-repository', null, InputOption::VALUE_NONE, 'Whether to generate the entity repository or not')
->setHelp(<<<EOT
The <info>doctrine:generate:entity</info> task generates a new Doctrine
entity inside a bundle:
<info>php app/console doctrine:generate:entity --entity=AcmeBlogBundle:Blog/Post</info>
The above command would initialize a new entity in the following entity
namespace <info>Acme\BlogBundle\Entity\Blog\Post</info>.
You can also optionally specify the fields you want to generate in the new
entity:
<info>php app/console doctrine:generate:entity --entity=AcmeBlogBundle:Blog/Post --fields="title:string(255) body:text"</info>
The command can also generate the corresponding entity repository class with the
<comment>--with-repository</comment> option:
<info>php app/console doctrine:generate:entity --entity=AcmeBlogBundle:Blog/Post --with-repository</info>
By default, the command uses annotations for the mapping information; change it
with <comment>--format</comment>:
<info>php app/console doctrine:generate:entity --entity=AcmeBlogBundle:Blog/Post --format=yml</info>
To deactivate the interaction mode, simply use the `--no-interaction` option
without forgetting to pass all needed options:
<info>php app/console doctrine:generate:entity --entity=AcmeBlogBundle:Blog/Post --format=annotation --fields="title:string(255) body:text" --with-repository --no-interaction</info>
EOT
);
}
/**
* @throws \InvalidArgumentException When the bundle doesn't end with Bundle (Example: "Bundle/MySampleBundle")
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$dialog = $this->getDialogHelper();
if ($input->isInteractive()) {
if (!$dialog->askConfirmation($output, $dialog->getQuestion('Do you confirm generation', 'yes', '?'), true)) {
$output->writeln('<error>Command aborted</error>');
return 1;
}
}
$entity = Validators::validateEntityName($input->getOption('entity'));
list($bundle, $entity) = $this->parseShortcutNotation($entity);
$format = Validators::validateFormat($input->getOption('format'));
$fields = $this->parseFields($input->getOption('fields'));
$dialog->writeSection($output, 'Entity generation');
$bundle = $this->getContainer()->get('kernel')->getBundle($bundle);
$generator = $this->getGenerator();
$generator->generate($bundle, $entity, $format, array_values($fields), $input->getOption('with-repository'));
$output->writeln('Generating the entity code: <info>OK</info>');
$dialog->writeGeneratorSummary($output, array());
}
protected function interact(InputInterface $input, OutputInterface $output)
{
$dialog = $this->getDialogHelper();
$dialog->writeSection($output, 'Welcome to the Doctrine2 entity generator');
// namespace
$output->writeln(array(
'',
'This command helps you generate Doctrine2 entities.',
'',
'First, you need to give the entity name you want to generate.',
'You must use the shortcut notation like <comment>AcmeBlogBundle:Post</comment>.',
''
));
while (true) {
$entity = $dialog->askAndValidate($output, $dialog->getQuestion('The Entity shortcut name', $input->getOption('entity')), array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateEntityName'), false, $input->getOption('entity'));
list($bundle, $entity) = $this->parseShortcutNotation($entity);
// check reserved words
if ($this->getGenerator()->isReservedKeyword($entity)){
$output->writeln(sprintf('<bg=red> "%s" is a reserved word</>.', $entity));
continue;
}
try {
$b = $this->getContainer()->get('kernel')->getBundle($bundle);
if (!file_exists($b->getPath().'/Entity/'.str_replace('\\', '/', $entity).'.php')) {
break;
}
$output->writeln(sprintf('<bg=red>Entity "%s:%s" already exists</>.', $bundle, $entity));
} catch (\Exception $e) {
$output->writeln(sprintf('<bg=red>Bundle "%s" does not exist.</>', $bundle));
}
}
$input->setOption('entity', $bundle.':'.$entity);
// format
$output->writeln(array(
'',
'Determine the format to use for the mapping information.',
'',
));
$format = $dialog->askAndValidate($output, $dialog->getQuestion('Configuration format (yml, xml, php, or annotation)', $input->getOption('format')), array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateFormat'), false, $input->getOption('format'));
$input->setOption('format', $format);
// fields
$input->setOption('fields', $this->addFields($input, $output, $dialog));
// repository?
$output->writeln('');
$withRepository = $dialog->askConfirmation($output, $dialog->getQuestion('Do you want to generate an empty repository class', $input->getOption('with-repository') ? 'yes' : 'no', '?'), $input->getOption('with-repository'));
$input->setOption('with-repository', $withRepository);
// summary
$output->writeln(array(
'',
$this->getHelper('formatter')->formatBlock('Summary before generation', 'bg=blue;fg=white', true),
'',
sprintf("You are going to generate a \"<info>%s:%s</info>\" Doctrine2 entity", $bundle, $entity),
sprintf("using the \"<info>%s</info>\" format.", $format),
'',
));
}
private function parseFields($input)
{
if (is_array($input)) {
return $input;
}
$fields = array();
foreach (explode(' ', $input) as $value) {
$elements = explode(':', $value);
$name = $elements[0];
if (strlen($name)) {
$type = isset($elements[1]) ? $elements[1] : 'string';
preg_match_all('/(.*)\((.*)\)/', $type, $matches);
$type = isset($matches[1][0]) ? $matches[1][0] : $type;
$length = isset($matches[2][0]) ? $matches[2][0] : null;
$fields[$name] = array('fieldName' => $name, 'type' => $type, 'length' => $length);
}
}
return $fields;
}
private function addFields(InputInterface $input, OutputInterface $output, DialogHelper $dialog)
{
$fields = $this->parseFields($input->getOption('fields'));
$output->writeln(array(
'',
'Instead of starting with a blank entity, you can add some fields now.',
'Note that the primary key will be added automatically (named <comment>id</comment>).',
'',
));
$output->write('<info>Available types:</info> ');
$types = array_keys(Type::getTypesMap());
$count = 20;
foreach ($types as $i => $type) {
if ($count > 50) {
$count = 0;
$output->writeln('');
}
$count += strlen($type);
$output->write(sprintf('<comment>%s</comment>', $type));
if (count($types) != $i + 1) {
$output->write(', ');
} else {
$output->write('.');
}
}
$output->writeln('');
$fieldValidator = function ($type) use ($types) {
// FIXME: take into account user-defined field types
if (!in_array($type, $types)) {
throw new \InvalidArgumentException(sprintf('Invalid type "%s".', $type));
}
return $type;
};
$lengthValidator = function ($length) {
if (!$length) {
return $length;
}
$result = filter_var($length, FILTER_VALIDATE_INT, array(
'options' => array('min_range' => 1)
));
if (false === $result) {
throw new \InvalidArgumentException(sprintf('Invalid length "%s".', $length));
}
return $length;
};
while (true) {
$output->writeln('');
$self = $this;
$name = $dialog->askAndValidate($output, $dialog->getQuestion('New field name (press <return> to stop adding fields)', null), function ($name) use ($fields, $self) {
if (isset($fields[$name]) || 'id' == $name) {
throw new \InvalidArgumentException(sprintf('Field "%s" is already defined.', $name));
}
// check reserved words
if ($self->getGenerator()->isReservedKeyword($name)){
throw new \InvalidArgumentException(sprintf('Name "%s" is a reserved word.', $name));
}
return $name;
});
if (!$name) {
break;
}
$defaultType = 'string';
if (substr($name, -3) == '_at') {
$defaultType = 'datetime';
} elseif (substr($name, -3) == '_id') {
$defaultType = 'integer';
}
$type = $dialog->askAndValidate($output, $dialog->getQuestion('Field type', $defaultType), $fieldValidator, false, $defaultType);
$data = array('fieldName' => $name, 'type' => $type);
if ($type == 'string') {
$data['length'] = $dialog->askAndValidate($output, $dialog->getQuestion('Field length', 255), $lengthValidator, false, 255);
}
$fields[$name] = $data;
}
return $fields;
}
public function getGenerator()
{
if (null === $this->generator) {
$this->generator = new DoctrineEntityGenerator($this->getContainer()->get('filesystem'), $this->getContainer()->get('doctrine'));
}
return $this->generator;
}
public function setGenerator(DoctrineEntityGenerator $generator)
{
$this->generator = $generator;
}
protected function getDialogHelper()
{
$dialog = $this->getHelperSet()->get('dialog');
if (!$dialog || get_class($dialog) !== 'Sensio\Bundle\GeneratorBundle\Command\Helper\DialogHelper') {
$this->getHelperSet()->set($dialog = new DialogHelper());
}
return $dialog;
}
}

View File

@@ -0,0 +1,71 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\GeneratorBundle\Command;
use Symfony\Component\Console\Input\InputArgument;
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 Sensio\Bundle\GeneratorBundle\Generator\DoctrineFormGenerator;
/**
* Generates a form type class for a given Doctrine entity.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Hugo Hamon <hugo.hamon@sensio.com>
*/
class GenerateDoctrineFormCommand extends GenerateDoctrineCommand
{
/**
* @see Command
*/
protected function configure()
{
$this
->setDefinition(array(
new InputArgument('entity', InputArgument::REQUIRED, 'The entity class name to initialize (shortcut notation)'),
))
->setDescription('Generates a form type class based on a Doctrine entity')
->setHelp(<<<EOT
The <info>doctrine:generate:form</info> command generates a form class based on a Doctrine entity.
<info>php app/console doctrine:generate:form AcmeBlogBundle:Post</info>
EOT
)
->setName('doctrine:generate:form')
->setAliases(array('generate:doctrine:form'))
;
}
/**
* @see Command
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$entity = Validators::validateEntityName($input->getArgument('entity'));
list($bundle, $entity) = $this->parseShortcutNotation($entity);
$entityClass = $this->getContainer()->get('doctrine')->getEntityNamespace($bundle).'\\'.$entity;
$metadata = $this->getEntityMetadata($entityClass);
$bundle = $this->getApplication()->getKernel()->getBundle($bundle);
$generator = new DoctrineFormGenerator($this->getContainer()->get('filesystem'), __DIR__.'/../Resources/skeleton/form');
$generator->generate($bundle, $entity, $metadata[0]);
$output->writeln(sprintf(
'The new %s.php class file has been created under %s.',
$generator->getClassName(),
$generator->getClassPath()
));
}
}

View File

@@ -0,0 +1,65 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\GeneratorBundle\Command\Helper;
use Symfony\Component\Console\Helper\DialogHelper as BaseDialogHelper;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Generates bundles.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class DialogHelper extends BaseDialogHelper
{
public function writeGeneratorSummary(OutputInterface $output, $errors)
{
if (!$errors) {
$this->writeSection($output, 'You can now start using the generated code!');
} else {
$this->writeSection($output, array(
'The command was not able to configure everything automatically.',
'You must do the following changes manually.',
), 'error');
$output->writeln($errors);
}
}
public function getRunner(OutputInterface $output, &$errors)
{
$runner = function ($err) use ($output, &$errors) {
if ($err) {
$output->writeln('<fg=red>FAILED</>');
$errors = array_merge($errors, $err);
} else {
$output->writeln('<info>OK</info>');
}
};
return $runner;
}
public function writeSection(OutputInterface $output, $text, $style = 'bg=blue;fg=white')
{
$output->writeln(array(
'',
$this->getHelperSet()->get('formatter')->formatBlock($text, $style, true),
'',
));
}
public function getQuestion($question, $default, $sep = ':')
{
return $default ? sprintf('<info>%s</info> [<comment>%s</comment>]%s ', $question, $default, $sep) : sprintf('<info>%s</info>%s ', $question, $sep);
}
}

View File

@@ -0,0 +1,160 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\GeneratorBundle\Command;
/**
* Validator functions.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Validators
{
static public function validateBundleNamespace($namespace)
{
if (!preg_match('/Bundle$/', $namespace)) {
throw new \InvalidArgumentException('The namespace must end with Bundle.');
}
$namespace = strtr($namespace, '/', '\\');
if (!preg_match('/^(?:[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*\\\?)+$/', $namespace)) {
throw new \InvalidArgumentException('The namespace contains invalid characters.');
}
// validate reserved keywords
$reserved = self::getReservedWords();
foreach (explode('\\', $namespace) as $word) {
if (in_array(strtolower($word), $reserved)) {
throw new \InvalidArgumentException(sprintf('The namespace cannot contain PHP reserved words ("%s").', $word));
}
}
// validate that the namespace is at least one level deep
if (false === strpos($namespace, '\\')) {
$msg = array();
$msg[] = sprintf('The namespace must contain a vendor namespace (e.g. "VendorName\%s" instead of simply "%s").', $namespace, $namespace);
$msg[] = 'If you\'ve specified a vendor namespace, did you forget to surround it with quotes (init:bundle "Acme\BlogBundle")?';
throw new \InvalidArgumentException(implode("\n\n", $msg));
}
return $namespace;
}
static public function validateBundleName($bundle)
{
if (!preg_match('/Bundle$/', $bundle)) {
throw new \InvalidArgumentException('The bundle name must end with Bundle.');
}
return $bundle;
}
static public function validateTargetDir($dir, $bundle, $namespace)
{
// add trailing / if necessary
return '/' === substr($dir, -1, 1) ? $dir : $dir.'/';
}
static public function validateFormat($format)
{
$format = strtolower($format);
if (!in_array($format, array('php', 'xml', 'yml', 'annotation'))) {
throw new \RuntimeException(sprintf('Format "%s" is not supported.', $format));
}
return $format;
}
static public function validateEntityName($entity)
{
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 $entity;
}
static public function getReservedWords()
{
return array(
'abstract',
'and',
'array',
'as',
'break',
'case',
'catch',
'class',
'clone',
'const',
'continue',
'declare',
'default',
'do',
'else',
'elseif',
'enddeclare',
'endfor',
'endforeach',
'endif',
'endswitch',
'endwhile',
'extends',
'final',
'for',
'foreach',
'function',
'global',
'goto',
'if',
'implements',
'interface',
'instanceof',
'namespace',
'new',
'or',
'private',
'protected',
'public',
'static',
'switch',
'throw',
'try',
'use',
'var',
'while',
'xor',
'__CLASS__',
'__DIR__',
'__FILE__',
'__LINE__',
'__FUNCTION__',
'__METHOD__',
'__NAMESPACE__',
'die',
'echo',
'empty',
'exit',
'eval',
'include',
'include_once',
'isset',
'list',
'require',
'require_once',
'return',
'print',
'unset',
);
}
}

View File

@@ -0,0 +1,76 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\GeneratorBundle\Generator;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\DependencyInjection\Container;
/**
* Generates a bundle.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class BundleGenerator extends Generator
{
private $filesystem;
private $skeletonDir;
public function __construct(Filesystem $filesystem, $skeletonDir)
{
$this->filesystem = $filesystem;
$this->skeletonDir = $skeletonDir;
}
public function generate($namespace, $bundle, $dir, $format, $structure)
{
$dir .= '/'.strtr($namespace, '\\', '/');
if (file_exists($dir)) {
throw new \RuntimeException(sprintf('Unable to generate the bundle as the target directory "%s" is not empty.', realpath($dir)));
}
$basename = substr($bundle, 0, -6);
$parameters = array(
'namespace' => $namespace,
'bundle' => $bundle,
'format' => $format,
'bundle_basename' => $basename,
'extension_alias' => Container::underscore($basename),
);
$this->renderFile($this->skeletonDir, 'Bundle.php', $dir.'/'.$bundle.'.php', $parameters);
$this->renderFile($this->skeletonDir, 'Extension.php', $dir.'/DependencyInjection/'.$basename.'Extension.php', $parameters);
$this->renderFile($this->skeletonDir, 'Configuration.php', $dir.'/DependencyInjection/Configuration.php', $parameters);
$this->renderFile($this->skeletonDir, 'DefaultController.php', $dir.'/Controller/DefaultController.php', $parameters);
$this->renderFile($this->skeletonDir, 'DefaultControllerTest.php', $dir.'/Tests/Controller/DefaultControllerTest.php', $parameters);
$this->renderFile($this->skeletonDir, 'index.html.twig', $dir.'/Resources/views/Default/index.html.twig', $parameters);
if ('xml' === $format || 'annotation' === $format) {
$this->renderFile($this->skeletonDir, 'services.xml', $dir.'/Resources/config/services.xml', $parameters);
} else {
$this->renderFile($this->skeletonDir, 'services.'.$format, $dir.'/Resources/config/services.'.$format, $parameters);
}
if ('annotation' != $format) {
$this->renderFile($this->skeletonDir, 'routing.'.$format, $dir.'/Resources/config/routing.'.$format, $parameters);
}
if ($structure) {
$this->filesystem->mkdir($dir.'/Resources/doc');
$this->filesystem->touch($dir.'/Resources/doc/index.rst');
$this->filesystem->mkdir($dir.'/Resources/translations');
$this->filesystem->copy($this->skeletonDir.'/messages.fr.xlf', $dir.'/Resources/translations/messages.fr.xlf');
$this->filesystem->mkdir($dir.'/Resources/public/css');
$this->filesystem->mkdir($dir.'/Resources/public/images');
$this->filesystem->mkdir($dir.'/Resources/public/js');
}
}
}

View File

@@ -0,0 +1,290 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\GeneratorBundle\Generator;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
/**
* Generates a CRUD controller.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class DoctrineCrudGenerator extends Generator
{
protected $filesystem;
protected $skeletonDir;
protected $routePrefix;
protected $routeNamePrefix;
protected $bundle;
protected $entity;
protected $metadata;
protected $format;
protected $actions;
/**
* Constructor.
*
* @param Filesystem $filesystem A Filesystem instance
* @param string $skeletonDir Path to the skeleton directory
*/
public function __construct(Filesystem $filesystem, $skeletonDir)
{
$this->filesystem = $filesystem;
$this->skeletonDir = $skeletonDir;
}
/**
* Generate the CRUD controller.
*
* @param BundleInterface $bundle A bundle object
* @param string $entity The entity relative class name
* @param ClassMetadataInfo $metadata The entity class metadata
* @param string $format The configuration format (xml, yaml, annotation)
* @param string $routePrefix The route name prefix
* @param array $needWriteActions Wether or not to generate write actions
*
* @throws \RuntimeException
*/
public function generate(BundleInterface $bundle, $entity, ClassMetadataInfo $metadata, $format, $routePrefix, $needWriteActions)
{
$this->routePrefix = $routePrefix;
$this->routeNamePrefix = str_replace('/', '_', $routePrefix);
$this->actions = $needWriteActions ? array('index', 'show', 'new', 'edit', 'delete') : array('index', 'show');
if (count($metadata->identifier) > 1) {
throw new \RuntimeException('The CRUD generator does not support entity classes with multiple primary keys.');
}
if (!in_array('id', $metadata->identifier)) {
throw new \RuntimeException('The CRUD generator expects the entity object has a primary key field named "id" with a getId() method.');
}
$this->entity = $entity;
$this->bundle = $bundle;
$this->metadata = $metadata;
$this->setFormat($format);
$this->generateControllerClass();
$dir = sprintf('%s/Resources/views/%s', $this->bundle->getPath(), str_replace('\\', '/', $this->entity));
if (!file_exists($dir)) {
$this->filesystem->mkdir($dir, 0777);
}
$this->generateIndexView($dir);
if (in_array('show', $this->actions)) {
$this->generateShowView($dir);
}
if (in_array('new', $this->actions)) {
$this->generateNewView($dir);
}
if (in_array('edit', $this->actions)) {
$this->generateEditView($dir);
}
$this->generateTestClass();
$this->generateConfiguration();
}
/**
* Sets the configuration format.
*
* @param string $format The configuration format
*/
private function setFormat($format)
{
switch ($format) {
case 'yml':
case 'xml':
case 'php':
case 'annotation':
$this->format = $format;
break;
default:
$this->format = 'yml';
break;
}
}
/**
* Generates the routing configuration.
*
*/
private function generateConfiguration()
{
if (!in_array($this->format, array('yml', 'xml', 'php'))) {
return;
}
$target = sprintf(
'%s/Resources/config/routing/%s.%s',
$this->bundle->getPath(),
strtolower(str_replace('\\', '_', $this->entity)),
$this->format
);
$this->renderFile($this->skeletonDir, 'config/routing.'.$this->format, $target, array(
'actions' => $this->actions,
'route_prefix' => $this->routePrefix,
'route_name_prefix' => $this->routeNamePrefix,
'bundle' => $this->bundle->getName(),
'entity' => $this->entity,
));
}
/**
* Generates the controller class only.
*
*/
private function generateControllerClass()
{
$dir = $this->bundle->getPath();
$parts = explode('\\', $this->entity);
$entityClass = array_pop($parts);
$entityNamespace = implode('\\', $parts);
$target = sprintf(
'%s/Controller/%s/%sController.php',
$dir,
str_replace('\\', '/', $entityNamespace),
$entityClass
);
if (file_exists($target)) {
throw new \RuntimeException('Unable to generate the controller as it already exists.');
}
$this->renderFile($this->skeletonDir, 'controller.php', $target, array(
'actions' => $this->actions,
'route_prefix' => $this->routePrefix,
'route_name_prefix' => $this->routeNamePrefix,
'dir' => $this->skeletonDir,
'bundle' => $this->bundle->getName(),
'entity' => $this->entity,
'entity_class' => $entityClass,
'namespace' => $this->bundle->getNamespace(),
'entity_namespace' => $entityNamespace,
'format' => $this->format,
));
}
/**
* Generates the functional test class only.
*
*/
private function generateTestClass()
{
$parts = explode('\\', $this->entity);
$entityClass = array_pop($parts);
$entityNamespace = implode('\\', $parts);
$dir = $this->bundle->getPath() .'/Tests/Controller';
$target = $dir .'/'. str_replace('\\', '/', $entityNamespace).'/'. $entityClass .'ControllerTest.php';
$this->renderFile($this->skeletonDir, 'tests/test.php', $target, array(
'route_prefix' => $this->routePrefix,
'route_name_prefix' => $this->routeNamePrefix,
'entity' => $this->entity,
'entity_class' => $entityClass,
'namespace' => $this->bundle->getNamespace(),
'entity_namespace' => $entityNamespace,
'actions' => $this->actions,
'dir' => $this->skeletonDir,
));
}
/**
* Generates the index.html.twig template in the final bundle.
*
* @param string $dir The path to the folder that hosts templates in the bundle
*/
private function generateIndexView($dir)
{
$this->renderFile($this->skeletonDir, 'views/index.html.twig', $dir.'/index.html.twig', array(
'dir' => $this->skeletonDir,
'entity' => $this->entity,
'fields' => $this->metadata->fieldMappings,
'actions' => $this->actions,
'record_actions' => $this->getRecordActions(),
'route_prefix' => $this->routePrefix,
'route_name_prefix' => $this->routeNamePrefix,
));
}
/**
* Generates the show.html.twig template in the final bundle.
*
* @param string $dir The path to the folder that hosts templates in the bundle
*/
private function generateShowView($dir)
{
$this->renderFile($this->skeletonDir, 'views/show.html.twig', $dir.'/show.html.twig', array(
'dir' => $this->skeletonDir,
'entity' => $this->entity,
'fields' => $this->metadata->fieldMappings,
'actions' => $this->actions,
'route_prefix' => $this->routePrefix,
'route_name_prefix' => $this->routeNamePrefix,
));
}
/**
* Generates the new.html.twig template in the final bundle.
*
* @param string $dir The path to the folder that hosts templates in the bundle
*/
private function generateNewView($dir)
{
$this->renderFile($this->skeletonDir, 'views/new.html.twig', $dir.'/new.html.twig', array(
'dir' => $this->skeletonDir,
'route_prefix' => $this->routePrefix,
'route_name_prefix' => $this->routeNamePrefix,
'entity' => $this->entity,
'actions' => $this->actions,
));
}
/**
* Generates the edit.html.twig template in the final bundle.
*
* @param string $dir The path to the folder that hosts templates in the bundle
*/
private function generateEditView($dir)
{
$this->renderFile($this->skeletonDir, 'views/edit.html.twig', $dir.'/edit.html.twig', array(
'dir' => $this->skeletonDir,
'route_prefix' => $this->routePrefix,
'route_name_prefix' => $this->routeNamePrefix,
'entity' => $this->entity,
'actions' => $this->actions,
));
}
/**
* Returns an array of record actions to generate (edit, show).
*
* @return array
*/
private function getRecordActions()
{
return array_filter($this->actions, function($item) {
return in_array($item, array('show', 'edit'));
});
}
}

View File

@@ -0,0 +1,119 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\GeneratorBundle\Generator;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
use Symfony\Bridge\Doctrine\RegistryInterface;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\ORM\Tools\EntityGenerator;
use Doctrine\ORM\Tools\EntityRepositoryGenerator;
use Doctrine\ORM\Tools\Export\ClassMetadataExporter;
/**
* Generates a form class based on a Doctrine entity.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jonathan H. Wage <jonwage@gmail.com>
*/
class DoctrineEntityGenerator extends Generator
{
private $filesystem;
private $registry;
public function __construct(Filesystem $filesystem, RegistryInterface $registry)
{
$this->filesystem = $filesystem;
$this->registry = $registry;
}
public function generate(BundleInterface $bundle, $entity, $format, array $fields, $withRepository)
{
// configure the bundle (needed if the bundle does not contain any Entities yet)
$config = $this->registry->getEntityManager(null)->getConfiguration();
$config->setEntityNamespaces(array_merge(
array($bundle->getName() => $bundle->getNamespace().'\\Entity'),
$config->getEntityNamespaces()
));
$entityClass = $this->registry->getEntityNamespace($bundle->getName()).'\\'.$entity;
$entityPath = $bundle->getPath().'/Entity/'.str_replace('\\', '/', $entity).'.php';
if (file_exists($entityPath)) {
throw new \RuntimeException(sprintf('Entity "%s" already exists.', $entityClass));
}
$class = new ClassMetadataInfo($entityClass);
if ($withRepository) {
$class->customRepositoryClassName = $entityClass.'Repository';
}
$class->mapField(array('fieldName' => 'id', 'type' => 'integer', 'id' => true));
$class->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_AUTO);
foreach ($fields as $field) {
$class->mapField($field);
}
$entityGenerator = $this->getEntityGenerator();
if ('annotation' === $format) {
$entityGenerator->setGenerateAnnotations(true);
$entityCode = $entityGenerator->generateEntityClass($class);
$mappingPath = $mappingCode = false;
} else {
$cme = new ClassMetadataExporter();
$exporter = $cme->getExporter('yml' == $format ? 'yaml' : $format);
$mappingPath = $bundle->getPath().'/Resources/config/doctrine/'.str_replace('\\', '.', $entity).'.orm.'.$format;
if (file_exists($mappingPath)) {
throw new \RuntimeException(sprintf('Cannot generate entity when mapping "%s" already exists.', $mappingPath));
}
$mappingCode = $exporter->exportClassMetadata($class);
$entityGenerator->setGenerateAnnotations(false);
$entityCode = $entityGenerator->generateEntityClass($class);
}
$this->filesystem->mkdir(dirname($entityPath));
file_put_contents($entityPath, $entityCode);
if ($mappingPath) {
$this->filesystem->mkdir(dirname($mappingPath));
file_put_contents($mappingPath, $mappingCode);
}
if ($withRepository) {
$path = $bundle->getPath().str_repeat('/..', substr_count(get_class($bundle), '\\'));
$this->getRepositoryGenerator()->writeEntityRepositoryClass($class->customRepositoryClassName, $path);
}
}
public function isReservedKeyword($keyword)
{
return $this->registry->getConnection()->getDatabasePlatform()->getReservedKeywordsList()->isKeyword($keyword);
}
protected function getEntityGenerator()
{
$entityGenerator = new EntityGenerator();
$entityGenerator->setGenerateAnnotations(false);
$entityGenerator->setGenerateStubMethods(true);
$entityGenerator->setRegenerateEntityIfExists(false);
$entityGenerator->setUpdateEntityIfExists(true);
$entityGenerator->setNumSpaces(4);
$entityGenerator->setAnnotationPrefix('ORM\\');
return $entityGenerator;
}
protected function getRepositoryGenerator()
{
return new EntityRepositoryGenerator();
}
}

View File

@@ -0,0 +1,109 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\GeneratorBundle\Generator;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
/**
* Generates a form class based on a Doctrine entity.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Hugo Hamon <hugo.hamon@sensio.com>
*/
class DoctrineFormGenerator extends Generator
{
private $filesystem;
private $skeletonDir;
private $className;
private $classPath;
public function __construct(Filesystem $filesystem, $skeletonDir)
{
$this->filesystem = $filesystem;
$this->skeletonDir = $skeletonDir;
}
public function getClassName()
{
return $this->className;
}
public function getClassPath()
{
return $this->classPath;
}
/**
* Generates the entity form class if it does not exist.
*
* @param BundleInterface $bundle The bundle in which to create the class
* @param string $entity The entity relative class name
* @param ClassMetadataInfo $metadata The entity metadata class
*/
public function generate(BundleInterface $bundle, $entity, ClassMetadataInfo $metadata)
{
$parts = explode('\\', $entity);
$entityClass = array_pop($parts);
$this->className = $entityClass.'Type';
$dirPath = $bundle->getPath().'/Form';
$this->classPath = $dirPath.'/'.str_replace('\\', '/', $entity).'Type.php';
if (file_exists($this->classPath)) {
throw new \RuntimeException(sprintf('Unable to generate the %s form class as it already exists under the %s file', $this->className, $this->classPath));
}
if (count($metadata->identifier) > 1) {
throw new \RuntimeException('The form generator does not support entity classes with multiple primary keys.');
}
$parts = explode('\\', $entity);
array_pop($parts);
$this->renderFile($this->skeletonDir, 'FormType.php', $this->classPath, array(
'dir' => $this->skeletonDir,
'fields' => $this->getFieldsFromMetadata($metadata),
'namespace' => $bundle->getNamespace(),
'entity_namespace' => implode('\\', $parts),
'entity_class' => $entityClass,
'form_class' => $this->className,
'form_type_name' => strtolower(str_replace('\\', '_', $bundle->getNamespace()).($parts ? '_' : '').implode('_', $parts).'_'.$this->className),
));
}
/**
* Returns an array of fields. Fields can be both column fields and
* association fields.
*
* @param ClassMetadataInfo $metadata
* @return array $fields
*/
private function getFieldsFromMetadata(ClassMetadataInfo $metadata)
{
$fields = (array) $metadata->fieldNames;
// Remove the primary key field if it's not managed manually
if (!$metadata->isIdentifierNatural()) {
$fields = array_diff($fields, $metadata->identifier);
}
foreach ($metadata->associationMappings as $fieldName => $relation) {
if ($relation['type'] !== ClassMetadataInfo::ONE_TO_MANY) {
$fields[] = $fieldName;
}
}
return $fields;
}
}

View File

@@ -0,0 +1,41 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\GeneratorBundle\Generator;
/**
* Generator is the base class for all generators.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Generator
{
protected function render($skeletonDir, $template, $parameters)
{
$twig = new \Twig_Environment(new \Twig_Loader_Filesystem($skeletonDir), array(
'debug' => true,
'cache' => false,
'strict_variables' => true,
'autoescape' => false,
));
return $twig->render($template, $parameters);
}
protected function renderFile($skeletonDir, $template, $target, $parameters)
{
if (!is_dir(dirname($target))) {
mkdir(dirname($target), 0777, true);
}
return file_put_contents($target, $this->render($skeletonDir, $template, $parameters));
}
}

View File

@@ -0,0 +1,19 @@
Copyright (c) 2011 Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,105 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\GeneratorBundle\Manipulator;
use Symfony\Component\HttpKernel\KernelInterface;
/**
* Changes the PHP code of a Kernel.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class KernelManipulator extends Manipulator
{
private $kernel;
private $reflected;
/**
* Constructor.
*
* @param KernelInterface $kernel A KernelInterface instance
*/
public function __construct(KernelInterface $kernel)
{
$this->kernel = $kernel;
$this->reflected = new \ReflectionObject($kernel);
}
/**
* Adds a bundle at the end of the existing ones.
*
* @param string $bundle The bundle class name
*
* @return Boolean true if it worked, false otherwise
*
* @throws \RuntimeException If bundle is already defined
*/
public function addBundle($bundle)
{
if (!$this->reflected->getFilename()) {
return false;
}
$src = file($this->reflected->getFilename());
$method = $this->reflected->getMethod('registerBundles');
$lines = array_slice($src, $method->getStartLine() - 1, $method->getEndLine() - $method->getStartLine() + 1);
// Don't add same bundle twice
if (false !== strpos(implode('', $lines), $bundle)) {
throw new \RuntimeException(sprintf('Bundle "%s" is already defined in "AppKernel::registerBundles()".', $bundle));
}
$this->setCode(token_get_all('<?php '.implode('', $lines)), $method->getStartLine());
while ($token = $this->next()) {
// $bundles
if (T_VARIABLE !== $token[0] || '$bundles' !== $token[1]) {
continue;
}
// =
$this->next();
// array
$token = $this->next();
if (T_ARRAY !== $token[0]) {
return false;
}
// add the bundle at the end of the array
while ($token = $this->next()) {
// look for );
if (')' !== $this->value($token)) {
continue;
}
if (';' !== $this->value($this->peek())) {
continue;
}
// ;
$this->next();
$lines = array_merge(
array_slice($src, 0, $this->line - 2),
// Appends a separator comma to the current last position of the array
array(rtrim(rtrim($src[$this->line - 2]), ',') . ",\n"),
array(sprintf(" new %s(),\n", $bundle)),
array_slice($src, $this->line - 1)
);
file_put_contents($this->reflected->getFilename(), implode('', $lines));
return true;
}
}
}
}

View File

@@ -0,0 +1,85 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\GeneratorBundle\Manipulator;
/**
* Changes the PHP code of a Kernel.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Manipulator
{
protected $tokens;
protected $line;
/**
* Sets the code to manipulate.
*
* @param array $tokens An array of PHP tokens
* @param integer $line The start line of the code
*/
protected function setCode(array $tokens, $line = 0)
{
$this->tokens = $tokens;
$this->line = $line;
}
/**
* Gets the next token.
*
* @param mixed A PHP token
*/
protected function next()
{
while ($token = array_shift($this->tokens)) {
$this->line += substr_count($this->value($token), "\n");
if (is_array($token) && in_array($token[0], array(T_WHITESPACE, T_COMMENT, T_DOC_COMMENT))) {
continue;
}
return $token;
}
}
/**
* Peeks the next token.
*
* @param mixed A PHP token
*/
protected function peek($nb = 1)
{
$i = 0;
$tokens = $this->tokens;
while ($token = array_shift($tokens)) {
if (is_array($token) && in_array($token[0], array(T_WHITESPACE, T_COMMENT, T_DOC_COMMENT))) {
continue;
}
++$i;
if ($i == $nb) {
return $token;
}
}
}
/**
* Gets the value of a token.
*
* @param string The token value
*/
protected function value($token)
{
return is_array($token) ? $token[1] : $token;
}
}

View File

@@ -0,0 +1,75 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\GeneratorBundle\Manipulator;
/**
* Changes the PHP code of a YAML routing file.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
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 $format
* @param string $prefix
* @param string $path
*
* @return Boolean true if it worked, false otherwise
*
* @throws \RuntimeException If bundle is already imported
*/
public function addResource($bundle, $format, $prefix = '/', $path = 'routing')
{
$current = '';
if (file_exists($this->file)) {
$current = file_get_contents($this->file);
// Don't add same bundle twice
if (false !== strpos($current, $bundle)) {
throw new \RuntimeException(sprintf('Bundle "%s" is already imported.', $bundle));
}
} elseif (!is_dir($dir = dirname($this->file))) {
mkdir($dir, 0777, true);
}
$code = sprintf("%s:\n", $bundle.('/' !== $prefix ? '_'.str_replace('/', '_', substr($prefix, 1)) : ''));
if ('annotation' == $format) {
$code .= sprintf(" resource: \"@%s/Controller/\"\n type: annotation\n", $bundle);
} else {
$code .= sprintf(" resource: \"@%s/Resources/config/%s.%s\"\n", $bundle, $path, $format);
}
$code .= sprintf(" prefix: %s\n", $prefix);
$code .= "\n";
$code .= $current;
if (false === file_put_contents($this->file, $code)) {
return false;
}
return true;
}
}

View File

@@ -0,0 +1,10 @@
SensioGeneratorBundle
=====================
The `SensioGeneratorBundle` extends the default Symfony2 command line
interface by providing new interactive and intuitive commands for generating
code skeletons like bundles, form classes, or CRUD controllers based on a
Doctrine 2 schema.
More information in the official
[documentation](http://symfony.com/doc/current/bundles/SensioGeneratorBundle/index.html).

View File

@@ -0,0 +1,68 @@
Generating a New Bundle Skeleton
================================
Usage
-----
The ``generate:bundle`` generates a new bundle structure and automatically
activates it in the application.
By default the command is run in the interactive mode and asks questions to
determine the bundle name, location, configuration format and default
structure:
.. code-block:: bash
php app/console generate:bundle
To deactivate the interactive mode, use the `--no-interaction` option but don't
forget to pass all needed options:
.. code-block:: bash
php app/console generate:bundle --namespace=Acme/Bundle/BlogBundle --no-interaction
Available Options
-----------------
* ``--namespace``: The namespace of the bundle to create. The namespace should
begin with a "vendor" name like your company name, your project name, or
your client name, followed by one or more optional category sub-namespaces,
and it should end with the bundle name itself (which must have Bundle as a
suffix):
.. code-block:: bash
php app/console generate:bundle --namespace=Acme/Bundle/BlogBundle
* ``--bundle-name``: The optional bundle name. It must be a string ending with
the ``Bundle`` suffix:
.. code-block:: bash
php app/console generate:bundle --bundle-name=AcmeBlogBundle
* ``--dir``: The directory in which to store the bundle. By convention, the
command detects and uses the applications's ``src/`` folder:
.. code-block:: bash
php app/console generate:bundle --dir=/var/www/myproject/src
* ``--format``: (**annotation**) [values: yml, xml, php or annotation]
Determine the format to use for the generated configuration files like
routing. By default, the command uses the ``annotation`` format. Choosing
the ``annotation`` format expects the ``SensioFrameworkExtraBundle`` is
already installed:
.. code-block:: bash
php app/console generate:bundle --format=annotation
* ``--structure``: (**no**) [values: yes|no] Whether or not to generate a
complete default directory structure including empty public folders for
documentation, web assets and translations dictionaries:
.. code-block:: bash
php app/console generate:bundle --structure=yes

View File

@@ -0,0 +1,65 @@
Generating a CRUD Controller Based on a Doctrine Entity
=======================================================
Usage
-----
The ``generate:doctrine:crud`` generates a basic controller for a given entity
located in a given bundle. This controller allows to perform the five basic
operations on a model.
* Listing all records,
* Showing one given record identified by its primary key,
* Creating a new record,
* Editing an existing record,
* Deleting an existing record.
By default the command is run in the interactive mode and asks questions to
determine the entity name, the route prefix or whether or not to generate write
actions:
.. code-block:: bash
php app/console generate:doctrine:crud
To deactivate the interactive mode, use the `--no-interaction` option but don't
forget to pass all needed options:
.. code-block:: bash
php app/console generate:doctrine:crud --entity=AcmeBlogBundle:Post --format=annotation --with-write --no-interaction
Available Options
-----------------
* ``--entity``: The entity name given as a shortcut notation containing the
bundle name in which the entity is located and the name of the entity. For
example: ``AcmeBlogBundle:Post``:
.. code-block:: bash
php app/console generate:doctrine:crud --entity=AcmeBlogBundle:Post
* ``--route-prefix``: The prefix to use for each route that identifies an
action:
.. code-block:: bash
php app/console generate:doctrine:crud --route-prefix=acme_post
* ``--with-write``: (**no**) [values: yes|no] Whether or not to generate the
`new`, `create`, `edit`, `update` and `delete` actions:
.. code-block:: bash
php app/console generate:doctrine:crud --with-write
* ``--format``: (**annotation**) [values: yml, xml, php or annotation]
Determine the format to use for the generated configuration files like
routing. By default, the command uses the ``annotation`` format. Choosing
the ``annotation`` format expects the ``SensioFrameworkExtraBundle`` is
already installed:
.. code-block:: bash
php app/console generate:doctrine:crud --format=annotation

View File

@@ -0,0 +1,55 @@
Generating a New Doctrine Entity Stub
=====================================
Usage
-----
The ``generate:doctrine:entity`` command generates a new Doctrine entity stub
including the mapping definition and the class properties, getters and setters.
By default the command is run in the interactive mode and asks questions to
determine the bundle name, location, configuration format and default
structure:
.. code-block:: bash
php app/console generate:doctrine:entity
The command can be run in a non interactive mode by using the
``--non-interaction`` option without forgetting all needed options:
.. code-block:: bash
php app/console generate:doctrine:entity --non-interaction --entity=AcmeBlogBundle:Post --fields="title:string(100) body:text" --format=xml
Available Options
-----------------
* ``--entity``: The entity name given as a shortcut notation containing the
bundle name in which the entity is located and the name of the entity. For
example: ``AcmeBlogBundle:Post``:
.. code-block:: bash
php app/console generate:doctrine:entity --entity=AcmeBlogBundle:Post
* ``--fields``: The list of fields to generate in the entity class:
.. code-block:: bash
php app/console generate:doctrine:entity --fields="title:string(100) body:text"
* ``--format``: (**annotation**) [values: yml, xml, php or annotation] This
option determines the format to use for the generated configuration files
like routing. By default, the command uses the ``annotation`` format:
.. code-block:: bash
php app/console generate:doctrine:entity --format=annotation
* ``--with-repository``: This option tells whether or not to generate the
related Doctrine `EntityRepository` class:
.. code-block:: bash
php app/console generate:doctrine:entity --with-repository

View File

@@ -0,0 +1,23 @@
Generating a New Form Type Class Based on a Doctrine Entity
===========================================================
Usage
-----
The ``generate:doctrine:form`` generates a basic form type class by using the
metadata mapping of a given entity class:
.. code-block:: bash
php app/console generate:doctrine:form AcmeBlogBundle:Post
Required Arguments
------------------
* ``entity``: The entity name given as a shortcut notation containing the
bundle name in which the entity is located and the name of the entity. For
example: ``AcmeBlogBundle:Post``:
.. code-block:: bash
php app/console generate:doctrine:form AcmeBlogBundle:Post

View File

@@ -0,0 +1,42 @@
SensioGeneratorBundle
==========================
The ``SensioGeneratorBundle`` extends the default Symfony2 command line
interface by providing new interactive and intuitive commands for generating
code skeletons like bundles, form classes or CRUD controllers based on a
Doctrine 2 schema.
Installation
------------
`Download`_ the bundle and put it under the ``Sensio\\Bundle\\`` namespace.
Then, like for any other bundle, include it in your Kernel class::
public function registerBundles()
{
$bundles = array(
...
new Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle(),
);
...
}
List of Available Commands
--------------------------
The ``SensioGeneratorBundle`` comes with four new commands that can be run in
interactive mode or not. The interactive mode asks you some questions to
configure the command parameters to generate the definitive code. The list of
new commands are listed below:
.. toctree::
:maxdepth: 1
commands/generate_bundle
commands/generate_doctrine_crud
commands/generate_doctrine_entity
commands/generate_doctrine_form
.. _Download: http://github.com/sensio/SensioGeneratorBundle

View File

@@ -0,0 +1,9 @@
<?php
namespace {{ namespace }};
use Symfony\Component\HttpKernel\Bundle\Bundle;
class {{ bundle }} extends Bundle
{
}

View File

@@ -0,0 +1,29 @@
<?php
namespace {{ namespace }}\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
/**
* This is the class that validates and merges configuration from your app/config files
*
* To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html#cookbook-bundles-extension-config-class}
*/
class Configuration implements ConfigurationInterface
{
/**
* {@inheritDoc}
*/
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('{{ extension_alias }}');
// Here you should define the parameters that are allowed to
// configure your bundle. See the documentation linked above for
// more information on that topic.
return $treeBuilder;
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace {{ namespace }}\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
{% if 'annotation' == format -%}
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
{% endif %}
class DefaultController extends Controller
{
{% if 'annotation' == format -%}
/**
* @Route("/hello/{name}")
* @Template()
*/
{% endif -%}
public function indexAction($name)
{
{% if 'annotation' != format -%}
return $this->render('{{ bundle }}:Default:index.html.twig', array('name' => $name));
{%- else -%}
return array('name' => $name);
{%- endif %}
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace {{ namespace }}\Tests\Controller;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class DefaultControllerTest extends WebTestCase
{
public function testIndex()
{
$client = static::createClient();
$crawler = $client->request('GET', '/hello/Fabien');
$this->assertTrue($crawler->filter('html:contains("Hello Fabien")')->count() > 0);
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace {{ namespace }}\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
/**
* This is the class that loads and manages your bundle configuration
*
* To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html}
*/
class {{ bundle_basename }}Extension extends Extension
{
/**
* {@inheritDoc}
*/
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
{% if format == 'yml' -%}
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yml');
{%- elseif format == 'xml' or format == 'annotation' -%}
$loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.xml');
{%- elseif format == 'php' -%}
$loader = new Loader\PhpFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.php');
{%- endif %}
}
}

View File

@@ -0,0 +1 @@
Hello {% raw %}{{ name }}{% endraw %}!

View File

@@ -0,0 +1,11 @@
<?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" datatype="plaintext" original="file.ext">
<body>
<trans-unit id="1">
<source>Symfony2 is great</source>
<target>J'aime Symfony2</target>
</trans-unit>
</body>
</file>
</xliff>

View File

@@ -0,0 +1,11 @@
<?php
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
$collection = new RouteCollection();
$collection->add('{{ bundle }}_homepage', new Route('/hello/{name}', array(
'_controller' => '{{ bundle }}:Default:index',
)));
return $collection;

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
<route id="{{ bundle }}_homepage" pattern="/hello/{name}">
<default key="_controller">{{ bundle }}:Default:index</default>
</route>
</routes>

View File

@@ -0,0 +1,3 @@
{{ bundle }}_homepage:
pattern: /hello/{name}
defaults: { _controller: {{ bundle }}:Default:index }

View File

@@ -0,0 +1,21 @@
<?php
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Parameter;
/*
$container->setDefinition(
'{{ extension_alias }}.example',
new Definition(
'{{ namespace }}\Example',
array(
new Reference('service_id'),
"plain_value",
new Parameter('parameter_name'),
)
)
);
*/

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<!--
<parameters>
<parameter key="{{ extension_alias }}.example.class">{{ namespace }}\Example</parameter>
</parameters>
<services>
<service id="{{ extension_alias }}.example" class="%{{ extension_alias }}.example.class%">
<argument type="service" id="service_id" />
<argument>plain_value</argument>
<argument>%parameter_name%</argument>
</service>
</services>
-->
</container>

View File

@@ -0,0 +1,7 @@
parameters:
# {{ extension_alias }}.example.class: {{ namespace }}\Example
services:
# {{ extension_alias }}.example:
# class: %{{ extension_alias }}.example.class%
# arguments: [@service_id, "plain_value", %parameter%]

View File

@@ -0,0 +1,42 @@
/**
* Creates a new {{ entity }} entity.
*
{% if 'annotation' == format %}
* @Route("/create", name="{{ route_name_prefix }}_create")
* @Method("post")
* @Template("{{ bundle }}:{{ entity }}:new.html.twig")
{% endif %}
*/
public function createAction()
{
$entity = new {{ entity_class }}();
$request = $this->getRequest();
$form = $this->createForm(new {{ entity_class }}Type(), $entity);
$form->bindRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($entity);
$em->flush();
{% if 'show' in actions -%}
return $this->redirect($this->generateUrl('{{ route_name_prefix }}_show', array('id' => $entity->getId())));
{%- else -%}
return $this->redirect($this->generateUrl('{{ route_name_prefix }}'));
{%- endif %}
}
{% if 'annotation' == format %}
return array(
'entity' => $entity,
'form' => $form->createView(),
);
{% else %}
return $this->render('{{ bundle }}:{{ entity|replace({'\\': '/'}) }}:new.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
));
{% endif %}
}

View File

@@ -0,0 +1,38 @@
/**
* Deletes a {{ entity }} entity.
*
{% if 'annotation' == format %}
* @Route("/{id}/delete", name="{{ route_name_prefix }}_delete")
* @Method("post")
{% endif %}
*/
public function deleteAction($id)
{
$form = $this->createDeleteForm($id);
$request = $this->getRequest();
$form->bindRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('{{ bundle }}:{{ entity }}')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find {{ entity }} entity.');
}
$em->remove($entity);
$em->flush();
}
return $this->redirect($this->generateUrl('{{ route_name_prefix }}'));
}
private function createDeleteForm($id)
{
return $this->createFormBuilder(array('id' => $id))
->add('id', 'hidden')
->getForm()
;
}

View File

@@ -0,0 +1,36 @@
/**
* Displays a form to edit an existing {{ entity }} entity.
*
{% if 'annotation' == format %}
* @Route("/{id}/edit", name="{{ route_name_prefix }}_edit")
* @Template()
{% endif %}
*/
public function editAction($id)
{
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('{{ bundle }}:{{ entity }}')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find {{ entity }} entity.');
}
$editForm = $this->createForm(new {{ entity_class }}Type(), $entity);
$deleteForm = $this->createDeleteForm($id);
{% if 'annotation' == format %}
return array(
'entity' => $entity,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
);
{% else %}
return $this->render('{{ bundle }}:{{ entity|replace({'\\': '/'}) }}:edit.html.twig', array(
'entity' => $entity,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
));
{% endif %}
}

View File

@@ -0,0 +1,25 @@
/**
* Lists all {{ entity }} entities.
*
{% if 'annotation' == format %}
* @Route("/", name="{{ route_name_prefix }}")
* @Template()
{% endif %}
*/
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository('{{ bundle }}:{{ entity }}')->findAll();
{% if 'annotation' == format %}
return array(
'entities' => $entities,
);
{% else %}
return $this->render('{{ bundle }}:{{ entity|replace({'\\': '/'}) }}:index.html.twig', array(
'entities' => $entities,
));
{% endif %}
}

View File

@@ -0,0 +1,26 @@
/**
* Displays a form to create a new {{ entity }} entity.
*
{% if 'annotation' == format %}
* @Route("/new", name="{{ route_name_prefix }}_new")
* @Template()
{% endif %}
*/
public function newAction()
{
$entity = new {{ entity_class }}();
$form = $this->createForm(new {{ entity_class }}Type(), $entity);
{% if 'annotation' == format %}
return array(
'entity' => $entity,
'form' => $form->createView(),
);
{% else %}
return $this->render('{{ bundle }}:{{ entity|replace({'\\': '/'}) }}:new.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
));
{% endif %}
}

View File

@@ -0,0 +1,39 @@
/**
* Finds and displays a {{ entity }} entity.
*
{% if 'annotation' == format %}
* @Route("/{id}/show", name="{{ route_name_prefix }}_show")
* @Template()
{% endif %}
*/
public function showAction($id)
{
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('{{ bundle }}:{{ entity }}')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find {{ entity }} entity.');
}
{% if 'delete' in actions %}
$deleteForm = $this->createDeleteForm($id);
{% endif %}
{% if 'annotation' == format %}
return array(
'entity' => $entity,
{% if 'delete' in actions %}
'delete_form' => $deleteForm->createView(),
{% endif %}
);
{% else %}
return $this->render('{{ bundle }}:{{ entity|replace({'\\': '/'}) }}:show.html.twig', array(
'entity' => $entity,
{% if 'delete' in actions %}
'delete_form' => $deleteForm->createView(),
{%- endif %}
));
{% endif %}
}

View File

@@ -0,0 +1,48 @@
/**
* Edits an existing {{ entity }} entity.
*
{% if 'annotation' == format %}
* @Route("/{id}/update", name="{{ route_name_prefix }}_update")
* @Method("post")
* @Template("{{ bundle }}:{{ entity }}:edit.html.twig")
{% endif %}
*/
public function updateAction($id)
{
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('{{ bundle }}:{{ entity }}')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find {{ entity }} entity.');
}
$editForm = $this->createForm(new {{ entity_class }}Type(), $entity);
$deleteForm = $this->createDeleteForm($id);
$request = $this->getRequest();
$editForm->bindRequest($request);
if ($editForm->isValid()) {
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('{{ route_name_prefix }}_edit', array('id' => $id)));
}
{% if 'annotation' == format %}
return array(
'entity' => $entity,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
);
{% else %}
return $this->render('{{ bundle }}:{{ entity|replace({'\\': '/'}) }}:edit.html.twig', array(
'entity' => $entity,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
));
{% endif %}
}

View File

@@ -0,0 +1,52 @@
<?php
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
$collection = new RouteCollection();
{% if 'index' in actions %}
$collection->add('{{ route_name_prefix }}', new Route('/', array(
'_controller' => '{{ bundle }}:{{ entity }}:index',
)));
{% endif %}
{% if 'show' in actions %}
$collection->add('{{ route_name_prefix }}_show', new Route('/{id}/show', array(
'_controller' => '{{ bundle }}:{{ entity }}:show',
)));
{% endif %}
{% if 'new' in actions %}
$collection->add('{{ route_name_prefix }}_new', new Route('/new', array(
'_controller' => '{{ bundle }}:{{ entity }}:new',
)));
$collection->add('{{ route_name_prefix }}_create', new Route(
'/create',
array('_controller' => '{{ bundle }}:{{ entity }}:create'),
array('_method' => 'post')
));
{% endif %}
{% if 'edit' in actions %}
$collection->add('{{ route_name_prefix }}_edit', new Route('/{id}/edit', array(
'_controller' => '{{ bundle }}:{{ entity }}:edit',
)));
$collection->add('{{ route_name_prefix }}_update', new Route(
'/{id}/update',
array('_controller' => '{{ bundle }}:{{ entity }}:update'),
array('_method' => 'post')
));
{% endif %}
{% if 'delete' in actions %}
$collection->add('{{ route_name_prefix }}_delete', new Route(
'/{id}/delete',
array('_controller' => '{{ bundle }}:{{ entity }}:delete'),
array('_method' => 'post')
));
{% endif %}
return $collection;

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
<route id="{{ route_name_prefix }}" pattern="/">
<default key="_controller">{{ bundle }}:{{ entity }}:index</default>
</route>
<route id="{{ route_name_prefix }}_show" pattern="/{id}/show">
<default key="_controller">{{ bundle }}:{{ entity }}:show</default>
</route>
{% if 'new' in actions %}
<route id="{{ route_name_prefix }}_new" pattern="/new">
<default key="_controller">{{ bundle }}:{{ entity }}:new</default>
</route>
<route id="{{ route_name_prefix }}_create" pattern="/create">
<default key="_controller">{{ bundle }}:{{ entity }}:create</default>
<requirement key="_method">post</requirement>
</route>
{% endif %}
{% if 'edit' in actions %}
<route id="{{ route_name_prefix }}_edit" pattern="/{id}/edit">
<default key="_controller">{{ bundle }}:{{ entity }}:edit</default>
</route>
<route id="{{ route_name_prefix }}_update" pattern="/{id}/update">
<default key="_controller">{{ bundle }}:{{ entity }}:update</default>
<requirement key="_method">post</requirement>
</route>
{% endif %}
{% if 'delete' in actions %}
<route id="{{ route_name_prefix }}_delete" pattern="/{id}/delete">
<default key="_controller">{{ bundle }}:{{ entity }}:delete</default>
<requirement key="_method">post</requirement>
</route>
{% endif %}
</routes>

View File

@@ -0,0 +1,40 @@
{% if 'index' in actions %}
{{ route_name_prefix }}:
pattern: /
defaults: { _controller: "{{ bundle }}:{{ entity }}:index" }
{% endif %}
{% if 'show' in actions %}
{{ route_name_prefix }}_show:
pattern: /{id}/show
defaults: { _controller: "{{ bundle }}:{{ entity }}:show" }
{% endif %}
{% if 'new' in actions %}
{{ route_name_prefix }}_new:
pattern: /new
defaults: { _controller: "{{ bundle }}:{{ entity }}:new" }
{{ route_name_prefix }}_create:
pattern: /create
defaults: { _controller: "{{ bundle }}:{{ entity }}:create" }
requirements: { _method: post }
{% endif %}
{% if 'edit' in actions %}
{{ route_name_prefix }}_edit:
pattern: /{id}/edit
defaults: { _controller: "{{ bundle }}:{{ entity }}:edit" }
{{ route_name_prefix }}_update:
pattern: /{id}/update
defaults: { _controller: "{{ bundle }}:{{ entity }}:update" }
requirements: { _method: post }
{% endif %}
{% if 'delete' in actions %}
{{ route_name_prefix }}_delete:
pattern: /{id}/delete
defaults: { _controller: "{{ bundle }}:{{ entity }}:delete" }
requirements: { _method: post }
{% endif %}

View File

@@ -0,0 +1,49 @@
<?php
namespace {{ namespace }}\Controller{{ entity_namespace ? '\\' ~ entity_namespace : '' }};
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
{% if 'annotation' == format -%}
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
{%- endif %}
use {{ namespace }}\Entity\{{ entity }};
{% if 'new' in actions or 'edit' in actions %}
use {{ namespace }}\Form\{{ entity }}Type;
{% endif %}
/**
* {{ entity }} controller.
*
{% if 'annotation' == format %}
* @Route("/{{ route_prefix }}")
{% endif %}
*/
class {{ entity_class }}Controller extends Controller
{
{%- if 'index' in actions %}
{%- include 'actions/index.php' %}
{%- endif %}
{%- if 'show' in actions %}
{%- include 'actions/show.php' %}
{%- endif %}
{%- if 'new' in actions %}
{%- include 'actions/new.php' %}
{%- include 'actions/create.php' %}
{%- endif %}
{%- if 'edit' in actions %}
{%- include 'actions/edit.php' %}
{%- include 'actions/update.php' %}
{%- endif %}
{%- if 'delete' in actions %}
{%- include 'actions/delete.php' %}
{%- endif %}
}

View File

@@ -0,0 +1,44 @@
public function testCompleteScenario()
{
// Create a new client to browse the application
$client = static::createClient();
// Create a new entry in the database
$crawler = $client->request('GET', '/{{ route_prefix }}/');
$this->assertTrue(200 === $client->getResponse()->getStatusCode());
$crawler = $client->click($crawler->selectLink('Create a new entry')->link());
// Fill in the form and submit it
$form = $crawler->selectButton('Create')->form(array(
'{{ entity_class|lower }}[field_name]' => 'Test',
// ... other fields to fill
));
$client->submit($form);
$crawler = $client->followRedirect();
// Check data in the show view
$this->assertTrue($crawler->filter('td:contains("Test")')->count() > 0);
// Edit the entity
$crawler = $client->click($crawler->selectLink('Edit')->link());
$form = $crawler->selectButton('Edit')->form(array(
'{{ entity_class|lower }}[field_name]' => 'Foo',
// ... other fields to fill
));
$client->submit($form);
$crawler = $client->followRedirect();
// Check the element contains an attribute with value equals "Foo"
$this->assertTrue($crawler->filter('[value="Foo"]')->count() > 0);
// Delete the entity
$client->submit($crawler->selectButton('Delete')->form());
$crawler = $client->followRedirect();
// Check the entity has been delete on the list
$this->assertNotRegExp('/Foo/', $client->getResponse()->getContent());
}

View File

@@ -0,0 +1,14 @@
public function testCompleteScenario()
{
// Create a new client to browse the application
$client = static::createClient();
// Go to the list view
$crawler = $client->request('GET', '/{{ route_prefix }}/');
$this->assertTrue(200 === $client->getResponse()->getStatusCode());
// Go to the show view
$crawler = $client->click($crawler->selectLink('show')->link());
$this->assertTrue(200 === $client->getResponse()->getStatusCode());
}

View File

@@ -0,0 +1,18 @@
<?php
namespace {{ namespace }}\Tests\Controller{{ entity_namespace ? '\\' ~ entity_namespace : '' }};
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class {{ entity_class }}ControllerTest extends WebTestCase
{
/*
{%- if 'new' in actions %}
{%- include 'tests/others/full_scenario.php' -%}
{%- else %}
{%- include 'tests/others/short_scenario.php' -%}
{%- endif %}
*/
}

View File

@@ -0,0 +1,11 @@
<h1>{{ entity }} edit</h1>
<form action="{{ "{{ path('"~ route_name_prefix ~"_update', { 'id': entity.id }) }}" }}" method="post" {{ "{{ form_enctype(edit_form) }}" }}>
{{ "{{ form_widget(edit_form) }}" }}
<p>
<button type="submit">Edit</button>
</p>
</form>
{% set hide_edit, hide_delete = true, false %}
{% include 'views/others/record_actions.html.twig' %}

View File

@@ -0,0 +1,56 @@
<h1>{{ entity }} list</h1>
<table class="records_list">
<thead>
<tr>
{%- for field, metadata in fields %}
<th>{{ field|capitalize }}</th>
{%- endfor %}
<th>Actions</th>
</tr>
</thead>
<tbody>
{{ '{% for entity in entities %}' }}
<tr>
{%- for field, metadata in fields %}
{%- if loop.first and ('show' in actions) %}
<td><a href="{{ "{{ path('"~ route_name_prefix ~"_show', { 'id': entity.id }) }}" }}">{{ '{{ entity.'~ field|replace({'_': ''}) ~' }}' }}</a></td>
{%- elseif metadata.type in ['date', 'datetime'] %}
<td>{{ '{% if entity.'~ field|replace({'_': ''}) ~' %}{{ entity.'~ field|replace({'_': ''}) ~'|date(\'Y-m-d H:i:s\') }}{% endif %}' }}</td>
{%- else %}
<td>{{ '{{ entity.'~ field|replace({'_': ''}) ~' }}' }}</td>
{%- endif %}
{%- if loop.last %}
<td>
{%- include "views/others/actions.html.twig" %}
</td>
{%- endif %}
{%- endfor %}
</tr>
{{ '{% endfor %}' }}
</tbody>
</table>
{% if 'new' in actions %}
<ul>
<li>
<a href="{{ "{{ path('"~ route_name_prefix ~"_new') }}" }}">
Create a new entry
</a>
</li>
</ul>
{% endif %}

View File

@@ -0,0 +1,11 @@
<h1>{{ entity }} creation</h1>
<form action="{{ "{{ path('"~ route_name_prefix ~"_create') }}" }}" method="post" {{ "{{ form_enctype(form) }}" }}>
{{ "{{ form_widget(form) }}" }}
<p>
<button type="submit">Create</button>
</p>
</form>
{% set hide_edit, hide_delete = true, true %}
{% include 'views/others/record_actions.html.twig' %}

View File

@@ -0,0 +1,12 @@
<ul>
{%- for action in record_actions %}
<li>
<a href="{{ "{{ path('"~ route_name_prefix ~"_"~ action ~"', { 'id': entity.id }) }}" }}">{{ action }}</a>
</li>
{%- endfor %}
</ul>

View File

@@ -0,0 +1,22 @@
<ul class="record_actions">
<li>
<a href="{{ "{{ path('"~ route_name_prefix ~"') }}" }}">
Back to the list
</a>
</li>
{% if ('edit' in actions) and (not hide_edit) %}
<li>
<a href="{{ "{{ path('"~ route_name_prefix ~"_edit', { 'id': entity.id }) }}" }}">
Edit
</a>
</li>
{% endif %}
{% if ('delete' in actions) and (not hide_delete) %}
<li>
<form action="{{ "{{ path('"~ route_name_prefix ~"_delete', { 'id': entity.id }) }}" }}" method="post">
{{ '{{ form_widget(delete_form) }}' }}
<button type="submit">Delete</button>
</form>
</li>
{% endif %}
</ul>

View File

@@ -0,0 +1,28 @@
<h1>{{ entity }}</h1>
<table class="record_properties">
<tbody>
{%- for field, metadata in fields %}
<tr>
<th>{{ field|capitalize }}</th>
{%- if metadata.type in ['date', 'datetime'] %}
<td>{{ '{{ entity.'~ field|replace({'_': ''}) ~'|date(\'Y-m-d H:i:s\') }}' }}</td>
{%- else %}
<td>{{ '{{ entity.'~ field|replace({'_': ''}) ~' }}' }}</td>
{%- endif %}
</tr>
{%- endfor %}
</tbody>
</table>
{% set hide_edit, hide_delete = false, false %}
{% include 'views/others/record_actions.html.twig' %}

View File

@@ -0,0 +1,34 @@
<?php
namespace {{ namespace }}\Form{{ entity_namespace ? '\\' ~ entity_namespace : '' }};
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class {{ form_class }} extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
{%- for field in fields %}
->add('{{ field }}')
{%- endfor %}
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => '{{ namespace }}\Entity{{ entity_namespace ? '\\' ~ entity_namespace : '' }}\{{ entity_class }}'
));
}
public function getName()
{
return '{{ form_type_name }}';
}
}

View File

@@ -0,0 +1,23 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Sensio\Bundle\GeneratorBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
/**
* SensioGeneratorBundle.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class SensioGeneratorBundle extends Bundle
{
}

View File

@@ -0,0 +1,30 @@
{
"name": "sensio/generator-bundle",
"description": "This bundle generates code for you",
"keywords": [],
"type": "symfony-bundle",
"license": "MIT",
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"require": {
"symfony/framework-bundle": "2.1.*"
},
"require-dev": {
"symfony/doctrine-bridge": "2.1.*",
"doctrine/orm": ">=2.1,<2.4-dev",
"twig/twig": ">=1.8,<2.0-dev"
},
"autoload": {
"psr-0": { "Sensio\\Bundle\\GeneratorBundle": "" }
},
"target-dir": "Sensio/Bundle/GeneratorBundle",
"extra": {
"branch-alias": {
"dev-master": "2.1.x-dev"
}
}
}