Started to work on Propel2 commands integration (only a few work at the moment)

This commit is contained in:
Kévin Gomez 2013-10-15 22:48:33 +01:00
parent 313057ca5a
commit 38e7f63a62
9 changed files with 824 additions and 0 deletions

172
Command/AbstractCommand.php Normal file
View file

@ -0,0 +1,172 @@
<?php
/**
* This file is part of the PropelBundle package.
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @license MIT License
*/
namespace Propel\PropelBundle\Command;
use Propel\Generator\Command\AbstractCommand as BaseCommand;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\HttpKernel\KernelInterface;
/**
* @author Kévin Gomez <contact@kevingomez.fr>
*/
abstract class AbstractCommand extends ContainerAwareCommand
{
/**
* @var string
*/
protected $cacheDir = null;
/**
* {@inheritdoc}
*/
protected function configure()
{
$this
->addOption('platform', null, InputOption::VALUE_REQUIRED, 'The platform', BaseCommand::DEFAULT_PLATFORM)
;
}
protected function runCommand(Command $command, array $parameters, InputInterface $input, OutputInterface $output)
{
array_unshift($parameters, $this->getName());
$parameters = array_merge(array(
'--input-dir' => $this->cacheDir,
'--verbose' => $input->getOption('verbose'),
), $parameters);
if ($input->hasOption('platform')) {
$parameters['--platform'] = $input->getOption('platform');
}
var_dump($parameters);
$commandInput = new ArrayInput($parameters);
$command->setApplication($this->getApplication());
return $command->run($commandInput, $output);
}
protected function setupBuildTimeFiles()
{
$kernel = $this->getApplication()->getKernel();
$this->cacheDir = $kernel->getCacheDir().'/propel';
$fs = new Filesystem();
$fs->mkdir($this->cacheDir);
// collect all schemas
//$this->copySchemas($kernel, $this->cacheDir);
// build.properties
$this->createBuildPropertiesFile($kernel, $this->cacheDir.'/build.properties');
// buildtime-conf.xml
$this->createBuildTimeFile($this->cacheDir.'/buildtime-conf.xml');
}
protected function getConnections(array $connections)
{
$knownConnections = $this->getContainer()->getParameter('propel.configuration');
$dsnList = array();
foreach ($connections as $connection) {
if (!isset($knownConnections[$connection])) {
throw new \InvalidArgumentException(sprintf('Unknown connection "%s"', $connection));
}
$dsnList[] = $this->buildDsn($connection, $knownConnections[$connection]['connection']);
}
return $dsnList;
}
protected function buildDsn($connectionName, array $connectionData)
{
return sprintf('%s=%s;user=%s;password=%s', $connectionName, $connectionData['dsn'], $connectionData['user'], $connectionData['password']);
}
/**
* Create a 'build.properties' file.
*
* @param KernelInterface $kernel The application kernel.
* @param string $file Should be 'build.properties'.
*/
protected function createBuildPropertiesFile(KernelInterface $kernel, $file)
{
$fs = new Filesystem();
$buildPropertiesFile = $kernel->getRootDir().'/config/propel.ini';
if ($fs->exists($file)) {
$fs->copy($buildPropertiesFile, $file);
} else {
$fs->touch($file);
}
}
/**
* Create an XML file which represents propel.configuration
*
* @param string $file Should be 'buildtime-conf.xml'.
*/
protected function createBuildTimeFile($file)
{
if (!$this->getContainer()->hasParameter('propel.configuration')) {
throw new \InvalidArgumentException('Could not find Propel configuration.');
}
$xml = strtr(<<<EOT
<?xml version="1.0"?>
<config>
<propel>
<datasources default="%default_connection%">
EOT
, array('%default_connection%' => $this->getContainer()->getParameter('propel.dbal.default_connection')));
$datasources = $this->getContainer()->getParameter('propel.configuration');
foreach ($datasources as $name => $datasource) {
$xml .= strtr(<<<EOT
<datasource id="%name%">
<adapter>%adapter%</adapter>
<connection>
<dsn>%dsn%</dsn>
<user>%username%</user>
<password>%password%</password>
</connection>
</datasource>
EOT
, array(
'%name%' => $name,
'%adapter%' => $datasource['adapter'],
'%dsn%' => $datasource['connection']['dsn'],
'%username%' => $datasource['connection']['user'],
'%password%' => isset($datasource['connection']['password']) ? $datasource['connection']['password'] : '',
));
}
$xml .= <<<EOT
</datasources>
</propel>
</config>
EOT;
file_put_contents($file, $xml);
}
}

