diff --git a/Command/AbstractPropelCommand.php b/Command/AbstractPropelCommand.php
index 314844f..18d3e6c 100644
--- a/Command/AbstractPropelCommand.php
+++ b/Command/AbstractPropelCommand.php
@@ -235,15 +235,25 @@ abstract class AbstractPropelCommand extends ContainerAwareCommand
{
$finalSchemas = array();
foreach ($kernel->getBundles() as $bundle) {
- if (is_dir($dir = $bundle->getPath().'/Resources/config')) {
- $finder = new Finder();
- $schemas = $finder->files()->name('*schema.xml')->followLinks()->in($dir);
+ $finalSchemas = array_merge($finalSchemas, $this->getSchemasFromBundle($bundle));
+ }
- if (!iterator_count($schemas)) {
- continue;
- }
+ return $finalSchemas;
+ }
+
+ /**
+ * @param \Symfony\Component\HttpKernel\Bundle\BundleInterface
+ */
+ protected function getSchemasFromBundle(BundleInterface $bundle)
+ {
+ $finalSchemas = array();
+
+ if (is_dir($dir = $bundle->getPath().'/Resources/config')) {
+ $finder = new Finder();
+ $schemas = $finder->files()->name('*schema.xml')->followLinks()->in($dir);
+
+ if (iterator_count($schemas)) {
foreach ($schemas as $schema) {
-
$logicalName = $this->transformToLogicalName($schema, $bundle);
$finalSchema = new \SplFileInfo($this->getFileLocator()->locate($logicalName));
@@ -255,6 +265,11 @@ abstract class AbstractPropelCommand extends ContainerAwareCommand
return $finalSchemas;
}
+ protected function getRelativeFileName(\SplFileInfo $file)
+ {
+ return substr(str_replace(realpath($this->getContainer()->getParameter('kernel.root_dir') . '/../'), '', $file), 1);
+ }
+
/**
* Create a 'build.properties' file.
*
@@ -424,7 +439,7 @@ EOT;
* Write Propel output as summary based on a Regexp.
*
* @param OutputInterface $output The output object.
- * @param string $taskname A task name
+ * @param string $taskname A task name
*/
protected function writeSummary(OutputInterface $output, $taskname)
{
@@ -447,8 +462,8 @@ EOT;
* @see https://github.com/sensio/SensioGeneratorBundle/blob/master/Command/Helper/DialogHelper.php#L52
*
* @param OutputInterface $output The output.
- * @param string $text A text message.
- * @param string $style A style to apply on the section.
+ * @param string $text A text message.
+ * @param string $style A style to apply on the section.
*/
protected function writeSection(OutputInterface $output, $text, $style = 'bg=blue;fg=white')
{
@@ -463,8 +478,8 @@ EOT;
* Renders an error message if a task has failed.
*
* @param OutputInterface $output The output.
- * @param string $taskName A task name.
- * @param Boolean $more Whether to add a 'more details' message or not.
+ * @param string $taskName A task name.
+ * @param Boolean $more Whether to add a 'more details' message or not.
*/
protected function writeTaskError($output, $taskName, $more = true)
{
@@ -479,25 +494,42 @@ EOT;
/**
* @param OutputInterface $output The output.
- * @param string $filename The filename.
+ * @param string $filename The filename.
*/
protected function writeNewFile($output, $filename)
{
return $output->writeln('>> File+ ' . $filename);
}
+ /**
+ * @param OutputInterface $output The output.
+ * @param string $directory The directory.
+ */
+ protected function writeNewDirectory($output, $directory)
+ {
+ return $output->writeln('>> Dir+ ' . $directory);
+ }
+
/**
* Ask confirmation from the user.
*
* @param OutputInterface $output The output.
- * @param string $question A given question.
- * @param string $default A default response.
+ * @param string $question A given question.
+ * @param string $default A default response.
*/
protected function askConfirmation(OutputInterface $output, $question, $default = null)
{
return $this->getHelperSet()->get('dialog')->askConfirmation($output, $question, $default);
}
+ /**
+ * @return \Symfony\Component\Config\FileLocatorInterface
+ */
+ protected function getFileLocator()
+ {
+ return $this->getContainer()->get('file_locator');
+ }
+
private function transformToLogicalName(\SplFileInfo $schema, BundleInterface $bundle)
{
$schemaPath = str_replace($bundle->getPath(). DIRECTORY_SEPARATOR . 'Resources' . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR, '', $schema->getRealPath());
@@ -505,11 +537,6 @@ EOT;
return sprintf('@%s/Resources/config/%s', $bundle->getName(), $schemaPath);
}
- protected function getFileLocator()
- {
- return $this->getContainer()->get('file_locator');
- }
-
/**
* Compiles arguments/properties for the Phing process.
* @return array
diff --git a/Command/FixturesLoadCommand.php b/Command/FixturesLoadCommand.php
index fed0b29..f1cb9f5 100644
--- a/Command/FixturesLoadCommand.php
+++ b/Command/FixturesLoadCommand.php
@@ -34,21 +34,25 @@ class FixturesLoadCommand extends AbstractPropelCommand
* @var string
*/
private $defaultFixturesDir = 'app/propel/fixtures';
+
/**
* Absolute path for fixtures directory
* @var string
*/
private $absoluteFixturesPath = '';
+
/**
* Filesystem for manipulating files
* @var \Symfony\Component\Filesystem\Filesystem
*/
private $filesystem = null;
+
/**
* Bundle the fixtures are being loaded from
* @var Symfony\Component\HttpKernel\Bundle\BundleInterface
*/
private $bundle;
+
/**
* @see Command
*/
@@ -108,9 +112,9 @@ YAML fixtures are:
Description: Hello world !
EOT
- )
+ )
->setName('propel:fixtures:load')
- ;
+ ;
}
/**
@@ -123,19 +127,20 @@ EOT
$this->writeSection($output, '[Propel] You are running the command: propel:fixtures:load');
$this->filesystem = new Filesystem();
-
+
if ('@' === substr($input->getArgument('bundle'), 0, 1)) {
$this->bundle = $this
->getContainer()
->get('kernel')
->getBundle(substr($input->getArgument('bundle'), 1));
+
$this->absoluteFixturesPath = $this->getFixturesPath($this->bundle);
} else {
$this->absoluteFixturesPath = realpath($this->getApplication()->getKernel()->getRootDir() . '/../' . $input->getOption('dir'));
}
if ($input->getOption('verbose')) {
- $this->additionalPhingArgs[] = 'verbose';
+ $this->additionalPhingArgs[] = 'verbose';
}
if (!$this->absoluteFixturesPath && !file_exists($this->absoluteFixturesPath)) {
@@ -318,7 +323,7 @@ EOT
if (null === $this->bundle) {
return $files;
}
-
+
$finalFixtureFiles = array();
foreach ($files as $file) {
@@ -338,5 +343,5 @@ EOT
protected function getFixturesPath(BundleInterface $bundle)
{
return $bundle->getPath().DIRECTORY_SEPARATOR.'Resources'.DIRECTORY_SEPARATOR.'fixtures';
- }
+ }
}
diff --git a/Command/FormGenerateCommand.php b/Command/FormGenerateCommand.php
index 662f5a7..2f09cc3 100644
--- a/Command/FormGenerateCommand.php
+++ b/Command/FormGenerateCommand.php
@@ -1,5 +1,6 @@
*/
-class FormGenerateCommand extends ContainerAwareCommand
+class FormGenerateCommand extends PropelGeneratorAwareCommand
{
+ const DEFAULT_FORM_TYPE_DIRECTORY = '/Form/Type';
+
/**
* @see Command
*/
@@ -29,7 +35,15 @@ class FormGenerateCommand extends ContainerAwareCommand
$this
->setDescription('Generate Form types stubs based on the schema.xml')
->addArgument('bundle', InputArgument::REQUIRED, 'The bundle to use to generate Form types')
- ->setHelp('')
+ ->addOption('force', 'f', InputOption::VALUE_NONE, 'Overwrite existing Form types')
+ ->setHelp(<<%command.name% command allows you to quickly generate Form Type stubs for a given bundle.
+
+ php app/console %command.full_name%
+
+The --force parameter allows you to overwrite existing files.
+EOT
+ )
->setName('propel:form:generate');
}
@@ -40,38 +54,77 @@ class FormGenerateCommand extends ContainerAwareCommand
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
- $propelPath = $this->getContainer()->getParameter('propel.path');
- require_once sprintf('%s/generator/lib/builder/util/XmlToAppData.php', $propelPath);
- require_once sprintf('%s/generator/lib/config/GeneratorConfig.php', $propelPath);
- require_once sprintf('%s/generator/lib/config/QuickGeneratorConfig.php', $propelPath);
-
- set_include_path(sprintf('%s/generator/lib', $propelPath) . PATH_SEPARATOR . get_include_path());
-
if ('@' === substr($input->getArgument('bundle'), 0, 1)) {
$bundle = $this
->getContainer()
->get('kernel')
->getBundle(substr($input->getArgument('bundle'), 1));
- if (is_dir($dir = $bundle->getPath().'/Resources/config')) {
- $finder = new Finder();
- $schemas = $finder->files()->name('*schema.xml')->followLinks()->in($dir);
+ $schemas = $this->getSchemasFromBundle($bundle);
- $array = array();
- foreach ($schemas as $schema) {
- $array[] = $schema->getPathName();
+ if ($schemas) {
+ foreach ($schemas as $fileName => $array) {
+ foreach ($this->getDatabasesFromSchema($array[1]) as $database) {
+ $this->createFormTypeFromDatabase($bundle, $database, $output, $input->getOption('force'));
+ }
}
+ } else {
+ $output->writeln(sprintf('No *schemas.xml files found in bundle %s.', $bundle->getName()));
}
-
- $transformer = new \XmlToAppData(null, null, 'UTF-8');
- $transformer->setGeneratorConfig(new \QuickGeneratorConfig());
-
- $appDatas = array();
- foreach ($array as $xmlFile) {
- $appDatas[] = $transformer->parseFile($xmlFile);
- }
-
- var_dump($appDatas);
}
}
+
+ private function createFormTypeFromDatabase(BundleInterface $bundle, \Database $database, OutputInterface $output, $force = false)
+ {
+ $dir = $this->createDirectory($bundle, $output);
+
+ foreach ($database->getTables() as $table) {
+ $file = new \SplFileInfo(sprintf('%s/%sType.php', $dir, $table->getPhpName()));
+
+ if (!file_exists($file) || true === $force) {
+ $this->writeFormType($bundle, $table, $file, $force, $output);
+ } else {
+ $output->writeln(sprintf('File %-60s exists, skipped. Try the --force option.', $this->getRelativeFileName($file)));
+ }
+ }
+ }
+
+ private function createDirectory(BundleInterface $bundle, OutputInterface $output)
+ {
+ $fs = new Filesystem();
+
+ if (!is_dir($dir = $bundle->getPath() . self::DEFAULT_FORM_TYPE_DIRECTORY)) {
+ $fs->mkdir($dir);
+ $this->writeNewDirectory($output, $dir);
+ }
+
+ return $dir;
+ }
+
+ private function writeFormType(BundleInterface $bundle, \Table $table, \SplFileInfo $file, $force, OutputInterface $output)
+ {
+ $modelName = $table->getPhpName();
+ $formTypeContent = file_get_contents(__DIR__ . '/../Resources/skeleton/FormType.php');
+
+ $formTypeContent = str_replace('##NAMESPACE##', $bundle->getNamespace() . str_replace('/', '\\', self::DEFAULT_FORM_TYPE_DIRECTORY), $formTypeContent);
+ $formTypeContent = str_replace('##CLASS##', $modelName . 'Type', $formTypeContent);
+ $formTypeContent = str_replace('##FQCN##', sprintf('%s\%s', $table->getNamespace(), $modelName), $formTypeContent);
+ $formTypeContent = str_replace('##TYPE_NAME##', strtolower($modelName), $formTypeContent);
+ $formTypeContent = $this->addFields($table, $formTypeContent);
+
+ file_put_contents($file->getPathName(), $formTypeContent);
+ $this->writeNewFile($output, $this->getRelativeFileName($file) . ($force ? ' (forced)' : ''));
+ }
+
+ private function addFields(\Table $table, $formTypeContent)
+ {
+ $buildCode = '';
+ foreach ($table->getColumns() as $column) {
+ if (!$column->isPrimaryKey()) {
+ $buildCode .= sprintf("\n \$builder->add('%s');", lcfirst($column->getPhpName()));
+ }
+ }
+
+ return str_replace('##BUILD_CODE##', $buildCode, $formTypeContent);
+ }
}
diff --git a/Command/PropelGeneratorAwareCommand.php b/Command/PropelGeneratorAwareCommand.php
new file mode 100644
index 0000000..d16cad6
--- /dev/null
+++ b/Command/PropelGeneratorAwareCommand.php
@@ -0,0 +1,49 @@
+
+ */
+abstract class PropelGeneratorAwareCommand extends AbstractPropelCommand
+{
+ /**
+ * {@inheritdoc}
+ */
+ protected function initialize(InputInterface $input, OutputInterface $output)
+ {
+ parent::initialize($input, $output);
+
+ $this->loadPropelGenerator();
+ }
+
+ protected function loadPropelGenerator()
+ {
+ $propelPath = $this->getContainer()->getParameter('propel.path');
+
+ require_once sprintf('%s/generator/lib/builder/util/XmlToAppData.php', $propelPath);
+ require_once sprintf('%s/generator/lib/config/GeneratorConfig.php', $propelPath);
+ require_once sprintf('%s/generator/lib/config/QuickGeneratorConfig.php', $propelPath);
+
+ set_include_path(sprintf('%s/generator/lib', $propelPath) . PATH_SEPARATOR . get_include_path());
+ }
+
+ protected function getDatabasesFromSchema(\SplFileInfo $file)
+ {
+ $transformer = new \XmlToAppData(null, null, 'UTF-8');
+ $transformer->setGeneratorConfig(new \QuickGeneratorConfig());
+
+ return $transformer->parseFile($file->getPathName())->getDatabases();
+ }
+}
diff --git a/Resources/doc/README.markdown b/Resources/doc/README.markdown
index 1e28671..81f125c 100644
--- a/Resources/doc/README.markdown
+++ b/Resources/doc/README.markdown
@@ -315,6 +315,15 @@ The table arguments define which table will be delete, by default all table.
Note that the `--force` option is needed to actually execute the deletion.
+### Form Types ###
+
+You can generate stub classes based on your `schema.xml` in a given bundle:
+
+ > php app/console propel:form:generate [-f|--force] bundle
+
+It will write Form Type classes in `src/YourVendor/YourBundle/Form/Type`.
+
+
## PropelParamConverter ##
You can use the Propel ParamConverter with the SensioFrameworkExtraBundle.
diff --git a/Resources/skeleton/FormType.php b/Resources/skeleton/FormType.php
new file mode 100644
index 0000000..9c96e81
--- /dev/null
+++ b/Resources/skeleton/FormType.php
@@ -0,0 +1,34 @@
+ '##FQCN##',
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getName()
+ {
+ return '##TYPE_NAME##';
+ }
+}
diff --git a/Tests/Command/AbstractPropelCommandTest.php b/Tests/Command/AbstractPropelCommandTest.php
index 5a17837..ca02639 100644
--- a/Tests/Command/AbstractPropelCommandTest.php
+++ b/Tests/Command/AbstractPropelCommandTest.php
@@ -8,7 +8,7 @@
* @license MIT License
*/
-namespace Tests\Command;
+namespace Propel\PropelBundle\Tests\Command;
use Propel\PropelBundle\Tests\TestCase;
use Propel\PropelBundle\Command\AbstractPropelCommand;
diff --git a/Tests/FixturesLoadCommandTest.php b/Tests/Command/FixturesLoadCommandTest.php
similarity index 97%
rename from Tests/FixturesLoadCommandTest.php
rename to Tests/Command/FixturesLoadCommandTest.php
index 2a85414..29aeb46 100644
--- a/Tests/FixturesLoadCommandTest.php
+++ b/Tests/Command/FixturesLoadCommandTest.php
@@ -8,7 +8,7 @@
* @license MIT License
*/
-namespace Tests\Command;
+namespace Propel\PropelBundle\Tests\Command;
use Symfony\Component\Filesystem\Filesystem;
diff --git a/Tests/Command/PropelGeneratorAwareCommandTest.php b/Tests/Command/PropelGeneratorAwareCommandTest.php
new file mode 100644
index 0000000..93cbc4d
--- /dev/null
+++ b/Tests/Command/PropelGeneratorAwareCommandTest.php
@@ -0,0 +1,73 @@
+
+ */
+class PropelGeneratorAwareCommandTest extends TestCase
+{
+ protected $container;
+
+ protected function setUp()
+ {
+ parent::setUp();
+
+ $this->container = $this->getContainer();
+ $this->container->setParameter('propel.path', __DIR__ . '/../../vendor/propel');
+ }
+
+ public function testGetDatabasesFromSchema()
+ {
+ $command = new PropelGeneratorAwareCommandTestable('testable-command');
+ $command->setContainer($this->container);
+ $databases = $command->getDatabasesFromSchema(new \SplFileInfo(__DIR__ . '/../Fixtures/schema.xml'));
+
+ $this->assertTrue(is_array($databases));
+
+ foreach ($databases as $database) {
+ $this->assertInstanceOf('\Database', $database);
+ }
+
+ $bookstore = $databases[0];
+ $this->assertEquals(1, count($bookstore->getTables()));
+
+ foreach ($bookstore->getTables() as $table) {
+ $this->assertInstanceOf('\Table', $table);
+ }
+ }
+}
+
+class PropelGeneratorAwareCommandTestable extends PropelGeneratorAwareCommand
+{
+ protected $container;
+
+ public function setContainer(ContainerInterface $container = null)
+ {
+ $this->container = $container;
+ }
+
+ protected function getContainer()
+ {
+ return $this->container;
+ }
+
+ public function getDatabasesFromSchema(\SplFileInfo $file)
+ {
+ $this->loadPropelGenerator();
+
+ return parent::getDatabasesFromSchema($file);
+ }
+}
diff --git a/Tests/DependencyInjection/PropelExtensionTest.php b/Tests/DependencyInjection/PropelExtensionTest.php
index 63379cd..9a8444b 100644
--- a/Tests/DependencyInjection/PropelExtensionTest.php
+++ b/Tests/DependencyInjection/PropelExtensionTest.php
@@ -44,9 +44,9 @@ class PropelExtensionTest extends TestCase
$container = $this->getContainer();
$loader = new PropelExtension();
$loader->load(array(array(
- 'path' => '/propel',
+ 'path' => '/propel',
'phing_path' => '/phing',
- 'dbal' => array()
+ 'dbal' => array()
)), $container);
$this->assertEquals('/propel', $container->getParameter('propel.path'), '->load() requires the Propel path');
$this->assertEquals('/phing', $container->getParameter('propel.phing_path'), '->load() requires the Phing path');
@@ -270,7 +270,7 @@ class PropelExtensionTest extends TestCase
$this->assertArrayHasKey('query', $config['datasources']['default']['connection']['settings']['queries']);
$this->assertEquals('SET NAMES UTF8', $config['datasources']['default']['connection']['settings']['queries']['query']);
}
-
+
public function testDbalWithSlaves()
{
$container = $this->getContainer();
@@ -299,13 +299,13 @@ class PropelExtensionTest extends TestCase
),
),
);
-
+
$configs = array($config_base, array('dbal' => $config_mysql));
$loader->load($configs, $container);
$arguments = $container->getDefinition('propel.configuration')->getArguments();
$config = $arguments[0];
-
+
$this->assertArrayHasKey('slaves', $config['datasources']['default']);
$this->assertArrayHasKey('connection', $config['datasources']['default']['slaves']);
$this->assertArrayHasKey('mysql_slave1', $config['datasources']['default']['slaves']['connection']);
diff --git a/Tests/Fixtures/schema.xml b/Tests/Fixtures/schema.xml
new file mode 100644
index 0000000..3e7f0de
--- /dev/null
+++ b/Tests/Fixtures/schema.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+