View file

@ -0,0 +1,58 @@
<?php
/**
* This file is part of the PropelBundle package.
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @license MIT License
*/
namespace Propel\PropelBundle\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Propel\Generator\Command\MigrationDownCommand as BaseCommand;
/**
* @author Kévin Gomez <contact@kevingomez.fr>
*/
class MigrationDownCommand extends AbstractCommand
{
/**
* {@inheritdoc}
*/
protected function configure()
{
parent::configure();
$this
->setName('propel:migration:down')
->setDescription('Execute migrations down')
->addOption('connection', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, 'Connection to use. Example: default, bookstore')
->addOption('migration-table', null, InputOption::VALUE_REQUIRED, 'Migration table name', BaseCommand::DEFAULT_MIGRATION_TABLE)
->addOption('output-dir', null, InputOption::VALUE_OPTIONAL, 'The output directory')
;
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$defaultOutputDir = $this->getApplication()->getKernel()->getRootDir().'/propel/migrations';
$this->setupBuildTimeFiles();
$params = array(
'--connection' => $this->getConnections($input->getOption('connection')),
'--migration-table' => $input->getOption('migration-table'),
'--output-dir' => $input->getOption('output-dir') ?: $defaultOutputDir,
);
return $this->runCommand(new BaseCommand(), $params, $input, $output);
}
}

View file

@ -0,0 +1,58 @@
<?php
/**
* This file is part of the PropelBundle package.
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @license MIT License
*/
namespace Propel\PropelBundle\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Propel\Generator\Command\MigrationStatusCommand as BaseCommand;
/**
* @author Kévin Gomez <contact@kevingomez.fr>
*/
class MigrationStatusCommand extends AbstractCommand
{
/**
* {@inheritdoc}
*/
protected function configure()
{
parent::configure();
$this
->setName('propel:migration:status')
->setDescription('Get migration status')
->addOption('connection', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, 'Connection to use. Example: default, bookstore')
->addOption('migration-table', null, InputOption::VALUE_REQUIRED, 'Migration table name', BaseCommand::DEFAULT_MIGRATION_TABLE)
->addOption('output-dir', null, InputOption::VALUE_OPTIONAL, 'The output directory')
;
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$defaultOutputDir = $this->getApplication()->getKernel()->getRootDir().'/propel/migrations';
$this->setupBuildTimeFiles();
$params = array(
'--connection' => $this->getConnections($input->getOption('connection')),
'--migration-table' => $input->getOption('migration-table'),
'--output-dir' => $input->getOption('output-dir') ?: $defaultOutputDir,
);
return $this->runCommand(new BaseCommand(), $params, $input, $output);
}
}

View file

@ -0,0 +1,58 @@
<?php
/**
* This file is part of the PropelBundle package.
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @license MIT License
*/
namespace Propel\PropelBundle\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Propel\Generator\Command\MigrationUpCommand as BaseCommand;
/**
* @author Kévin Gomez <contact@kevingomez.fr>
*/
class MigrationUpCommand extends AbstractCommand
{
/**
* {@inheritdoc}
*/
protected function configure()
{
parent::configure();
$this
->setName('propel:migration:up')
->setDescription('Execute migrations up')
->addOption('connection', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, 'Connection to use. Example: default, bookstore')
->addOption('migration-table', null, InputOption::VALUE_REQUIRED, 'Migration table name', BaseCommand::DEFAULT_MIGRATION_TABLE)
->addOption('output-dir', null, InputOption::VALUE_OPTIONAL, 'The output directory')
;
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$defaultOutputDir = $this->getApplication()->getKernel()->getRootDir().'/propel/migrations';
$this->setupBuildTimeFiles();
$params = array(
'--connection' => $this->getConnections($input->getOption('connection')),
'--migration-table' => $input->getOption('migration-table'),
'--output-dir' => $input->getOption('output-dir') ?: $defaultOutputDir,
);
return $this->runCommand(new BaseCommand(), $params, $input, $output);
}
}

View file

@ -0,0 +1,48 @@
<?php
/**
* This file is part of the PropelBundle package.
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @license MIT License
*/
namespace Propel\PropelBundle\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* @author Kévin Gomez <contact@kevingomez.fr>
*/
class SqlInsertCommand extends AbstractCommand
{
/**
* {@inheritdoc}
*/
protected function configure()
{
$this
->setName('propel:sql:insert')
->setDescription('Insert SQL statements')
->addOption('connection', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, 'Connection to use. Example: default, bookstore')
;
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->setupBuildTimeFiles();
$params = array(
'--connection' => $this->getConnections($input->getOption('connection')),
);
$command = new \Propel\Generator\Command\SqlInsertCommand();
return $this->runCommand($command, $params, $input, $output);
}
}

View file

@ -0,0 +1,252 @@
<?php
/**
* This file is part of the PropelBundle package.
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @license MIT License
*/
namespace Propel\PropelBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
/**
* This class contains the configuration information for the bundle
*
* This information is solely responsible for how the different configuration
* sections are normalized, and merged.
*
* @author William DURAND <william.durand1@gmail.com>
*/
class Configuration implements ConfigurationInterface
{
private $debug;
/**
* Constructor
*
* @param Boolean $debug Wether to use the debug mode
*/
public function __construct($debug)
{
$this->debug = (Boolean) $debug;
}
/**
* Generates the configuration tree builder.
*
* @return \Symfony\Component\Config\Definition\Builder\TreeBuilder The tree builder
*/
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('propel');
$this->addGeneralSection($rootNode);
$this->addDbalSection($rootNode);
return $treeBuilder;
}
/**
* Adds 'general' configuration.
*
* propel:
* logging: %kernel.debug%
* build_properties:
* xxxx.xxxx: xxxxxx
* ...
* behaviors:
* fooable: My\FooableBehavior
* barable: src.barable.BarableBehavior
*/
private function addGeneralSection(ArrayNodeDefinition $node)
{
$node
->children()
->scalarNode('logging')->defaultValue($this->debug)->end()
->arrayNode('build_properties')
->useAttributeAsKey('key')
->prototype('scalar')->end()
->end()
->arrayNode('behaviors')
->useAttributeAsKey('key')
->prototype('scalar')->end()
->end()
;
}
/**
* Adds 'dbal' configuration.
*
* propel:
* dbal:
* driver: mysql
* user: root
* password: null
* dsn: xxxxxxxx
* options: {}
* attributes: {}
* settings: {}
* default_connection: xxxxxx
*/
private function addDbalSection(ArrayNodeDefinition $node)
{
$node
->children()
->arrayNode('dbal')
->beforeNormalization()
->ifNull()
->then(function ($v) { return array ('connections' => array('default' => array())); })
->end()
->children()
->scalarNode('default_connection')->defaultValue('default')->end()
->scalarNode('driver')
->beforeNormalization()
->always()
->then(function ($v) { return str_replace('pdo_', '', $v); })
->end()
->defaultValue('mysql')
->end()
->scalarNode('user')->defaultValue('root')->end()
->scalarNode('password')->defaultValue('')->end()
->scalarNode('dsn')
->beforeNormalization()
->always()
->then(function ($v) { return str_replace('pdo_', '', $v); })
->end()
->defaultValue('')
->end()
->scalarNode('classname')->defaultValue($this->debug ? 'DebugPDO' : 'PropelPDO')->end()
->end()
->fixXmlConfig('option')
->children()
->arrayNode('options')
->useAttributeAsKey('key')
->prototype('scalar')->end()
->end()
->end()
->fixXmlConfig('attribute')
->children()
->arrayNode('attributes')
->useAttributeAsKey('key')
->prototype('scalar')->end()
->end()
->end()
->fixXmlConfig('setting')
->children()
->arrayNode('settings')
->useAttributeAsKey('key')
->prototype('array')
->useAttributeAsKey('key')
->prototype('scalar')->end()
->end()
->end()
->end()
->fixXmlConfig('connection')
->append($this->getDbalConnectionsNode())
->end()
;
}
/**
* Returns a tree configuration for this part of configuration:
*
* connections:
* default:
* driver: mysql
* user: root
* password: null
* dsn: xxxxxxxx
* classname: PropelPDO
* options: {}
* attributes: {}
* settings: {}
*
* @return \Symfony\Component\Config\Definition\Builder\TreeBuilder The tree builder
*/
private function getDbalConnectionsNode()
{
$treeBuilder = new TreeBuilder();
$node = $treeBuilder->root('connections');
$node
->requiresAtLeastOneElement()
->useAttributeAsKey('name')
->prototype('array')
->children()
->scalarNode('driver')
->beforeNormalization()
->always()
->then(function ($v) { return str_replace('pdo_', '', $v); })
->end()
->defaultValue('mysql')
->end()
->scalarNode('user')->defaultValue('root')->end()
->scalarNode('password')->defaultValue('')->end()
->scalarNode('dsn')
->beforeNormalization()
->always()
->then(function ($v) { return str_replace('pdo_', '', $v); })
->end()
->defaultValue('')
->end()
->scalarNode('classname')->defaultValue($this->debug ? 'DebugPDO' : 'PropelPDO')->end()
->arrayNode('slaves')
->useAttributeAsKey('name')
->prototype('array')
->children()
->scalarNode('driver')
->beforeNormalization()
->always()
->then(function ($v) { return str_replace('pdo_', '', $v); })
->end()
->defaultValue('mysql')
->end()
->scalarNode('user')->defaultValue('root')->end()
->scalarNode('password')->defaultValue('')->end()
->scalarNode('dsn')
->beforeNormalization()
->always()
->then(function ($v) { return str_replace('pdo_', '', $v); })
->end()
->defaultValue('')
->end()
->scalarNode('classname')->defaultValue($this->debug ? 'DebugPDO' : 'PropelPDO')->end()
->end()
->end()
->end()
->end()
->fixXmlConfig('option')
->children()
->arrayNode('options')
->useAttributeAsKey('key')
->prototype('scalar')->end()
->end()
->end()
->fixXmlConfig('attribute')
->children()
->arrayNode('attributes')
->useAttributeAsKey('key')
->prototype('scalar')->end()
->end()
->end()
->fixXmlConfig('setting')
->children()
->arrayNode('settings')
->useAttributeAsKey('key')
->prototype('array')
->useAttributeAsKey('key')
->prototype('scalar')
->end()
->end()
->end()
;
return $node;
}
}

View file

@ -0,0 +1,141 @@
<?php
/**
* This file is part of the PropelBundle package.
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @license MIT License
*/
namespace Propel\PropelBundle\DependencyInjection;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Config\Definition\Processor;
/**
* PropelExtension loads the PropelBundle configuration.
*
* @author William DURAND <william.durand1@gmail.com>
*/
class PropelExtension extends Extension
{
/**
* Loads the Propel configuration.
*
* @param array $configs An array of configuration settings
* @param ContainerBuilder $container A ContainerBuilder instance
*/
public function load(array $configs, ContainerBuilder $container)
{
$processor = new Processor();
$configuration = $this->getConfiguration($configs, $container);
$config = $processor->processConfiguration($configuration, $configs);
if (isset($config['logging']) && $config['logging']) {
$logging = $config['logging'];
} else {
$logging = false;
}
$container->setParameter('propel.logging', $logging);
// Load services
if (!$container->hasDefinition('propel')) {
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('propel.xml');
//$loader->load('converters.xml');
}
// build properties
if (isset($config['build_properties']) && is_array($config['build_properties'])) {
$buildProperties = $config['build_properties'];
} else {
$buildProperties = array();
}
// behaviors
if (isset($config['behaviors']) && is_array($config['behaviors'])) {
foreach ($config['behaviors'] as $name => $class) {
$buildProperties[sprintf('propel.behavior.%s.class', $name)] = $class;
}
}
if (!empty($config['dbal'])) {
$this->dbalLoad($config['dbal'], $container);
}
}
/**
* Loads the DBAL configuration.
*
* @param array $config An array of configuration settings
* @param ContainerBuilder $container A ContainerBuilder instance
*/
protected function dbalLoad(array $config, ContainerBuilder $container)
{
if (empty($config['default_connection'])) {
$keys = array_keys($config['connections']);
$config['default_connection'] = reset($keys);
}
$connectionName = $config['default_connection'];
$container->setParameter('propel.dbal.default_connection', $connectionName);
if (0 === count($config['connections'])) {
$config['connections'] = array($connectionName => $config);
}
$c = array();
foreach ($config['connections'] as $name => $conf) {
$c[$name]['adapter'] = $conf['driver'];
if (!empty($conf['slaves'])) {
$c[$name]['slaves']['connection'] = $conf['slaves'];
}
foreach (array('dsn', 'user', 'password', 'classname', 'options', 'attributes', 'settings') as $att) {
if (isset($conf[$att])) {
$c[$name]['connection'][$att] = $conf[$att];
}
}
}
// Alias the default connection if not defined
if (!isset($c['default'])) {
$c['default'] = $c[$connectionName];
}
$container->setParameter('propel.configuration', $c);
}
public function getConfiguration(array $config, ContainerBuilder $container)
{
return new Configuration($container->getParameter('kernel.debug'));
}
/**
* Returns the base path for the XSD files.
*
* @return string The XSD base path
*/
public function getXsdValidationBasePath()
{
return __DIR__.'/../Resources/config/schema';
}
/**
* Returns the recommended alias to use in XML.
*
* This alias is also the mandatory prefix to use when using YAML.
*
* @return string The alias
*/
public function getAlias()
{
return 'propel';
}
}

View file

@ -10,6 +10,9 @@
namespace Propel\PropelBundle;
use Propel\Runtime\Propel;
use Propel\Runtime\Connection\ConnectionManagerSingle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\DependencyInjection\ContainerBuilder;
@ -20,11 +23,35 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
*/
class PropelBundle extends Bundle
{
/**
* {@inheritdoc}
*/
public function boot()
{
$this->configureConnections();
}
/**
* {@inheritdoc}
*/
public function build(ContainerBuilder $container)
{
}
protected function configureConnections()
{
$connections_config = $this->container->getParameter('propel.configuration');
$default_datasource = $this->container->getParameter('propel.dbal.default_connection');
$serviceContainer = Propel::getServiceContainer();
$serviceContainer->setDefaultDatasource($default_datasource);
foreach ($connections_config as $name => $config) {
$manager = new ConnectionManagerSingle();
$manager->setConfiguration($config['connection']);
$serviceContainer->setAdapterClass($name, $config['adapter']);
$serviceContainer->setConnectionManager($name, $manager);
}
}
}

View file

@ -0,0 +1,10 @@
<?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="propel.dbal.default_connection">default</parameter>
</parameters>
</container>