Compare commits

...

No commits in common. "1.1.0" and "4.0" have entirely different histories.
1.1.0 ... 4.0

161 changed files with 9193 additions and 6374 deletions

13
.gitignore vendored
View file

@ -1,4 +1,9 @@
Model/*/map
Model/*/om
vendor
composer.lock
# Propel2 generated base classes, those are environment dependent
/Model/Base
/Model/Map
/Model/**/Base
/Model/**/Map
# Composer
/vendor
/composer.lock

View file

@ -1,14 +1,33 @@
sudo: false
language: php
php:
- 5.3
- 5.4
- 7.0
- 7.1
- 7.2
cache:
directories:
- $HOME/.composer/cache/files
env:
- SYMFONY_VERSION=origin/master
- SYMFONY_VERSION="^3.0"
- SYMFONY_VERSION="^4.0"
before_script:
- curl -s http://getcomposer.org/installer | php
- php composer.phar --dev install
matrix:
fast_finish: true
script: phpunit --coverage-text
exclude:
- php: 7.0
env: SYMFONY_VERSION="^4.0"
before_install:
- composer self-update
- if [ "${SYMFONY_VERSION}" != "" ]; then composer require --no-update "symfony/symfony:${SYMFONY_VERSION}"; fi;
install:
- composer update ${COMPOSER_FLAGS} --prefer-source
script:
- vendor/bin/phpunit --colors

View file

@ -8,84 +8,55 @@
* @license MIT License
*/
namespace Propel\PropelBundle\Command;
namespace Propel\Bundle\PropelBundle\Command;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Finder\Finder;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
use Symfony\Component\HttpKernel\KernelInterface;
/**
* Wrapper for Propel commands.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @author William DURAND <william.durand1@gmail.com>
* @author Kévin Gomez <contact@kevingomez.fr>
*/
abstract class AbstractCommand extends ContainerAwareCommand
{
/**
* Additional Phing args to add in specialized commands.
* @var array
*/
protected $additionalPhingArgs = array();
/**
* Temporary XML schemas used on command execution.
* @var array
*/
protected $tempSchemas = array();
/**
* @var string
*/
protected $cacheDir = null;
/**
* The Phing output.
* @string
*/
protected $buffer = null;
/**
* @var Symfony\Component\HttpKernel\Bundle\BundleInterface
* @var \Symfony\Component\HttpKernel\Bundle\BundleInterface
*/
protected $bundle = null;
/**
* Return the package prefix for a given bundle.
*
* @param Bundle $bundle
* @param string $baseDirectory The base directory to exclude from prefix.
*
* @return string
* @var InputInterface
*/
protected function getPackagePrefix(Bundle $bundle, $baseDirectory = '')
{
$parts = explode(DIRECTORY_SEPARATOR, realpath($bundle->getPath()));
$length = count(explode('\\', $bundle->getNamespace())) * (-1);
protected $input;
$prefix = implode(DIRECTORY_SEPARATOR, array_slice($parts, 0, $length));
$prefix = ltrim(str_replace($baseDirectory, '', $prefix), DIRECTORY_SEPARATOR);
/**
* @var OutputInterface
*/
protected $output;
if (!empty($prefix)) {
$prefix = str_replace(DIRECTORY_SEPARATOR, '.', $prefix).'.';
}
return $prefix;
}
use FormattingHelpers;
/**
* {@inheritdoc}
*/
protected function initialize(InputInterface $input, OutputInterface $output)
{
parent::initialize($input, $output);
$kernel = $this->getApplication()->getKernel();
$this->checkConfiguration();
$this->input = $input;
$this->output = $output;
$this->cacheDir = $kernel->getCacheDir().'/propel';
if ($input->hasArgument('bundle') && '@' === substr($input->getArgument('bundle'), 0, 1)) {
$this->bundle = $this
@ -96,112 +67,42 @@ abstract class AbstractCommand extends ContainerAwareCommand
}
/**
* Call a Phing task.
*
* @param string $taskName A Propel task name.
* @param array $properties An array of properties to pass to Phing.
* Create all the files needed by Propel's commands.
*/
protected function callPhing($taskName, $properties = array())
protected function setupBuildTimeFiles()
{
$kernel = $this->getApplication()->getKernel();
if (isset($properties['propel.schema.dir'])) {
$this->cacheDir = $properties['propel.schema.dir'];
} else {
$this->cacheDir = $kernel->getCacheDir().'/propel';
$filesystem = new Filesystem();
$filesystem->remove($this->cacheDir);
$filesystem->mkdir($this->cacheDir);
}
$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');
// Verbosity
$bufferPhingOutput = $this->getContainer()->getParameter('kernel.debug');
// Phing arguments
$args = $this->getPhingArguments($kernel, $this->cacheDir, $properties);
// Add any arbitrary arguments last
foreach ($this->additionalPhingArgs as $arg) {
if (in_array($arg, array('verbose', 'debug'))) {
$bufferPhingOutput = false;
}
$args[] = '-'.$arg;
}
$args[] = $taskName;
// Enable output buffering
Phing::setOutputStream(new \OutputStream(fopen('php://output', 'w')));
Phing::setErrorStream(new \OutputStream(fopen('php://output', 'w')));
Phing::startup();
Phing::setProperty('phing.home', getenv('PHING_HOME'));
ob_start();
$phing = new Phing();
$returnStatus = true; // optimistic way
try {
$phing->execute($args);
$phing->runBuild();
$this->buffer = ob_get_contents();
// Guess errors
if (strstr($this->buffer, 'failed. Aborting.') ||
strstr($this->buffer, 'Failed to execute') ||
strstr($this->buffer, 'failed for the following reason:')) {
$returnStatus = false;
}
} catch(\Exception $e) {
$returnStatus = false;
}
if ($bufferPhingOutput) {
ob_end_clean();
} else {
ob_end_flush();
}
return $returnStatus;
// propel.json
$this->createPropelConfigurationFile($this->cacheDir.'/propel.json');
}
/**
* @param KernelInterface $kernel The application kernel.
* @param string $cacheDir The directory in which the schemas will
* be copied.
*/
protected function copySchemas(KernelInterface $kernel, $cacheDir)
{
$filesystem = new Filesystem();
if (!is_dir($cacheDir)) {
$filesystem->mkdir($cacheDir);
}
$base = ltrim(realpath($kernel->getRootDir().'/..'), DIRECTORY_SEPARATOR);
$finalSchemas = $this->getFinalSchemas($kernel, $this->bundle);
foreach ($finalSchemas as $schema) {
/** @var null|Bundle $bundle */
list($bundle, $finalSchema) = $schema;
$packagePrefix = $this->getPackagePrefix($bundle, $base);
$tempSchema = $bundle->getName().'-'.$finalSchema->getBaseName();
$this->tempSchemas[$tempSchema] = array(
'bundle' => $bundle->getName(),
'basename' => $finalSchema->getBaseName(),
'path' => $finalSchema->getPathname(),
);
if ($bundle) {
$file = $cacheDir.DIRECTORY_SEPARATOR.'bundle-'.$bundle->getName().'-'.$finalSchema->getBaseName();
} else {
$file = $cacheDir.DIRECTORY_SEPARATOR.'app-'.$finalSchema->getBaseName();
}
$file = $cacheDir.DIRECTORY_SEPARATOR.$tempSchema;
$filesystem->copy((string) $finalSchema, $file, true);
// the package needs to be set absolute
@ -214,19 +115,47 @@ abstract class AbstractCommand extends ContainerAwareCommand
// This is used to override the package resulting from namespace conversion.
$database['package'] = $database['package'];
} elseif (isset($database['namespace'])) {
$database['package'] = $packagePrefix . str_replace('\\', '.', $database['namespace']);
if ($bundle) {
$database['package'] = $this->getPackageFromBundle($bundle, (string)$database['namespace']);
} else {
$database['package'] = $this->getPackageFromApp((string)$database['namespace']);
}
} else {
throw new \RuntimeException(
sprintf('%s : Please define a `package` attribute or a `namespace` attribute for schema `%s`',
$bundle->getName(), $finalSchema->getBaseName())
sprintf(
'%s : Please define a `package` attribute or a `namespace` attribute for schema `%s`',
$bundle ? $bundle->getName() : 'App',
$finalSchema->getBaseName()
)
);
}
if ($this->input->hasOption('connection')) {
$connections = $this->input->getOption('connection') ?: array($this->getDefaultConnection());
if (!in_array((string) $database['name'], $connections)) {
// we skip this schema because the connection name doesn't
// match the input values
$filesystem->remove($file);
$this->output->writeln(sprintf(
'<info>Skipped schema %s due to database name missmatch (%s not in [%s]).</info>',
$finalSchema->getPathname(),
$database['name'],
implode(',', $connections)
));
continue;
}
}
foreach ($database->table as $table) {
if (isset($table['package'])) {
$table['package'] = $table['package'];
} elseif (isset($table['namespace'])) {
$table['package'] = $packagePrefix . str_replace('\\', '.', $table['namespace']);
if ($bundle) {
$table['package'] = $this->getPackageFromBundle($bundle, (string)$table['namespace']);
} else {
$table['package'] = $this->getPackageFromApp((string)$table['namespace']);
}
} else {
$table['package'] = $database['package'];
}
@ -239,168 +168,205 @@ abstract class AbstractCommand extends ContainerAwareCommand
/**
* Return a list of final schema files that will be processed.
*
* @param \Symfony\Component\HttpKernel\KernelInterface $kernel
* @param KernelInterface $kernel The application kernel.
* @param BundleInterface $bundle If given, only the bundle's schemas will
* be returned.
*
* @return array
* @return array A list of schemas.
*/
protected function getFinalSchemas(KernelInterface $kernel, BundleInterface $bundle = null)
{
if (null !== $bundle) {
return $this->getSchemasFromBundle($bundle);
return $this->getSchemaLocator()->locateFromBundle($bundle);
}
$finalSchemas = array();
foreach ($kernel->getBundles() as $bundle) {
$finalSchemas = array_merge($finalSchemas, $this->getSchemasFromBundle($bundle));
}
return $finalSchemas;
return $this->getSchemaLocator()->locateFromBundlesAndConfiguration($kernel->getBundles());
}
/**
* @param \Symfony\Component\HttpKernel\Bundle\BundleInterface
* Run a Symfony command.
*
* @param Command $command The command to run.
* @param array $parameters An array of parameters to give to the command.
* @param InputInterface $input An InputInterface instance
* @param OutputInterface $output An OutputInterface instance
*
* @return int The command return code.
*/
protected function getSchemasFromBundle(BundleInterface $bundle)
protected function runCommand(Command $command, array $parameters, InputInterface $input, OutputInterface $output)
{
$finalSchemas = array();
// add the command's name to the parameters
array_unshift($parameters, $this->getName());
if (is_dir($dir = $bundle->getPath().'/Resources/config')) {
$finder = new Finder();
$schemas = $finder->files()->name('*schema.xml')->followLinks()->in($dir);
// merge the default parameters
$extraParameters = [
'--verbose' => $input->getOption('verbose')
];
if (iterator_count($schemas)) {
foreach ($schemas as $schema) {
$logicalName = $this->transformToLogicalName($schema, $bundle);
$finalSchema = new \SplFileInfo($this->getFileLocator()->locate($logicalName));
if ($command->getDefinition()->hasOption('schema-dir')) {
$extraParameters['--schema-dir'] = $this->cacheDir;
}
$finalSchemas[(string)$finalSchema] = array($bundle, $finalSchema);
}
if ($command->getDefinition()->hasOption('config-dir')) {
$extraParameters['--config-dir'] = $this->cacheDir;
}
$parameters = array_merge($extraParameters, $parameters);
if ($input->hasOption('platform')) {
if ($platform = $input->getOption('platform') ?: $this->getPlatform()) {
$parameters['--platform'] = $platform;
}
}
return $finalSchemas;
}
$command->setApplication($this->getApplication());
/**
* @param \SplFileInfo $file
* @return string
*/
protected function getRelativeFileName(\SplFileInfo $file)
{
return substr(str_replace(realpath($this->getContainer()->getParameter('kernel.root_dir') . '/../'), '', $file), 1);
}
/**
* Create a 'build.properties' file.
*
* @param KernelInterface $kernel The application kernel.
* @param string $file Should be 'build.properties'.
*/
protected function createBuildPropertiesFile(KernelInterface $kernel, $file)
{
$filesystem = new Filesystem();
$buildPropertiesFile = $kernel->getRootDir().'/config/propel.ini';
if (file_exists($buildPropertiesFile)) {
$filesystem->copy($buildPropertiesFile, $file);
} else {
$filesystem->touch($file);
}
// and run the sub-command
return $command->run(new ArrayInput($parameters), $output);
}
/**
* Create an XML file which represents propel.configuration
*
* @param string $file Should be 'buildtime-conf.xml'.
* @param string $file Should be 'propel.json'.
*/
protected function createBuildTimeFile($file)
protected function createPropelConfigurationFile($file)
{
$container = $this->getContainer();
$propelConfig = $this->getContainer()->getParameter('propel.configuration');
if (!$container->has('propel.configuration')) {
throw new \InvalidArgumentException('Could not find Propel configuration.');
}
//needed because because Propel2's configuration tree is a bit different
//propel.runtime.logging is PropelBundle feature only.
unset($propelConfig['runtime']['logging']);
$xml = strtr(<<<EOT
<?xml version="1.0"?>
<config>
<propel>
<datasources default="%default_connection%">
$config = array(
'propel' => $propelConfig
);
EOT
, array('%default_connection%' => $container->getParameter('propel.dbal.default_connection')));
$propelConfiguration = $container->get('propel.configuration');
foreach ($propelConfiguration['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);
file_put_contents($file, json_encode($config, JSON_PRETTY_PRINT));
}
/**
* Returns an array of properties as key/value pairs from an input file.
* Translates a list of connection names to their DSN equivalents.
*
* @param string $file A file properties.
* @return array An array of properties as key/value pairs.
* @param array $connections The names.
*
* @return array
*/
protected function getProperties($file)
protected function getConnections(array $connections)
{
$properties = array();
if (false === $lines = @file($file)) {
throw new \Exception(sprintf('Unable to parse contents of "%s".', $file));
$dsnList = array();
foreach ($connections as $connection) {
$dsnList[] = sprintf('%s=%s', $connection, $this->getDsn($connection));
}
foreach ($lines as $line) {
$line = trim($line);
return $dsnList;
}
if ('' == $line || in_array($line[0], array('#', ';'))) {
continue;
}
$pos = strpos($line, '=');
$property = trim(substr($line, 0, $pos));
$value = trim(substr($line, $pos + 1));
if ("true" === $value) {
$value = true;
} else if ("false" === $value) {
$value = false;
}
$properties[$property] = $value;
/**
* Get the data (host, user, ...) for a given connection.
*
* @param string $name The connection name.
*
* @return array The connection data.
*/
protected function getConnectionData($name)
{
$knownConnections = $this->getContainer()->getParameter('propel.configuration');
if (!isset($knownConnections['database']['connections'][$name])) {
throw new \InvalidArgumentException(sprintf('Unknown connection "%s"', $name));
}
return $properties;
return $knownConnections['database']['connections'][$name];
}
/**
* Get the DSN for a given connection.
*
* @param string $connectionName The connection name.
*
* @return string The DSN.
*/
protected function getDsn($connectionName)
{
$connection = $this->getConnectionData($connectionName);
// Add user and password to dsn string
$dsn = explode(';', $connection['dsn']);
if (isset($connection['user'])) {
$dsn[] = 'user=' . urlencode($connection['user']);
}
if (isset($connection['password'])) {
$dsn[] = 'password=' . urlencode($connection['password']);
}
return implode(';', $dsn);
}
/**
* @return \Symfony\Component\Config\FileLocatorInterface
*/
protected function getSchemaLocator()
{
return $this->getContainer()->get('propel.schema_locator');
}
/**
* @param string $namespace
*
* @return string
*/
protected function getPackageFromApp($namespace)
{
if ('\\' === $namespace[0]) {
$namespace = substr($namespace, 1);
}
if (0 === stripos($namespace, 'App\\')) {
$namespace = substr($namespace, 4);
}
return 'src.'.str_replace('\\', '.', $namespace);
}
/**
* @param Bundle $bundle
* @param string $namespace
*
* @return string
*/
protected function getPackageFromBundle(Bundle $bundle, $namespace)
{
//find relative path from namespace to bundle->getNamespace()
$baseNamespace = (new \ReflectionClass($bundle))->getNamespaceName();
if (0 === strpos($namespace, $baseNamespace)) {
//base namespace fits
//eg.
// Base: Jarves/JarvesBundle => Jarves
// Model namespace: Jarves\Model
// strpos(Jarves\Model, Jarves) === 0
// $namespaceDiff = Model
$namespaceDiff = substr($namespace, strlen($baseNamespace) + 1);
$bundlePath = realpath($bundle->getPath()) . '/' . str_replace('\\', '/', $namespaceDiff);
$appPath = realpath($this->getApplication()->getKernel()->getRootDir() . '/..');
$path = static::getRelativePath($bundlePath, $appPath);
return str_replace('/', '.', $path);
}
//does not match or its a absolute path, so return it without suffix
if ('\\' === $namespace[0]) {
$namespace = substr($namespace, 1);
}
return str_replace('\\', '.', $namespace);
}
/**
* Return the current Propel cache directory.
* @return string The current Propel cache directory.
*
* @return string The current Propel cache directory.
*/
protected function getCacheDir()
{
@ -408,52 +374,43 @@ EOT;
}
/**
* @return \Symfony\Component\Config\FileLocatorInterface
*/
protected function getFileLocator()
{
return $this->getContainer()->get('file_locator');
}
/**
* Get connection by checking the input option named 'connection'.
* Returns the default connection if no option specified or an exception
* if the specified connection doesn't exist.
* Returns a relative path from $path to $current.
*
* @param InputInterface $input
* @param OutputInterface $output
* @throw \InvalidArgumentException If the connection does not exist.
* @return array
* @param string $from
* @param string $to relative to this
*
* @return string relative path without trailing slash
*/
protected function getConnection(InputInterface $input, OutputInterface $output)
public static function getRelativePath($from, $to)
{
$propelConfiguration = $this->getContainer()->get('propel.configuration');
$name = $input->getOption('connection') ?: $this->getContainer()->getParameter('propel.dbal.default_connection');
$from = '/' . trim($from, '/');
$to = '/' . trim($to, '/');
if (isset($propelConfiguration['datasources'][$name])) {
$defaultConfig = $propelConfiguration['datasources'][$name];
} else {
throw new \InvalidArgumentException(sprintf('Connection named %s doesn\'t exist', $name));
if (0 === $pos = strpos($from, $to)) {
return substr($from, strlen($to) + ('/' === $to ? 0 : 1));
}
$output->writeln(sprintf('Use connection named <comment>%s</comment> in <comment>%s</comment> environment.',
$name, $this->getApplication()->getKernel()->getEnvironment()));
$result = '';
while ($to && false === strpos($from, $to)) {
$result .= '../';
$to = substr($to, 0, strrpos($to, '/'));
}
return array($name, $defaultConfig);
return !$to /*we reached root*/ ? $result . substr($from, 1) : $result. substr($from, strlen($to) + 1);
}
/**
* Extract the database name from a given DSN
*
* @param string $dsn A DSN
* @return string The database name extracted from the given DSN
* @param string $dsn A DSN
* @return string The database name extracted from the given DSN
*/
protected function parseDbName($dsn)
{
preg_match('#dbname=([a-zA-Z0-9\_]+)#', $dsn, $matches);
preg_match('#(dbname|Database)=([a-zA-Z0-9\_]+)#', $dsn, $matches);
if (isset($matches[1])) {
return $matches[1];
if (isset($matches[2])) {
return $matches[2];
}
// e.g. SQLite
@ -461,151 +418,37 @@ EOT;
}
/**
* Check the PropelConfiguration object.
*/
protected function checkConfiguration()
{
$parameters = $this->getContainer()->get('propel.configuration')->getParameters();
if (!isset($parameters['datasources']) || 0 === count($parameters['datasources'])) {
throw new \RuntimeException('Propel should be configured (no database configuration found).');
}
}
/**
* Write Propel output as summary based on a Regexp.
* Returns the name of the migrations table.
*
* @param OutputInterface $output The output object.
* @param string $taskname A task name
*/
protected function writeSummary(OutputInterface $output, $taskname)
{
foreach (explode("\n", $this->buffer) as $line) {
if (false !== strpos($line, '[' . $taskname . ']')) {
$arr = preg_split('#\[' . $taskname . '\] #', $line);
$info = $arr[1];
if ('"' === $info[0]) {
$info = sprintf('<info>%s</info>', $info);
}
$output->writeln($info);
}
}
}
/**
* Comes from the SensioGeneratorBundle.
* @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.
*/
protected function writeSection(OutputInterface $output, $text, $style = 'bg=blue;fg=white')
{
$output->writeln(array(
'',
$this->getHelperSet()->get('formatter')->formatBlock($text, $style, true),
'',
));
}
/**
* 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.
*/
protected function writeTaskError($output, $taskName, $more = true)
{
$moreText = $more ? ' To get more details, run the command with the "--verbose" option.' : '';
return $this->writeSection($output, array(
'[Propel] Error',
'',
'An error has occured during the "' . $taskName . '" task process.' . $moreText
), 'fg=white;bg=red');
}
/**
* @param OutputInterface $output The output.
* @param string $filename The filename.
*/
protected function writeNewFile(OutputInterface $output, $filename)
{
$output->writeln('>> <info>File+</info> ' . $filename);
}
/**
* @param OutputInterface $output The output.
* @param string $directory The directory.
*/
protected function writeNewDirectory(OutputInterface $output, $directory)
{
$output->writeln('>> <info>Dir+</info> ' . $directory);
}
/**
* Ask confirmation from the user.
*
* @param OutputInterface $output The output.
* @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);
}
/**
* @param \SplFileInfo $schema
* @param BundleInterface $bundle
* @return string
*/
protected function transformToLogicalName(\SplFileInfo $schema, BundleInterface $bundle)
protected function getMigrationsTable()
{
$schemaPath = str_replace(
$bundle->getPath(). DIRECTORY_SEPARATOR . 'Resources' . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR,
'',
$schema->getRealPath()
);
$config = $this->getContainer()->getParameter('propel.configuration');
return sprintf('@%s/Resources/config/%s', $bundle->getName(), $schemaPath);
return $config['migrations']['tableName'];
}
/**
* Compiles arguments/properties for the Phing process.
* @return array
* Returns the name of the default connection.
*
* @return string
*/
private function getPhingArguments(KernelInterface $kernel, $workingDirectory, $properties)
protected function getDefaultConnection()
{
$args = array();
$config = $this->getContainer()->getParameter('propel.configuration');
// Default properties
$properties = array_merge(array(
'propel.database' => 'mysql',
'project.dir' => $workingDirectory,
'propel.output.dir' => $kernel->getRootDir().'/propel',
'propel.php.dir' => $kernel->getRootDir().'/..',
'propel.packageObjectModel' => true,
), $properties);
return !empty($config['generator']['defaultConnection']) ? $config['generator']['defaultConnection'] : key($config['database']['connections']);
}
// Adding user defined properties from the configuration
$properties = array_merge(
$properties,
$this->getContainer()->get('propel.build_properties')->getProperties()
);
foreach ($properties as $key => $value) {
$args[] = "-D$key=$value";
}
// Build file
$args[] = '-f';
$args[] = realpath($this->getContainer()->getParameter('propel.path').'/generator/build.xml');
return $args;
/**
* Reads the platform class from the configuration
*
* @return string The platform class name.
*/
protected function getPlatform()
{
$config = $this->getContainer()->getParameter('propel.configuration');
return $config['generator']['platformClass'];
}
}

136
Command/AclInitCommand.php Normal file
View file

@ -0,0 +1,136 @@
<?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\Bundle\PropelBundle\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\Bundle\BundleInterface;
use Symfony\Component\HttpKernel\KernelInterface;
/**
* @author Toni Uebernickel <tuebernickel@gmail.com>
*/
class AclInitCommand extends AbstractCommand
{
protected function configure()
{
$this
->setDescription('Initialize "Access Control Lists" model and SQL')
->addOption('force', null, InputOption::VALUE_NONE, 'Set this parameter to execute this action.')
->addOption('connection', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, 'Connection to use. Example: default, bookstore')
->setHelp(<<<EOT
The <info>%command.name%</info> command connects to the database and executes all SQL statements required to setup the ACL database, it also generates the ACL model.
<info>php %command.full_name%</info>
The <info>--force</info> parameter has to be used to actually insert SQL.
The <info>--connection</info> parameter allows you to change the connection to use.
The default connection is the active connection (propel.dbal.default_connection).
EOT
)
->setName('propel:acl:init')
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$outputDir = realpath($this->getApplication()->getKernel()->getRootDir().'/../');
// Generate ACL model
$modelBuildCmd = new \Propel\Generator\Command\ModelBuildCommand();
$modelBuildArgs = array(
'--output-dir' => $outputDir,
);
if ($this->runCommand($modelBuildCmd, $modelBuildArgs, $input, $output) === 0) {
$output->writeln(sprintf(
'>> <info>%20s</info> Generated model classes from <comment>%s</comment>',
$this->getApplication()->getKernel()->getBundle('PropelBundle')->getName(),
'acl_schema.xml'
));
} else {
$this->writeTaskError($output, 'model:build');
return 1;
}
// Prepare SQL
$sqlBuildCmd = new \Propel\Generator\Command\SqlBuildCommand();
$sqlBuildArgs = array(
'--connection' => $this->getConnections($input->getOption('connection')),
'--output-dir' => $this->getCacheDir(),
);
if ($this->runCommand($sqlBuildCmd, $sqlBuildArgs, $input, $output) === 0) {
$this->writeSection(
$output,
'<comment>1</comment> <info>SQL file has been generated.</info>'
);
} else {
$this->writeTaskError($output, 'sql:build');
return 2;
}
if ($input->getOption('force')) {
// insert sql
$sqlInsertCmd = new \Propel\Generator\Command\SqlInsertCommand();
$sqlInsertArgs = array(
'--connection' => $this->getConnections($input->getOption('connection')),
'--sql-dir' => $this->getCacheDir(),
);
if ($this->runCommand($sqlInsertCmd, $sqlInsertArgs, $input, $output) === 0) {
$this->writeSection(
$output,
'<comment>1</comment> <info>SQL file has been inserted.</info>'
);
} else {
$this->writeTaskError($output, 'sql:insert');
return 3;
}
}
return 0;
}
/**
* {@inheritdoc}
*
* @note We override this method to only return the acl-related schema
*/
protected function getFinalSchemas(KernelInterface $kernel, BundleInterface $bundle = null)
{
$aclSchema = new \SplFileInfo($kernel->locateResource('@PropelBundle/Resources/acl_schema.xml'));
return array(
array($kernel->getBundle('PropelBundle'), $aclSchema)
);
}
/**
* {@inheritdoc}
*
* @note We override this method to modify the cache directory
*/
protected function initialize(InputInterface $input, OutputInterface $output)
{
parent::initialize($input, $output);
$this->cacheDir = $this->cacheDir . '/acl';
$this->setupBuildTimeFiles();
}
}

View file

@ -8,40 +8,35 @@
* @license MIT License
*/
namespace Propel\PropelBundle\Command;
namespace Propel\Bundle\PropelBundle\Command;
use Propel\PropelBundle\Command\AbstractCommand;
use Propel\PropelBundle\Command\ModelBuildCommand;
use Propel\PropelBundle\Command\SqlBuildCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
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\Console\Output\Output;
/**
* BuildCommand.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @author William DURAND <william.durand1@gmail.com>
* @author Kévin Gomez <contact@kevingomez.fr>
*/
class BuildCommand extends AbstractCommand
class BuildCommand extends ContainerAwareCommand
{
/**
* @see Command
* {@inheritdoc}
*/
protected function configure()
{
$this
->setName('propel:build')
->setDescription('Hub for Propel build commands (Model classes, SQL)')
->setDefinition(array(
new InputOption('classes', '', InputOption::VALUE_NONE, 'Build only classes'),
new InputOption('sql', '', InputOption::VALUE_NONE, 'Build only SQL'),
new InputOption('insert-sql', '', InputOption::VALUE_NONE, 'Build all and insert SQL'),
new InputOption('connection', null, InputOption::VALUE_OPTIONAL, 'Set this parameter to define a connection to use')
new InputOption('connection', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, 'Connection to use. Example: default, bookstore')
))
->setName('propel:build');
;
}
/**
@ -52,26 +47,34 @@ class BuildCommand extends AbstractCommand
protected function execute(InputInterface $input, OutputInterface $output)
{
if (!$input->getOption('sql')) {
$modelCommand = new ModelBuildCommand();
$modelCommand->setApplication($this->getApplication());
$modelCommand->execute($input, $output);
$in = new ArrayInput(array(
'command' => 'propel:model:build',
'--connection' => $input->getOption('connection'),
'--verbose' => $input->getOption('verbose')
));
$cmd = $this->getApplication()->find('propel:model:build');
$cmd->run($in, $output);
}
if (!$input->getOption('classes')) {
$sqlCommand = new SqlBuildCommand();
$sqlCommand->setApplication($this->getApplication());
$sqlCommand->execute($input, $output);
$in = new ArrayInput(array(
'command' => 'propel:build:sql',
'--connection' => $input->getOption('connection'),
'--verbose' => $input->getOption('verbose'),
));
$cmd = $this->getApplication()->find('propel:sql:build');
$cmd->run($in, $output);
}
if ($input->getOption('insert-sql')) {
$insertCommand = new SqlInsertCommand();
$insertCommand->setApplication($this->getApplication());
// By-pass the '--force' required option
$this->addOption('force', '', InputOption::VALUE_NONE, '');
$input->setOption('force', true);
$insertCommand->execute($input, $output);
$in = new ArrayInput(array(
'command' => 'propel:sql:insert',
'--connection' => $input->getOption('connection'),
'--force' => true,
'--verbose' => $input->getOption('verbose'),
));
$cmd = $this->getApplication()->find('propel:sql:insert');
$cmd->run($in, $output);
}
}
}

65
Command/BundleTrait.php Normal file
View file

@ -0,0 +1,65 @@
<?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\Bundle\PropelBundle\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
/**
* @author Moritz Schroeder <moritz.schroeder@molabs.de>
*/
trait BundleTrait
{
/**
* @return ContainerInterface
*/
protected abstract function getContainer();
/**
* Returns the selected bundle.
* If no bundle argument is set, the user will get ask for it.
*
* @param InputInterface $input
* @param OutputInterface $output
*
* @return BundleInterface
*/
protected function getBundle(InputInterface $input, OutputInterface $output)
{
$kernel = $this
->getContainer()
->get('kernel');
if ($input->hasArgument('bundle') && '@' === substr($input->getArgument('bundle'), 0, 1)) {
return $kernel->getBundle(substr($input->getArgument('bundle'), 1));
}
$bundleNames = array_keys($kernel->getBundles());
do {
$question = '<info>Select the bundle</info>: ';
$question = new Question($question);
$question->setAutocompleterValues($bundleNames);
$bundleName = $this->getHelperSet()->get('question')->ask($input, $output, $question);
if (in_array($bundleName, $bundleNames)) {
break;
}
$output->writeln(sprintf('<bg=red>Bundle "%s" does not exist.</bg>', $bundleName));
} while (true);
return $kernel->getBundle($bundleName);
}
}

View file

@ -8,9 +8,10 @@
* @license MIT License
*/
namespace Propel\PropelBundle\Command;
namespace Propel\Bundle\PropelBundle\Command;
use Propel\PropelBundle\Command\AbstractCommand;
use Propel\Runtime\Connection\ConnectionManagerSingle;
use Propel\Runtime\Propel;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\InputOption;
@ -29,9 +30,11 @@ class DatabaseCreateCommand extends AbstractCommand
protected function configure()
{
$this
->setName('propel:database:create')
->setDescription('Create a given database or the default one.')
->addOption('connection', null, InputOption::VALUE_OPTIONAL, 'Set this parameter to define a connection to use')
->setName('propel:database:create');
;
}
/**
@ -41,10 +44,9 @@ class DatabaseCreateCommand extends AbstractCommand
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->writeSection($output, '[Propel] You are running the command: propel:database:create');
list($name, $config) = $this->getConnection($input, $output);
$dbName = $this->parseDbName($config['connection']['dsn']);
$connectionName = $input->getOption('connection') ?: $this->getDefaultConnection();
$config = $this->getConnectionData($connectionName);
$dbName = $this->parseDbName($config['dsn']);
if (null === $dbName) {
return $output->writeln('<error>No database name found.</error>');
@ -52,21 +54,19 @@ class DatabaseCreateCommand extends AbstractCommand
$query = 'CREATE DATABASE '. $dbName .';';
}
try {
\Propel::setConfiguration($this->getTemporaryConfiguration($name, $config));
$connection = \Propel::getConnection($name);
$manager = new ConnectionManagerSingle();
$manager->setConfiguration($this->getTemporaryConfiguration($config));
$statement = $connection->prepare($query);
$statement->execute();
$serviceContainer = Propel::getServiceContainer();
$serviceContainer->setAdapterClass($connectionName, $config['adapter']);
$serviceContainer->setConnectionManager($connectionName, $manager);
$output->writeln(sprintf('<info>Database <comment>%s</comment> has been created.</info>', $dbName));
} catch (\Exception $e) {
$this->writeSection($output, array(
'[Propel] Exception catched',
'',
$e->getMessage()
), 'fg=white;bg=red');
}
$connection = Propel::getConnection($connectionName);
$statement = $connection->prepare($query);
$statement->execute();
$output->writeln(sprintf('<info>Database <comment>%s</comment> has been created.</info>', $dbName));
}
/**
@ -75,22 +75,19 @@ class DatabaseCreateCommand extends AbstractCommand
*
* @see https://github.com/doctrine/doctrine1/blob/master/lib/Doctrine/Connection.php#L1491
*
* @param string $name A connection name.
* @param array $config A Propel connection configuration.
* @param array $config A Propel connection configuration.
* @return array
*/
private function getTemporaryConfiguration($name, $config)
private function getTemporaryConfiguration($config)
{
$dbName = $this->parseDbName($config['connection']['dsn']);
$dbName = $this->parseDbName($config['dsn']);
$config['connection']['dsn'] = preg_replace(
'#dbname='.$dbName.'#',
'dbname='.strtolower($config['adapter']),
$config['connection']['dsn']
$config['dsn'] = preg_replace(
'#;?(dbname|Database)='.$dbName.'#',
'',
$config['dsn']
);
return array(
'datasources' => array($name => $config)
);
return $config;
}
}

View file

@ -8,12 +8,13 @@
* @license MIT License
*/
namespace Propel\PropelBundle\Command;
namespace Propel\Bundle\PropelBundle\Command;
use Propel\PropelBundle\Command\AbstractCommand;
use Propel\Runtime\Propel;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Propel\Runtime\Connection\ConnectionManagerSingle;
/**
* DatabaseDropCommand class.
@ -24,14 +25,13 @@ use Symfony\Component\Console\Input\InputOption;
class DatabaseDropCommand extends AbstractCommand
{
/**
* @see Command
* {@inheritdoc}
*/
protected function configure()
{
$this
->setName('propel:database:drop')
->setDescription('Drop a given database or the default one.')
->addOption('force', null, InputOption::VALUE_NONE, 'Set this parameter to execute this action.')
->addOption('connection', null, InputOption::VALUE_OPTIONAL, 'Set this parameter to define a connection to use')
->setHelp(<<<EOT
The <info>propel:database:drop</info> command will drop your database.
@ -42,53 +42,79 @@ The <info>--connection</info> parameter allows you to change the connection to u
The default connection is the active connection (propel.dbal.default_connection).
EOT
)
->setName('propel:database:drop');
->addOption('connection', null, InputOption::VALUE_OPTIONAL, 'Connection to use. Example: default, bookstore')
->addOption('force', null, InputOption::VALUE_NONE, 'Set this parameter to execute this action.')
;
}
/**
* @see Command
*
* @throws \InvalidArgumentException When the target directory does not exist
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->writeSection($output, '[Propel] You are running the command: propel:database:drop');
if ($input->getOption('force')) {
if ('prod' === $this->getApplication()->getKernel()->getEnvironment()) {
$this->writeSection($output, 'WARNING: you are about to drop a database in production !', 'bg=red;fg=white');
if (false === $this->askConfirmation($output, 'Are you sure ? (y/n) ', false)) {
$output->writeln('Aborted, nice decision !');
return -2;
}
}
list($name, $config) = $this->getConnection($input, $output);
$dbName = $this->parseDbName($config['connection']['dsn']);
if (null === $dbName) {
return $output->writeln('<error>No database name found.</error>');
} else {
$query = 'DROP DATABASE '. $dbName .';';
}
try {
$connection = \Propel::getConnection($name);
$statement = $connection->prepare($query);
$statement->execute();
$output->writeln(sprintf('<info>Database <comment>%s</comment> has been dropped.</info>', $dbName));
} catch (\Exception $e) {
$this->writeSection($output, array(
'[Propel] Exception catched',
'',
$e->getMessage()
), 'fg=white;bg=red');
}
} else {
if (!$input->getOption('force')) {
$output->writeln('<error>You have to use the "--force" option to drop the database.</error>');
return;
}
if ('prod' === $this->getApplication()->getKernel()->getEnvironment()) {
$this->writeSection($output, 'WARNING: you are about to drop a database in production !', 'bg=red;fg=white');
if (false === $this->askConfirmation($input, $output, 'Are you sure ? (y/n) ', false)) {
$output->writeln('Aborted, nice decision !');
return -2;
}
}
$connectionName = $input->getOption('connection') ?: $this->getDefaultConnection();
$config = $this->getConnectionData($connectionName);
$connection = Propel::getConnection($connectionName);
$dbName = $this->parseDbName($config['dsn']);
if (null === $dbName) {
return $output->writeln('<error>No database name found.</error>');
} else {
$query = 'DROP DATABASE '. $dbName .';';
}
$manager = new ConnectionManagerSingle();
$manager->setConfiguration($this->getTemporaryConfiguration($config));
$serviceContainer = Propel::getServiceContainer();
$serviceContainer->setAdapterClass($connectionName, $config['adapter']);
$serviceContainer->setConnectionManager($connectionName, $manager);
$connection = Propel::getConnection($connectionName);
$statement = $connection->prepare($query);
$statement->execute();
$output->writeln(sprintf('<info>Database <comment>%s</comment> has been dropped.</info>', $dbName));
}
/**
* Create a temporary configuration to connect to the database in order
* to create a given database. This idea comes from Doctrine1.
*
* @see https://github.com/doctrine/doctrine1/blob/master/lib/Doctrine/Connection.php#L1491
*
* @param array $config A Propel connection configuration.
* @return array
*/
private function getTemporaryConfiguration($config)
{
$dbName = $this->parseDbName($config['dsn']);
$config['dsn'] = preg_replace(
'#;?(dbname|Database)='.$dbName.'#',
'',
$config['dsn']
);
return $config;
}
}

View file

@ -0,0 +1,63 @@
<?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\Bundle\PropelBundle\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Propel\Generator\Command\DatabaseReverseCommand as BaseDatabaseReverseCommand;
/**
* @author Kévin Gomez <contact@kevingomez.fr>
*/
class DatabaseReverseCommand extends WrappedCommand
{
/**
* {@inheritdoc}
*/
protected function configure()
{
parent::configure();
$this
->setName('propel:database:reverse')
->setDescription('Reverse-engineer a XML schema file based on given database')
->addArgument('connection', InputArgument::REQUIRED, 'Connection to use. Example: "default"')
->addOption('output-dir', null, InputOption::VALUE_REQUIRED, 'The output directory', BaseDatabaseReverseCommand::DEFAULT_OUTPUT_DIRECTORY)
->addOption('database-name', null, InputOption::VALUE_REQUIRED, 'The database name to reverse', BaseDatabaseReverseCommand::DEFAULT_DATABASE_NAME)
->addOption('schema-name', null, InputOption::VALUE_REQUIRED, 'The schema name to generate', BaseDatabaseReverseCommand::DEFAULT_SCHEMA_NAME)
;
}
/**
* {@inheritdoc}
*/
protected function createSubCommandInstance()
{
return new BaseDatabaseReverseCommand();
}
/**
* {@inheritdoc}
*/
protected function getSubCommandArguments(InputInterface $input)
{
return array(
'--output-dir' => $input->getOption('output-dir'),
'--database-name' => $input->getOption('database-name'),
'--schema-name' => $input->getOption('schema-name'),
// this one is an argument, so no leading '--'
'connection' => $this->getDsn($input->getArgument('connection')),
);
}
}

View file

@ -8,13 +8,12 @@
* @license MIT License
*/
namespace Propel\PropelBundle\Command;
namespace Propel\Bundle\PropelBundle\Command;
use Propel\PropelBundle\Command\AbstractCommand;
use Propel\PropelBundle\DataFixtures\Dumper\YamlDataDumper;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Filesystem\Filesystem;
/**
* FixturesDumpCommand.
@ -27,7 +26,7 @@ class FixturesDumpCommand extends AbstractCommand
* Default fixtures directory.
* @var string
*/
private $defaultFixturesDir = 'app/propel/fixtures';
protected $defaultFixturesDir = 'app/propel/fixtures';
/**
* @see Command
@ -35,50 +34,49 @@ class FixturesDumpCommand extends AbstractCommand
protected function configure()
{
$this
->setName('propel:fixtures:dump')
->setDescription('Dump data from database into YAML fixtures file.')
->addOption('connection', null, InputOption::VALUE_OPTIONAL, 'Set this parameter to define a connection to use')
->setHelp(<<<EOT
The <info>propel:fixtures:dump</info> dumps data from database into YAML fixtures file.
<info>php app/console propel:fixtures:dump</info>
The <info>--connection</info> parameter allows you to change the connection to use.
The <info>--dir</info> parameter allows you to change the output directory.
The default connection is the active connection (propel.dbal.default_connection).
EOT
)
->setName('propel:fixtures:dump')
->addOption('connection', null, InputOption::VALUE_OPTIONAL, 'Set this parameter to define a connection to use')
->addOption('dir', null, InputOption::VALUE_OPTIONAL, 'Set this parameter to define a fixture directory')
;
}
/**
* @see Command
*
* @throws \InvalidArgumentException When the target directory does not exist
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->writeSection($output, '[Propel] You are running the command: propel:fixtures:dump');
$fixtureDir = $input->getOption('dir') ?: $this->defaultFixturesDir;
$path = realpath($this->getApplication()->getKernel()->getRootDir() . '/../') . '/' . $fixtureDir;
list($name, $defaultConfig) = $this->getConnection($input, $output);
$path = realpath($this->getApplication()->getKernel()->getRootDir() . '/../') . '/' . $this->defaultFixturesDir;
$filename = $path . '/fixtures_' . time() . '.yml';
$dumper = new YamlDataDumper($this->getApplication()->getKernel()->getRootDir());
try {
$dumper->dump($filename, $name);
} catch (\Exception $e) {
$this->writeSection($output, array(
'[Propel] Exception',
'',
$e->getMessage()), 'fg=white;bg=red');
return false;
if (!file_exists($path)) {
$output->writeln("<info>The $path folder does not exists.</info>");
if ($this->askConfirmation($input, $output, "<question>Do you want me to create it for you ?</question> [Yes]")) {
$fs = new Filesystem();
$fs->mkdir($path);
$this->writeNewDirectory($output, $path);
} else {
throw new \IOException(sprintf('Unable to find the %s folder', $path));
}
}
$output->writeln('');
$output->writeln(sprintf('>> <info>File+</info> %s', $filename));
$filename = $path . '/fixtures_' . time() . '.yml';
$dumper = $this->getContainer()->get('propel.dumper.yaml');
$dumper->dump($filename, $input->getOption('connection'));
$this->writeNewFile($output, $filename);
return true;
}

View file

@ -8,8 +8,9 @@
* @license MIT License
*/
namespace Propel\PropelBundle\Command;
namespace Propel\Bundle\PropelBundle\Command;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
@ -18,10 +19,6 @@ use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Finder\Finder;
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
use Propel\PropelBundle\Command\AbstractCommand;
use Propel\PropelBundle\DataFixtures\Loader\YamlDataLoader;
use Propel\PropelBundle\DataFixtures\Loader\XmlDataLoader;
/**
* FixturesLoadCommand
*
@ -53,17 +50,8 @@ class FixturesLoadCommand extends AbstractCommand
protected function configure()
{
$this
->setName('propel:fixtures:load')
->setDescription('Load XML, SQL and/or YAML fixtures')
->addArgument('bundle', InputArgument::OPTIONAL, 'The bundle to load fixtures from')
->addOption(
'dir', 'd', InputOption::VALUE_OPTIONAL,
'The directory where XML, SQL and/or YAML fixtures files are located',
$this->defaultFixturesDir
)
->addOption('xml', '', InputOption::VALUE_NONE, 'Load XML fixtures')
->addOption('sql', '', InputOption::VALUE_NONE, 'Load SQL fixtures')
->addOption('yml', '', InputOption::VALUE_NONE, 'Load YAML fixtures')
->addOption('connection', null, InputOption::VALUE_OPTIONAL, 'Set this parameter to define a connection to use')
->setHelp(<<<EOT
The <info>propel:fixtures:load</info> loads <info>XML</info>, <info>SQL</info> and/or <info>YAML</info> fixtures.
@ -107,19 +95,25 @@ YAML fixtures are:
</comment>
EOT
)
->setName('propel:fixtures:load')
;
->addArgument('bundle', InputArgument::OPTIONAL, 'The bundle to load fixtures from')
->addOption(
'dir', 'd', InputOption::VALUE_OPTIONAL,
'The directory where XML, SQL and/or YAML fixtures files are located',
$this->defaultFixturesDir
)
->addOption('xml', '', InputOption::VALUE_NONE, 'Load XML fixtures')
->addOption('sql', '', InputOption::VALUE_NONE, 'Load SQL fixtures')
->addOption('yml', '', InputOption::VALUE_NONE, 'Load YAML fixtures')
->addOption('connection', null, InputOption::VALUE_OPTIONAL, 'Set this parameter to define a connection to use')
;
}
/**
* @see Command
*
* @throws \InvalidArgumentException When the target directory does not exist
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->writeSection($output, '[Propel] You are running the command: propel:fixtures:load');
$this->filesystem = new Filesystem();
if (null !== $this->bundle) {
@ -128,33 +122,29 @@ EOT
$this->absoluteFixturesPath = realpath($this->getApplication()->getKernel()->getRootDir() . '/../' . $input->getOption('dir'));
}
if ($input->getOption('verbose')) {
$this->additionalPhingArgs[] = 'verbose';
}
if (!$this->absoluteFixturesPath && !file_exists($this->absoluteFixturesPath)) {
return $this->writeSection($output, array(
'The fixtures directory "' . $this->absoluteFixturesPath . '" does not exist.'
), 'fg=white;bg=red');
}
$noOptions = (!$input->getOption('xml') && !$input->getOption('sql') && !$input->getOption('yml'));
$noOptions = !$input->getOption('xml') && !$input->getOption('sql') && !$input->getOption('yml');
if ($input->getOption('sql') || $noOptions) {
if (-1 === $this->loadSqlFixtures($input, $output)) {
$output->writeln('<info>No SQL fixtures found.</info>');
$output->writeln('No <info>SQL</info> fixtures found.');
}
}
if ($input->getOption('xml') || $noOptions) {
if (-1 === $this->loadFixtures($input, $output, 'xml')) {
$output->writeln('<info>No XML fixtures found.</info>');
$output->writeln('No <info>XML</info> fixtures found.');
}
}
if ($input->getOption('yml') || $noOptions) {
if (-1 === $this->loadFixtures($input, $output, 'yml')) {
$output->writeln('<info>No YAML fixtures found.</info>');
$output->writeln('No <info>YML</info> fixtures found.');
}
}
}
@ -162,9 +152,9 @@ EOT
/**
* Load fixtures
*
* @param \Symfony\Component\Console\Input\InputInterface $input
* @param \Symfony\Component\Console\Input\InputInterface $input
* @param \Symfony\Component\Console\Output\OutputInterface $output
* @return void
* @param string $type If specified, only fixtures with the given type will be loaded (yml, xml).
*/
protected function loadFixtures(InputInterface $input, OutputInterface $output, $type = null)
{
@ -178,32 +168,19 @@ EOT
return -1;
}
list($name, $defaultConfig) = $this->getConnection($input, $output);
$connectionName = $input->getOption('connection') ?: $this->getDefaultConnection();
if ('yml' === $type) {
$loader = new YamlDataLoader($this->getApplication()->getKernel()->getRootDir());
$loader = $this->getContainer()->get('propel.loader.yaml');
} elseif ('xml' === $type) {
$loader = new XmlDataLoader($this->getApplication()->getKernel()->getRootDir());
$loader = $this->getContainer()->get('propel.loader.xml');
} else {
return;
}
try {
$nb = $loader->load($datas, $name);
} catch (\Exception $e) {
$this->writeSection($output, array(
'[Propel] Exception',
'',
$e->getMessage()), 'fg=white;bg=red');
$nb = $loader->load($datas, $connectionName);
return false;
}
$this->writeSection(
$output,
sprintf('<comment>%s</comment> %s fixtures file%s loaded.', $nb, strtoupper($type), $nb > 1 ? 's' : ''),
'fg=white;bg=black'
);
$output->writeln(sprintf('<comment>%s</comment> %s fixtures file%s loaded.', $nb, strtoupper($type), $nb > 1 ? 's' : ''));
return true;
}
@ -211,36 +188,35 @@ EOT
/**
* Load SQL fixtures
*
* @param \Symfony\Component\Console\Input\InputInterface $input
* @param \Symfony\Component\Console\Input\InputInterface $input
* @param \Symfony\Component\Console\Output\OutputInterface $output
* @return void
*/
protected function loadSqlFixtures(InputInterface $input, OutputInterface $output)
{
$tmpdir = $this->getApplication()->getKernel()->getRootDir() . '/cache/propel';
$tmpdir = $this->getCacheDir();
$datas = $this->getFixtureFiles('sql');
$this->prepareCache($tmpdir);
list($name, $defaultConfig) = $this->getConnection($input, $output);
$connectionName = $input->getOption('connection') ?: $this->getContainer()->getParameter('propel.dbal.default_connection');
// Create a "sqldb.map" file
$sqldbContent = '';
foreach($datas as $data) {
foreach ($datas as $data) {
$output->writeln(sprintf('<info>Loading SQL fixtures from</info> <comment>%s</comment>.', $data));
$sqldbContent .= $data->getFilename() . '=' . $name . PHP_EOL;
$this->filesystem->copy($data, $tmpdir . '/fixtures/' . $data->getFilename(), true);
$sqldbContent .= $data->getFilename() . '=' . $connectionName . PHP_EOL;
$this->filesystem->copy($data, $tmpdir . '/' . $data->getFilename(), true);
}
if ('' === $sqldbContent) {
return -1;
}
$sqldbFile = $tmpdir . '/fixtures/sqldb.map';
$sqldbFile = $tmpdir . '/sqldb.map';
file_put_contents($sqldbFile, $sqldbContent);
if (!$this->insertSql($defaultConfig, $tmpdir . '/fixtures', $tmpdir, $output)) {
if (!$this->insertSql($connectionName, $input, $output)) {
return -1;
}
@ -252,60 +228,60 @@ EOT
/**
* Prepare the cache directory
*
* @param string $tmpdir The temporary directory path.
* @param string $tmpdir The temporary directory path.
*/
protected function prepareCache($tmpdir)
{
// Recreate a propel directory in cache
$this->filesystem->remove($tmpdir);
$this->filesystem->mkdir($tmpdir);
$fixturesdir = $tmpdir . '/fixtures/';
$this->filesystem->remove($fixturesdir);
$this->filesystem->mkdir($fixturesdir);
}
/**
* Insert SQL
*/
protected function insertSql($config, $sqlDir, $schemaDir, $output)
protected function insertSql($connectionName, InputInterface $input, OutputInterface $output)
{
// Insert SQL
$ret = $this->callPhing('insert-sql', array(
'propel.database.url' => $config['connection']['dsn'],
'propel.database.database' => $config['adapter'],
'propel.database.user' => $config['connection']['user'],
'propel.database.password' => $config['connection']['password'],
'propel.schema.dir' => $schemaDir,
'propel.sql.dir' => $sqlDir,
));
$parameters = array(
'--connection' => array($connectionName),
'--verbose' => $input->getOption('verbose'),
'--sql-dir' => $this->getCacheDir(),
'--force' => 'force'
);
if (true === $ret) {
$this->writeSection($output, array(
'', 'All SQL statements have been inserted.'
), 'fg=green;bg=black');
// add the command's name to the parameters
array_unshift($parameters, $this->getName());
$command = $this->getApplication()->find('propel:sql:insert');
$command->setApplication($this->getApplication());
// and run the sub-command
$ret = $command->run(new ArrayInput($parameters), $output);
if ($ret === 0) {
$output->writeln('All SQL statements have been inserted.');
} else {
$this->writeTaskError($output, 'insert-sql', false);
return false;
}
return true;
return $ret === 0;
}
/**
* Returns the fixtures files to load.
*
* @param string $type The extension of the files.
* @param string $in The directory in which we search the files. If null,
* we'll use the absoluteFixturesPath property.
* @param string $type The extension of the files.
* @param string $in The directory in which we search the files. If null,
* we'll use the absoluteFixturesPath property.
*
* @return \Iterator An iterator through the files.
*/
protected function getFixtureFiles($type = 'sql', $in = null)
{
$finder = new Finder();
$finder->sortByName()->name('*.' . $type);
$finder->sort(function ($a, $b) {
return strcmp($a->getPathname(), $b->getPathname());
})->name('*.' . $type);
$files = $finder->in(null !== $in ? $in : $this->absoluteFixturesPath);
@ -326,10 +302,20 @@ EOT
/**
* Returns the path the command will look into to find fixture files
*
* @param BundleInterface $bundle The bundle to explore.
*
* @return String
*/
protected function getFixturesPath(BundleInterface $bundle)
{
return $bundle->getPath() . DIRECTORY_SEPARATOR . 'Resources' . DIRECTORY_SEPARATOR . 'fixtures';
}
/**
* @return \Symfony\Component\Config\FileLocatorInterface
*/
protected function getFileLocator()
{
return $this->getContainer()->get('file_locator');
}
}

View file

@ -1,6 +1,5 @@
<?php
/**
* This file is part of the PropelBundle package.
* For the full copyright and license information, please view the LICENSE
@ -9,64 +8,91 @@
* @license MIT License
*/
namespace Propel\PropelBundle\Command;
namespace Propel\Bundle\PropelBundle\Command;
use Propel\Bundle\PropelBundle\Form\FormBuilder;
use Propel\Generator\Config\GeneratorConfig;
use Propel\Generator\Model\Database;
use Propel\Generator\Model\Table;
use Propel\Generator\Manager\ModelManager;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Finder\Finder;
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
/**
* @author William DURAND <william.durand1@gmail.com>
*/
class FormGenerateCommand extends GeneratorAwareCommand
class FormGenerateCommand extends AbstractCommand
{
const DEFAULT_FORM_TYPE_DIRECTORY = '/Form/Type';
use BundleTrait;
/**
* @see Command
* {@inheritdoc}
*/
protected function configure()
{
$this
->setName('propel:form:generate')
->setDescription('Generate Form types stubs based on the schema.xml')
->addArgument('bundle', InputArgument::REQUIRED, 'The bundle to use to generate Form types')
->addArgument('models', InputArgument::IS_ARRAY, 'Model classes to generate Form Types from')
->addOption('force', 'f', InputOption::VALUE_NONE, 'Overwrite existing Form types')
->addOption('platform', null, InputOption::VALUE_REQUIRED, 'The platform')
->addArgument('bundle', InputArgument::OPTIONAL, 'The bundle to use to generate Form types (Ex: @AcmeDemoBundle)')
->addArgument('models', InputArgument::IS_ARRAY, 'Model classes to generate Form Types from')
->setHelp(<<<EOT
The <info>%command.name%</info> command allows you to quickly generate Form Type stubs for a given bundle.
<info>php app/console %command.full_name%</info>
<info>php app/console %command.full_name%</info>
The <info>--force</info> parameter allows you to overwrite existing files.
EOT
)
->setName('propel:form:generate');
);
}
/**
* @see Command
*
* @throws \InvalidArgumentException When the target directory does not exist
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
if ($schemas = $this->getSchemasFromBundle($this->bundle)) {
foreach ($schemas as $fileName => $array) {
foreach ($this->getDatabasesFromSchema($array[1]) as $database) {
$this->createFormTypeFromDatabase($this->bundle, $database, $input->getArgument('models'), $output, $input->getOption('force'));
}
$kernel = $this->getApplication()->getKernel();
$models = $input->getArgument('models');
$force = $input->getOption('force');
$bundle = $this->getBundle($input, $output);
$this->setupBuildTimeFiles();
$schemas = $this->getFinalSchemas($kernel, $bundle);
if (!$schemas) {
$output->writeln(sprintf('No <comment>*schemas.xml</comment> files found in bundle <comment>%s</comment>.', $bundle->getName()));
return;
}
$manager = $this->getModelManager($input, $schemas);
foreach ($manager->getDataModels() as $dataModel) {
foreach ($dataModel->getDatabases() as $database) {
$this->createFormTypeFromDatabase($bundle, $database, $models, $output, $force);
}
} else {
$output->writeln(sprintf('No <comment>*schemas.xml</comment> files found in bundle <comment>%s</comment>.', $this->bundle->getName()));
}
}
private function createFormTypeFromDatabase(BundleInterface $bundle, \Database $database, $models, OutputInterface $output, $force = false)
/**
* Create FormTypes from a given database, bundle and models.
*
* @param BundleInterface $bundle The bundle for which the FormTypes will be generated.
* @param Database $database The database to inspect.
* @param array $models The models to build.
* @param OutputInterface $output An OutputInterface instance
* @param boolean $force Override files if present.
*/
protected function createFormTypeFromDatabase(BundleInterface $bundle, Database $database, $models, OutputInterface $output, $force = false)
{
$dir = $this->createDirectory($bundle, $output);
@ -85,11 +111,19 @@ EOT
}
}
private function createDirectory(BundleInterface $bundle, OutputInterface $output)
/**
* Create the FormType directory and log the result.
*
* @param BundleInterface $bundle The bundle in which we'll create the directory.
* @param OutputInterface $output An OutputInterface instance.
*
* @return string The path to the created directory.
*/
protected function createDirectory(BundleInterface $bundle, OutputInterface $output)
{
$fs = new Filesystem();
if (!is_dir($dir = $bundle->getPath() . self::DEFAULT_FORM_TYPE_DIRECTORY)) {
if (!$fs->exists($dir = $bundle->getPath() . self::DEFAULT_FORM_TYPE_DIRECTORY)) {
$fs->mkdir($dir);
$this->writeNewDirectory($output, $dir);
}
@ -97,30 +131,71 @@ EOT
return $dir;
}
private function writeFormType(BundleInterface $bundle, \Table $table, \SplFileInfo $file, $force, OutputInterface $output)
/**
* Write a FormType.
*
* @param BundleInterface $bundle The bundle in which the FormType will be created.
* @param Table $table The table for which the FormType will be created.
* @param \SplFileInfo $file File representing the FormType.
* @param boolean $force Is the write forced?
* @param OutputInterface $output An OutputInterface instance.
*/
protected 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);
$formBuilder = new FormBuilder();
$formTypeContent = $formBuilder->buildFormType($bundle, $table, self::DEFAULT_FORM_TYPE_DIRECTORY);
file_put_contents($file->getPathName(), $formTypeContent);
$this->writeNewFile($output, $this->getRelativeFileName($file) . ($force ? ' (forced)' : ''));
}
private function addFields(\Table $table, $formTypeContent)
/**
* @param \SplFileInfo $file
* @return string
*/
protected function getRelativeFileName(\SplFileInfo $file)
{
$buildCode = '';
foreach ($table->getColumns() as $column) {
if (!$column->isPrimaryKey()) {
$buildCode .= sprintf("\n \$builder->add('%s');", lcfirst($column->getPhpName()));
}
return substr(str_replace(realpath($this->getContainer()->getParameter('kernel.root_dir') . '/../'), '', $file), 1);
}
/**
* Get the GeneratorConfig instance to use.
*
* @param InputInterface $input An InputInterface instance.
*
* @return GeneratorConfig
*/
protected function getGeneratorConfig(InputInterface $input)
{
$generatorConfig = null;
if (null !== $input->getOption('platform')) {
$generatorConfig['propel']['generator']['platformClass'] = $input->getOption('platform');
}
return str_replace('##BUILD_CODE##', $buildCode, $formTypeContent);
return new GeneratorConfig($this->getCacheDir().'/propel.json', $generatorConfig);
}
/**
* Get the ModelManager to use.
*
* @param InputInterface $input An InputInterface instance.
* @param array $schemas A list of schemas.
*
* @return ModelManager
*/
protected function getModelManager(InputInterface $input, array $schemas)
{
$schemaFiles = array();
foreach ($schemas as $data) {
$schemaFiles[] = $data[1];
}
$manager = new ModelManager();
$manager->setFilesystem(new Filesystem());
$manager->setGeneratorConfig($this->getGeneratorConfig($input));
$manager->setSchemas($schemaFiles);
return $manager;
}
}

View file

@ -0,0 +1,100 @@
<?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\Bundle\PropelBundle\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* @author Kévin Gomez <contact@kevingomez.fr>
*/
trait FormattingHelpers
{
/**
* Comes from the SensioGeneratorBundle.
* @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.
*/
protected function writeSection(OutputInterface $output, $text, $style = 'bg=blue;fg=white')
{
$output->writeln(array(
'',
$this->getHelperSet()->get('formatter')->formatBlock($text, $style, true),
'',
));
}
/**
* Asks a confirmation to the user.
*
* The question will be asked until the user answers by nothing, yes, or no.
*
* @param InputInterface $input An Input instance
* @param OutputInterface $output An Output instance
* @param string|array $question The question to ask
* @param bool $default The default answer if the user enters nothing
*
* @return bool true if the user has confirmed, false otherwise
*/
protected function askConfirmation(InputInterface $input, OutputInterface $output, $question, $default = true)
{
$question = new Question($question);
do {
$answer = $this->getHelperSet()->get('question')->ask($input, $output, $question);
} while ($answer && !in_array(strtolower($answer[0]), array('y', 'n')));
if (false === $default) {
return $answer && 'y' == strtolower($answer[0]);
}
return !$answer || 'y' == strtolower($answer[0]);
}
/**
* @param OutputInterface $output The output.
* @param string $filename The filename.
*/
protected function writeNewFile(OutputInterface $output, $filename)
{
$output->writeln('>> <info>File+</info> ' . $filename);
}
/**
* @param OutputInterface $output The output.
* @param string $directory The directory.
*/
protected function writeNewDirectory(OutputInterface $output, $directory)
{
$output->writeln('>> <info>Dir+</info> ' . $directory);
}
/**
* 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.
*/
protected function writeTaskError($output, $taskName, $more = true)
{
$moreText = $more ? ' To get more details, run the command with the "--verbose" option.' : '';
$this->writeSection($output, array(
'[Propel] Error',
'',
'An error has occured during the "' . $taskName . '" task process.' . $moreText
), 'fg=white;bg=red');
}
}

View file

@ -1,67 +0,0 @@
<?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\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* @author William Durand <william.durand1@gmail.com>
*/
abstract class GeneratorAwareCommand extends AbstractCommand
{
/**
* {@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');
$config = new \QuickGeneratorConfig();
if (file_exists($propelIni = $this->getContainer()->getParameter('kernel.root_dir') . '/config/propel.ini')) {
foreach ($this->getProperties($propelIni) as $key => $value) {
if (0 === strpos($key, 'propel.')) {
$newKey = substr($key, strlen('propel.'));
$j = strpos($newKey, '.');
while (false !== $j) {
$newKey = substr($newKey, 0, $j) . ucfirst(substr($newKey, $j + 1));
$j = strpos($newKey, '.');
}
$config->setBuildProperty($newKey, $value);
}
}
}
$transformer->setGeneratorConfig($config);
return $transformer->parseFile($file->getPathName())->getDatabases();
}
}

View file

@ -1,59 +0,0 @@
<?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\PropelBundle\Command\AbstractCommand;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* GraphvizCommand.
*
* @author William DURAND <william.durand1@gmail.com>
*/
class GraphvizCommand extends AbstractCommand
{
/**
* @see Command
*/
protected function configure()
{
$this
->setDescription('Generates Graphviz file for your project')
->setHelp(<<<EOT
The <info>propel:graphviz</info> generates Graphviz file for your project.
<info>php app/console propel:graphviz</info>
EOT
)
->setName('propel:graphviz')
;
}
/**
* @see Command
*
* @throws \InvalidArgumentException When the target directory does not exist
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->writeSection($output, '[Propel] You are running the command: propel:graphviz');
$dest = $this->getApplication()->getKernel()->getRootDir() . '/propel/graph/';
$this->callPhing('graphviz', array(
'propel.graph.dir' => $dest,
));
$output->writeln(sprintf('Graphviz file is in <info>%s</info>.', $dest));
}
}

View file

@ -0,0 +1,55 @@
<?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\Bundle\PropelBundle\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Propel\Generator\Command\GraphvizGenerateCommand as BaseGraphvizGenerateCommand;
/**
* @author Kévin Gomez <contact@kevingomez.fr>
*/
class GraphvizGenerateCommand extends WrappedCommand
{
/**
* {@inheritdoc}
*/
protected function configure()
{
parent::configure();
$this
->setName('propel:graphviz:generate')
->setDescription('Generate Graphviz files (.dot)')
->addOption('output-dir', null, InputOption::VALUE_REQUIRED, 'The output directory', BaseGraphvizGenerateCommand::DEFAULT_OUTPUT_DIRECTORY)
;
}
/**
* {@inheritdoc}
*/
protected function createSubCommandInstance()
{
return new BaseGraphvizGenerateCommand();
}
/**
* {@inheritdoc}
*/
protected function getSubCommandArguments(InputInterface $input)
{
return array(
'--output-dir' => $input->getOption('output-dir'),
);
}
}

View file

@ -1,100 +0,0 @@
<?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\InputArgument;
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\Filesystem\Filesystem;
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
use Symfony\Component\HttpKernel\KernelInterface;
/**
* @author Toni Uebernickel <tuebernickel@gmail.com>
*/
class InitAclCommand extends SqlInsertCommand
{
protected function configure()
{
$this
->setDescription('Initialize "Access Control Lists" model and SQL')
->addOption('force', null, InputOption::VALUE_NONE, 'Set this parameter to execute this action.')
->addOption('connection', null, InputOption::VALUE_OPTIONAL, 'Set this parameter to define a connection to use')
->setHelp(<<<EOT
The <info>%command.name%</info> command connects to the database and executes all SQL statements required to setup the ACL database, it also generates the ACL model.
<info>php %command.full_name%</info>
The <info>--force</info> parameter has to be used to actually insert SQL.
The <info>--connection</info> parameter allows you to change the connection to use.
The default connection is the active connection (propel.dbal.default_connection).
EOT
)
->setName('propel:init:acl')
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->writeSection($output, '[Propel] You are running the command: propel:init:acl');
if ($input->getOption('verbose')) {
$this->additionalPhingArgs[] = 'verbose';
}
// Generate ACL model
if (true == $result = $this->callPhing('om')) {
$output->writeln(sprintf(
'>> <info>%20s</info> Generated model classes from <comment>%s</comment>',
$this->getApplication()->getKernel()->getBundle('PropelBundle')->getName(),
'acl_schema.xml'
));
} else {
$this->writeTaskError($output, 'om');
return 1;
}
// Prepare SQL directory
$sqlDirectory = $this->getSqlDir();
$filesystem = new Filesystem();
$filesystem->remove($sqlDirectory);
$filesystem->mkdir($sqlDirectory);
if (true == $result = $this->callPhing('build-sql', array('propel.sql.dir' => $sqlDirectory))) {
$this->writeSection(
$output,
'<comment>1</comment> <info>SQL file has been generated.</info>',
'bg=black'
);
} else {
$this->writeTaskError($output, 'build-sql');
return 2;
}
return parent::execute($input, $output);
}
protected function getFinalSchemas(KernelInterface $kernel, BundleInterface $bundle = null)
{
$aclSchema = new \SplFileInfo($kernel->locateResource('@PropelBundle/Resources/acl_schema.xml'));
return array((string) $aclSchema => array($kernel->getBundle('PropelBundle'), $aclSchema));
}
protected function getSqlDir()
{
return sprintf('%s/cache/%s/propel/acl/sql',
$this->getApplication()->getKernel()->getRootDir(),
$this->getApplication()->getKernel()->getEnvironment()
);
}
}

View file

@ -0,0 +1,69 @@
<?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\Bundle\PropelBundle\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Propel\Generator\Command\MigrationDiffCommand as BaseMigrationCommand;
/**
* @author Kévin Gomez <contact@kevingomez.fr>
*/
class MigrationDiffCommand extends WrappedCommand
{
/**
* {@inheritdoc}
*/
protected function configure()
{
parent::configure();
$this
->setName('propel:migration:diff')
->setDescription('Generate diff classes')
->addOption('connection', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, 'Connection to use. Example: default, bookstore')
->addOption('output-dir', null, InputOption::VALUE_OPTIONAL, 'The output directory')
->addOption('migration-table', null, InputOption::VALUE_OPTIONAL, 'Migration table name (if none given, the configured table is used)', null)
->addOption('table-renaming', null, InputOption::VALUE_NONE, 'Detect table renaming', null)
->addOption('editor', null, InputOption::VALUE_OPTIONAL, 'The text editor to use to open diff files', null)
->addOption('skip-removed-table', null, InputOption::VALUE_NONE, 'Option to skip removed table from the migration')
->addOption('skip-tables', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, 'List of excluded tables', array())
;
}
/**
* {@inheritdoc}
*/
protected function createSubCommandInstance()
{
return new BaseMigrationCommand();
}
/**
* {@inheritdoc}
*/
protected function getSubCommandArguments(InputInterface $input)
{
$defaultOutputDir = $this->getContainer()->getParameter('propel.configuration')['paths']['migrationDir'];
return array(
'--connection' => $this->getConnections($input->getOption('connection')),
'--migration-table' => $input->getOption('migration-table') ?: $this->getMigrationsTable(),
'--output-dir' => $input->getOption('output-dir') ?: $defaultOutputDir,
'--table-renaming' => $input->getOption('table-renaming'),
'--editor' => $input->getOption('editor'),
'--skip-removed-table' => $input->getOption('skip-removed-table'),
'--skip-tables' => $input->getOption('skip-tables'),
);
}
}

View file

@ -0,0 +1,65 @@
<?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\Bundle\PropelBundle\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Propel\Generator\Command\MigrationDownCommand as BaseMigrationCommand;
/**
* @author Kévin Gomez <contact@kevingomez.fr>
*/
class MigrationDownCommand extends WrappedCommand
{
/**
* {@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_OPTIONAL, 'Migration table name (if none given, the configured table is used)', null)
->addOption('output-dir', null, InputOption::VALUE_OPTIONAL, 'The output directory')
->addOption('fake', null, InputOption::VALUE_NONE, 'Does not touch the actual schema, but marks previous migration as executed.')
->addOption('force', null, InputOption::VALUE_NONE, 'Continues with the migration even when errors occur.')
;
}
/**
* {@inheritdoc}
*/
protected function createSubCommandInstance()
{
return new BaseMigrationCommand();
}
/**
* {@inheritdoc}
*/
protected function getSubCommandArguments(InputInterface $input)
{
$defaultOutputDir = $this->getContainer()->getParameter('propel.configuration')['paths']['migrationDir'];
return array(
'--connection' => $this->getConnections($input->getOption('connection')),
'--migration-table' => $input->getOption('migration-table') ?: $this->getMigrationsTable(),
'--output-dir' => $input->getOption('output-dir') ?: $defaultOutputDir,
'--fake' => $input->getOption('fake'),
'--force' => $input->getOption('force'),
);
}
}

View file

@ -1,52 +0,0 @@
<?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\PropelBundle\Command\AbstractCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* MigrationGenerateDiffCommand.
*
* @author William DURAND <william.durand1@gmail.com>
*/
class MigrationGenerateDiffCommand extends AbstractCommand
{
/**
* @see Command
*/
protected function configure()
{
$this
->setDescription('Generates SQL diff between the XML schemas and the current database structure')
->setHelp(<<<EOT
The <info>propel:migration:generate-diff</info> command compares the current database structure and the available schemas. If there is a difference, it creates a migration file.
<info>php app/console propel:migration:generate-diff</info>
EOT
)
->setName('propel:migration:generate-diff')
;
}
/**
* @see Command
*
* @throws \InvalidArgumentException When the target directory does not exist
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->callPhing('diff');
$this->writeSummary($output, 'propel-sql-diff');
}
}

View file

@ -8,63 +8,58 @@
* @license MIT License
*/
namespace Propel\PropelBundle\Command;
namespace Propel\Bundle\PropelBundle\Command;
use Propel\PropelBundle\Command\AbstractCommand;
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 Propel\Generator\Command\MigrationMigrateCommand as BaseMigrationCommand;
/**
* MigrationMigrateCommand.
*
* @author William DURAND <william.durand1@gmail.com>
* @author Kévin Gomez <contact@kevingomez.fr>
*/
class MigrationMigrateCommand extends AbstractCommand
class MigrationMigrateCommand extends WrappedCommand
{
/**
* @see Command
* {@inheritdoc}
*/
protected function configure()
{
parent::configure();
$this
->setDescription('Executes the next migrations up')
->setDefinition(array(
new InputOption('--up', '', InputOption::VALUE_NONE, 'Executes the next migration up'),
new InputOption('--down', '', InputOption::VALUE_NONE, 'Executes the next migration down'),
))
->setHelp(<<<EOT
The <info>propel:migration:migrate</info> command checks the version of the database structure, looks for migrations files not yet executed (i.e. with a greater version timestamp), and executes them.
<info>php app/console propel:migration:migrate [--up] [--down]</info>
<info>php app/console propel:migration:migrate</info> : is the default command, it <comment>executes all</comment> migrations files.
<info>php app/console propel:migration:migrate --up</info> : checks the version of the database structure, looks for migrations files not yet executed (i.e. with a greater version timestamp), and <comment>executes the first one</comment> of them.
<info>php app/console propel:migration:migrate --down</info> : checks the version of the database structure, and looks for migration files already executed (i.e. with a lower version timestamp). <comment>The last executed migration found is reversed.</comment>
EOT
)
->setName('propel:migration:migrate')
->setDescription('Execute all pending migrations')
->addOption('connection', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, 'Connection to use. Example: default, bookstore')
->addOption('migration-table', null, InputOption::VALUE_OPTIONAL, 'Migration table name (if none given, the configured table is used)', null)
->addOption('output-dir', null, InputOption::VALUE_OPTIONAL, 'The output directory')
->addOption('fake', null, InputOption::VALUE_NONE, 'Does not touch the actual schema, but marks all migration as executed.')
->addOption('force', null, InputOption::VALUE_NONE, 'Continues with the migration even when errors occur.')
;
}
/**
* @see Command
*
* @throws \InvalidArgumentException When the target directory does not exist
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
protected function createSubCommandInstance()
{
if ($input->getOption('down')) {
$this->callPhing('migration-down');
} else if($input->getOption('up')) {
$this->callPhing('migration-up');
} else {
$this->callPhing('migrate');
}
return new BaseMigrationCommand();
}
$this->writeSummary($output, 'propel-migration');
/**
* {@inheritdoc}
*/
protected function getSubCommandArguments(InputInterface $input)
{
$defaultOutputDir = $this->getContainer()->getParameter('propel.configuration')['paths']['migrationDir'];
return array(
'--connection' => $this->getConnections($input->getOption('connection')),
'--migration-table' => $input->getOption('migration-table') ?: $this->getMigrationsTable(),
'--output-dir' => $input->getOption('output-dir') ?: $defaultOutputDir,
'--fake' => $input->getOption('fake'),
'--force' => $input->getOption('force'),
);
}
}

View file

@ -8,45 +8,54 @@
* @license MIT License
*/
namespace Propel\PropelBundle\Command;
namespace Propel\Bundle\PropelBundle\Command;
use Propel\PropelBundle\Command\AbstractCommand;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Propel\Generator\Command\MigrationStatusCommand as BaseMigrationCommand;
/**
* MigrationStatusCommand.
*
* @author William DURAND <william.durand1@gmail.com>
* @author Kévin Gomez <contact@kevingomez.fr>
*/
class MigrationStatusCommand extends AbstractCommand
class MigrationStatusCommand extends WrappedCommand
{
/**
* @see Command
* {@inheritdoc}
*/
protected function configure()
{
$this
->setDescription('Lists the migrations yet to be executed')
->setHelp(<<<EOT
The <info>propel:migration:status</info> command checks the version of the database structure, and looks for migration files not yet executed (i.e. with a greater version timestamp).
parent::configure();
<info>php app/console propel:migration:status</info>
EOT
)
$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_OPTIONAL, 'Migration table name (if none given, the configured table is used)', null)
->addOption('output-dir', null, InputOption::VALUE_OPTIONAL, 'The output directory')
;
}
/**
* @see Command
*
* @throws \InvalidArgumentException When the target directory does not exist
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
protected function createSubCommandInstance()
{
$this->callPhing('status');
return new BaseMigrationCommand();
}
$this->writeSummary($output, 'propel-migration-status');
/**
* {@inheritdoc}
*/
protected function getSubCommandArguments(InputInterface $input)
{
$defaultOutputDir = $this->getContainer()->getParameter('propel.configuration')['paths']['migrationDir'];
return array(
'--connection' => $this->getConnections($input->getOption('connection')),
'--migration-table' => $input->getOption('migration-table') ?: $this->getMigrationsTable(),
'--output-dir' => $input->getOption('output-dir') ?: $defaultOutputDir,
);
}
}

View file

@ -0,0 +1,65 @@
<?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\Bundle\PropelBundle\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Propel\Generator\Command\MigrationUpCommand as BaseMigrationCommand;
/**
* @author Kévin Gomez <contact@kevingomez.fr>
*/
class MigrationUpCommand extends WrappedCommand
{
/**
* {@inheritdoc}
*/
protected function configure()
{
parent::configure();
$this
->setName('propel:migration:up')
->setDescription('Execute (only one) next migration')
->addOption('connection', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, 'Connection to use. Example: default, bookstore')
->addOption('migration-table', null, InputOption::VALUE_OPTIONAL, 'Migration table name (if none given, the configured table is used)', null)
->addOption('output-dir', null, InputOption::VALUE_OPTIONAL, 'The output directory')
->addOption('fake', null, InputOption::VALUE_NONE, 'Does not touch the actual schema, but marks next migration as executed.')
->addOption('force', null, InputOption::VALUE_NONE, 'Continues with the migration even when errors occur.')
;
}
/**
* {@inheritdoc}
*/
protected function createSubCommandInstance()
{
return new BaseMigrationCommand();
}
/**
* {@inheritdoc}
*/
protected function getSubCommandArguments(InputInterface $input)
{
$defaultOutputDir = $this->getContainer()->getParameter('propel.configuration')['paths']['migrationDir'];
return array(
'--connection' => $this->getConnections($input->getOption('connection')),
'--migration-table' => $input->getOption('migration-table') ?: $this->getMigrationsTable(),
'--output-dir' => $input->getOption('output-dir') ?: $defaultOutputDir,
'--fake' => $input->getOption('fake'),
'--force' => $input->getOption('force'),
);
}
}

View file

@ -8,62 +8,52 @@
* @license MIT License
*/
namespace Propel\PropelBundle\Command;
namespace Propel\Bundle\PropelBundle\Command;
use Propel\PropelBundle\Command\AbstractCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Propel\Generator\Command\ModelBuildCommand as BaseModelBuildCommand;
/**
* ModelBuildCommand.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @author William DURAND <william.durand1@gmail.com>
* @author Kévin Gomez <contact@kevingomez.fr>
*/
class ModelBuildCommand extends AbstractCommand
class ModelBuildCommand extends WrappedCommand
{
/**
* @see Command
* {@inheritdoc}
*/
protected function configure()
{
$this
->setDescription('Build the Propel Object Model classes based on XML schemas')
->addArgument('bundle', InputArgument::OPTIONAL, 'The bundle to generate model classes from')
->setHelp(<<<EOT
The <info>%command.name%</info> command builds the Propel runtime model classes (ActiveRecord, Query, Peer, and TableMap classes) based on the XML schemas defined in all Bundles.
parent::configure();
<info>php app/console %command.full_name%</info>
EOT
)
$this
->setName('propel:model:build')
->setDescription('Build the model classes based on Propel XML schemas')
->addOption('connection', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, 'Connection to use. Example: default, bookstore')
->addArgument('bundle', InputArgument::OPTIONAL, 'The bundle to generate model classes from')
;
}
/**
* @see Command
*
* @throws \InvalidArgumentException When the target directory does not exist
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
protected function createSubCommandInstance()
{
$this->writeSection($output, '[Propel] You are running the command: propel:model:build');
return new BaseModelBuildCommand();
}
if ($input->getOption('verbose')) {
$this->additionalPhingArgs[] = 'verbose';
}
/**
* {@inheritdoc}
*/
protected function getSubCommandArguments(InputInterface $input)
{
$outputDir = $this->getApplication()->getKernel()->getRootDir().'/../';
if (true === $this->callPhing('om')) {
foreach ($this->tempSchemas as $schemaFile => $schemaDetails) {
$output->writeln(sprintf(
'>> <info>%20s</info> Generated model classes from <comment>%s</comment>',
$schemaDetails['bundle'],
$schemaDetails['basename']
));
}
} else {
$this->writeTaskError($output, 'om');
}
return array(
'--output-dir' => $outputDir,
);
}
}

View file

@ -1,37 +0,0 @@
<?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;
/**
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class Phing extends \Phing
{
static public function getPhingVersion()
{
return 'Phing/Symfony';
}
/**
* @see Phing
*/
public function runBuild()
{
// workaround for included phing 2.3 which by default loads many tasks
// that are not needed and incompatible (eg phing.tasks.ext.FtpDeployTask)
// by placing current directory on the include path our defaults will be loaded
// see ticket #5054
$includePath = get_include_path();
set_include_path(dirname(__FILE__).PATH_SEPARATOR.$includePath);
parent::runBuild();
set_include_path($includePath);
}
}

View file

@ -1,90 +0,0 @@
<?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\PropelBundle\Command\AbstractCommand;
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\Finder\Finder;
/**
* ReverseCommand.
*
* @author William DURAND <william.durand1@gmail.com>
*/
class ReverseCommand extends AbstractCommand
{
/**
* @see Command
*/
protected function configure()
{
$this
->setDescription('Generate XML schema from reverse-engineered database')
->addOption('connection', null, InputOption::VALUE_OPTIONAL, 'Set this parameter to define a connection to use')
->setHelp(<<<EOT
The <info>propel:reverse</info> command generates an XML schema from reverse-engineered database.
<info>php app/console propel:reverse</info>
The <info>--connection</info> parameter allows you to change the connection to use.
The default connection is the active connection (propel.dbal.default_connection).
EOT
)
->setName('propel:reverse')
;
}
/**
* @see Command
*
* @throws \InvalidArgumentException When the target directory does not exist
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->writeSection($output, '[Propel] You are running the command: propel:reverse');
if ($input->getOption('verbose')) {
$this->additionalPhingArgs[] = 'verbose';
}
list($name, $defaultConfig) = $this->getConnection($input, $output);
$ret = $this->callPhing('reverse', array(
'propel.project' => $name,
'propel.database' => $defaultConfig['adapter'],
'propel.database.url' => $defaultConfig['connection']['dsn'],
'propel.database.user' => $defaultConfig['connection']['user'],
'propel.database.password' => isset($defaultConfig['connection']['password']) ? $defaultConfig['connection']['password'] : '',
));
if (true === $ret) {
$filesystem = new Filesystem();
$generated = $this->getCacheDir().'/schema.xml';
$filename = $name . '_reversed_schema.xml';
$destFile = $this->getApplication()->getKernel()->getRootDir() . '/propel/generated-schemas/' . $filename;
if (file_exists($generated)) {
$filesystem->copy($generated, $destFile);
$output->writeln(array(
'',
sprintf('>> <info>File+</info> %s', $destFile),
));
} else {
$output->writeln(array('', 'No generated files.'));
}
} else {
$this->writeTaskError($output, 'reverse');
}
}
}

View file

@ -8,94 +8,51 @@
* @license MIT License
*/
namespace Propel\PropelBundle\Command;
namespace Propel\Bundle\PropelBundle\Command;
use Symfony\Component\Console\Input\InputArgument;
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\Filesystem\Filesystem;
use Symfony\Component\Finder\Finder;
use Propel\PropelBundle\Command\AbstractCommand;
/**
* SqlBuildCommand.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @author William DURAND <william.durand1@gmail.com>
* @author Kévin Gomez <contact@kevingomez.fr>
*/
class SqlBuildCommand extends AbstractCommand
class SqlBuildCommand extends WrappedCommand
{
/**
* @see Command
* {@inheritdoc}
*/
protected function configure()
{
$this
->setDescription('Build the SQL generation code for all tables based on Propel XML schemas')
->setHelp(<<<EOT
The <info>%command.name%</info> command builds the SQL table generation code based on the XML schemas defined in all Bundles.
parent::configure();
<info>php %command.full_name%</info>
EOT
)
$this
->setName('propel:sql:build')
->setDescription('Build SQL files')
->addOption('sql-dir', null, InputOption::VALUE_REQUIRED, 'The SQL files directory')
->addOption('overwrite', null, InputOption::VALUE_NONE, '')
->addOption('connection', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, 'Connection to use. Example: default, bookstore')
;
}
/**
* @see Command
*
* @throws \InvalidArgumentException When the target directory does not exist
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
protected function createSubCommandInstance()
{
$this->writeSection($output, '[Propel] You are running the command: propel:sql:build');
return new \Propel\Generator\Command\SqlBuildCommand();
}
if ($input->getOption('verbose')) {
$this->additionalPhingArgs[] = 'verbose';
}
/**
* {@inheritdoc}
*/
protected function getSubCommandArguments(InputInterface $input)
{
$defaultSqlDir = $this->getContainer()->getParameter('propel.configuration')['paths']['sqlDir'];
$finder = new Finder();
$filesystem = new Filesystem();
$sqlDir = $this->getApplication()->getKernel()->getRootDir(). DIRECTORY_SEPARATOR . 'propel'. DIRECTORY_SEPARATOR . 'sql';
$filesystem->remove($sqlDir);
$filesystem->mkdir($sqlDir);
// Execute the task
$ret = $this->callPhing('build-sql', array(
'propel.sql.dir' => $sqlDir
));
if (true === $ret) {
$files = $finder->name('*')->in($sqlDir);
$nbFiles = 0;
foreach ($files as $file) {
$this->writeNewFile($output, (string) $file);
if ('sql' === pathinfo($file->getFilename(), PATHINFO_EXTENSION)) {
$nbFiles++;
}
}
$this->writeSection(
$output,
sprintf('<comment>%d</comment> <info>SQL file%s ha%s been generated.</info>',
$nbFiles, $nbFiles > 1 ? 's' : '', $nbFiles > 1 ? 've' : 's'
),
'bg=black'
);
} else {
$this->writeSection($output, array(
'[Propel] Error',
'',
'An error has occured during the "propel:sql:build" command process. To get more details, run the command with the "--verbose" option.'
), 'fg=white;bg=red');
}
return array(
'--connection' => $this->getConnections($input->getOption('connection')),
'--output-dir' => $input->getOption('sql-dir') ?: $defaultSqlDir,
'--overwrite' => $input->getOption('overwrite')
);
}
}

View file

@ -8,131 +8,62 @@
* @license MIT License
*/
namespace Propel\PropelBundle\Command;
namespace Propel\Bundle\PropelBundle\Command;
use Propel\PropelBundle\Command\AbstractCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* SqlInsertCommand.
*
* @author William DURAND <william.durand1@gmail.com>
* @author Kévin Gomez <contact@kevingomez.fr>
*/
class SqlInsertCommand extends AbstractCommand
class SqlInsertCommand extends WrappedCommand
{
/**
* @see Command
* {@inheritdoc}
*/
protected function configure()
{
$this
->setDescription('Insert SQL for current model')
->addOption('force', null, InputOption::VALUE_NONE, 'Set this parameter to execute this action.')
->addOption('connection', null, InputOption::VALUE_OPTIONAL, 'Set this parameter to define a connection to use')
->setHelp(<<<EOT
The <info>%command.name%</info> command connects to the database and executes all SQL statements found in <comment>app/propel/sql/*schema.sql</comment>.
<info>php %command.full_name%</info>
The <info>--force</info> parameter has to be used to actually insert SQL.
The <info>--connection</info> parameter allows you to change the connection to use.
The default connection is the active connection (propel.dbal.default_connection).
EOT
)
->setName('propel:sql:insert')
->setDescription('Insert SQL statements')
->addOption('force', null, InputOption::VALUE_NONE, 'Set this parameter to execute this action.')
->addOption('sql-dir', null, InputOption::VALUE_REQUIRED, 'The SQL files directory')
->addOption('connection', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, 'Connection to use. Example: default, bookstore')
;
}
/**
* @see Command
*
* @throws \InvalidArgumentException When the target directory does not exist
* {@inheritdoc}
*/
protected function createSubCommandInstance()
{
return new \Propel\Generator\Command\SqlInsertCommand();
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
// Bad require but needed :(
require_once $this->getContainer()->getParameter('propel.path') . '/generator/lib/util/PropelSqlManager.php';
$this->writeSection($output, '[Propel] You are running the command: propel:sql:insert');
if ($input->getOption('force')) {
if ($input->getOption('verbose')) {
$this->additionalPhingArgs[] = 'verbose';
}
$connections = $this->getConnections();
$sqlDir = $this->getSqlDir();
$manager = new \PropelSqlManager();
$manager->setWorkingDirectory($sqlDir);
$manager->setConnections($connections);
if ($input->getOption('connection')) {
list($name, $config) = $this->getConnection($input, $output);
$this->doSqlInsert($manager, $output, $name);
} else {
foreach ($connections as $name => $config) {
$output->writeln(sprintf('Use connection named <comment>%s</comment> in <comment>%s</comment> environment.',
$name, $this->getApplication()->getKernel()->getEnvironment()));
$this->doSqlInsert($manager, $output, $name);
}
}
parent::execute($input, $output);
} else {
$output->writeln('<error>You have to use --force to execute all SQL statements.</error>');
}
}
protected function getSqlDir()
{
return sprintf('%s/propel/sql', $this->getApplication()->getKernel()->getRootDir());
}
/**
* @param \PropelSqlManager $manager
* @param OutputInterface $output
* @param string $connectionName
*/
protected function doSqlInsert(\PropelSqlManager $manager, OutputInterface $output, $connectionName)
{
try {
$statusCode = $manager->insertSql($connectionName);
} catch (\Exception $e) {
return $this->writeSection(
$output,
array('[Propel] Exception', '', $e),
'fg=white;bg=red'
);
}
if (true === $statusCode) {
$this->writeSection(
$output,
'<info>All SQL statements have been inserted.</info>',
'bg=black'
);
} else {
$this->writeSection(
$output,
'<comment>No SQL statements found.</comment>',
'bg=black'
);
return 1;
}
}
/**
* @return array
* {@inheritdoc}
*/
protected function getConnections()
protected function getSubCommandArguments(InputInterface $input)
{
$propelConfiguration = $this->getContainer()->get('propel.configuration');
$defaultSqlDir = $this->getContainer()->getParameter('propel.configuration')['paths']['sqlDir'];
$connections = array();
foreach ($propelConfiguration['datasources'] as $name => $config) {
$connections[$name] = $config['connection'];
}
return $connections;
return array(
'--connection' => $this->getConnections($input->getOption('connection')),
'--sql-dir' => $input->getOption('sql-dir') ?: $defaultSqlDir,
);
}
}

View file

@ -8,125 +8,105 @@
* @license MIT License
*/
namespace Propel\PropelBundle\Command;
namespace Propel\Bundle\PropelBundle\Command;
use Propel\PropelBundle\Command\AbstractCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\InputOption;
use Propel\Runtime\Adapter\Pdo\MysqlAdapter;
use Propel\Runtime\Propel;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* TableDropCommand class.
* Useful to drop table in a database.
*
* @author Maxime AILLOUD
* @author Kévin Gomez <contact@kevingomez.fr>
*/
class TableDropCommand extends AbstractCommand
class TableDropCommand extends ContainerAwareCommand
{
use FormattingHelpers;
/**
* @see Command
* {@inheritdoc}
*/
protected function configure()
{
$this
->setName('propel:table:drop')
->setDescription('Drop a given table or all tables in the database.')
->addArgument('table', InputArgument::IS_ARRAY, 'Set this parameter to défine which table to delete (default all the table in the database.')
->addOption('force', null, InputOption::VALUE_NONE, 'Set this parameter to execute this action.')
->addOption('connection', null, InputOption::VALUE_OPTIONAL, 'Set this parameter to define a connection to use')
->setHelp(<<<EOT
The <info>propel:table:drop</info> command will drop one or several table.
<info>php app/console propel:table:drop</info>
The <info>table</info> arguments define the list of table which has to be delete <comment>(default: all table)</comment>.
The <info>--force</info> parameter has to be used to actually drop the table.
The <info>--connection</info> parameter allows you to change the connection to use.
The default connection is the active connection (propel.dbal.default_connection).
EOT
)
->setName('propel:table:drop');
->addOption('connection', null, InputOption::VALUE_OPTIONAL, 'Connection to use. Example: default, bookstore')
->addOption('force', null, InputOption::VALUE_NONE, 'Set this parameter to execute this action.')
->addArgument('table', InputArgument::IS_ARRAY, 'Set this parameter to défine which table to delete (default all the table in the database.')
;
}
/**
* @see Command
*
* @throws \InvalidArgumentException When the target directory does not exist
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->writeSection($output, '[Propel] You are running the command: propel:table:drop');
$connection = Propel::getConnection($input->getOption('connection'));
$adapter = Propel::getAdapter($connection->getName());
if (!$adapter instanceof MysqlAdapter) {
return $output->writeln('<error>This command is MySQL only.</error>');
}
if (!$input->getOption('force')) {
return $output->writeln('<error>You have to use the "--force" option to drop some tables.</error>');
}
$tablesToDelete = $input->getArgument('table');
$nbTable = count($tablesToDelete);
$tablePlural = (($nbTable > 1 || $nbTable == 0) ? 's' : '' );
if ($input->getOption('force')) {
$nbTable = count($tablesToDelete);
$tablePlural = (($nbTable > 1 || $nbTable == 0) ? 's' : '' );
if ('prod' === $this->getApplication()->getKernel()->getEnvironment()) {
$count = $nbTable ?: 'all';
if ('prod' === $this->getApplication()->getKernel()->getEnvironment()) {
$count = (count($input->getArgument('table')) ?: 'all');
$this->writeSection(
$output,
'WARNING: you are about to drop ' . $count . ' table' . $tablePlural . ' in production !',
'bg=red;fg=white'
);
$this->writeSection(
$output,
'WARNING: you are about to drop ' . $count . ' table' . $tablePlural . ' in production !',
'bg=red;fg=white'
);
if (false === $this->askConfirmation($input, $output, 'Are you sure ? (y/n) ', false)) {
$output->writeln('<info>Aborted, nice decision !</info>');
if (false === $this->askConfirmation($output, 'Are you sure ? (y/n) ', false)) {
$output->writeln('<info>Aborted, nice decision !</info>');
return -2;
}
}
try {
list($name, $config) = $this->getConnection($input, $output);
$connection = \Propel::getConnection($name);
$adapter = \Propel::getDB($name);
$showStatement = $connection->prepare('SHOW TABLES;');
$showStatement->execute();
$allTables = $showStatement->fetchAll(\PDO::FETCH_COLUMN);
if ($nbTable) {
foreach ($tablesToDelete as $tableToDelete) {
if(!array_search($tableToDelete, $allTables)) {
throw new \InvalidArgumentException(sprintf('Table %s doesn\'t exist in the database.', $tableToDelete));
}
}
}
else {
$tablesToDelete = $allTables;
}
$connection->exec('SET FOREIGN_KEY_CHECKS = 0;');
array_walk($tablesToDelete, function(&$table, $key, $dbAdapter) { $table = $dbAdapter->quoteIdentifierTable($table); }, $adapter);
$tablesToDelete = join(', ', $tablesToDelete);
if ('' !== $tablesToDelete) {
$connection->exec('DROP TABLE ' . $tablesToDelete . ' ;');
$output->writeln(sprintf('Table' . $tablePlural . ' <info><comment>%s</comment> has been dropped.</info>', $tablesToDelete));
}
else {
$output->writeln('<info>No tables have been dropped</info>');
}
$connection->exec('SET FOREIGN_KEY_CHECKS = 1;');
}
catch (\Exception $e) {
$this->writeSection($output, array(
'[Propel] Exception catched',
'',
$e->getMessage()
), 'fg=white;bg=red');
return -2;
}
}
else {
$output->writeln('<error>You have to use the "--force" option to drop some tables.</error>');
$showStatement = $connection->prepare('SHOW TABLES;');
$showStatement->execute();
$allTables = $showStatement->fetchAll(\PDO::FETCH_COLUMN);
if ($nbTable) {
foreach ($tablesToDelete as $tableToDelete) {
if (!array_search($tableToDelete, $allTables)) {
throw new \InvalidArgumentException(sprintf('Table %s doesn\'t exist in the database.', $tableToDelete));
}
}
} else {
$tablesToDelete = $allTables;
}
$connection->exec('SET FOREIGN_KEY_CHECKS = 0;');
array_walk($tablesToDelete, function (&$table, $key, $dbAdapter) {
$table = $dbAdapter->quoteIdentifierTable($table);
}, $adapter);
$tablesToDelete = join(', ', $tablesToDelete);
if ('' !== $tablesToDelete) {
$connection->exec('DROP TABLE ' . $tablesToDelete . ' ;');
$output->writeln(sprintf('Table' . $tablePlural . ' <info><comment>%s</comment> has been dropped.</info>', $tablesToDelete));
} else {
$output->writeln('<info>No tables have been dropped</info>');
}
$connection->exec('SET FOREIGN_KEY_CHECKS = 1;');
}
}

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\Bundle\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>
*/
abstract class WrappedCommand extends AbstractCommand
{
/**
* Creates the instance of the Propel sub-command to execute.
*
* @return \Symfony\Component\Console\Command\Command
*/
abstract protected function createSubCommandInstance();
/**
* Returns all the arguments and options needed by the Propel sub-command.
*
* @return array
*/
abstract protected function getSubCommandArguments(InputInterface $input);
/**
* {@inheritdoc}
*/
protected function configure()
{
$this
->addOption('platform', null, InputOption::VALUE_OPTIONAL, 'The platform')
;
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$params = $this->getSubCommandArguments($input);
$command = $this->createSubCommandInstance();
$this->setupBuildTimeFiles();
return $this->runCommand($command, $params, $input, $output);
}
}

View file

@ -8,10 +8,12 @@
* @license MIT License
*/
namespace Propel\PropelBundle\Controller;
namespace Propel\Bundle\PropelBundle\Controller;
use Symfony\Bridge\Propel1\DataCollector\PropelDataCollector;
use Symfony\Component\DependencyInjection\ContainerAware;
use Propel\Runtime\Propel;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
use Symfony\Component\HttpFoundation\Response;
/**
@ -19,24 +21,19 @@ use Symfony\Component\HttpFoundation\Response;
*
* @author William DURAND <william.durand1@gmail.com>
*/
class PanelController extends ContainerAware
class PanelController extends Controller
{
/**
* This method renders the global Propel configuration.
*/
public function configurationAction()
{
$templating = $this->container->get('templating');
return $templating->renderResponse(
'PropelBundle:Panel:configuration.html.twig',
return $this->render(
'@Propel/Panel/configuration.html.twig',
array(
'propel_version' => \Propel::VERSION,
'configuration' => $this->container->get('propel.configuration')->getParameters(),
'default_connection' => $this->container->getParameter('propel.dbal.default_connection'),
'logging' => $this->container->getParameter('propel.logging'),
'path' => $this->container->getParameter('propel.path'),
'phing_path' => $this->container->getParameter('propel.phing_path'),
'propel_version' => Propel::VERSION,
'configuration' => $this->getParameter('propel.configuration'),
'logging' => $this->getParameter('propel.logging'),
)
);
}
@ -44,15 +41,15 @@ class PanelController extends ContainerAware
/**
* Renders the profiler panel for the given token.
*
* @param string $token The profiler token
* @param string $connection The connection name
* @param string $token The profiler token
* @param string $connection The connection name
* @param integer $query
*
* @return Symfony\Component\HttpFoundation\Response A Response instance
* @return Response A Response instance
*/
public function explainAction($token, $connection, $query)
{
$profiler = $this->container->get('profiler');
$profiler = $this->get('profiler');
$profiler->disable();
$profile = $profiler->loadProfile($token);
@ -63,20 +60,18 @@ class PanelController extends ContainerAware
}
// Open the connection
$con = \Propel::getConnection($connection);
// Get the adapter
$db = \Propel::getDB($connection);
$con = Propel::getConnection($connection);
try {
$stmt = $db->doExplainPlan($con, $queries[$query]['sql']);
$results = $stmt->fetchAll(\PDO::FETCH_ASSOC);
$dataFetcher = $con->query('EXPLAIN ' . $queries[$query]['sql']);
$results = array();
while (($results[] = $dataFetcher->fetch(\PDO::FETCH_ASSOC)));
} catch (\Exception $e) {
return new Response('<div class="error">This query cannot be explained.</div>');
}
return $this->container->get('templating')->renderResponse(
'PropelBundle:Panel:explain.html.twig',
return $this->render(
'@Propel/Panel/explain.html.twig',
array(
'data' => $results,
'query' => $query,

View file

@ -0,0 +1,115 @@
<?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\Bundle\PropelBundle\DataCollector;
use Propel\Bundle\PropelBundle\Logger\PropelLogger;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
/**
* The PropelDataCollector collector class collects information.
*
* @author Kévin Gomez <contact@kevingomez.fr>
*/
class PropelDataCollector extends DataCollector
{
protected $logger;
public function __construct(PropelLogger $logger)
{
$this->logger = $logger;
}
/**
* {@inheritdoc}
*/
public function collect(Request $request, Response $response, \Exception $exception = null)
{
$this->data = array(
'queries' => $this->cloneVar($this->buildQueries()),
'querycount' => $this->countQueries(),
);
}
/**
* Returns the collector name.
*
* @return string The collector name.
*/
public function getName()
{
return 'propel';
}
/**
* Returns queries.
*
* @return array Queries
*/
public function getQueries()
{
return $this->data['queries'];
}
/**
* Returns the query count.
*
* @return int The query count
*/
public function getQueryCount()
{
return $this->data['querycount'];
}
/**
* Returns the total time of queries.
*
* @return float The total time of queries
*/
public function getTime()
{
$time = 0;
foreach ($this->data['queries'] as $query) {
$time += (float) $query['time'];
}
return $time;
}
/**
* Creates an array of Build objects.
*
* @return array An array of Build objects
*/
private function buildQueries()
{
return $this->logger->getQueries();
}
/**
* Count queries.
*
* @return int The number of queries.
*/
private function countQueries()
{
return count($this->logger->getQueries());
}
/**
* @inheritdoc
*/
public function reset()
{
// TODO: Implement reset() method.
}
}

View file

@ -8,10 +8,10 @@
* @license MIT License
*/
namespace Propel\PropelBundle\DataFixtures;
use \Propel;
namespace Propel\Bundle\PropelBundle\DataFixtures;
use Propel\Runtime\Map\DatabaseMap;
use Propel\Runtime\Propel;
use Symfony\Component\Finder\Finder;
/**
@ -23,23 +23,32 @@ abstract class AbstractDataHandler
* @var string
*/
protected $rootDir;
/**
* @var \PDO
*/
protected $con;
/**
* @var \DatabaseMap
* @var DatabaseMap
*/
protected $dbMap;
/**
* @var array
*/
protected $datasources = array();
/**
* Default constructor
*
* @param string $rootDir The root directory.
* @param string $rootDir The root directory.
* @param array $datasources
*/
public function __construct($rootDir)
public function __construct($rootDir, array $datasources)
{
$this->rootDir = $rootDir;
$this->datasources = $datasources;
}
/**
@ -53,7 +62,7 @@ abstract class AbstractDataHandler
/**
* Load Map builders.
*
* @param string $connectionName A connection name.
* @param string $connectionName A connection name.
*/
protected function loadMapBuilders($connectionName = null)
{
@ -62,27 +71,46 @@ abstract class AbstractDataHandler
}
$this->dbMap = Propel::getDatabaseMap($connectionName);
if (0 === count($this->dbMap->getTables())) {
$finder = new Finder();
$files = $finder->files()->name('*TableMap.php')->in($this->getRootDir() . '/../')->exclude('Tests');
$files = $finder
->files()->name('*TableMap.php')
->in($this->getModelSearchPaths($connectionName))
->notName('TableMap.php')
->exclude('PropelBundle')
->exclude('Tests');
foreach ($files as $file) {
$class = $this->guessFullClassName($file->getRelativePath(), basename($file, '.php'));
if (null !== $class) {
if (null !== $class && $this->isInDatabase($class, $connectionName)) {
$this->dbMap->addTableFromMapClass($class);
}
}
}
}
/**
* Check if a table is in a database
*
* @param string $class
* @param string $connectionName
*
* @return boolean
*/
protected function isInDatabase($class, $connectionName)
{
return constant($class.'::DATABASE_NAME') === $connectionName;
}
/**
* Try to find a valid class with its namespace based on the filename.
* Based on the PSR-0 standard, the namespace should be the directory structure.
*
* @param string $path The relative path of the file.
* @param string $shortClassName The short class name aka the filename without extension.
* @param string $path The relative path of the file.
* @param string $shortClassName The short class name aka the filename without extension.
*
* @return string|null
*/
private function guessFullClassName($path, $shortClassName)
{
@ -106,4 +134,27 @@ abstract class AbstractDataHandler
return null;
}
/**
* Gets the search path for models out of the configuration.
*
* @param string $connectionName A connection name.
*
* @return string[]
*/
protected function getModelSearchPaths($connectionName)
{
$searchPath = array();
if (!empty($this->datasources['database']['connections'][$connectionName]['model_paths'])) {
$modelPaths = $this->datasources['database']['connections'][$connectionName]['model_paths'];
foreach ($modelPaths as $modelPath) {
$searchPath[] = $this->getRootDir() . '/../' . $modelPath;
}
} else {
$searchPath[] = $this->getRootDir() . '/../';
}
return $searchPath;
}
}

View file

@ -8,13 +8,12 @@
* @license MIT License
*/
namespace Propel\PropelBundle\DataFixtures\Dumper;
namespace Propel\Bundle\PropelBundle\DataFixtures\Dumper;
use \PDO;
use \Propel;
use \BaseObject;
use Propel\PropelBundle\DataFixtures\AbstractDataHandler;
use Propel\Bundle\PropelBundle\DataFixtures\AbstractDataHandler;
use Propel\Generator\Model\PropelTypes;
use Propel\Runtime\Propel;
/**
* Abstract class to manage a common logic to dump data.
@ -56,7 +55,7 @@ abstract class AbstractDataDumper extends AbstractDataHandler implements DataDum
* Dumps data to fixture from a given connection and
* returns an array.
*
* @param string $connectionName The connection name
* @param string $connectionName The connection name
* @return array
*/
protected function getDataAsArray()
@ -70,7 +69,7 @@ abstract class AbstractDataDumper extends AbstractDataHandler implements DataDum
$dumpData = array();
foreach ($tables as $tableName) {
$tableMap = $this->dbMap->getTable(constant(constant($tableName.'::PEER').'::TABLE_NAME'));
$tableMap = $this->dbMap->getTable(constant(constant($tableName.'::TABLE_MAP').'::TABLE_NAME'));
$hasParent = false;
$haveParents = false;
$fixColumn = null;
@ -108,15 +107,19 @@ abstract class AbstractDataDumper extends AbstractDataHandler implements DataDum
}
$stmt = $this
->con
->query(sprintf('SELECT %s FROM %s', implode(',', $in), constant(constant($tableName.'::PEER').'::TABLE_NAME')));
->query(sprintf('SELECT `%s` FROM `%s`', implode('`, `', $in), constant(constant($tableName.'::TABLE_MAP').'::TABLE_NAME')));
$resultsSets[] = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt->closeCursor();
$set = array();
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
$set[] = $row;
}
$resultsSets[] = $set;
$stmt->close();
unset($stmt);
}
foreach ($resultsSets as $rows) {
if(count($rows) > 0 && !isset($dumpData[$tableName])) {
if (count($rows) > 0 && !isset($dumpData[$tableName])) {
$dumpData[$tableName] = array();
foreach ($rows as $row) {
@ -148,16 +151,19 @@ abstract class AbstractDataDumper extends AbstractDataHandler implements DataDum
$values[$col] = $relatedTable->getPhpName().'_'.$row[$col];
$values[$col] = strlen($row[$col]) ? $relatedTable->getPhpName().'_'.$row[$col] : '';
}
}
elseif (!$isPrimaryKey || ($isPrimaryKey && !$tableMap->isUseIdGenerator())) {
if (!empty($row[$col]) && 'ARRAY' === $column->getType()) {
} elseif (!$isPrimaryKey || ($isPrimaryKey && !$tableMap->isUseIdGenerator())) {
if (!empty($row[$col]) && PropelTypes::PHP_ARRAY === $column->getType()) {
$serialized = substr($row[$col], 2, -2);
$row[$col] = $serialized ? explode(' | ', $serialized) : array();
$row[$col] = $serialized ? explode(' | ', $serialized) : array();
}
// We did not want auto incremented primary keys
$values[$col] = $row[$col];
}
if (PropelTypes::OBJECT === $column->getType()) {
$values[$col] = unserialize($row[$col]);
}
}
if (count($primaryKeys) > 1 || (count($primaryKeys) > 0 && count($foreignKeys) > 0)) {
@ -177,7 +183,7 @@ abstract class AbstractDataDumper extends AbstractDataHandler implements DataDum
* Fixes the ordering of foreign key data, by outputting data
* a foreign key depends on before the table with the foreign key.
*
* @param array $classes The array with the class names
* @param array $classes The array with the class names
* @return array
*/
protected function fixOrderingOfForeignKeyData($classes)
@ -185,7 +191,7 @@ abstract class AbstractDataDumper extends AbstractDataHandler implements DataDum
// reordering classes to take foreign keys into account
for ($i = 0, $count = count($classes); $i < $count; $i++) {
$class = $classes[$i];
$tableMap = $this->dbMap->getTable(constant(constant($class.'::PEER').'::TABLE_NAME'));
$tableMap = $this->dbMap->getTable(constant(constant($class.'::TABLE_MAP').'::TABLE_NAME'));
foreach ($tableMap->getColumns() as $column) {
if ($column->isForeignKey()) {
@ -214,7 +220,7 @@ abstract class AbstractDataDumper extends AbstractDataHandler implements DataDum
protected function fixOrderingOfForeignKeyDataInSameTable($resultsSets, $tableName, $column, $in = null)
{
$sql = sprintf('SELECT * FROM %s WHERE %s %s',
constant(constant($tableName.'::PEER').'::TABLE_NAME'),
constant(constant($tableName.'::TABLE_MAP').'::TABLE_NAME'),
strtolower($column->getName()),
null === $in ? 'IS NULL' : 'IN ('.$in.')');

View file

@ -8,7 +8,7 @@
* @license MIT License
*/
namespace Propel\PropelBundle\DataFixtures\Dumper;
namespace Propel\Bundle\PropelBundle\DataFixtures\Dumper;
/**
* Interface that exposes how Propel data dumpers should work.
@ -20,8 +20,8 @@ interface DataDumperInterface
/**
* Dumps data to fixtures from a given connection.
*
* @param string $filename The file name to write data.
* @param string $connectionName The Propel connection name.
* @param string $filename The file name to write data.
* @param string $connectionName The Propel connection name.
*/
function dump($filename, $connectionName = null);
public function dump($filename, $connectionName = null);
}

View file

@ -8,7 +8,7 @@
* @license MIT License
*/
namespace Propel\PropelBundle\DataFixtures\Dumper;
namespace Propel\Bundle\PropelBundle\DataFixtures\Dumper;
use Symfony\Component\Yaml\Yaml;
@ -24,6 +24,11 @@ class YamlDataDumper extends AbstractDataDumper
*/
protected function transformArrayToData($data)
{
return Yaml::dump($data, 3);
return Yaml::dump(
$data,
$inline = 3,
$indent = 4,
Yaml::DUMP_OBJECT
);
}
}

View file

@ -8,18 +8,15 @@
* @license MIT License
*/
namespace Propel\PropelBundle\DataFixtures\Loader;
namespace Propel\Bundle\PropelBundle\DataFixtures\Loader;
use \Propel;
use \BasePeer;
use \BaseObject;
use \ColumnMap;
use \PropelException;
use Propel\PropelBundle\DataFixtures\AbstractDataHandler;
use Propel\PropelBundle\Util\PropelInflector;
use Symfony\Component\Finder\Finder;
use Propel\Bundle\PropelBundle\DataFixtures\AbstractDataHandler;
use Propel\Bundle\PropelBundle\Util\PropelInflector;
use Propel\Generator\Model\PropelTypes;
use Propel\Runtime\ActiveRecord\ActiveRecordInterface;
use Propel\Runtime\Map\Exception\TableNotFoundException;
use Propel\Runtime\Map\TableMap;
use Propel\Runtime\Propel;
/**
* Abstract class to manage a common logic to load datas.
@ -41,7 +38,7 @@ abstract class AbstractDataLoader extends AbstractDataHandler implements DataLoa
/**
* Transforms a file containing data in an array.
*
* @param string $file A filename.
* @param string $file A filename.
* @return array
*/
abstract protected function transformDataToArray($file);
@ -60,16 +57,19 @@ abstract class AbstractDataLoader extends AbstractDataHandler implements DataLoa
try {
$this->con->beginTransaction();
$datas = array();
foreach ($files as $file) {
$datas = $this->transformDataToArray($file);
$content = $this->transformDataToArray($file);
if (count($datas) > 0) {
$this->deleteCurrentData($datas);
$this->loadDataFromArray($datas);
if (count($content) > 0) {
$datas = array_merge_recursive($datas, $content);
$nbFiles++;
}
}
$this->deleteCurrentData($datas);
$this->loadDataFromArray($datas);
$this->con->commit();
} catch (\Exception $e) {
$this->con->rollBack();
@ -82,7 +82,7 @@ abstract class AbstractDataLoader extends AbstractDataHandler implements DataLoa
/**
* Deletes current data.
*
* @param array $data The data to delete
* @param array $data The data to delete
*/
protected function deleteCurrentData($data = null)
{
@ -93,18 +93,28 @@ abstract class AbstractDataLoader extends AbstractDataHandler implements DataLoa
if (in_array($class, $this->deletedClasses)) {
continue;
}
$this->deleteClassData($class);
}
}
}
// Check that peer class exists before calling doDeleteAll()
$peerClass = constant($class.'::PEER');
if (!class_exists($peerClass)) {
throw new \InvalidArgumentException(sprintf('Unknown class "%sPeer".', $class));
}
/**
* Delete data for a given class, and for its ancestors (if any).
*
* @param string $class Class name to delete
*/
protected function deleteClassData($class)
{
$tableMap = $this->dbMap->getTable(constant(constant($class.'::TABLE_MAP').'::TABLE_NAME'));
$tableMap->doDeleteAll($this->con);
// bypass the soft_delete behavior if enabled
$deleteMethod = method_exists($peerClass, 'doForceDeleteAll') ? 'doForceDeleteAll' : 'doDeleteAll';
call_user_func(array($peerClass, $deleteMethod), $this->con);
$this->deletedClasses[] = $class;
$this->deletedClasses[] = $class;
// Remove ancestors data
if (false !== ($parentClass = get_parent_class(get_parent_class($class)))) {
$reflectionClass = new \ReflectionClass($parentClass);
if (!$reflectionClass->isAbstract()) {
$this->deleteClassData($parentClass);
}
}
}
@ -112,7 +122,7 @@ abstract class AbstractDataLoader extends AbstractDataHandler implements DataLoa
/**
* Loads the data using the generated data model.
*
* @param array $data The data to be loaded
* @param array|null $data The data to be loaded
*/
protected function loadDataFromArray($data = null)
{
@ -121,17 +131,20 @@ abstract class AbstractDataLoader extends AbstractDataHandler implements DataLoa
}
foreach ($data as $class => $datas) {
$class = trim($class);
$tableMap = $this->dbMap->getTable(constant(constant($class.'::PEER').'::TABLE_NAME'));
$column_names = call_user_func_array(array(constant($class.'::PEER'), 'getFieldNames'), array(BasePeer::TYPE_FIELDNAME));
// iterate through datas for this class
// might have been empty just for force a table to be emptied on import
if (!is_array($datas)) {
continue;
}
foreach ($datas as $key => $data) {
$class = trim($class);
if ('\\' == $class[0]) {
$class = substr($class, 1);
}
$tableMap = $this->dbMap->getTable(constant(constant($class.'::TABLE_MAP').'::TABLE_NAME'));
$column_names = $tableMap->getFieldnames(TableMap::TYPE_PHPNAME);
foreach ($datas as $key => $values) {
// create a new entry in the database
if (!class_exists($class)) {
throw new \InvalidArgumentException(sprintf('Unknown class "%s".', $class));
@ -139,28 +152,30 @@ abstract class AbstractDataLoader extends AbstractDataHandler implements DataLoa
$obj = new $class();
if (!$obj instanceof BaseObject) {
if (!$obj instanceof ActiveRecordInterface) {
throw new \RuntimeException(
sprintf('The class "%s" is not a Propel class. There is probably another class named "%s" somewhere.', $class, $class)
);
}
if (!is_array($data)) {
if (!is_array($values)) {
throw new \InvalidArgumentException(sprintf('You must give a name for each fixture data entry (class %s).', $class));
}
foreach ($data as $name => $value) {
try {
if (is_array($value) && 's' === substr($name, -1)) {
foreach ($values as $name => $value) {
if (is_array($value) && 's' === substr($name, -1)) {
try {
// many to many relationship
$this->loadManyToMany($obj, substr($name, 0, -1), $value);
continue;
}
} catch (\PropelException $e) {
// Check whether this is actually an array stored in the object.
if ('Cannot fetch TableMap for undefined table: '.substr($name, 0, -1) === $e->getMessage()) {
if ('ARRAY' !== $tableMap->getColumn($name)->getType()) {
throw $e;
} catch (TableNotFoundException $e) {
// Check whether this is actually an array stored in the object.
if ('Cannot fetch TableMap for undefined table: ' . substr($name, 0, -1) === $e->getMessage()) {
if (PropelTypes::PHP_ARRAY !== $tableMap->getColumn($name)->getType()
&& PropelTypes::OBJECT !== $tableMap->getColumn($name)->getType()) {
throw $e;
}
}
}
}
@ -168,7 +183,7 @@ abstract class AbstractDataLoader extends AbstractDataHandler implements DataLoa
$isARealColumn = true;
if ($tableMap->hasColumn($name)) {
$column = $tableMap->getColumn($name);
} else if ($tableMap->hasColumnByPhpName($name)) {
} elseif ($tableMap->hasColumnByPhpName($name)) {
$column = $tableMap->getColumnByPhpName($name);
} else {
$isARealColumn = false;
@ -176,10 +191,13 @@ abstract class AbstractDataLoader extends AbstractDataHandler implements DataLoa
// foreign key?
if ($isARealColumn) {
// self referencing entry
if ($column->isPrimaryKey() && null !== $value) {
if (isset($this->object_references[$class.'_'.$value])) {
$obj = $this->object_references[$class.'_'.$value];
/*
* A column, which is a PrimaryKey (self referencing, e.g. versionable behavior),
* but which is not a ForeignKey (e.g. delegatable behavior on 1:1 relation).
*/
if ($column->isPrimaryKey() && null !== $value && !$column->isForeignKey()) {
if (isset($this->object_references[$this->cleanObjectRef($class.'_'.$value)])) {
$obj = $this->object_references[$this->cleanObjectRef($class.'_'.$value)];
continue;
}
@ -187,21 +205,25 @@ abstract class AbstractDataLoader extends AbstractDataHandler implements DataLoa
if ($column->isForeignKey() && null !== $value) {
$relatedTable = $this->dbMap->getTable($column->getRelatedTableName());
if (!isset($this->object_references[$relatedTable->getClassname().'_'.$value])) {
throw new \InvalidArgumentException(
sprintf('The object "%s" from class "%s" is not defined in your data file.', $value, $relatedTable->getClassname())
);
if (isset($this->object_references[$this->cleanObjectRef($relatedTable->getClassname().'_'.$value)])) {
$value = $this
->object_references[$this->cleanObjectRef($relatedTable->getClassname().'_'.$value)]
->getByName($column->getRelatedName(), TableMap::TYPE_COLNAME);
} else {
$relatedClass = $this->cleanObjectRef($relatedTable->getClassName());
if (isset($data[$relatedClass]) || isset($data['\\' . $relatedClass])) {
throw new \InvalidArgumentException(
sprintf('The object "%s" from class "%s" is not defined in your data file.', $value, $relatedTable->getClassname())
);
}
}
$value = $this
->object_references[$relatedTable->getClassname().'_'.$value]
->getByName($column->getRelatedName(), BasePeer::TYPE_COLNAME);
}
}
if (false !== $pos = array_search($name, $column_names)) {
$obj->setByPosition($pos, $value);
}
elseif (is_callable(array($obj, $method = 'set'.ucfirst(PropelInflector::camelize($name))))) {
} elseif (is_callable(array($obj, $method = 'set'.ucfirst(PropelInflector::camelize($name))))) {
$obj->$method($value);
} else {
throw new \InvalidArgumentException(sprintf('Column "%s" does not exist for class "%s".', $name, $class));
@ -210,10 +232,34 @@ abstract class AbstractDataLoader extends AbstractDataHandler implements DataLoa
$obj->save($this->con);
// save the object for future reference
if (method_exists($obj, 'getPrimaryKey')) {
$this->object_references[$class.'_'.$key] = $obj;
}
$this->saveParentReference($class, $key, $obj);
}
}
}
/**
* Save a reference to the specified object (and its ancestors) before loading them.
*
* @param string $class Class name of passed object
* @param string $key Key identifying specified object
* @param ActiveRecordInterface $obj A Propel object
*/
protected function saveParentReference($class, $key, &$obj)
{
if (!method_exists($obj, 'getPrimaryKey')) {
return;
}
$this->object_references[$this->cleanObjectRef($class.'_'.$key)] = $obj;
// Get parent (schema ancestor) of parent (Propel base class) in case of inheritance
if (false !== ($parentClass = get_parent_class(get_parent_class($class)))) {
$reflectionClass = new \ReflectionClass($parentClass);
if (!$reflectionClass->isAbstract()) {
$parentObj = new $parentClass();
$parentObj->fromArray($obj->toArray());
$this->saveParentReference($parentClass, $key, $parentObj);
}
}
}
@ -221,19 +267,24 @@ abstract class AbstractDataLoader extends AbstractDataHandler implements DataLoa
/**
* Loads many to many objects.
*
* @param BaseObject $obj A Propel object
* @param string $middleTableName The middle table name
* @param array $values An array of values
* @param ActiveRecordInterface $obj A Propel object
* @param string $middleTableName The middle table name
* @param array $values An array of values
*/
protected function loadManyToMany($obj, $middleTableName, $values)
{
$middleTable = $this->dbMap->getTable($middleTableName);
$middleClass = $middleTable->getPhpName();
$middleClass = $middleTable->getClassname();
$tableName = constant(constant(get_class($obj).'::TABLE_MAP').'::TABLE_NAME');
foreach ($middleTable->getColumns() as $column) {
if ($column->isForeignKey() && constant(constant(get_class($obj).'::PEER').'::TABLE_NAME') != $column->getRelatedTableName()) {
$relatedClass = $this->dbMap->getTable($column->getRelatedTableName())->getPhpName();
break;
if ($column->isForeignKey()) {
if ($tableName !== $column->getRelatedTableName()) {
$relatedClass = $this->dbMap->getTable($column->getRelatedTableName())->getClassname();
$relatedSetter = 'set' . $column->getRelation()->getName();
} else {
$setter = 'set' . $column->getRelation()->getName();
}
}
}
@ -241,11 +292,8 @@ abstract class AbstractDataLoader extends AbstractDataHandler implements DataLoa
throw new \InvalidArgumentException(sprintf('Unable to find the many-to-many relationship for object "%s".', get_class($obj)));
}
$setter = 'set'.get_class($obj);
$relatedSetter = 'set'.$relatedClass;
foreach ($values as $value) {
if (!isset($this->object_references[$relatedClass.'_'.$value])) {
if (!isset($this->object_references[$this->cleanObjectRef($relatedClass.'_'.$value)])) {
throw new \InvalidArgumentException(
sprintf('The object "%s" from class "%s" is not defined in your data file.', $value, $relatedClass)
);
@ -253,8 +301,13 @@ abstract class AbstractDataLoader extends AbstractDataHandler implements DataLoa
$middle = new $middleClass();
$middle->$setter($obj);
$middle->$relatedSetter($this->object_references[$relatedClass.'_'.$value]);
$middle->save();
$middle->$relatedSetter($this->object_references[$this->cleanObjectRef($relatedClass.'_'.$value)]);
$middle->save($this->con);
}
}
protected function cleanObjectRef($ref)
{
return $ref[0] === '\\' ? substr($ref, 1) : $ref;
}
}

View file

@ -8,7 +8,7 @@
* @license MIT License
*/
namespace Propel\PropelBundle\DataFixtures\Loader;
namespace Propel\Bundle\PropelBundle\DataFixtures\Loader;
/**
* Interface that exposes how Propel data loaders should work.
@ -20,8 +20,8 @@ interface DataLoaderInterface
/**
* Loads data from a set of files.
*
* @param array $files A set of files containing datas to load.
* @param string $connectionName The Propel connection name
* @param array $files A set of files containing datas to load.
* @param string $connectionName The Propel connection name
*/
function load($files = array(), $connectionName);
public function load($files = array(), $connectionName);
}

View file

@ -1,67 +0,0 @@
<?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\DataFixtures\Loader;
/**
* @author Toni Uebernickel <tuebernickel@gmail.com>
*/
class DataWiper extends AbstractDataLoader
{
/**
* Clears the database completely.
*
* @param array $files A set of files containing datas to load.
* @param string $connectionName The Propel connection name
*/
public function load($files = array(), $connectionName)
{
$this->deletedClasses = array();
$this->loadMapBuilders($connectionName);
$this->con = \Propel::getConnection($connectionName);
try {
$this->con->beginTransaction();
if ('mysql' === $this->con->getAttribute(\PDO::ATTR_DRIVER_NAME)) {
$this->con->exec('SET FOREIGN_KEY_CHECKS = 0;');
}
$tables = array();
foreach ($this->dbMap->getTables() as $eachTable) {
/* @var $eachTable \TableMap */
$tables[$eachTable->getClassname()] = array();
}
$this->deleteCurrentData($tables);
if ('mysql' === $this->con->getAttribute(\PDO::ATTR_DRIVER_NAME)) {
$this->con->exec('SET FOREIGN_KEY_CHECKS = 1;');
}
$this->con->commit();
} catch (\Exception $e) {
$this->con->rollBack();
throw $e;
}
}
/**
* Not used by this data loader.
*
* @param string $file A filename.
*
* @return array
*/
protected function transformDataToArray($file)
{
return array();
}
}

View file

@ -8,7 +8,7 @@
* @license MIT License
*/
namespace Propel\PropelBundle\DataFixtures\Loader;
namespace Propel\Bundle\PropelBundle\DataFixtures\Loader;
/**
* XML fixtures loader.
@ -28,7 +28,7 @@ class XmlDataLoader extends AbstractDataLoader
}
/**
* @param SimpleXMLElement $xml
* @param SimpleXMLElement $xml
* @return array
*/
protected function simpleXmlToArray($xml)
@ -39,7 +39,7 @@ class XmlDataLoader extends AbstractDataLoader
// First make a valid key which is the Ns (Namespace) attribute
// + the element name (the class name)
foreach ($value->attributes() as $subkey => $subvalue) {
if ('Namespace' === (string)$subkey) {
if ('Namespace' === (string) $subkey) {
$key = $subvalue . '\\' . $key;
break;
}

View file

@ -8,8 +8,10 @@
* @license MIT License
*/
namespace Propel\PropelBundle\DataFixtures\Loader;
namespace Propel\Bundle\PropelBundle\DataFixtures\Loader;
use Faker\Generator;
use Symfony\Component\Yaml\ParseException;
use Symfony\Component\Yaml\Yaml;
/**
@ -19,11 +21,58 @@ use Symfony\Component\Yaml\Yaml;
*/
class YamlDataLoader extends AbstractDataLoader
{
/**
* @var \Faker\Generator
*/
private $faker;
/**
* {@inheritdoc}
*/
public function __construct($rootDir, array $datasources, Generator $faker = null)
{
parent::__construct($rootDir, $datasources);
$this->faker = $faker;
}
/**
* {@inheritdoc}
*/
protected function transformDataToArray($file)
{
if (strpos($file, "\n") === false && is_file($file)) {
if (false === is_readable($file)) {
throw new ParseException(sprintf('Unable to parse "%s" as the file is not readable.', $file));
}
if (null !== $this->faker) {
$generator = $this->faker;
$faker = function ($type) use ($generator) {
$args = func_get_args();
array_shift($args);
echo Yaml::dump(call_user_func_array(array($generator, $type), $args)) . "\n";
};
} else {
$faker = function ($text) {
echo $text . "\n";
};
}
ob_start();
$retval = include $file;
$content = ob_get_clean();
// if an array is returned by the config file assume it's in plain php form else in YAML
$file = is_array($retval) ? $retval : $content;
// if an array is returned by the config file assume it's in plain php form else in YAML
if (is_array($file)) {
return $file;
}
}
return Yaml::parse($file);
}
}

View file

@ -8,217 +8,238 @@
* @license MIT License
*/
namespace Propel\PropelBundle\DependencyInjection;
namespace Propel\Bundle\PropelBundle\DependencyInjection;
use Propel\Common\Config\PropelConfiguration;
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
* This class contains the configuration information for the bundle
*/
class Configuration extends PropelConfiguration
{
private $debug;
private $defaultDir;
/**
* Constructor
*
* @param Boolean $debug Wether to use the debug mode
*/
public function __construct($debug)
public function __construct($debug, $kernelDir)
{
$this->debug = (Boolean) $debug;
$this->debug = $debug;
$this->defaultDir = $kernelDir.'/propel';
}
/**
* 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:
* path: xxxxxxx
* path_phing: xxxxxxx
* logging: %kernel.debug%
* build_properties:
* xxxx.xxxx: xxxxxx
* ...
*/
private function addGeneralSection(ArrayNodeDefinition $node)
protected function addPathsSection(ArrayNodeDefinition $node)
{
$node
->children()
->scalarNode('path')->end()
->scalarNode('phing_path')->end()
->scalarNode('logging')->defaultValue($this->debug)->end()
->arrayNode('build_properties')
->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')->defaultValue('mysql')->end()
->scalarNode('user')->defaultValue('root')->end()
->scalarNode('password')->defaultValue('')->end()
->scalarNode('dsn')->defaultValue('')->end()
->scalarNode('classname')->defaultValue($this->debug ? 'DebugPDO' : 'PropelPDO')->end()
->arrayNode('slaves')
->useAttributeAsKey('name')
->prototype('array')
->children()
->scalarNode('user')->defaultValue('root')->end()
->scalarNode('password')->defaultValue('')->end()
->scalarNode('dsn')->defaultValue('')->end()
->scalarNode('classname')->defaultValue($this->debug ? 'DebugPDO' : 'PropelPDO')->end()
->end()
->end()
->arrayNode('paths')
->addDefaultsIfNotSet()
->children()
->scalarNode('schemaDir')->defaultValue($this->defaultDir)->end()
->scalarNode('sqlDir')->defaultValue($this->defaultDir.'/sql')->end()
->scalarNode('migrationDir')->defaultValue($this->defaultDir.'/migrations')->end()
->scalarNode('phpConfDir')->defaultValue($this->defaultDir.'/generated-conf')->end()
->scalarNode('composerDir')->defaultNull()->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()
->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()
protected function addRuntimeSection(ArrayNodeDefinition $node)
{
$treeBuilder = new TreeBuilder();
$node = $treeBuilder->root('connections');
$node
->requiresAtLeastOneElement()
->useAttributeAsKey('name')
->prototype('array')
->children()
->scalarNode('driver')->defaultValue('mysql')->end()
->scalarNode('user')->defaultValue('root')->end()
->scalarNode('password')->defaultValue('')->end()
->scalarNode('dsn')->defaultValue('')->end()
->scalarNode('classname')->defaultValue($this->debug ? 'DebugPDO' : 'PropelPDO')->end()
->arrayNode('slaves')
->useAttributeAsKey('name')
->prototype('array')
->children()
->arrayNode('runtime')
->addDefaultsIfNotSet()
->fixXmlConfig('connection')
->children()
->scalarNode('defaultConnection')->end()
->arrayNode('connections')
->prototype('scalar')->end()
->end()
->booleanNode('logging')->defaultValue($this->debug)->end()
->arrayNode('log')
->useAttributeAsKey('name')
->prototype('array')
->children()
->scalarNode('type')->end()
->scalarNode('path')->end()
->enumNode('level')->values(array(100, 200, 250, 300, 400, 500, 550, 600))->end()
->end()
->end()
->end()
->arrayNode('profiler')
->children()
->scalarNode('driver')->defaultValue('mysql')->end()
->scalarNode('user')->defaultValue('root')->end()
->scalarNode('password')->defaultValue('')->end()
->scalarNode('dsn')->defaultValue('')->end()
->scalarNode('classname')->defaultValue($this->debug ? 'DebugPDO' : 'PropelPDO')->end()
->scalarNode('classname')->defaultValue('\Propel\Runtime\Util\Profiler')->end()
->floatNode('slowTreshold')->defaultValue(0.1)->end()
->arrayNode('details')
->children()
->arrayNode('time')
->addDefaultsIfNotSet()
->children()
->integerNode('precision')->min(0)->defaultValue(3)->end()
->integerNode('pad')->min(0)->defaultValue(8)->end()
->end()
->end()
->arrayNode('memory')
->addDefaultsIfNotSet()
->children()
->integerNode('precision')->min(0)->defaultValue(3)->end()
->integerNode('pad')->min(0)->defaultValue(8)->end()
->end()
->end()
->arrayNode('memDelta')
->addDefaultsIfNotSet()
->children()
->integerNode('precision')->min(0)->defaultValue(3)->end()
->integerNode('pad')->min(0)->defaultValue(8)->end()
->end()
->end()
->arrayNode('memPeak')
->addDefaultsIfNotSet()
->children()
->integerNode('precision')->min(0)->defaultValue(3)->end()
->integerNode('pad')->min(0)->defaultValue(8)->end()
->end()
->end()
->end()
->end()
->scalarNode('innerGlue')->defaultValue(':')->end()
->scalarNode('outerGlue')->defaultValue('|')->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()
;
->end() //runtime
->end();
}
return $node;
protected function addDatabaseSection(ArrayNodeDefinition $node)
{
$validAdapters = array('mysql', 'pgsql', 'sqlite', 'mssql', 'sqlsrv', 'oracle');
$node
->children()
->arrayNode('database')
->isRequired()
->addDefaultsIfNotSet()
->children()
->arrayNode('connections')
->isRequired()
->validate()
->always()
->then(function($connections) {
foreach ($connections as $name => $connection) {
if (strpos($name, '.') !== false) {
throw new \InvalidArgumentException('Dots are not allowed in connection names');
}
}
return $connections;
})
->end()
->requiresAtLeastOneElement()
->normalizeKeys(false)
->useAttributeAsKey('id')
->prototype('array')
->fixXmlConfig('slave')
->fixXmlConfig('model_path')
->children()
->scalarNode('classname')->defaultValue($this->debug ? '\Propel\Runtime\Connection\DebugPDO' : '\Propel\Runtime\Connection\ConnectionWrapper')->end()
->scalarNode('adapter')
->isRequired()
->cannotBeEmpty()
->beforeNormalization()
->ifString()
->then(function ($v) { return preg_replace('/^pdo_/', '', strtolower($v)); })
->end()
->validate()
->ifNotInArray($validAdapters)
->thenInvalid('The adapter %s is not supported. Please choose one of ' . implode(', ', $validAdapters))
->end()
->end()
->scalarNode('dsn')
->isRequired()
->cannotBeEmpty()
->beforeNormalization()
->ifString()
->then(function ($v) { return preg_replace('/^pdo_/', '', $v); })
->end()
->end()
->scalarNode('user')->isRequired()->end()
->scalarNode('password')->isRequired()->treatNullLike('')->end()
->arrayNode('options')
->addDefaultsIfNotSet()
->children()
->booleanNode('ATTR_PERSISTENT')->defaultFalse()->end()
->scalarNode('MYSQL_ATTR_SSL_CA')->end()
->scalarNode('MYSQL_ATTR_SSL_CERT')->end()
->scalarNode('MYSQL_ATTR_SSL_KEY')->end()
->scalarNode('MYSQL_ATTR_MAX_BUFFER_SIZE')->end()
->end()
->end()
->arrayNode('attributes')
->addDefaultsIfNotSet()
->children()
->booleanNode('ATTR_EMULATE_PREPARES')->defaultFalse()->end()
->scalarNode('SQLSRV_ATTR_ENCODING')->end()
->end()
->end()
->arrayNode('model_paths')
->defaultValue(['src', 'vendor'])
->prototype('scalar')->end()
->end()
->arrayNode('settings')
->fixXmlConfig('query', 'queries')
->children()
->scalarNode('charset')->defaultValue('utf8')->end()
->arrayNode('queries')
->prototype('scalar')->end()
->end()
->end()
->end()
->arrayNode('slaves')
->prototype('array')
->children()
->scalarNode('dsn')
->beforeNormalization()
->ifString()
->then(function ($v) { return preg_replace('/^pdo_/', '', $v); })
->end()
->end()
->end()
->end()
->end()
->end()
->end()
->end()
->arrayNode('adapters')
->addDefaultsIfNotSet()
->children()
->arrayNode('mysql')
->addDefaultsIfNotSet()
->children()
->scalarNode('tableType')->defaultValue('InnoDB')->treatNullLike('InnoDB')->end()
->scalarNode('tableEngineKeyword')->defaultValue('ENGINE')->end()
->end()
->end()
->arrayNode('sqlite')
->addDefaultsIfNotSet()
->children()
->scalarNode('foreignKey')->end()
->scalarNode('tableAlteringWorkaround')->end()
->end()
->end()
->arrayNode('oracle')
->addDefaultsIfNotSet()
->children()
->scalarNode('autoincrementSequencePattern')->defaultValue('${table}_SEQ')->end()
->end()
->end()
->end()
->end() //adapters
->end()
->end() //database
->end()
;
}
}

View file

@ -8,15 +8,13 @@
* @license MIT License
*/
namespace Propel\PropelBundle\DependencyInjection;
namespace Propel\Bundle\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\DependencyInjection\Reference;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Config\Definition\Processor;
/**
* PropelExtension loads the PropelBundle configuration.
@ -33,102 +31,35 @@ class PropelExtension extends Extension
*/
public function load(array $configs, ContainerBuilder $container)
{
$processor = new Processor();
$configuration = new Configuration($container->getParameter('kernel.debug'));
$config = $processor->processConfiguration($configuration, $configs);
$configuration = $this->getConfiguration($configs, $container);
$config = $this->processConfiguration($configuration, $configs);
// Composer
if (file_exists($propelPath = $container->getParameter('kernel.root_dir') . '/../vendor/propel/propel1')) {
$container->setParameter('propel.path', $propelPath);
}
if (file_exists($phingPath = $container->getParameter('kernel.root_dir') . '/../vendor/phing/phing/classes')) {
$container->setParameter('propel.phing_path', $phingPath);
}
if (!$container->hasParameter('propel.path')) {
if (!isset($config['path'])) {
throw new \InvalidArgumentException('PropelBundle expects a "path" parameter that must contain the absolute path to the Propel ORM vendor library. The "path" parameter must be defined under the "propel" root node in your configuration.');
} else {
$container->setParameter('propel.path', $config['path']);
if (1 === count($config['database']['connections'])) {
$defaultConnection = array_keys($config['database']['connections'])[0];
if (!isset($config['runtime']['defaultConnection'])) {
$config['runtime']['defaultConnection'] = $defaultConnection;
}
if (!isset($config['generator']['defaultConnection'])) {
$config['generator']['defaultConnection'] = $defaultConnection;
}
}
if (!$container->hasParameter('propel.phing_path')) {
if (!isset($config['phing_path'])) {
throw new \InvalidArgumentException('PropelBundle expects a "phing_path" parameter that must contain the absolute path to the Phing vendor library. The "phing_path" parameter must be defined under the "propel" root node in your configuration.');
} else {
$container->setParameter('propel.phing_path', $config['phing_path']);
}
}
if (isset($config['logging']) && $config['logging']) {
$logging = $config['logging'];
} else {
$logging = false;
}
$container->setParameter('propel.logging', $logging);
$container->setParameter('propel.logging', $config['runtime']['logging']);
$container->setParameter('propel.configuration', $config);
// Load services
if (!$container->hasDefinition('propel')) {
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('propel.xml');
$loader->load('converters.xml');
}
if (isset($config['build_properties']) && is_array($config['build_properties'])) {
$buildProperties = $config['build_properties'];
} else {
$buildProperties = array();
}
$container->getDefinition('propel.build_properties')->setArguments(array($buildProperties));
if (!empty($config['dbal'])) {
$this->dbalLoad($config['dbal'], $container);
$loader->load('security.xml');
$loader->load('console.xml');
}
}
/**
* 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)
public function getConfiguration(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['datasources'][$name]['adapter'] = $conf['driver'];
if (!empty($conf['slaves'])) {
$c['datasources'][$name]['slaves']['connection'] = $conf['slaves'];
}
foreach (array('dsn', 'user', 'password', 'classname', 'options', 'attributes', 'settings') as $att) {
if (isset($conf[$att])) {
$c['datasources'][$name]['connection'][$att] = $conf[$att];
}
}
}
// Alias the default connection if not defined
if (!isset($c['datasources']['default'])) {
$c['datasources']['default'] = $c['datasources'][$connectionName];
}
$container->getDefinition('propel.configuration')->setArguments(array($c));
return new Configuration($container->getParameter('kernel.debug'), $container->getParameter('kernel.root_dir'));
}
/**
@ -141,16 +72,6 @@ class PropelExtension extends Extension
return __DIR__.'/../Resources/config/schema';
}
/**
* Returns the namespace to be used for this extension (XML namespace).
*
* @return string The XML namespace
*/
public function getNamespace()
{
return 'http://www.symfony-project.org/schema/dic/propel';
}
/**
* Returns the recommended alias to use in XML.
*

View file

@ -1,44 +0,0 @@
<?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;
/**
* Properties.
*
* @author William Durand <william.durand1@gmail.com>
*/
class Properties
{
/**
* Build properties.
*
* @var array
*/
private $properties;
/**
* Default constructor.
*
* @param $properties An array of properties.
*/
public function __construct(array $properties = array()) {
$this->properties = $properties;
}
/**
* Get properties.
*
* @return array An array of properties.
*/
public function getProperties() {
return $this->properties;
}
}

View file

@ -0,0 +1,51 @@
<?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 Propel\Bundle\PropelBundle\DependencyInjection\Security\UserProvider;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\UserProviderFactoryInterface;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* PropelFactory creates services for Propel user provider.
*
* @author William Durand <william.durand1@gmail.com>
*/
class PropelFactory implements UserProviderFactoryInterface
{
private $key;
private $providerId;
public function __construct($key, $providerId)
{
$this->key = $key;
$this->providerId = $providerId;
}
public function create(ContainerBuilder $container, $id, $config)
{
$container
->setDefinition($id, new ChildDefinition($this->providerId))
->addArgument($config['class'])
->addArgument($config['property'])
;
}
public function getKey()
{
return $this->key;
}
public function addConfiguration(NodeDefinition $node)
{
$node
->children()
->scalarNode('class')->isRequired()->cannotBeEmpty()->end()
->scalarNode('property')->defaultNull()->end()
->end()
;
}
}

72
Form/BaseAbstractType.php Normal file
View file

@ -0,0 +1,72 @@
<?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\Bundle\PropelBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* @author William DURAND <william.durand1@gmail.com>
* @deprecated use AbstractType directly
*/
abstract class BaseAbstractType extends AbstractType
{
protected $options = array();
public function __construct($mergeOptions = null)
{
if ($mergeOptions) {
$this->mergeOptions($mergeOptions);
}
}
public function setOption($name, $value)
{
$this->options[$name] = $value;
}
public function getOption($name)
{
return $this->options[$name];
}
public function setOptions($options)
{
$this->options = $options;
}
public function getOptions()
{
return $this->options;
}
public function mergeOptions($options)
{
$this->options = array_merge($this->options, $options);
}
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults($this->options);
}
/**
* {@inheritdoc}
*/
public function getName()
{
return get_class($this);
}
}

View file

@ -0,0 +1,213 @@
<?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\Bundle\PropelBundle\Form\ChoiceList;
use Propel\Runtime\ActiveQuery\Criteria;
use Propel\Runtime\ActiveQuery\ModelCriteria;
use Propel\Runtime\ActiveRecord\ActiveRecordInterface;
use Propel\Runtime\Map\ColumnMap;
use Symfony\Component\Form\ChoiceList\ChoiceListInterface;
use Symfony\Component\Form\ChoiceList\Factory\ChoiceListFactoryInterface;
use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
/**
* @author William Durand <william.durand1@gmail.com>
* @author Toni Uebernickel <tuebernickel@gmail.com>
* @author Moritz Schroeder <moritz.schroeder@molabs.de>
*/
class PropelChoiceLoader implements ChoiceLoaderInterface
{
/**
* @var ChoiceListFactoryInterface
*/
protected $factory;
/**
* @var string
*/
protected $class;
/**
* @var ModelCriteria
*/
protected $query;
/**
* The fields of which the identifier of the underlying class consists
*
* This property should only be accessed through identifier.
*
* @var array
*/
protected $identifier = array();
/**
* Whether to use the identifier for index generation.
*
* @var bool
*/
protected $identifierAsIndex = false;
/**
* @var ChoiceListInterface
*/
protected $choiceList;
/**
* PropelChoiceListLoader constructor.
*
* @param ChoiceListFactoryInterface $factory
* @param string $class
*/
public function __construct(ChoiceListFactoryInterface $factory, $class, ModelCriteria $queryObject, $useAsIdentifier = null)
{
$this->factory = $factory;
$this->class = $class;
$this->query = $queryObject;
if ($useAsIdentifier) {
$this->identifier = array($this->query->getTableMap()->getColumn($useAsIdentifier));
} else {
$this->identifier = $this->query->getTableMap()->getPrimaryKeys();
}
if (1 === count($this->identifier) && $this->isScalar(current($this->identifier))) {
$this->identifierAsIndex = true;
}
}
/**
* {@inheritdoc}
*/
public function loadChoiceList($value = null)
{
if ($this->choiceList) {
return $this->choiceList;
}
$models = iterator_to_array($this->query->find());
$this->choiceList = $this->factory->createListFromChoices($models, $value);
return $this->choiceList;
}
/**
* {@inheritdoc}
*/
public function loadChoicesForValues(array $values, $value = null)
{
// Performance optimization
if (empty($values)) {
return array();
}
// Optimize performance in case we have a single-field identifier
if (!$this->choiceList && $this->identifierAsIndex && current($this->identifier) instanceof ColumnMap) {
$phpName = current($this->identifier)->getPhpName();
$query = clone $this->query;
$unorderedObjects = $query->filterBy($phpName, $values, Criteria::IN);
$objectsById = array();
$objects = array();
// Maintain order and indices from the given $values
foreach ($unorderedObjects as $object) {
$objectsById[(string) current($this->getIdentifierValues($object))] = $object;
}
foreach ($values as $i => $id) {
if (isset($objectsById[$id])) {
$objects[$i] = $objectsById[$id];
}
}
return $objects;
}
return $this->loadChoiceList($value)->getChoicesForValues($values);
}
/**
* {@inheritdoc}
*/
public function loadValuesForChoices(array $choices, $value = null)
{
// Performance optimization
if (empty($choices)) {
return array();
}
if (!$this->choiceList && $this->identifierAsIndex) {
$values = array();
// Maintain order and indices of the given objects
foreach ($choices as $i => $object) {
if ($object instanceof $this->class) {
// Make sure to convert to the right format
$values[$i] = (string) current($this->getIdentifierValues($object));
}
}
return $values;
}
return $this->loadChoiceList($value)->getValuesForChoices($choices);
}
/**
* Whether this column contains scalar values (to be used as indices).
*
* @param ColumnMap $column
*
* @return bool
*/
private function isScalar(ColumnMap $column)
{
return in_array(
$column->getPdoType(),
array(
\PDO::PARAM_BOOL,
\PDO::PARAM_INT,
\PDO::PARAM_STR,
)
);
}
/**
* Returns the values of the identifier fields of a model.
*
* Propel must know about this model, that is, the model must already
* be persisted or added to the idmodel map before. Otherwise an
* exception is thrown.
*
* @param object $model The model for which to get the identifier
*
* @return array
*/
private function getIdentifierValues($model)
{
if (!$model instanceof $this->class) {
return array();
}
if (1 === count($this->identifier) && current($this->identifier) instanceof ColumnMap) {
$phpName = current($this->identifier)->getPhpName();
if (method_exists($model, 'get' . $phpName)) {
return array($model->{'get' . $phpName}());
}
}
if ($model instanceof ActiveRecordInterface) {
return array($model->getPrimaryKey());
}
return $model->getPrimaryKeys();
}
}

View file

@ -0,0 +1,55 @@
<?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 Propel\Bundle\PropelBundle\Form\DataTransformer;
use Propel\Runtime\Collection\ObjectCollection;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException;
/**
* CollectionToArrayTransformer class.
*
* @author William Durand <william.durand1@gmail.com>
* @author Pierre-Yves Lebecq <py.lebecq@gmail.com>
*/
class CollectionToArrayTransformer implements DataTransformerInterface
{
public function transform($collection)
{
if (null === $collection) {
return array();
}
if (!$collection instanceof ObjectCollection) {
throw new TransformationFailedException('Expected a \Propel\Runtime\Collection\ObjectCollection.');
}
return $collection->getData();
}
public function reverseTransform($array)
{
$collection = new ObjectCollection();
if ('' === $array || null === $array) {
return $collection;
}
if (!is_array($array)) {
throw new TransformationFailedException('Expected an array.');
}
$collection->setData($array);
return $collection;
}
}

View file

@ -0,0 +1,101 @@
<?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 Propel\Bundle\PropelBundle\Form\EventListener;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\FormEvent;
/**
* listener class for propel_translatable_collection
*
* @author Patrick Kaufmann
*/
class TranslationCollectionFormListener implements EventSubscriberInterface
{
private $i18nClass;
private $languages;
public function __construct($languages, $i18nClass)
{
$this->i18nClass = $i18nClass;
$this->languages = $languages;
}
public static function getSubscribedEvents()
{
return array(
FormEvents::PRE_SET_DATA => array('preSetData', 1),
);
}
public function preSetData(FormEvent $event)
{
$form = $event->getForm();
$data = $event->getData();
if (null === $data) {
return;
}
if (!is_array($data) && !($data instanceof \Traversable && $data instanceof \ArrayAccess)) {
throw new UnexpectedTypeException($data, 'array or (\Traversable and \ArrayAccess)');
}
//get the class name of the i18nClass
$temp = explode('\\', $this->i18nClass);
$dataClass = end($temp);
$rootData = $form->getRoot()->getData();
$foundData = false;
$addFunction = 'add'.$dataClass;
//add a database row for every needed language
foreach ($this->languages as $lang) {
$found = false;
foreach ($data as $i18n) {
if (!method_exists($i18n, 'getLocale')) {
throw new UnexpectedTypeException($i18n, 'Propel i18n object');
}
if ($i18n->getLocale() == $lang) {
$found = true;
break;
}
}
if (!$found) {
$currentForm = $form;
while (!$foundData) {
if (method_exists($rootData, $addFunction)) {
$foundData = true;
break;
} elseif (null != ($currentForm = $currentForm->getParent())) {
$rootData = $currentForm->getData();
} else {
break;
}
}
if (!$foundData) {
throw new UnexpectedTypeException($rootData, 'Propel i18n object');
}
$newTranslation = new $this->i18nClass();
$newTranslation->setLocale($lang);
$rootData->$addFunction($newTranslation);
}
}
}
}

View file

@ -0,0 +1,82 @@
<?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 Propel\Bundle\PropelBundle\Form\EventListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
/**
* Event Listener class for propel_translation
*
* @author Patrick Kaufmann
*/
class TranslationFormListener implements EventSubscriberInterface
{
private $columns;
private $dataClass;
public function __construct($columns, $dataClass)
{
$this->columns = $columns;
$this->dataClass = $dataClass;
}
public static function getSubscribedEvents()
{
return array(
FormEvents::PRE_SET_DATA => array('preSetData', 1),
);
}
public function preSetData(FormEvent $event)
{
$form = $event->getForm();
$data = $event->getData();
if (!$data instanceof $this->dataClass) {
return;
}
//loop over all columns and add the input
foreach ($this->columns as $column => $options) {
if (is_string($options)) {
$column = $options;
$options = array();
}
if (null === $options) {
$options = array();
}
$type = TextType::class;
if (array_key_exists('type', $options)) {
$type = $options['type'];
}
$label = $column;
if (array_key_exists('label', $options)) {
$label = $options['label'];
}
$customOptions = array();
if (array_key_exists('options', $options)) {
$customOptions = $options['options'];
}
$options = array(
'label' => $label.' '.strtoupper($data->getLocale())
);
$options = array_merge($options, $customOptions);
$form->add($column, $type, $options);
}
}
}

72
Form/FormBuilder.php Normal file
View file

@ -0,0 +1,72 @@
<?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\Bundle\PropelBundle\Form;
use Propel\Generator\Model\ForeignKey;
use Propel\Generator\Model\Table;
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
/**
* @author Moritz Schroeder <moritz.schroeder@molabs.de>
*/
class FormBuilder
{
/**
* Build a form based on the given table.
*
* @param BundleInterface $bundle
* @param Table $table
* @param string $formTypeNamespace
*
* @return string
*/
public function buildFormType(BundleInterface $bundle, Table $table, $formTypeNamespace)
{
$modelName = $table->getPhpName();
$formTypeContent = file_get_contents(__DIR__ . '/../Resources/skeleton/FormType.php');
$formTypeContent = str_replace('##NAMESPACE##', $bundle->getNamespace() . str_replace('/', '\\', $formTypeNamespace), $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 = str_replace('##BUILD_CODE##', $this->buildFormFields($table), $formTypeContent);
return $formTypeContent;
}
/**
* Build the fields in the FormType.
*
* @param Table $table Table from which the fields will be extracted.
*
* @return string The FormType code.
*/
protected function buildFormFields(Table $table)
{
$buildCode = '';
foreach ($table->getColumns() as $column) {
if ($column->isPrimaryKey()) {
continue;
}
$name = $column->getPhpName();
// Use foreignKey table name, so the TypeGuesser gets it right
if ($column->isForeignKey()) {
/** @var ForeignKey $foreignKey */
$foreignKey = current($column->getForeignKeys());
$name = $foreignKey->getForeignTable()->getPhpName();
}
$buildCode .= sprintf("\n \$builder->add('%s');", lcfirst($name));
}
return $buildCode;
}
}

64
Form/PropelExtension.php Normal file
View file

@ -0,0 +1,64 @@
<?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 Propel\Bundle\PropelBundle\Form;
use Symfony\Component\Form\AbstractExtension;
use Symfony\Component\Form\ChoiceList\Factory\ChoiceListFactoryInterface;
use Symfony\Component\Form\ChoiceList\Factory\DefaultChoiceListFactory;
use Symfony\Component\Form\ChoiceList\Factory\PropertyAccessDecorator;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
/**
* Represents the Propel form extension, which loads the Propel functionality.
*
* @author Joseph Rouff <rouffj@gmail.com>
*/
class PropelExtension extends AbstractExtension
{
/**
* @var PropertyAccessorInterface
*/
protected $propertyAccessor;
/**
* @var ChoiceListFactoryInterface
*/
protected $choiceListFactory;
/**
* PropelExtension constructor.
*
* @param PropertyAccessorInterface|null $propertyAccessor
* @param ChoiceListFactoryInterface|null $choiceListFactory
*/
public function __construct(PropertyAccessorInterface $propertyAccessor = null, ChoiceListFactoryInterface $choiceListFactory = null)
{
$this->propertyAccessor = $propertyAccessor ?: PropertyAccess::createPropertyAccessor();
$this->choiceListFactory = $choiceListFactory ?: new PropertyAccessDecorator(new DefaultChoiceListFactory(), $this->propertyAccessor);
}
protected function loadTypes()
{
return array(
new Type\ModelType($this->propertyAccessor, $this->choiceListFactory),
new Type\TranslationCollectionType(),
new Type\TranslationType()
);
}
protected function loadTypeGuesser()
{
return new TypeGuesser();
}
}

263
Form/Type/ModelType.php Normal file
View file

@ -0,0 +1,263 @@
<?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 Propel\Bundle\PropelBundle\Form\Type;
use Propel\Bundle\PropelBundle\Form\ChoiceList\PropelChoiceLoader;
use Propel\Bundle\PropelBundle\Form\DataTransformer\CollectionToArrayTransformer;
use Propel\Runtime\ActiveQuery\ModelCriteria;
use Propel\Runtime\Map\ColumnMap;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\ChoiceList\Factory\ChoiceListFactoryInterface;
use Symfony\Component\Form\ChoiceList\Factory\DefaultChoiceListFactory;
use Symfony\Component\Form\ChoiceList\Factory\PropertyAccessDecorator;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
use Symfony\Component\OptionsResolver\Exception\MissingOptionsException;
/**
* ModelType class.
*
* @author William Durand <william.durand1@gmail.com>
* @author Toni Uebernickel <tuebernickel@gmail.com>
*
* Example using the preferred_choices option.
*
* <code>
* public function buildForm(FormBuilderInterface $builder, array $options)
* {
* $builder
* ->add('product', 'model', array(
* 'class' => 'Model\Product',
* 'query' => ProductQuery::create()
* ->filterIsActive(true)
* ->useI18nQuery($options['locale'])
* ->orderByName()
* ->endUse()
* ,
* 'preferred_choices' => ProductQuery::create()
* ->filterByIsTopProduct(true)
* ,
* ))
* ;
* }
* </code>
*/
class ModelType extends AbstractType
{
/**
* @var ChoiceListFactoryInterface
*/
private $choiceListFactory;
/**
* ModelType constructor.
*
* @param PropertyAccessorInterface|null $propertyAccessor
* @param ChoiceListFactoryInterface|null $choiceListFactory
*/
public function __construct(PropertyAccessorInterface $propertyAccessor = null, ChoiceListFactoryInterface $choiceListFactory = null)
{
$this->choiceListFactory = $choiceListFactory ?: new PropertyAccessDecorator(
new DefaultChoiceListFactory(),
$propertyAccessor
);
}
/**
* Creates the label for a choice.
*
* For backwards compatibility, objects are cast to strings by default.
*
* @param object $choice The object.
*
* @return string The string representation of the object.
*
* @internal This method is public to be usable as callback. It should not
* be used in user code.
*/
public static function createChoiceLabel($choice)
{
return (string) $choice;
}
/**
* Creates the field name for a choice.
*
* This method is used to generate field names if the underlying object has
* a single-column integer ID. In that case, the value of the field is
* the ID of the object. That ID is also used as field name.
*
* @param object $choice The object.
* @param int|string $key The choice key.
* @param string $value The choice value. Corresponds to the object's
* ID here.
*
* @return string The field name.
*
* @internal This method is public to be usable as callback. It should not
* be used in user code.
*/
public static function createChoiceName($choice, $key, $value)
{
return str_replace('-', '_', (string) $value);
}
/**
* {@inheritDoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
if ($options['multiple']) {
$builder
->addViewTransformer(new CollectionToArrayTransformer(), true)
;
}
}
/**
* {@inheritDoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$choiceLoader = function (Options $options) {
// Unless the choices are given explicitly, load them on demand
if (null === $options['choices']) {
$propelChoiceLoader = new PropelChoiceLoader(
$this->choiceListFactory,
$options['class'],
$options['query'],
$options['index_property']
);
return $propelChoiceLoader;
}
return null;
};
$choiceName = function (Options $options) {
/** @var ModelCriteria $query */
$query = $options['query'];
if ($options['index_property']) {
$identifier = array($query->getTableMap()->getColumn($options['index_property']));
} else {
$identifier = $query->getTableMap()->getPrimaryKeys();
}
/** @var ColumnMap $firstIdentifier */
$firstIdentifier = current($identifier);
if (count($identifier) === 1 && $firstIdentifier->getPdoType() === \PDO::PARAM_INT) {
return array(__CLASS__, 'createChoiceName');
}
return null;
};
$choiceValue = function (Options $options) {
/** @var ModelCriteria $query */
$query = $options['query'];
if ($options['index_property']) {
$identifier = array($query->getTableMap()->getColumn($options['index_property']));
} else {
$identifier = $query->getTableMap()->getPrimaryKeys();
}
/** @var ColumnMap $firstIdentifier */
$firstIdentifier = current($identifier);
if (count($identifier) === 1 && in_array($firstIdentifier->getPdoType(), [\PDO::PARAM_BOOL, \PDO::PARAM_INT, \PDO::PARAM_STR])) {
return function($object) use ($firstIdentifier) {
if ($object) {
return call_user_func([$object, 'get' . ucfirst($firstIdentifier->getPhpName())]);
}
return null;
};
}
return null;
};
$queryNormalizer = function (Options $options, $query) {
if ($query === null) {
$queryClass = $options['class'] . 'Query';
if (!class_exists($queryClass)) {
if (empty($options['class'])) {
throw new MissingOptionsException('The "class" parameter is empty, you should provide the model class');
}
throw new InvalidOptionsException(
sprintf(
'The query class "%s" is not found, you should provide the FQCN of the model class',
$queryClass
)
);
}
$query = new $queryClass();
}
return $query;
};
$choiceLabelNormalizer = function (Options $options, $choiceLabel) {
if ($choiceLabel === null) {
if ($options['property'] == null) {
$choiceLabel = array(__CLASS__, 'createChoiceLabel');
} else {
$valueProperty = $options['property'];
/** @var ModelCriteria $query */
$query = $options['query'];
$choiceLabel = function($choice) use ($valueProperty) {
$getter = 'get'.ucfirst($valueProperty);
if (!method_exists($choice, $getter)) {
$getter = 'get' . ucfirst($query->getTableMap()->getColumn($valueProperty)->getPhpName());
}
return call_user_func([$choice, $getter]);
};
}
}
return $choiceLabel;
};
$resolver->setDefaults([
'query' => null,
'index_property' => null,
'property' => null,
'choices' => null,
'choice_loader' => $choiceLoader,
'choice_label' => null,
'choice_name' => $choiceName,
'choice_value' => $choiceValue,
'choice_translation_domain' => false,
'by_reference' => false,
]);
$resolver->setRequired(array('class'));
$resolver->setNormalizer('query', $queryNormalizer);
$resolver->setNormalizer('choice_label', $choiceLabelNormalizer);
$resolver->setAllowedTypes('query', ['null', 'Propel\Runtime\ActiveQuery\ModelCriteria']);
}
/**
* {@inheritdoc}
*/
public function getBlockPrefix()
{
return 'model';
}
public function getParent()
{
return 'Symfony\Component\Form\Extension\Core\Type\ChoiceType';
}
}

View file

@ -0,0 +1,84 @@
<?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 Propel\Bundle\PropelBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\OptionsResolver\Exception\MissingOptionsException;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Propel\Bundle\PropelBundle\Form\EventListener\TranslationCollectionFormListener;
/**
* form type for i18n-columns in propel
*
* @author Patrick Kaufmann
*/
class TranslationCollectionType extends AbstractType
{
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
if (!isset($options['entry_options']['data_class']) || null === $options['entry_options']['data_class']) {
throw new MissingOptionsException('data_class must be set');
}
if (!isset($options['entry_options']['columns']) || null === $options['entry_options']['columns']) {
throw new MissingOptionsException('columns must be set');
}
$listener = new TranslationCollectionFormListener($options['languages'], $options['entry_options']['data_class']);
$builder->addEventSubscriber($listener);
}
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setRequired(array(
'languages'
));
$resolver->setDefaults(array(
'entry_type' => TranslationType::class,
'allow_add' => false,
'allow_delete' => false,
'entry_options' => array(
'data_class' => null,
'columns' => null
)
));
}
/**
* {@inheritdoc}
*/
public function getParent()
{
return CollectionType::class;
}
/**
* {@inheritdoc}
*/
public function getBlockPrefix()
{
return 'propel_translation_collection';
}
public function getName()
{
return $this->getBlockPrefix();
}
}

View file

@ -0,0 +1,60 @@
<?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 Propel\Bundle\PropelBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Propel\Bundle\PropelBundle\Form\EventListener\TranslationFormListener;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Translation type class
*
* @author Patrick Kaufmann
*/
class TranslationType extends AbstractType
{
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->addEventSubscriber(
new TranslationFormListener($options['columns'], $options['data_class'])
);
}
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setRequired(array(
'data_class',
'columns'
));
}
/**
* {@inheritdoc}
*/
public function getBlockPrefix()
{
return 'propel_translation';
}
public function getName()
{
return $this->getBlockPrefix();
}
}

209
Form/TypeGuesser.php Normal file
View file

@ -0,0 +1,209 @@
<?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 Propel\Bundle\PropelBundle\Form;
use Propel\Bundle\PropelBundle\Form\Type\ModelType;
use Propel\Runtime\Map\ColumnMap;
use Propel\Runtime\Map\RelationMap;
use Propel\Generator\Model\PropelTypes;
use Propel\Runtime\Map\TableMap;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\TimeType;
use Symfony\Component\Form\FormTypeGuesserInterface;
use Symfony\Component\Form\Guess\Guess;
use Symfony\Component\Form\Guess\TypeGuess;
use Symfony\Component\Form\Guess\ValueGuess;
/**
* Propel Type guesser.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class TypeGuesser implements FormTypeGuesserInterface
{
private $cache = array();
/**
* {@inheritDoc}
*/
public function guessType($class, $property)
{
if (!$table = $this->getTable($class)) {
return new TypeGuess(TextType::class, array(), Guess::LOW_CONFIDENCE);
}
foreach ($table->getRelations() as $relation) {
if ($relation->getType() === RelationMap::MANY_TO_ONE) {
if (strtolower($property) === strtolower($relation->getName())) {
return new TypeGuess(ModelType::class, array(
'class' => $relation->getForeignTable()->getClassName(),
'multiple' => false,
), Guess::HIGH_CONFIDENCE);
}
} elseif ($relation->getType() === RelationMap::ONE_TO_MANY) {
if (strtolower($property) === strtolower($relation->getPluralName())) {
return new TypeGuess(ModelType::class, array(
'class' => $relation->getForeignTable()->getClassName(),
'multiple' => true,
), Guess::HIGH_CONFIDENCE);
}
} elseif ($relation->getType() === RelationMap::MANY_TO_MANY) {
if (strtolower($property) == strtolower($relation->getPluralName())) {
return new TypeGuess(ModelType::class, array(
'class' => $relation->getLocalTable()->getClassName(),
'multiple' => true,
), Guess::HIGH_CONFIDENCE);
}
}
}
if (!$column = $this->getColumn($class, $property)) {
return new TypeGuess(TextType::class, array(), Guess::LOW_CONFIDENCE);
}
switch ($column->getType()) {
case PropelTypes::BOOLEAN:
case PropelTypes::BOOLEAN_EMU:
return new TypeGuess(CheckboxType::class, array(), Guess::HIGH_CONFIDENCE);
case PropelTypes::TIMESTAMP:
case PropelTypes::BU_TIMESTAMP:
return new TypeGuess(DateTimeType::class, array(), Guess::HIGH_CONFIDENCE);
case PropelTypes::DATE:
case PropelTypes::BU_DATE:
return new TypeGuess(DateType::class, array(), Guess::HIGH_CONFIDENCE);
case PropelTypes::TIME:
return new TypeGuess(TimeType::class, array(), Guess::HIGH_CONFIDENCE);
case PropelTypes::FLOAT:
case PropelTypes::REAL:
case PropelTypes::DOUBLE:
case PropelTypes::DECIMAL:
return new TypeGuess(NumberType::class, array(), Guess::MEDIUM_CONFIDENCE);
case PropelTypes::TINYINT:
case PropelTypes::SMALLINT:
case PropelTypes::INTEGER:
case PropelTypes::BIGINT:
case PropelTypes::NUMERIC:
return new TypeGuess(IntegerType::class, array(), Guess::MEDIUM_CONFIDENCE);
case PropelTypes::ENUM:
case PropelTypes::CHAR:
if ($column->getValueSet()) {
//check if this is mysql enum
$choices = $column->getValueSet();
$labels = array_map('ucfirst', $choices);
return new TypeGuess(ChoiceType::class, array('choices' => array_combine($choices, $labels)), Guess::MEDIUM_CONFIDENCE);
}
case PropelTypes::VARCHAR:
return new TypeGuess(TextType::class, array(), Guess::MEDIUM_CONFIDENCE);
case PropelTypes::LONGVARCHAR:
case PropelTypes::BLOB:
case PropelTypes::CLOB:
case PropelTypes::CLOB_EMU:
return new TypeGuess(TextareaType::class, array(), Guess::MEDIUM_CONFIDENCE);
default:
return new TypeGuess(TextType::class, array(), Guess::LOW_CONFIDENCE);
}
}
/**
* {@inheritDoc}
*/
public function guessRequired($class, $property)
{
if ($column = $this->getColumn($class, $property)) {
return new ValueGuess($column->isNotNull(), Guess::HIGH_CONFIDENCE);
}
}
/**
* {@inheritDoc}
*/
public function guessMaxLength($class, $property)
{
if ($column = $this->getColumn($class, $property)) {
if ($column->isText()) {
return new ValueGuess($column->getSize(), Guess::HIGH_CONFIDENCE);
}
switch ($column->getType()) {
case PropelTypes::FLOAT:
case PropelTypes::REAL:
case PropelTypes::DOUBLE:
case PropelTypes::DECIMAL:
return new ValueGuess(null, Guess::MEDIUM_CONFIDENCE);
}
}
}
/**
* {@inheritDoc}
*/
public function guessPattern($class, $property)
{
if ($column = $this->getColumn($class, $property)) {
switch ($column->getType()) {
case PropelTypes::FLOAT:
case PropelTypes::REAL:
case PropelTypes::DOUBLE:
case PropelTypes::DECIMAL:
return new ValueGuess(null, Guess::MEDIUM_CONFIDENCE);
}
}
}
/**
* @param string $class
*
* @return TableMap|null
*/
protected function getTable($class)
{
if (isset($this->cache[$class])) {
return $this->cache[$class];
}
if (class_exists($queryClass = $class.'Query')) {
$query = new $queryClass();
return $this->cache[$class] = $query->getTableMap();
}
return null;
}
/**
* @param string $class
* @param string $property
*
* @return ColumnMap|null
*/
protected function getColumn($class, $property)
{
if (isset($this->cache[$class.'::'.$property])) {
return $this->cache[$class.'::'.$property];
}
$table = $this->getTable($class);
if ($table && $table->hasColumn($property)) {
return $this->cache[$class.'::'.$property] = $table->getColumn($property);
}
return null;
}
}

105
Logger/PropelLogger.php Normal file
View file

@ -0,0 +1,105 @@
<?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\Bundle\PropelBundle\Logger;
use Psr\Log\LoggerInterface;
use Psr\Log\LoggerTrait;
use Symfony\Component\Stopwatch\Stopwatch;
use Symfony\Component\VarDumper\Caster\TraceStub;
/**
* @author Kévin Gomez <contact@kevingomez.fr>
*/
class PropelLogger implements LoggerInterface
{
/**
* @var LoggerInterface
*/
protected $logger;
/**
* @var array
*/
protected $queries = array();
/**
* @var Stopwatch
*/
protected $stopwatch;
use LoggerTrait;
/**
* Constructor.
*
* @param LoggerInterface $logger A LoggerInterface instance
* @param Stopwatch $stopwatch A Stopwatch instance
*/
public function __construct(LoggerInterface $logger = null, Stopwatch $stopwatch = null)
{
$this->logger = $logger;
$this->stopwatch = $stopwatch;
$this->isPrepared = false;
}
/**
* Logs with an arbitrary level.
*
* @param mixed $level
* @param string $message
* @param array $context
* @return null
*/
public function log($level, $message, array $context = array())
{
if (null === $this->logger) {
return;
}
$add = true;
$trace = debug_backtrace();
if (null !== $this->stopwatch) {
$method = $trace[3]['function'];
$watch = 'Propel Query '.(count($this->queries)+1);
if ('prepare' === $method) {
$this->isPrepared = true;
$this->stopwatch->start($watch, 'propel');
$add = false;
} elseif ($this->isPrepared) {
$this->isPrepared = false;
$event = $this->stopwatch->stop($watch);
}
}
// $trace[2] has no 'object' key if an exception is thrown while executing a query
if ($add && isset($event) && isset($trace[2]['object'])) {
$connection = $trace[2]['object'];
$this->queries[] = array(
'sql' => $message,
'connection' => $connection->getName(),
'time' => $event->getDuration() / 1000,
'memory' => $event->getMemory(),
'trace' => new TraceStub($trace),
);
}
$this->logger->log($level, $message, $context);
}
public function getQueries()
{
return $this->queries;
}
}

View file

@ -8,9 +8,10 @@
* @license MIT License
*/
namespace Propel\PropelBundle\Model\Acl;
namespace Propel\Bundle\PropelBundle\Model\Acl;
use Propel\PropelBundle\Model\Acl\om\BaseAclClass;
use Propel\Bundle\PropelBundle\Model\Acl\Base\AclClass as BaseAclClass;
use Propel\Runtime\Connection\ConnectionInterface;
use Symfony\Component\Security\Acl\Model\ObjectIdentityInterface;
@ -22,11 +23,11 @@ class AclClass extends BaseAclClass
* If none can be found, a new one will be saved.
*
* @param \Symfony\Component\Security\Acl\Model\ObjectIdentityInterface $objectIdentity
* @param \PropelPDO $con
* @param ConnectionInterface $con
*
* @return \Propel\PropelBundle\Model\Acl\AclClass
* @return \Propel\Bundle\PropelBundle\Model\Acl\AclClass
*/
static public function fromAclObjectIdentity(ObjectIdentityInterface $objectIdentity, \PropelPDO $con = null)
public static function fromAclObjectIdentity(ObjectIdentityInterface $objectIdentity, ConnectionInterface $con = null)
{
$obj = AclClassQuery::create()
->filterByType($objectIdentity->getType())

View file

@ -1,18 +0,0 @@
<?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\Model\Acl;
use Propel\PropelBundle\Model\Acl\om\BaseAclClassPeer;
class AclClassPeer extends BaseAclClassPeer
{
}

View file

@ -8,9 +8,9 @@
* @license MIT License
*/
namespace Propel\PropelBundle\Model\Acl;
namespace Propel\Bundle\PropelBundle\Model\Acl;
use Propel\PropelBundle\Model\Acl\om\BaseAclClassQuery;
use Propel\Bundle\PropelBundle\Model\Acl\Base\AclClassQuery as BaseAclClassQuery;
class AclClassQuery extends BaseAclClassQuery
{

View file

@ -8,12 +8,12 @@
* @license MIT License
*/
namespace Propel\PropelBundle\Model\Acl;
namespace Propel\Bundle\PropelBundle\Model\Acl;
use Propel\PropelBundle\Model\Acl\om\BaseEntry;
use Propel\Bundle\PropelBundle\Model\Acl\Base\Entry as BaseEntry;
use Propel\PropelBundle\Security\Acl\Domain\Entry as AclEntry;
use Propel\PropelBundle\Security\Acl\Domain\FieldEntry as AclFieldEntry;
use Propel\Bundle\PropelBundle\Security\Acl\Domain\Entry as AclEntry;
use Propel\Bundle\PropelBundle\Security\Acl\Domain\FieldEntry as AclFieldEntry;
use Symfony\Component\Security\Acl\Model\AclInterface;
use Symfony\Component\Security\Acl\Model\EntryInterface;
@ -29,9 +29,9 @@ class Entry extends BaseEntry
*
* @param \Symfony\Component\Security\Acl\Model\EntryInterface $aclEntry
*
* @return \Propel\PropelBundle\Model\Acl\Entry
* @return \Propel\Bundle\PropelBundle\Model\Acl\Entry
*/
static public function fromAclEntry(EntryInterface $aclEntry)
public static function fromAclEntry(EntryInterface $aclEntry)
{
$entry = new self();
@ -64,12 +64,12 @@ class Entry extends BaseEntry
/**
* Transform a given model entry into an ACL related Entry (ACE).
*
* @param \Propel\PropelBundle\Model\Acl\Entry $modelEntry
* @param \Propel\Bundle\PropelBundle\Model\Acl\Entry $modelEntry
* @param \Symfony\Component\Security\Acl\Model\AclInterface $acl
*
* @return \Symfony\Component\Security\Acl\Model\EntryInterface
*/
static public function toAclEntry(Entry $modelEntry, AclInterface $acl)
public static function toAclEntry(Entry $modelEntry, AclInterface $acl)
{
if (null === $modelEntry->getFieldName()) {
return new AclEntry($modelEntry, $acl);

View file

@ -1,18 +0,0 @@
<?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\Model\Acl;
use Propel\PropelBundle\Model\Acl\om\BaseEntryPeer;
class EntryPeer extends BaseEntryPeer
{
}

View file

@ -8,10 +8,14 @@
* @license MIT License
*/
namespace Propel\PropelBundle\Model\Acl;
namespace Propel\Bundle\PropelBundle\Model\Acl;
use Propel\PropelBundle\Model\Acl\om\BaseEntryQuery;
use Propel\PropelBundle\Model\Acl\EntryPeer;
use Propel\Bundle\PropelBundle\Model\Acl\Base\EntryQuery as BaseEntryQuery;
use Propel\Bundle\PropelBundle\Model\Acl\Map\EntryTableMap;
use Propel\Bundle\PropelBundle\Model\Acl\Map\ObjectIdentityTableMap;
use Propel\Runtime\ActiveQuery\Criteria;
use Propel\Runtime\Connection\ConnectionInterface;
use Symfony\Component\Security\Acl\Model\ObjectIdentityInterface;
use Symfony\Component\Security\Acl\Model\SecurityIdentityInterface;
@ -23,13 +27,13 @@ class EntryQuery extends BaseEntryQuery
*
* @see find()
*
* @param \Symfony\Component\Security\Acl\Model\ObjectIdentityInterface $objectIdentity An ACL related ObjectIdentity.
* @param array $securityIdentities A list of SecurityIdentity to filter by.
* @param \PropelPDO $con
* @param \Symfony\Component\Security\Acl\Model\ObjectIdentityInterface $objectIdentity An ACL related ObjectIdentity.
* @param array $securityIdentities A list of SecurityIdentity to filter by.
* @param \ConnectionInterface $con
*
* @return \PropelObjectCollection
*/
public function findByAclIdentity(ObjectIdentityInterface $objectIdentity, array $securityIdentities = array(), \PropelPDO $con = null)
public function findByAclIdentity(ObjectIdentityInterface $objectIdentity, array $securityIdentities = array(), ConnectionInterface $con = null)
{
$securityIds = array();
foreach ($securityIdentities as $eachIdentity) {
@ -49,12 +53,12 @@ class EntryQuery extends BaseEntryQuery
}
$this
->useAclClassQuery(null, \Criteria::INNER_JOIN)
->useAclClassQuery(null, Criteria::INNER_JOIN)
->filterByType((string) $objectIdentity->getType())
->endUse()
->leftJoinObjectIdentity()
->add(ObjectIdentityPeer::OBJECT_IDENTIFIER, (string) $objectIdentity->getIdentifier(), \Criteria::EQUAL)
->addOr(EntryPeer::OBJECT_IDENTITY_ID, null, \Criteria::ISNULL)
->add(ObjectIdentityTableMap::COL_OBJECT_IDENTIFIER, (string) $objectIdentity->getIdentifier(), Criteria::EQUAL)
->addOr(EntryTableMap::COL_OBJECT_IDENTITY_ID, null, Criteria::ISNULL)
;
if (!empty($securityIdentities)) {

View file

@ -8,13 +8,17 @@
* @license MIT License
*/
namespace Propel\PropelBundle\Model\Acl;
namespace Propel\Bundle\PropelBundle\Model\Acl;
use Propel\PropelBundle\Model\Acl\om\BaseObjectIdentity;
use Propel\Bundle\PropelBundle\Model\Acl\Base\ObjectIdentity as BaseObjectIdentity;
use Propel\Bundle\PropelBundle\Model\Acl\Map\ObjectIdentityTableMap;
use Propel\Runtime\ActiveQuery\Criteria;
use Propel\Runtime\Connection\ConnectionInterface;
class ObjectIdentity extends BaseObjectIdentity
{
public function preInsert(\PropelPDO $con = null)
public function preInsert(ConnectionInterface $con = null)
{
// Compatibility with default implementation.
$ancestor = new ObjectIdentityAncestor();
@ -30,16 +34,16 @@ class ObjectIdentity extends BaseObjectIdentity
return true;
}
public function preUpdate(\PropelPDO $con = null)
public function preUpdate(ConnectionInterface $con = null)
{
if ($this->isColumnModified(ObjectIdentityPeer::PARENT_OBJECT_IDENTITY_ID)) {
if ($this->isColumnModified(ObjectIdentityTableMap::COL_PARENT_OBJECT_IDENTITY_ID)) {
$this->updateAncestorsTree($con);
}
return true;
}
public function preDelete(\PropelPDO $con = null)
public function preDelete(ConnectionInterface $con = null)
{
// Only retrieve direct children, it's faster and grand children will be retrieved recursively.
$children = ObjectIdentityQuery::create()->findChildren($this, $con);
@ -51,7 +55,7 @@ class ObjectIdentity extends BaseObjectIdentity
// Manually delete those for DBAdapter not capable of cascading the DELETE.
ObjectIdentityAncestorQuery::create()
->filterByObjectIdentityId($objIds, \Criteria::IN)
->filterByObjectIdentityId($objIds, Criteria::IN)
->delete($con)
;
@ -61,11 +65,11 @@ class ObjectIdentity extends BaseObjectIdentity
/**
* Update all ancestor entries to reflect changes on this instance.
*
* @param \PropelPDO $con
* @param ConnectionInterface $con
*
* @return \Propel\PropelBundle\Model\Acl\ObjectIdentity $this
* @return \Propel\Bundle\PropelBundle\Model\Acl\ObjectIdentity $this
*/
protected function updateAncestorsTree(\PropelPDO $con = null)
protected function updateAncestorsTree(ConnectionInterface $con = null)
{
$con->beginTransaction();
@ -82,13 +86,13 @@ class ObjectIdentity extends BaseObjectIdentity
*/
$query = ObjectIdentityAncestorQuery::create()
->filterByObjectIdentityId($eachChild->getId())
->filterByObjectIdentityRelatedByAncestorId($oldAncestors, \Criteria::IN)
->filterByObjectIdentityRelatedByAncestorId($oldAncestors, Criteria::IN)
;
if ($eachChild->getId() !== $this->getId()) {
$query->filterByAncestorId(array($eachChild->getId(), $this->getId()), \Criteria::NOT_IN);
$query->filterByAncestorId(array($eachChild->getId(), $this->getId()), Criteria::NOT_IN);
} else {
$query->filterByAncestorId($this->getId(), \Criteria::NOT_EQUAL);
$query->filterByAncestorId($this->getId(), Criteria::NOT_EQUAL);
}
$query->delete($con);

View file

@ -8,9 +8,9 @@
* @license MIT License
*/
namespace Propel\PropelBundle\Model\Acl;
namespace Propel\Bundle\PropelBundle\Model\Acl;
use Propel\PropelBundle\Model\Acl\om\BaseObjectIdentityAncestor;
use Propel\Bundle\PropelBundle\Model\Acl\Base\ObjectIdentityAncestor as BaseObjectIdentityAncestor;
class ObjectIdentityAncestor extends BaseObjectIdentityAncestor
{

View file

@ -1,18 +0,0 @@
<?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\Model\Acl;
use Propel\PropelBundle\Model\Acl\om\BaseObjectIdentityAncestorPeer;
class ObjectIdentityAncestorPeer extends BaseObjectIdentityAncestorPeer
{
}

View file

@ -8,9 +8,9 @@
* @license MIT License
*/
namespace Propel\PropelBundle\Model\Acl;
namespace Propel\Bundle\PropelBundle\Model\Acl;
use Propel\PropelBundle\Model\Acl\om\BaseObjectIdentityAncestorQuery;
use Propel\Bundle\PropelBundle\Model\Acl\Base\ObjectIdentityAncestorQuery as BaseObjectIdentityAncestorQuery;
class ObjectIdentityAncestorQuery extends BaseObjectIdentityAncestorQuery
{

View file

@ -1,18 +0,0 @@
<?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\Model\Acl;
use Propel\PropelBundle\Model\Acl\om\BaseObjectIdentityPeer;
class ObjectIdentityPeer extends BaseObjectIdentityPeer
{
}

View file

@ -8,10 +8,12 @@
* @license MIT License
*/
namespace Propel\PropelBundle\Model\Acl;
namespace Propel\Bundle\PropelBundle\Model\Acl;
use Propel\PropelBundle\Model\Acl\ObjectIdentity;
use Propel\PropelBundle\Model\Acl\om\BaseObjectIdentityQuery;
use Propel\Bundle\PropelBundle\Model\Acl\Base\ObjectIdentityQuery as BaseObjectIdentityQuery;
use Propel\Runtime\ActiveQuery\Criteria;
use Propel\Runtime\Connection\ConnectionInterface;
use Symfony\Component\Security\Acl\Model\ObjectIdentityInterface;
@ -21,11 +23,11 @@ class ObjectIdentityQuery extends BaseObjectIdentityQuery
* Filter by an ObjectIdentity object belonging to the given ACL related ObjectIdentity.
*
* @param \Symfony\Component\Security\Acl\Model\ObjectIdentityInterface $objectIdentity
* @param \PropelPDO $con
* @param ConnectionInterface $con
*
* @return \Propel\PropelBundle\Model\Acl\ObjectIdentityQuery $this
* @return \Propel\Bundle\PropelBundle\Model\Acl\ObjectIdentityQuery $this
*/
public function filterByAclObjectIdentity(ObjectIdentityInterface $objectIdentity, \PropelPDO $con = null)
public function filterByAclObjectIdentity(ObjectIdentityInterface $objectIdentity, ConnectionInterface $con = null)
{
/*
* Not using a JOIN here, because the filter may be applied on 'findOneOrCreate',
@ -44,11 +46,11 @@ class ObjectIdentityQuery extends BaseObjectIdentityQuery
* Return an ObjectIdentity object belonging to the given ACL related ObjectIdentity.
*
* @param \Symfony\Component\Security\Acl\Model\ObjectIdentityInterface $objectIdentity
* @param \PropelPDO $con
* @param ConnectionInterface $con
*
* @return \Propel\PropelBundle\Model\Acl\ObjectIdentity
* @return \Propel\Bundle\PropelBundle\Model\Acl\ObjectIdentity
*/
public function findOneByAclObjectIdentity(ObjectIdentityInterface $objectIdentity, \PropelPDO $con = null)
public function findOneByAclObjectIdentity(ObjectIdentityInterface $objectIdentity, ConnectionInterface $con = null)
{
return $this
->filterByAclObjectIdentity($objectIdentity, $con)
@ -59,12 +61,12 @@ class ObjectIdentityQuery extends BaseObjectIdentityQuery
/**
* Return all children of the given object identity.
*
* @param \Propel\PropelBundle\Model\Acl\ObjectIdentity $objectIdentity
* @param \PropelPDO $con
* @param \Propel\Bundle\PropelBundle\Model\Acl\ObjectIdentity $objectIdentity
* @param ConnectionInterface $con
*
* @return \PropelObjectCollection
*/
public function findChildren(ObjectIdentity $objectIdentity, \PropelPDO $con = null)
public function findChildren(ObjectIdentity $objectIdentity, ConnectionInterface $con = null)
{
return $this
->filterByObjectIdentityRelatedByParentObjectIdentityId($objectIdentity)
@ -75,17 +77,17 @@ class ObjectIdentityQuery extends BaseObjectIdentityQuery
/**
* Return all children and grand-children of the given object identity.
*
* @param \Propel\PropelBundle\Model\Acl\ObjectIdentity $objectIdentity
* @param \PropelPDO $con
* @param \Propel\Bundle\PropelBundle\Model\Acl\ObjectIdentity $objectIdentity
* @param ConnectionInterface $con
*
* @return \PropelObjectCollection
*/
public function findGrandChildren(ObjectIdentity $objectIdentity, \PropelPDO $con = null)
public function findGrandChildren(ObjectIdentity $objectIdentity, ConnectionInterface $con = null)
{
return $this
->useObjectIdentityAncestorRelatedByObjectIdentityIdQuery()
->filterByObjectIdentityRelatedByAncestorId($objectIdentity)
->filterByObjectIdentityRelatedByObjectIdentityId($objectIdentity, \Criteria::NOT_EQUAL)
->filterByObjectIdentityRelatedByObjectIdentityId($objectIdentity, Criteria::NOT_EQUAL)
->endUse()
->find($con)
;
@ -94,17 +96,17 @@ class ObjectIdentityQuery extends BaseObjectIdentityQuery
/**
* Return all ancestors of the given object identity.
*
* @param ObjectIdentity $objectIdentity
* @param \PropelPDO $con
* @param ObjectIdentity $objectIdentity
* @param ConnectionInterface $con
*
* @return \PropelObjectCollection
*/
public function findAncestors(ObjectIdentity $objectIdentity, \PropelPDO $con = null)
public function findAncestors(ObjectIdentity $objectIdentity, ConnectionInterface $con = null)
{
return $this
->useObjectIdentityAncestorRelatedByAncestorIdQuery()
->filterByObjectIdentityRelatedByObjectIdentityId($objectIdentity)
->filterByObjectIdentityRelatedByAncestorId($objectIdentity, \Criteria::NOT_EQUAL)
->filterByObjectIdentityRelatedByAncestorId($objectIdentity, Criteria::NOT_EQUAL)
->endUse()
->find($con)
;

View file

@ -8,9 +8,11 @@
* @license MIT License
*/
namespace Propel\PropelBundle\Model\Acl;
namespace Propel\Bundle\PropelBundle\Model\Acl;
use Propel\PropelBundle\Model\Acl\om\BaseSecurityIdentity;
use Propel\Bundle\PropelBundle\Model\Acl\Base\SecurityIdentity as BaseSecurityIdentity;
use Propel\Runtime\Connection\ConnectionInterface;
use Symfony\Component\Security\Acl\Domain\RoleSecurityIdentity;
use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity;
@ -21,11 +23,11 @@ class SecurityIdentity extends BaseSecurityIdentity
/**
* Transform a given mode security identity into an ACL related SecurityIdentity.
*
* @param \Propel\PropelBundle\Model\Acl\SecurityIdentity $securityIdentity
* @param \Propel\Bundle\PropelBundle\Model\Acl\SecurityIdentity $securityIdentity
*
* @return \Symfony\Component\Security\Acl\Model\SecurityIdentityInterface
*/
static public function toAclIdentity(SecurityIdentity $securityIdentity)
public static function toAclIdentity(SecurityIdentity $securityIdentity)
{
$identifier = $securityIdentity->getIdentifier();
@ -34,7 +36,7 @@ class SecurityIdentity extends BaseSecurityIdentity
throw new \InvalidArgumentException('The given identifier does not resolve to a UserSecurityIdentity.');
}
list($class, $username) = explode('-', $identifier);
list($class, $username) = explode('-', $identifier, 2);
return new UserSecurityIdentity($username, $class);
}
@ -54,11 +56,11 @@ class SecurityIdentity extends BaseSecurityIdentity
* @throws \InvalidArgumentException
*
* @param \Symfony\Component\Security\Acl\Model\SecurityIdentityInterface $aclIdentity
* @param \PropelPDO $con
* @param ConnectionInterface $con
*
* @return \Propel\PropelBundle\Model\Acl\SecurityIdentity
* @return \Propel\Bundle\PropelBundle\Model\Acl\SecurityIdentity
*/
static public function fromAclIdentity(SecurityIdentityInterface $aclIdentity, \PropelPDO $con = null)
public static function fromAclIdentity(SecurityIdentityInterface $aclIdentity, ConnectionInterface $con = null)
{
if ($aclIdentity instanceof UserSecurityIdentity) {
$identifier = $aclIdentity->getClass().'-'.$aclIdentity->getUsername();

View file

@ -1,18 +0,0 @@
<?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\Model\Acl;
use Propel\PropelBundle\Model\Acl\om\BaseSecurityIdentityPeer;
class SecurityIdentityPeer extends BaseSecurityIdentityPeer
{
}

View file

@ -8,9 +8,9 @@
* @license MIT License
*/
namespace Propel\PropelBundle\Model\Acl;
namespace Propel\Bundle\PropelBundle\Model\Acl;
use Propel\PropelBundle\Model\Acl\om\BaseSecurityIdentityQuery;
use Propel\Bundle\PropelBundle\Model\Acl\Base\SecurityIdentityQuery as BaseSecurityIdentityQuery;
class SecurityIdentityQuery extends BaseSecurityIdentityQuery
{

View file

@ -8,9 +8,13 @@
* @license MIT License
*/
namespace Propel\PropelBundle;
namespace Propel\Bundle\PropelBundle;
use Propel\Bundle\PropelBundle\DependencyInjection\Security\UserProvider\PropelFactory;
use Propel\Runtime\Propel;
use Propel\Runtime\Connection\ConnectionManagerSingle;
use Propel\Runtime\Connection\ConnectionManagerMasterSlave;
use Symfony\Bridge\Propel1\DependencyInjection\Security\UserProvider\PropelFactory;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\DependencyInjection\ContainerBuilder;
@ -26,44 +30,18 @@ class PropelBundle extends Bundle
*/
public function boot()
{
require_once $this->container->getParameter('propel.path').'/runtime/lib/Propel.php';
if (0 === strncasecmp(PHP_SAPI, 'cli', 3)) {
set_include_path($this->container->getParameter('kernel.root_dir').'/..'.PATH_SEPARATOR.
$this->container->getParameter('propel.phing_path').PATH_SEPARATOR.
$this->container->getParameter('propel.phing_path').'/classes'.PATH_SEPARATOR.
get_include_path());
}
if (!\Propel::isInit()) {
\Propel::setConfiguration($this->container->get('propel.configuration'));
try {
$this->configureConnections();
if ($this->container->getParameter('propel.logging')) {
$config = $this
->container
->get('propel.configuration')
;
$config->setParameter('debugpdo.logging.methods', array(
'PropelPDO::exec',
'PropelPDO::query',
'PropelPDO::prepare',
'DebugPDOStatement::execute',
), false);
$config->setParameter('debugpdo.logging.details', array(
'time' => array('enabled' => true),
'mem' => array('enabled' => true),
'connection' => array('enabled' => true),
));
\Propel::setLogger($this->container->get('propel.logger'));
$this->configureLogging();
}
\Propel::initialize();
} catch( \Exception $e ) {
}
}
/**
* {@inheritdoc}
* {@inheritdoc}
*/
public function build(ContainerBuilder $container)
{
@ -73,4 +51,55 @@ class PropelBundle extends Bundle
$container->getExtension('security')->addUserProviderFactory(new PropelFactory('propel', 'propel.security.user.provider'));
}
}
protected function configureConnections()
{
$config = $this->container->getParameter('propel.configuration');
$defaultConnection = !empty($config['runtime']['defaultConnection']) ? $config['runtime']['defaultConnection'] : key($config['database']['connections']);
$serviceContainer = Propel::getServiceContainer();
$serviceContainer->setDefaultDatasource($defaultConnection);
foreach ($config['database']['connections'] as $name => $config) {
if (!empty($config['slaves'])) {
$manager = new ConnectionManagerMasterSlave();
// configure the master (write) connection
$manager->setWriteConfiguration($config);
// configure the slave (read) connections
$slaveConnections = [];
foreach ($config['slaves'] as $slave) {
$slaveConnections[] = array_merge($config, [
'dsn' => $slave['dsn'],
'slaves' => null
]);
}
$manager->setReadConfiguration($slaveConnections);
} else {
$manager = new ConnectionManagerSingle();
$manager->setConfiguration($config);
}
$serviceContainer->setAdapterClass($name, $config['adapter']);
$serviceContainer->setConnectionManager($name, $manager);
}
$serviceContainer->initDatabaseMaps([]);
}
protected function configureLogging()
{
$serviceContainer = Propel::getServiceContainer();
$serviceContainer->setLogger('defaultLogger', $this->container->get('propel.logger'));
foreach ($serviceContainer->getConnectionManagers() as $manager) {
$connection = $manager->getReadConnection($serviceContainer->getAdapter($manager->getName()));
$connection->setLogMethods(array_merge($connection->getLogMethods(), array('prepare')));
$connection = $manager->getWriteConnection();
$connection->setLogMethods(array_merge($connection->getLogMethods(), array('prepare')));
}
}
}

View file

@ -1,50 +1,38 @@
PropelBundle
============
[![Build Status](https://secure.travis-ci.org/propelorm/PropelBundle.png)](http://travis-ci.org/propelorm/PropelBundle)
This is the official implementation of [Propel](http://www.propelorm.org/) in Symfony2.
## Branching model
As `Propel2` will be released in the near future, we are migrating the branching model of this bundle in advance!
* The `1.0` branch will contain Propel *1.6* integration for Symfony *2.0* (*currently 2.0 branch*).
* The `1.1` branch will contain Propel *1.6* integration for Symfony *2.1* (*currently master branch*).
* The `2.0` branch will contain `Propel2` integration for Symfony *2.1*.
We are still considering to integrate `Propel2` with Symfony *2.0*.
In case, we will do so, there will be a `2.1` and `2.0` branch integrating the respective Symfony version!
**The 1.x branches are already available and you are encouraged to migrate your dependencies according to the listings!**
* If you depend on the `master` branch, switch to the `1.1` branch.
* If you depend on the `2.0` branch, switch to the `1.0` branch.
We will keep all branches in sync until `Propel2` will be released.
## Features
* Generation of model classes based on an XML schema (not YAML) placed under `BundleName/Resources/config/*schema.xml`.
* Insertion of SQL statements.
* Runtime autoloading of Propel and generated classes.
* Propel runtime initialization through the XML configuration.
* Migrations [Propel 1.6](http://www.propelorm.org/documentation/10-migrations.html).
* Reverse engineering from [existing database](http://www.propelorm.org/cookbook/working-with-existing-databases.html).
* Integration to the Symfony2 Profiler.
* Load SQL, YAML and XML fixtures.
* Create/Drop databases.
* Integration with the Form component.
* Integration with the Security component.
* Propel ParamConverter can be used with Sensio Framework Extra Bundle.
* Schema Inheritance.
* Symfony2 ACL.
For documentation, see:
Resources/doc/
[Read the documentation](https://github.com/propelorm/PropelBundle/blob/master/Resources/doc/README.markdown)
For license, see:
Resources/meta/LICENSE
PropelBundle
============
[![Build Status](https://travis-ci.org/propelorm/PropelBundle.svg?branch=4.0)](https://travis-ci.org/propelorm/PropelBundle)
This is the official implementation of [Propel](http://www.propelorm.org/) in Symfony.
## Branching model
As `Propel2` will be released in the near future, we are migrating the branching model of this bundle in advance!
* The `1.0` branch contains Propel *1.6* integration for Symfony *2.0* (*currently 2.0 branch*).
* The `1.1` branch contains Propel *1.6* integration for Symfony *2.1* (*currently 2.1 branch*).
* The `1.2` branch contains Propel *1.6* integration for Symfony *2.2* (*currently master branch*).
* The `2.0` branch contains `Propel2` integration for Symfony *2.5-2.8*.
* The `3.0` branch contains `Propel2` integration for Symfony *2.8-3.x*.
* The `4.0` branch contains `Propel2` integration for Symfony *3.4-4.x*.
## Features
* Generation of model classes based on an XML schema (not YAML) placed under `BundleName/Resources/*schema.xml`;
* Insertion of SQL statements;
* Runtime autoloading of Propel and generated classes;
* Propel runtime initialization through the XML configuration;
* [Propel Migrations](http://propelorm.org/documentation/09-migrations.html);
* Reverse engineering from [existing database](http://propelorm.org/documentation/cookbook/working-with-existing-databases.html);
* Integration to the Symfony Profiler;
* Load SQL, YAML and XML fixtures;
* Create/Drop databases;
* Integration with the Form component;
* Integration with the Security component;
* Propel ParamConverter can be used with Sensio Framework Extra Bundle.
[Read the documentation](http://propelorm.org/documentation/)
For license, see:
Resources/meta/LICENSE

View file

@ -1,36 +1,144 @@
<?php
namespace Propel\PropelBundle\Request\ParamConverter;
namespace Propel\Bundle\PropelBundle\Request\ParamConverter;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ConfigurationInterface;
use Propel\Bundle\PropelBundle\Util\PropelInflector;
use Propel\Runtime\ActiveQuery\Criteria;
use Propel\Runtime\ActiveQuery\ModelCriteria;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\ParamConverterInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpFoundation\Request;
/**
* PropelConverter.
* PropelParamConverter
*
* This convert action parameter to a Propel Object
* there is two option for this converter:
*
* mapping : take an array of routeParam => column
* exclude : take an array of routeParam to exclude from the conversion process
*
*
* @author Jérémie Augustin <jeremie.augustin@pixel-cookers.com>
*/
class PropelParamConverter implements ParamConverterInterface
{
/**
* the pk column (e.g. id)
* @var string
*/
protected $pk;
public function apply(Request $request, ConfigurationInterface $configuration)
/**
* list of column/value to use with filterBy
* @var array
*/
protected $filters = array();
/**
* list of route parameters to exclude from the conversion process
* @var array
*/
protected $exclude = array();
/**
* list of with option use to hydrate related object
* @var array
*/
protected $withs;
/**
* name of method use to call a query method
* @var string
*/
protected $queryMethod;
/**
* @var bool
*/
protected $hasWith = false;
/**
* @param Request $request
* @param ParamConverter $configuration
*
* @return bool
*
* @throws \LogicException
* @throws NotFoundHttpException
* @throws \Exception
*/
public function apply(Request $request, ParamConverter $configuration)
{
$classQuery = $configuration->getClass() . 'Query';
$class = $configuration->getClass();
$classQuery = $class . 'Query';
$classTableMap = $class::TABLE_MAP;
$this->filters = array();
$this->exclude = array();
if (!class_exists($classQuery)) {
throw new \Exception(sprintf('The %s Query class does not exist', $classQuery));
}
$options = $configuration->getOptions();
$exclude = isset($options['exclude'])? $options['exclude'] : array();
// find by Pk
if (in_array('id', $exclude) || false === $object = $this->findPk($classQuery, $request)) {
// find by criteria
if (false === $object = $this->findOneBy($classQuery, $request, $exclude)) {
throw new \LogicException('Unable to guess how to get a Propel object from the request information.');
$tableMap = new $classTableMap();
$pkColumns = $tableMap->getPrimaryKeys();
if (count($pkColumns) === 1) {
$pk = array_pop($pkColumns);
$this->pk = strtolower($pk->getName());
}
$options = $configuration->getOptions();
// Check request attributes for converter options, if there are non provided.
if (empty($options) && $request->attributes->has('propel_converter') && $configuration instanceof ParamConverter) {
$converterOption = $request->attributes->get('propel_converter');
if (!empty($converterOption[$configuration->getName()])) {
$options = $converterOption[$configuration->getName()];
}
}
if (isset($options['mapping'])) {
// We use the mapping for calling findPk or filterBy
foreach ($options['mapping'] as $routeParam => $column) {
if ($request->attributes->has($routeParam)) {
if ($this->pk === $column) {
$this->pk = $routeParam;
} else {
$this->filters[$column] = $request->attributes->get($routeParam);
}
}
}
} else {
$this->exclude = isset($options['exclude']) ? $options['exclude'] : array();
$this->filters = $request->attributes->all();
}
if (array_key_exists($configuration->getName(), $this->filters)) {
unset($this->filters[$configuration->getName()]);
}
$this->withs = isset($options['with']) ? is_array($options['with']) ? $options['with'] : array($options['with']) : array();
$this->queryMethod = $queryMethod = isset($options['query_method']) ? $options['query_method'] : null;
if (null !== $this->queryMethod && method_exists($classQuery, $this->queryMethod)) {
// find by custom method
$query = $this->getQuery($classQuery);
// execute a custom query
$object = $query->$queryMethod($request->attributes);
} else {
// find by Pk
if (false === $object = $this->findPk($classQuery, $request)) {
// find by criteria
if (false === $object = $this->findOneBy($classQuery, $request)) {
if ($configuration->isOptional()) {
//we find nothing but the object is optional
$object = null;
} else {
throw new \LogicException('Unable to guess how to get a Propel object from the request information.');
}
}
}
}
@ -39,27 +147,75 @@ class PropelParamConverter implements ParamConverterInterface
}
$request->attributes->set($configuration->getName(), $object);
return true;
}
protected function findPk($classQuery, Request $request)
/**
* @param ParamConverter $configuration
*
* @return bool
*/
public function supports(ParamConverter $configuration)
{
if (!$request->attributes->has('id')) {
if (null === ($classname = $configuration->getClass())) {
return false;
}
return $classQuery::create()->findPk($request->attributes->get('id'));
if (!class_exists($classname)) {
return false;
}
// Propel Class?
$class = new \ReflectionClass($configuration->getClass());
if ($class->implementsInterface('\Propel\Runtime\ActiveRecord\ActiveRecordInterface')) {
return true;
}
return false;
}
protected function findOneBy($classQuery, Request $request, $exclude)
/**
* Try to find the object with the id
*
* @param string $classQuery the query class
* @param Request $request
*
* @return mixed
*/
protected function findPk($classQuery, Request $request)
{
$query = $classQuery::create();
if (in_array($this->pk, $this->exclude) || !$request->attributes->has($this->pk)) {
return false;
}
$query = $this->getQuery($classQuery);
if (!$this->hasWith) {
return $query->findPk($request->attributes->get($this->pk));
} else {
return $query->filterByPrimaryKey($request->attributes->get($this->pk))->find()->getFirst();
}
}
/**
* Try to find the object with all params from the $request
*
* @param string $classQuery the query class
* @param Request $request
*
* @return mixed
*/
protected function findOneBy($classQuery, Request $request)
{
$query = $this->getQuery($classQuery);
$hasCriteria = false;
foreach ($request->attributes->all() as $key => $value) {
if (!in_array($key, $exclude)) {
foreach ($this->filters as $column => $value) {
if (!in_array($column, $this->exclude)) {
try {
$query->{'filterBy' . ucfirst($key)}($value);
$query->{'filterBy' . PropelInflector::camelize($column)}($value);
$hasCriteria = true;
} catch (\PropelException $e) { }
} catch (\Exception $e) { }
}
}
@ -67,23 +223,68 @@ class PropelParamConverter implements ParamConverterInterface
return false;
}
return $query->findOne();
if (!$this->hasWith) {
return $query->findOne();
} else {
return $query->find()->getFirst();
}
}
public function supports(ConfigurationInterface $configuration)
/**
* Init the query class with optional joinWith
*
* @param string $classQuery
*
* @return ModelCriteria
*
* @throws \Exception
*/
protected function getQuery($classQuery)
{
if (null === ($classname = $configuration->getClass())) {
return false;
}
if (!class_exists($classname)) {
return false;
}
// Propel Class?
$class = new \ReflectionClass($configuration->getClass());
if ($class->isSubclassOf('BaseObject')) {
return true;
$query = $classQuery::create();
foreach ($this->withs as $with) {
if (is_array($with)) {
if (2 == count($with)) {
$query->joinWith($with[0], $this->getValidJoin($with));
$this->hasWith = true;
} else {
throw new \Exception(sprintf('ParamConverter : "with" parameter "%s" is invalid,
only string relation name (e.g. "Book") or an array with two keys (e.g. {"Book", "LEFT_JOIN"}) are allowed',
var_export($with, true)));
}
} else {
$query->joinWith($with);
$this->hasWith = true;
}
}
return false;
return $query;
}
/**
* Return the valid join Criteria base on the with parameter
*
* @param array $with
*
* @return string
*
* @throws \Exception
*/
protected function getValidJoin($with)
{
switch (trim(str_replace(array('_', 'JOIN'), '', strtoupper($with[1])))) {
case 'LEFT':
return Criteria::LEFT_JOIN;
case 'RIGHT':
return Criteria::RIGHT_JOIN;
case 'INNER':
return Criteria::INNER_JOIN;
}
throw new \Exception(sprintf('ParamConverter : "with" parameter "%s" is invalid,
only "left", "right" or "inner" are allowed for join option',
var_export($with, true)));
}
}

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<database name="default" namespace="Propel\PropelBundle\Model\Acl" defaultIdMethod="native" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://xsd.propelorm.org/1.6/database.xsd">
<database name="default" namespace="Propel\Bundle\PropelBundle\Model\Acl" defaultIdMethod="native" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://xsd.propelorm.org/1.6/database.xsd">
<table name="acl_classes" phpName="AclClass">
<column name="id" type="integer" autoIncrement="true" primaryKey="true" />
<column name="class_type" type="varchar" size="200" required="true" phpName="Type" />
@ -48,10 +48,10 @@
<column name="object_identity_id" type="integer" primaryKey="true" />
<column name="ancestor_id" type="integer" primaryKey="true" />
<foreign-key foreignTable="acl_object_identities" onDelete="CASCADE" onUpdate="CASCADE">
<foreign-key name="acl_object_identity_id" foreignTable="acl_object_identities" onDelete="CASCADE" onUpdate="CASCADE">
<reference local="object_identity_id" foreign="id" />
</foreign-key>
<foreign-key foreignTable="acl_object_identities" onDelete="CASCADE" onUpdate="CASCADE">
<foreign-key name="acl_object_ancestor_id" foreignTable="acl_object_identities" onDelete="CASCADE" onUpdate="CASCADE">
<reference local="ancestor_id" foreign="id" />
</foreign-key>
</table>

View file

@ -0,0 +1,78 @@
<?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">
<services>
<service id="propel_bundle_propel.command.acl_init_command" class="Propel\Bundle\PropelBundle\Command\AclInitCommand">
<tag name="console.command" command="propel:acl:init" />
</service>
<service id="propel_bundle_propel.command.build_command" class="Propel\Bundle\PropelBundle\Command\BuildCommand">
<tag name="console.command" command="propel:build" />
</service>
<service id="propel_bundle_propel.command.database_create_command" class="Propel\Bundle\PropelBundle\Command\DatabaseCreateCommand">
<tag name="console.command" command="propel:database:create" />
</service>
<service class="Propel\Bundle\PropelBundle\Command\DatabaseDropCommand"
id="propel_bundle_propel.command.database_drop_command">
<tag name="console.command" command="propel:database:drop"/>
</service>
<service class="Propel\Bundle\PropelBundle\Command\DatabaseReverseCommand"
id="propel_bundle_propel.command.database_reverse_command">
<tag name="console.command" command="propel:database:reverse"/>
</service>
<service class="Propel\Bundle\PropelBundle\Command\FixturesDumpCommand"
id="propel_bundle_propel.command.fixtures_dump_command">
<tag name="console.command" command="propel:fixtures:dump"/>
</service>
<service class="Propel\Bundle\PropelBundle\Command\FixturesLoadCommand"
id="propel_bundle_propel.command.fixtures_load_command">
<tag name="console.command" command="propel:fixtures:load"/>
</service>
<service class="Propel\Bundle\PropelBundle\Command\FormGenerateCommand"
id="propel_bundle_propel.command.form_generate_command">
<tag name="console.command" command="propel:form:generate"/>
</service>
<service class="Propel\Bundle\PropelBundle\Command\GraphvizGenerateCommand"
id="propel_bundle_propel.command.graphviz_generate_command">
<tag name="console.command" command="propel:graphviz:generate"/>
</service>
<service class="Propel\Bundle\PropelBundle\Command\MigrationDiffCommand"
id="propel_bundle_propel.command.migration_diff_command">
<tag name="console.command" command="propel:migration:diff"/>
</service>
<service class="Propel\Bundle\PropelBundle\Command\MigrationDownCommand"
id="propel_bundle_propel.command.migration_down_command">
<tag name="console.command" command="propel:migration:down"/>
</service>
<service class="Propel\Bundle\PropelBundle\Command\MigrationMigrateCommand"
id="propel_bundle_propel.command.migration_migrate_command">
<tag name="console.command" command="propel:migration:migrate"/>
</service>
<service class="Propel\Bundle\PropelBundle\Command\MigrationStatusCommand"
id="propel_bundle_propel.command.migration_status_command">
<tag name="console.command" command="propel:migration:status"/>
</service>
<service class="Propel\Bundle\PropelBundle\Command\MigrationUpCommand"
id="propel_bundle_propel.command.migration_up_command">
<tag name="console.command" command="propel:migration:up"/>
</service>
<service class="Propel\Bundle\PropelBundle\Command\ModelBuildCommand"
id="propel_bundle_propel.command.model_build_command">
<tag name="console.command" command="propel:model:build"/>
</service>
<service class="Propel\Bundle\PropelBundle\Command\SqlBuildCommand"
id="propel_bundle_propel.command.sql_build_command">
<tag name="console.command" command="propel:sql:build"/>
</service>
<service class="Propel\Bundle\PropelBundle\Command\SqlInsertCommand"
id="propel_bundle_propel.command.sql_insert_command">
<tag name="console.command" command="propel:sql:insert"/>
</service>
<service class="Propel\Bundle\PropelBundle\Command\TableDropCommand"
id="propel_bundle_propel.command.table_drop_command">
<tag name="console.command" command="propel:table:drop"/>
</service>
</services>
</container>

View file

@ -5,12 +5,12 @@
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="propel.converter.propel.class">Propel\PropelBundle\Request\ParamConverter\PropelParamConverter</parameter>
<parameter key="propel.converter.propel.class">Propel\Bundle\PropelBundle\Request\ParamConverter\PropelParamConverter</parameter>
</parameters>
<services>
<service id="propel.converter.propel.orm" class="%propel.converter.propel.class%">
<tag name="request.param_converter" priority="10" />
<tag name="request.param_converter" converter="propel" priority="1" />
</service>
</services>
</container>

View file

@ -6,36 +6,33 @@
<parameters>
<parameter key="propel.dbal.default_connection">default</parameter>
<parameter key="propel.configuration.class">PropelConfiguration</parameter>
<parameter key="propel.logger.class">Symfony\Bridge\Propel1\Logger\PropelLogger</parameter>
<parameter key="propel.data_collector.class">Symfony\Bridge\Propel1\DataCollector\PropelDataCollector</parameter>
<parameter key="propel.build_properties.class">Propel\PropelBundle\DependencyInjection\Properties</parameter>
<parameter key="propel.form.type.model.class">Symfony\Bridge\Propel1\Form\Type\ModelType</parameter>
<parameter key="propel.twig.extension.syntax.class">Propel\PropelBundle\Twig\Extension\SyntaxExtension</parameter>
<parameter key="form.type_guesser.propel.class">Symfony\Bridge\Propel1\Form\PropelTypeGuesser</parameter>
<parameter key="propel.security.acl.provider.model.class">Propel\PropelBundle\Security\Acl\AuditableAclProvider</parameter>
<parameter key="propel.security.user.provider.class">Symfony\Bridge\Propel1\Security\User\PropelUserProvider</parameter>
<parameter key="propel.schema_locator.class">Propel\Bundle\PropelBundle\Service\SchemaLocator</parameter>
<parameter key="propel.data_collector.class">Propel\Bundle\PropelBundle\DataCollector\PropelDataCollector</parameter>
<parameter key="propel.logger.class">Propel\Bundle\PropelBundle\Logger\PropelLogger</parameter>
<parameter key="propel.twig.extension.syntax.class">Propel\Bundle\PropelBundle\Twig\Extension\SyntaxExtension</parameter>
<parameter key="form.type_guesser.propel.class">Propel\Bundle\PropelBundle\Form\TypeGuesser</parameter>
<parameter key="propel.form.type.model.class">Propel\Bundle\PropelBundle\Form\Type\ModelType</parameter>
<parameter key="propel.dumper.yaml.class">Propel\Bundle\PropelBundle\DataFixtures\Dumper\YamlDataDumper</parameter>
<parameter key="propel.loader.yaml.class">Propel\Bundle\PropelBundle\DataFixtures\Loader\YamlDataLoader</parameter>
<parameter key="propel.loader.xml.class">Propel\Bundle\PropelBundle\DataFixtures\Loader\XmlDataLoader</parameter>
</parameters>
<services>
<service id="propel.configuration" class="%propel.configuration.class%" />
<service id="propel.schema_locator" class="%propel.schema_locator.class%" public="true">
<argument type="service" id="file_locator" />
<argument>%propel.configuration%</argument>
</service>
<service id="propel.build_properties" class="%propel.build_properties.class%" />
<service id="propel.logger" class="%propel.logger.class%">
<service id="propel.logger" class="%propel.logger.class%" public="true">
<tag name="monolog.logger" channel="propel" />
<argument type="service" id="logger" on-invalid="null" />
<argument type="service" id="debug.stopwatch" on-invalid="null" />
</service>
<service id="propel.data_collector" class="%propel.data_collector.class%" public="false">
<tag name="data_collector" template="PropelBundle:Collector:propel" id="propel" />
<argument type="service" id="propel.logger" />
<argument type="service" id="propel.configuration" />
</service>
<service id="propel.form.type.model" class="%propel.form.type.model.class%">
<tag name="form.type" alias="model" />
<tag name="data_collector" template="@Propel/Collector/propel" id="propel" />
</service>
<service id="propel.twig.extension.syntax" class="%propel.twig.extension.syntax.class%">
@ -46,13 +43,26 @@
<tag name="form.type_guesser" />
</service>
<service id="propel.security.acl.provider" class="%propel.security.acl.provider.model.class%" public="false">
<argument type="service" id="security.acl.permission_granting_strategy" />
<argument type="service" id="propel.security.acl.connection" on-invalid="null" />
<argument type="service" id="security.acl.cache" on-invalid="null" />
<service id="propel.form.type.model" class="%propel.form.type.model.class%">
<tag name="form.type" alias="model" />
</service>
<service id="propel.security.user.provider" class="%propel.security.user.provider.class%" abstract="true" public="false">
<service id="propel.dumper.yaml" class="%propel.dumper.yaml.class%">
<argument>%kernel.root_dir%</argument>
<argument>%propel.configuration%</argument>
</service>
<service id="propel.loader.yaml" class="%propel.loader.yaml.class%">
<argument>%kernel.root_dir%</argument>
<argument>%propel.configuration%</argument>
<argument type="service" id="faker.generator" on-invalid="null" />
</service>
<service id="propel.loader.xml" class="%propel.loader.xml.class%">
<argument>%kernel.root_dir%</argument>
<argument>%propel.configuration%</argument>
</service>
</services>
</container>

View file

@ -0,0 +1,21 @@
<?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.security.acl.provider.model.class">Propel\Bundle\PropelBundle\Security\Acl\AuditableAclProvider</parameter>
<parameter key="propel.security.user.provider.class">Propel\Bundle\PropelBundle\Security\User\PropelUserProvider</parameter>
</parameters>
<services>
<service id="propel.security.acl.provider" class="%propel.security.acl.provider.model.class%" public="false">
<argument type="service" id="security.acl.permission_granting_strategy" />
<argument type="service" id="propel.security.acl.connection" on-invalid="null" />
<argument type="service" id="security.acl.cache" on-invalid="null" />
</service>
<service id="propel.security.user.provider" class="%propel.security.user.provider.class%" abstract="true" public="false" />
</services>
</container>

View file

@ -1,456 +0,0 @@
# PropelBundle #
This is the official implementation of [Propel](http://www.propelorm.org/) in Symfony2.
## Installation ##
* Clone this bundle in the `vendor/bundles/Propel` directory:
> git submodule add https://github.com/propelorm/PropelBundle.git vendor/bundles/Propel/PropelBundle
* Checkout Propel and Phing in the `vendor` directory:
> svn checkout http://svn.github.com/propelorm/Propel.git vendor/propel
> svn checkout http://svn.phing.info/tags/2.4.6/ vendor/phing
* Instead of using svn, you can clone the unofficial Git repositories:
> git submodule add https://github.com/phingofficial/phing.git vendor/phing
> git submodule add https://github.com/propelorm/Propel.git vendor/propel
* Instead of doing this manually, you can use the Symfony vendor management via the deps file:
See http://www.propelorm.org/cookbook/symfony2/working-with-symfony2.html#via_symfony2_vendor_management
If you are using a Symfony2 2.x.x version (actually, a version which is not 2.1 or above), be sure to deps.lock the PropelBundle to a commit on the 2.0 branch,
which does not use the Bridge
* Register this bundle in the `AppKernel` class:
``` php
public function registerBundles()
{
$bundles = array(
...
// PropelBundle
new Propel\PropelBundle\PropelBundle(),
// register your bundles
new Sensio\HelloBundle\HelloBundle(),
);
...
}
```
* Don't forget to register the PropelBundle namespace in `app/autoload.php` if you are not using Composer:
``` php
$loader->registerNamespaces(array(
...
'Propel' => __DIR__.'/../vendor/bundles',
));
$loader->registerPrefixes(array(
...
'Phing' => __DIR__.'/../vendor/phing/classes/phing',
));
```
## Sample Configuration ##
### Project configuration ###
``` yaml
# in app/config/config.yml
propel:
path: "%kernel.root_dir%/../vendor/propel"
phing_path: "%kernel.root_dir%/../vendor/phing"
# logging: %kernel.debug%
# build_properties:
# xxxxx.xxxxx: xxxxxx
# xxxxx.xxxxx: xxxxxx
# in app/config/config*.yml
propel:
dbal:
driver: mysql
user: root
password: null
dsn: mysql:host=localhost;dbname=test;charset=UTF8
options: {}
attributes: {}
# default_connection: default
# connections:
# default:
# driver: mysql
# user: root
# password: null
# dsn: mysql:host=localhost;dbname=test
# slaves:
# slave_server_1:
# user: root
# password: null
# dsn: mysql:host=localhost;dbname=test_slave_1
#
# options:
# ATTR_PERSISTENT: false
# attributes:
# ATTR_EMULATE_PREPARES: true
# settings:
# charset: { value: UTF8 }
# queries: { query: 'INSERT INTO BAR ('hey', 'there')' }
```
`options`, `attributes` and `settings` are parts of the runtime configuration. See [Runtime Configuration File](http://www.propelorm.org/reference/runtime-configuration.html) documentation for more explanation.
### Build properties ###
You can define _build properties_ by creating a `propel.ini` file in `app/config` and put build properties (see [Build properties Reference](http://www.propelorm.org/reference/buildtime-configuration.html)).
``` ini
# in app/config/propel.ini
xxxx.xxxx.xxxx = XXXX
````
But you can follow the Symfony2 way by adding build properties in `app/config/config.yml`:
``` yaml
# in app/config/config.yml
propel:
build_properties:
xxxxx.xxxx.xxxxx: XXXX
xxxxx.xxxx.xxxxx: XXXX
...
```
### Sample Schema ###
Place the following schema in `src/Sensio/HelloBundle/Resources/config/schema.xml` :
``` xml
<?xml version="1.0" encoding="UTF-8"?>
<database name="default" namespace="Sensio\HelloBundle\Model" defaultIdMethod="native">
<table name="book">
<column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true" />
<column name="title" type="varchar" primaryString="1" size="100" />
<column name="ISBN" type="varchar" size="20" />
<column name="author_id" type="integer" />
<foreign-key foreignTable="author">
<reference local="author_id" foreign="id" />
</foreign-key>
</table>
<table name="author">
<column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true" />
<column name="first_name" type="varchar" size="100" />
<column name="last_name" type="varchar" size="100" />
</table>
</database>
```
Commands
--------
### Build Process ###
Call the application console with the `propel:build` command:
> php app/console propel:build [--classes] [--sql] [--insert-sql]
### Insert SQL ###
Call the application console with the `propel:sql:insert` command:
> php app/console propel:sql:insert [--force]
Note that the `--force` option is needed to actually execute the SQL statements.
### Use The Model Classes ###
Use the Model classes as any other class in Symfony2. Just use the correct namespace, and Symfony2 will autoload them:
class HelloController extends Controller
{
public function indexAction($name)
{
$author = new \Sensio\HelloBundle\Model\Author();
$author->setFirstName($name);
$author->save();
return $this->render('HelloBundle:Hello:index.html.twig', array('name' => $name, 'author' => $author));
}
}
### Migrations ###
Generates SQL diff between the XML schemas and the current database structure:
> php app/console propel:migration:generate-diff
Executes the migrations:
> php app/console propel:migration:migrate
Executes the next migration up:
> php app/console propel:migration:migrate --up
Executes the previous migration down:
> php app/console propel:migration:migrate --down
Lists the migrations yet to be executed:
> php app/console propel:migration:status
### Working with existing databases ###
Run the following command to generate an XML schema from your `default` database:
> php app/console propel:reverse
You can define which connection to use:
> php app/console propel:reverse --connection=default
### Fixtures ###
You can load your own fixtures by using the following command:
> php app/console propel:fixtures:load [-d|--dir[="..."]] [--xml] [--sql] [--yml] [--connection[="..."]] [bundle]
As usual, `--connection` allows to specify a connection.
The `--dir` option allows to specify a directory containing the fixtures (default is: `app/propel/fixtures/`).
Note that the `--dir` expects a relative path from the root dir (which is `app/`).
The `--xml` parameter allows you to load only XML fixtures.
The `--sql` parameter allows you to load only SQL fixtures.
The `--yml` parameter allows you to load only YAML fixtures.
You can mix `--xml`, `--yml` and `--sql` parameters to load XML, YAML and SQL fixtures.
If none of this parameter are set all files YAML, XML and SQL in the directory will be load.
A valid _XML fixtures file_ is:
``` xml
<Fixtures>
<Object Namespace="Awesome">
<o1 Title="My title" MyFoo="bar" />
</Object>
<Related Namespace="Awesome">
<r1 ObjectId="o1" Description="Hello world !" />
</Related>
</Fixtures>
```
A valid _YAML fixtures file_ is:
``` yaml
\Awesome\Object:
o1:
Title: My title
MyFoo: bar
\Awesome\Related:
r1:
ObjectId: o1
Description: Hello world !
```
You can load all fixtures files from a given _bundle_:
> php app/console propel:fixtures:load @MySuperBundle
You can dump data into YAML fixtures file by using this command:
> php app/console propel:fixtures:dump [--connection[="..."]]
Dumped files will be written in the fixtures directory: `app/propel/fixtures/` with the following name: `fixtures_99999.yml` where `99999`
is a timestamp.
Once done, you will be able to load this files by using the `propel:fixtures:load` command.
### Graphviz ###
You can generate **Graphviz** file for your project by using the following command line:
> php app/console propel:graphviz
It will write files in `app/propel/graph/`.
### Database ###
You can create a **database**:
> php app/console propel:database:create [--connection[=""]]
As usual, `--connection` allows to specify a connection.
You can drop a **database**:
> php app/console propel:database:drop [--connection[=""]] [--force]
As usual, `--connection` allows to specify a connection.
Note that the `--force` option is needed to actually execute the SQL statements.
### Table ###
You can drop one or several **table**:
> php app/console propel:table:drop [--force] [--connection[="..."]] [table1] ... [tableN]
As usual, `--connection` allows to specify a connection.
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 [models1] ... [modelsN]
It will write Form Type classes in `src/YourVendor/YourBundle/Form/Type`.
You can choose which Form Type to build by specifing Model names:
> php app/console propel:form:generate @MySuperBundle Book Author
## PropelParamConverter ##
You can use the Propel ParamConverter with the SensioFrameworkExtraBundle.
You just need to put the right _Annotation_ on top of your controller:
``` php
<?php
/**
* @ParamConverter("post", class="BlogBundle\Model\Post")
*/
public function myAction(Post $post)
{
}
```
Your request needs to have an `id` parameter or any field as parameter (slug, title, ...).
The _Annotation_ is optional if your parameter is typed you could only have this:
``` php
<?php
public function myAction(Post $post)
{
}
```
Exclude some parameters:
You can exclude some attributes from being used by the converter:
If you have a route like `/my-route/{slug}/{name}/edit/{id}`
you can exclude `name` and `slug` by setting the option "exclude":
``` php
<?php
/**
* @ParamConverter("post", class="BlogBundle\Model\Post", options={"exclude"={"name", "slug"}})
*/
public function myAction(Post $post)
{
}
```
## UniqueObjectValidator ##
In a form, if you want to validate the unicity of a field in a table you have to use the UniqueObjectValidator.
The only way to use it is in a validation.yml file, like this:
``` yaml
BundleNamespace\Model\User:
constraints:
- Propel\PropelBundle\Validator\Constraints\UniqueObject: username
```
For validate the unicity of more than just one fields:
``` yaml
BundleNamespace\Model\User:
constraints:
- Propel\PropelBundle\Validator\Constraints\UniqueObject: [username, login]
```
As many validator of this type as you want can be used.
## Bundle Inheritance ##
The `PropelBundle` makes use of the bundle inheritance.
Currently only schema inheritance is provided.
### Schema Inheritance ###
You can override the defined schema of a bundle from within its child bundle.
To make use of the inheritance you only need to drop a schema file in the `Resources/config` folder of the child bundle.
Each file can be overridden without interfering with other schema files.
If you want to remove parts of a schema, you only need to add an empty schema file.
## ACL implementation ##
The `PropelBundle` provides a model-based implementation of the Security components' interfaces.
To make us of this `AuditableAclProvider` you only need to change your security configuration.
``` yaml
security:
acl:
provider: propel.security.acl.provider
```
This will switch the provider to be the `AuditableAclProvider` of the `PropelBundle`.
The auditing of this provider is set to a sensible default. It will audit all ACL failures but no success by default.
If you also want to audit successful authorizations, you need to update the auditing of the given ACL accordingly.
After adding the provider, you only need to run the `propel:init:acl` command in order to get the model generated.
If you already got an ACL database, the schema of the `PropelBundle` is compatible with the default schema of Symfony2.
### Separate database connection for ACL ###
In case you want to use a different database for your ACL than your business model, you only need to configure this service.
``` yaml
services:
propel.security.acl.connection:
class: PropelPDO
factory_class: Propel
factory_method: getConnection
arguments:
- "acl"
```
The `PropelBundle` looks for this service, and if given uses the provided connection for all ACL related operations.
The given argument (`acl` in the example) is the name of the connection to use, as defined in your runtime configuration.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

View file

@ -3,32 +3,26 @@
namespace ##NAMESPACE##;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ##CLASS## extends AbstractType
{
/**
* {@inheritdoc}
* {@inheritdoc}
*/
public function buildForm(FormBuilder $builder, array $options)
public function buildForm(FormBuilderInterface $builder, array $options)
{##BUILD_CODE##
}
/**
* {@inheritdoc}
* {@inheritdoc}
*/
public function getDefaultOptions()
public function configureOptions(OptionsResolver $resolver)
{
return array(
$resolver->setDefaults([
'data_class' => '##FQCN##',
);
}
/**
* {@inheritdoc}
*/
public function getName()
{
return '##TYPE_NAME##';
'name' => '##TYPE_NAME##',
]);
}
}

View file

@ -1,33 +1,39 @@
{% extends 'WebProfilerBundle:Profiler:layout.html.twig' %}
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
{% block toolbar %}
{# the web debug toolbar content #}
{% set icon %}
<img alt="Propel" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAcCAYAAAB2+A+pAAACzmlDQ1BJQ0MgUHJvZmlsZQAAeNqNk8trFFkUh7/q3KCQIAy0r14Ml1lIkCSUDzQiPtJJbKKxbcpEkyBIp/p2d5mb6ppb1XEUEcnGpc4we/GxcOEf4MKFK90oEXwhiHsVRRTcqLSL6nRX8HlWX/3Oub9zzi0udNrFINApCXN+ZJxcVk5OTcsVz0ixni4ydBXdMBgsFMYAikGg+SY+PsECeNj3/fxPo6sUunNgrYTU+5IKXej4DNQqk1PTIDSQPhkFEYhzQNrE+v9Aeibm60DajDtDIG4Bq9zARCDuAQNutViCTgH0VhI1Mwme03W3Oc8fQLfyJw4DGyB1VoUjTbYWSsXhA0A/WK9KangE6AXretnbNwr0AM/LZt9EzNZGLxodjzl1xNf5sSav82fyh5qeIoiyzpJ/OH94ZEk/UdxfADJgObO1Aw6wBlJ7T1fHj8Zs6dPVoXyTH5m6MwH8BalrgS6MxbOl7jCFRuHho/CROOTI0keAoUYZDw+NRw6Fj8LgETL73UpNIcGSHC/xeYnB42/qKCQOR8jmWehtOUj7qf3Gfmxftq/Zry9m6j3tzII57rmLF95RQGFavs1sc6bY36XGIBpNBcVca6cwMWliurJ/MdN2chcvvFPn8x8TW6pEpz5mUITMYvCYR6EJUQwmuv3o9hT67plb69q9Houbxx523z2z7K5q32ylWlst/27XJc8r8afYJEbFgNiBFHvEXrFbDIsBsVOMtU5M4ONxEoUhpIjG5xRy2f9bqiV+awCkc8pXxnOlk8vKgqmVPa0ST/QX6d+MyalpGdN0HW6EsHZrW/vgYAHWmsW2Fh2EXW+h40Fb68nA6ktwc5tbN/NNa8u6D5H6JwIYqgWnjFepRnKzbW+Xg0GglRz13f5eWdRaGq9SjUJpVKjMvCr1E5a3bI5durPQ+aLR+LABVvwHX/5tND5daTS+XIWO53BbfwXAvP1FP6ZP5AAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAG9gAABvYBDBXjEwAAAAd0SU1FB9sEBhQVGSw3+igAAAWFSURBVEjH3VdfTFtVGP+d09v2jna0pYzC7EbpmsBguAw6+kCydJJh4nhYiMaYKInbNMvigpo49+CDL0bji89LlhgWH8TFaII004VIMmDAKGF/Ojr5t1YYpfS2pZTb295/vgACg4Hb9MHzdr/zfd/v/r7zO+d8B/i/jWQyueXcvXv3/h3QyclJspm9p6eHjIyMUABgXgTQ4OAg6urqcPv2bQ3HcXA6nTIAjI+PV8disUZVVc+m0+mKxcXFrMlkegfANfK8oAMDA/B4POtsIyMjJwVBOJtKpV4VBGGXoigghMBoNM6Wl5cfstvt8WdmnEgkcOXKlXWgfr//eCKR+DwSiRzL5XKQZRmEEAUAZVkWJSUlZ+12e3x4eFjzTMAPHjzQtLS0KB0dHery2p3I5XJfRSKRGlmWIUkSCCGglEKn01FKqWgymV6vrKz0VVZWoqamRib/RKVms3nj2r6RSqXO53I5bzabBQBoNBqwLAtK6RTDMCMGgyFotVp/dDqd/uWqoLa2dntxhcNhDAwMrAO9f//+W3Nzc5disdjLoigCACwWCzQaTS+ltJ1SOujxeMYppdyaLUSMRqNaVlYGAHgqY1VVQcjfLkNDQ69KknQ1nU4XZbNZ6PX6P81m87DJZPoulUpdr62tTa/4jo2NFTAMc0Kv15v37t17eWOuLRlnMplVxzt37rwmiuK5paWlGlEUg2az+arNZvt+3759/g0/ag0EAqclSWpJJBKHeJ6HVqudEUWxkxAyvdZ3O8a6aDTanEwma+bn5wMOh6Pfbrc/3Og3OzvriUajrTzP10uStD+Xy0FVVeh0Olgslm+qq6s/7u3t1dTX18s7BaahUMjicDi4zeYnJibqFhYWPuV5vlmWZSiKAkVRBAAswzDIz8/3HTly5CQAxGIxFBYWbl9qnudX9uAToKOjow1LS0vnHz9+3CxJElRVhaqqAACWZVlKKQwGw0B1dfV5AAgEAutAt2UcDAZRUVGx+j09PX18Zmbmg1wu1yyK4qoGWJYFwzAhrVY7RCm9vnv37kmHw9FLCMkCgCAIYFl2XW5mi0Mera2tq6Acx3nC4fDXU1NTxwBAlmWwLBs3GAwhlmU7M5nM5cOHD0cIIdLaPGNjY9Tlcilr1bwl40gkQoqLi9XlwKp4PP4RIeTM8vr5jUbjWH5+/g+lpaW/EkL4NXEv+f3+g4uLi2U2my3t9Xp/IoQIW1VzHeO5uTlis9lUjuOq4vH4Z6IoHmMYZpQQ8mFxcfHdkpKSHkKIuDbm1q1b9clk8mJPT0+NLMv2bDaLTCbzB4BfAOwM2Gazqel0Ok8QhFcopdd4nv/E7XZPbwwSRdHQ19f3Nsdx56ampg7KsqxfEVleXl5mz5497xNCFp+mnydKHYvFSGFhobrFeW3p7+9/M51OX8pkMqUralYURQFA9Xo9bDbb6YaGhm/dbjd8Ph+Kioq2B15YWIDJZNpsP7M+n+8Cz/NnBEEolyQJlFKoqgpK6Yqqx3Q63RdNTU1tO7l0nmDc1dVFGhoa1GVAY1dX13vz8/MXRFEsWwFavoGiOp2uW6/XX1dVtdflcnFOp5MDgM7OTmi1WjQ2Nm4NHAgEUFVVheHhYRIOh8mpU6cUALh582ZrNBr9kuf5XQzDgBAyYzQapzUazc9VVVUD+/fv/32zhN3d3fB6vdsz5jgOVqt11XDjxo13U6nURUpphSRJI3l5eX0ajab76NGjd61W68PNkrS3t8PtduPAgQM7bibI0NAQ3G43gsFgUywWuygIgqooym9arbbD6/VOEkJWr7qysjLS1tZG7Xa74nQ61efuEkOhUOHExMSJ/v5+22bz0Wj0xffAoVBoU3tBQcFTm/LnHQQAHj16BEVRiKIoqsvl+k9eGn8BMMeiAFTierUAAAAASUVORK5CYII=" />
<span class="sf-toolbar-status">{{ collector.querycount }}</span>
{% endset %}
{% set text %}
<div class="sf-toolbar-info-piece">
<b>DB Queries</b>
<span>{{ collector.querycount }}</span>
</div>
<div class="sf-toolbar-info-piece">
<b>Query time</b>
<span>{{ '%0.2f'|format(collector.time * 1000) }} ms</span>
</div>
{% endset %}
{% include 'WebProfilerBundle:Profiler:toolbar_item.html.twig' with { 'link': profiler_url } %}
{% if collector.querycount %}
{% set icon %}
<img alt="Propel" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAcCAYAAAB2+A+pAAACzmlDQ1BJQ0MgUHJvZmlsZQAAeNqNk8trFFkUh7/q3KCQIAy0r14Ml1lIkCSUDzQiPtJJbKKxbcpEkyBIp/p2d5mb6ppb1XEUEcnGpc4we/GxcOEf4MKFK90oEXwhiHsVRRTcqLSL6nRX8HlWX/3Oub9zzi0udNrFINApCXN+ZJxcVk5OTcsVz0ixni4ydBXdMBgsFMYAikGg+SY+PsECeNj3/fxPo6sUunNgrYTU+5IKXej4DNQqk1PTIDSQPhkFEYhzQNrE+v9Aeibm60DajDtDIG4Bq9zARCDuAQNutViCTgH0VhI1Mwme03W3Oc8fQLfyJw4DGyB1VoUjTbYWSsXhA0A/WK9KangE6AXretnbNwr0AM/LZt9EzNZGLxodjzl1xNf5sSav82fyh5qeIoiyzpJ/OH94ZEk/UdxfADJgObO1Aw6wBlJ7T1fHj8Zs6dPVoXyTH5m6MwH8BalrgS6MxbOl7jCFRuHho/CROOTI0keAoUYZDw+NRw6Fj8LgETL73UpNIcGSHC/xeYnB42/qKCQOR8jmWehtOUj7qf3Gfmxftq/Zry9m6j3tzII57rmLF95RQGFavs1sc6bY36XGIBpNBcVca6cwMWliurJ/MdN2chcvvFPn8x8TW6pEpz5mUITMYvCYR6EJUQwmuv3o9hT67plb69q9Houbxx523z2z7K5q32ylWlst/27XJc8r8afYJEbFgNiBFHvEXrFbDIsBsVOMtU5M4ONxEoUhpIjG5xRy2f9bqiV+awCkc8pXxnOlk8vKgqmVPa0ST/QX6d+MyalpGdN0HW6EsHZrW/vgYAHWmsW2Fh2EXW+h40Fb68nA6ktwc5tbN/NNa8u6D5H6JwIYqgWnjFepRnKzbW+Xg0GglRz13f5eWdRaGq9SjUJpVKjMvCr1E5a3bI5durPQ+aLR+LABVvwHX/5tND5daTS+XIWO53BbfwXAvP1FP6ZP5AAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAG9gAABvYBDBXjEwAAAAd0SU1FB9sEBhQVGSw3+igAAAWFSURBVEjH3VdfTFtVGP+d09v2jna0pYzC7EbpmsBguAw6+kCydJJh4nhYiMaYKInbNMvigpo49+CDL0bji89LlhgWH8TFaII004VIMmDAKGF/Ojr5t1YYpfS2pZTb295/vgACg4Hb9MHzdr/zfd/v/r7zO+d8B/i/jWQyueXcvXv3/h3QyclJspm9p6eHjIyMUABgXgTQ4OAg6urqcPv2bQ3HcXA6nTIAjI+PV8disUZVVc+m0+mKxcXFrMlkegfANfK8oAMDA/B4POtsIyMjJwVBOJtKpV4VBGGXoigghMBoNM6Wl5cfstvt8WdmnEgkcOXKlXWgfr//eCKR+DwSiRzL5XKQZRmEEAUAZVkWJSUlZ+12e3x4eFjzTMAPHjzQtLS0KB0dHery2p3I5XJfRSKRGlmWIUkSCCGglEKn01FKqWgymV6vrKz0VVZWoqamRib/RKVms3nj2r6RSqXO53I5bzabBQBoNBqwLAtK6RTDMCMGgyFotVp/dDqd/uWqoLa2dntxhcNhDAwMrAO9f//+W3Nzc5disdjLoigCACwWCzQaTS+ltJ1SOujxeMYppdyaLUSMRqNaVlYGAHgqY1VVQcjfLkNDQ69KknQ1nU4XZbNZ6PX6P81m87DJZPoulUpdr62tTa/4jo2NFTAMc0Kv15v37t17eWOuLRlnMplVxzt37rwmiuK5paWlGlEUg2az+arNZvt+3759/g0/ag0EAqclSWpJJBKHeJ6HVqudEUWxkxAyvdZ3O8a6aDTanEwma+bn5wMOh6Pfbrc/3Og3OzvriUajrTzP10uStD+Xy0FVVeh0Olgslm+qq6s/7u3t1dTX18s7BaahUMjicDi4zeYnJibqFhYWPuV5vlmWZSiKAkVRBAAswzDIz8/3HTly5CQAxGIxFBYWbl9qnudX9uAToKOjow1LS0vnHz9+3CxJElRVhaqqAACWZVlKKQwGw0B1dfV5AAgEAutAt2UcDAZRUVGx+j09PX18Zmbmg1wu1yyK4qoGWJYFwzAhrVY7RCm9vnv37kmHw9FLCMkCgCAIYFl2XW5mi0Mera2tq6Acx3nC4fDXU1NTxwBAlmWwLBs3GAwhlmU7M5nM5cOHD0cIIdLaPGNjY9Tlcilr1bwl40gkQoqLi9XlwKp4PP4RIeTM8vr5jUbjWH5+/g+lpaW/EkL4NXEv+f3+g4uLi2U2my3t9Xp/IoQIW1VzHeO5uTlis9lUjuOq4vH4Z6IoHmMYZpQQ8mFxcfHdkpKSHkKIuDbm1q1b9clk8mJPT0+NLMv2bDaLTCbzB4BfAOwM2Gazqel0Ok8QhFcopdd4nv/E7XZPbwwSRdHQ19f3Nsdx56ampg7KsqxfEVleXl5mz5497xNCFp+mnydKHYvFSGFhobrFeW3p7+9/M51OX8pkMqUralYURQFA9Xo9bDbb6YaGhm/dbjd8Ph+Kioq2B15YWIDJZNpsP7M+n+8Cz/NnBEEolyQJlFKoqgpK6Yqqx3Q63RdNTU1tO7l0nmDc1dVFGhoa1GVAY1dX13vz8/MXRFEsWwFavoGiOp2uW6/XX1dVtdflcnFOp5MDgM7OTmi1WjQ2Nm4NHAgEUFVVheHhYRIOh8mpU6cUALh582ZrNBr9kuf5XQzDgBAyYzQapzUazc9VVVUD+/fv/32zhN3d3fB6vdsz5jgOVqt11XDjxo13U6nURUpphSRJI3l5eX0ajab76NGjd61W68PNkrS3t8PtduPAgQM7bibI0NAQ3G43gsFgUywWuygIgqooym9arbbD6/VOEkJWr7qysjLS1tZG7Xa74nQ61efuEkOhUOHExMSJ/v5+22bz0Wj0xffAoVBoU3tBQcFTm/LnHQQAHj16BEVRiKIoqsvl+k9eGn8BMMeiAFTierUAAAAASUVORK5CYII=" />
<span class="sf-toolbar-value">{{ collector.querycount }}</span>
<span class="sf-toolbar-info-piece-additional-detail">
<span class="sf-toolbar-label">in</span>
<span class="sf-toolbar-value">{{ '%0.2f'|format(collector.time * 1000) }}</span>
<span class="sf-toolbar-label">ms</span>
</span>
{% endset %}
{% set text %}
<div class="sf-toolbar-info-piece">
<b>DB Queries</b>
<span class="sf-toolbar-status {{ collector.querycount > 50 ? 'sf-toolbar-status-yellow' : '' }}">{{ collector.querycount }}</span>
</div>
<div class="sf-toolbar-info-piece">
<b>Query time</b>
<span>{{ '%0.2f'|format(collector.time * 1000) }} ms</span>
</div>
{% endset %}
{% set status = collector.querycount > 50 ? 'yellow' : '' %}
{% include '@WebProfiler/Profiler/toolbar_item.html.twig' with { 'link': profiler_url, status: status } %}
{% endif %}
{% endblock %}
{% block menu %}
{# the menu content #}
<span class="label">
<span class="icon"><img src="{{ asset('bundles/propel/images/profiler/propel.png') }}" alt="" /></span>
<span class="label {{ not collector.querycount ? 'disabled' }}">
<span class="icon"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAcCAYAAAB2+A+pAAACzmlDQ1BJQ0MgUHJvZmlsZQAAeNqNk8trFFkUh7/q3KCQIAy0r14Ml1lIkCSUDzQiPtJJbKKxbcpEkyBIp/p2d5mb6ppb1XEUEcnGpc4we/GxcOEf4MKFK90oEXwhiHsVRRTcqLSL6nRX8HlWX/3Oub9zzi0udNrFINApCXN+ZJxcVk5OTcsVz0ixni4ydBXdMBgsFMYAikGg+SY+PsECeNj3/fxPo6sUunNgrYTU+5IKXej4DNQqk1PTIDSQPhkFEYhzQNrE+v9Aeibm60DajDtDIG4Bq9zARCDuAQNutViCTgH0VhI1Mwme03W3Oc8fQLfyJw4DGyB1VoUjTbYWSsXhA0A/WK9KangE6AXretnbNwr0AM/LZt9EzNZGLxodjzl1xNf5sSav82fyh5qeIoiyzpJ/OH94ZEk/UdxfADJgObO1Aw6wBlJ7T1fHj8Zs6dPVoXyTH5m6MwH8BalrgS6MxbOl7jCFRuHho/CROOTI0keAoUYZDw+NRw6Fj8LgETL73UpNIcGSHC/xeYnB42/qKCQOR8jmWehtOUj7qf3Gfmxftq/Zry9m6j3tzII57rmLF95RQGFavs1sc6bY36XGIBpNBcVca6cwMWliurJ/MdN2chcvvFPn8x8TW6pEpz5mUITMYvCYR6EJUQwmuv3o9hT67plb69q9Houbxx523z2z7K5q32ylWlst/27XJc8r8afYJEbFgNiBFHvEXrFbDIsBsVOMtU5M4ONxEoUhpIjG5xRy2f9bqiV+awCkc8pXxnOlk8vKgqmVPa0ST/QX6d+MyalpGdN0HW6EsHZrW/vgYAHWmsW2Fh2EXW+h40Fb68nA6ktwc5tbN/NNa8u6D5H6JwIYqgWnjFepRnKzbW+Xg0GglRz13f5eWdRaGq9SjUJpVKjMvCr1E5a3bI5durPQ+aLR+LABVvwHX/5tND5daTS+XIWO53BbfwXAvP1FP6ZP5AAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAG9gAABvYBDBXjEwAAAAd0SU1FB9sEBhQVGSw3+igAAAWFSURBVEjH3VdfTFtVGP+d09v2jna0pYzC7EbpmsBguAw6+kCydJJh4nhYiMaYKInbNMvigpo49+CDL0bji89LlhgWH8TFaII004VIMmDAKGF/Ojr5t1YYpfS2pZTb295/vgACg4Hb9MHzdr/zfd/v/r7zO+d8B/i/jWQyueXcvXv3/h3QyclJspm9p6eHjIyMUABgXgTQ4OAg6urqcPv2bQ3HcXA6nTIAjI+PV8disUZVVc+m0+mKxcXFrMlkegfANfK8oAMDA/B4POtsIyMjJwVBOJtKpV4VBGGXoigghMBoNM6Wl5cfstvt8WdmnEgkcOXKlXWgfr//eCKR+DwSiRzL5XKQZRmEEAUAZVkWJSUlZ+12e3x4eFjzTMAPHjzQtLS0KB0dHery2p3I5XJfRSKRGlmWIUkSCCGglEKn01FKqWgymV6vrKz0VVZWoqamRib/RKVms3nj2r6RSqXO53I5bzabBQBoNBqwLAtK6RTDMCMGgyFotVp/dDqd/uWqoLa2dntxhcNhDAwMrAO9f//+W3Nzc5disdjLoigCACwWCzQaTS+ltJ1SOujxeMYppdyaLUSMRqNaVlYGAHgqY1VVQcjfLkNDQ69KknQ1nU4XZbNZ6PX6P81m87DJZPoulUpdr62tTa/4jo2NFTAMc0Kv15v37t17eWOuLRlnMplVxzt37rwmiuK5paWlGlEUg2az+arNZvt+3759/g0/ag0EAqclSWpJJBKHeJ6HVqudEUWxkxAyvdZ3O8a6aDTanEwma+bn5wMOh6Pfbrc/3Og3OzvriUajrTzP10uStD+Xy0FVVeh0Olgslm+qq6s/7u3t1dTX18s7BaahUMjicDi4zeYnJibqFhYWPuV5vlmWZSiKAkVRBAAswzDIz8/3HTly5CQAxGIxFBYWbl9qnudX9uAToKOjow1LS0vnHz9+3CxJElRVhaqqAACWZVlKKQwGw0B1dfV5AAgEAutAt2UcDAZRUVGx+j09PX18Zmbmg1wu1yyK4qoGWJYFwzAhrVY7RCm9vnv37kmHw9FLCMkCgCAIYFl2XW5mi0Mera2tq6Acx3nC4fDXU1NTxwBAlmWwLBs3GAwhlmU7M5nM5cOHD0cIIdLaPGNjY9Tlcilr1bwl40gkQoqLi9XlwKp4PP4RIeTM8vr5jUbjWH5+/g+lpaW/EkL4NXEv+f3+g4uLi2U2my3t9Xp/IoQIW1VzHeO5uTlis9lUjuOq4vH4Z6IoHmMYZpQQ8mFxcfHdkpKSHkKIuDbm1q1b9clk8mJPT0+NLMv2bDaLTCbzB4BfAOwM2Gazqel0Ok8QhFcopdd4nv/E7XZPbwwSRdHQ19f3Nsdx56ampg7KsqxfEVleXl5mz5497xNCFp+mnydKHYvFSGFhobrFeW3p7+9/M51OX8pkMqUralYURQFA9Xo9bDbb6YaGhm/dbjd8Ph+Kioq2B15YWIDJZNpsP7M+n+8Cz/NnBEEolyQJlFKoqgpK6Yqqx3Q63RdNTU1tO7l0nmDc1dVFGhoa1GVAY1dX13vz8/MXRFEsWwFavoGiOp2uW6/XX1dVtdflcnFOp5MDgM7OTmi1WjQ2Nm4NHAgEUFVVheHhYRIOh8mpU6cUALh582ZrNBr9kuf5XQzDgBAyYzQapzUazc9VVVUD+/fv/32zhN3d3fB6vdsz5jgOVqt11XDjxo13U6nURUpphSRJI3l5eX0ajab76NGjd61W68PNkrS3t8PtduPAgQM7bibI0NAQ3G43gsFgUywWuygIgqooym9arbbD6/VOEkJWr7qysjLS1tZG7Xa74nQ61efuEkOhUOHExMSJ/v5+22bz0Wj0xffAoVBoU3tBQcFTm/LnHQQAHj16BEVRiKIoqsvl+k9eGn8BMMeiAFTierUAAAAASUVORK5CYII=" alt="" /></span>
<strong>Propel</strong>
<span class="count">
<span>{{ collector.querycount }}</span>
<span>{{ '%0.0f'|format(collector.time * 1000) }} ms</span>
</span>
</span>
{% endblock %}
@ -42,7 +48,7 @@
color: #464646;
white-space: nowrap;
}
.SQLInfo, .SQLComment {
.SQLComment {
color: gray;
display: block;
font-size: 0.9em;
@ -60,52 +66,80 @@
padding: 8px 35px 8px 14px;
font-weight: bold;
}
#content .SQLExplain h2 {
font-size: 17px;
margin-bottom: 0;
}
</style>
<h2>Queries</h2>
<table summary="Show logged queries">
<thead>
<tr>
<th>SQL queries</th>
</tr>
</thead>
<tbody>
{% if not collector.querycount %}
<tr><td>No queries.</td></tr>
{% else %}
{% for i, query in collector.queries %}
<tr>
<td>
<a name="propel-query-{{ i }}" ></a>
<code>{{ query.sql|format_sql }}</code>
{% if app.request.query.has('query') and app.request.query.get('query') == i %}
<div class="SQLExplain">
{% render 'PropelBundle:Panel:explain' with {
'token': token,
'panel': 'propel',
'query': app.request.query.get('query'),
'connection': app.request.query.get('connection')
} %}
<h2>Query Metrics</h2>
{% if not collector.querycount %}
<div class="empty">
<p>No database queries were performed.</p>
</div>
{% else %}
<div class="metrics">
<div class="metric">
<span class="value">{{ collector.querycount }}</span>
<span class="label">Database Queries</span>
</div>
<div class="metric">
<span class="value">{{ '%0.2f'|format(collector.time * 1000) }} <span class="unit">ms</span></span>
<span class="label">Query time</span>
</div>
</div>
<h2>Queries</h2>
<table summary="Show logged queries">
<thead>
<tr>
<th nowrap>#</th>
<th nowrap>Time</th>
<th nowrap>Memory</th>
<th style="width: 100%;">Query</th>
</tr>
</thead>
<tbody id="queries">
{% for i, query in collector.queries %}
<tr>
<td class="font-normal text-small" nowrap>{{ loop.index }}</td>
<td class="font-normal text-small" nowrap>{{ '%0.2f'|format(query.time * 1000) }}&nbsp;ms</td>
<td class="font-normal text-small" nowrap>{{ query.memory|format_memory }}</td>
<td>
<a name="propel-query-{{ i }}" ></a>
<code>{{ query.sql|format_sql|raw }}</code>
<div class="metadata font-normal text-muted">
<span class="text-small">
Connection: {{ query.connection }}
</span>
-
<a class="btn btn-link text-small sf-toggle" data-toggle-selector="#propel-stack-trace-{{ i }}" data-toggle-alt-content="Hide trace">Show trace</a>
-
{% if app.request.query.get('query', -1) == i %}
<a class="btn btn-link text-small" href="{{ path('_profiler', {'panel': 'propel', 'token': token, 'connection': query.connection}) }}}#propel-query-{{ i }}">Hide query explanation</a>
{% else %}
<a class="btn btn-link text-small" href="{{ path('_profiler', {'panel': 'propel', 'token': token, 'connection': query.connection, 'query': i}) }}#propel-query-{{ i }}">Explain query</a>
{% endif %}
</div>
{% endif %}
<div class="SQLInfo">
Time: {{ query.time }} - Memory: {{ query.memory }} - Connection: {{ query.connection }}
{% if app.request.query.get('query', -1) != i %}
- <a href="{{ path('_profiler', {'panel': 'propel', 'token': token, 'connection': query.connection, 'query': i}) }}#propel-query-{{ i }}">Explain the query</a>
{% if app.request.query.has('query') and app.request.query.get('query') == i %}
<div class="SQLExplain">
{{ render(controller('PropelBundle:Panel:explain', {
'token': token,
'panel': 'propel',
'query': app.request.query.get('query'),
'connection': app.request.query.get('connection')
})) }}
</div>
{% endif %}
</div>
</td>
</tr>
{% endfor %}
{% endif %}
</tbody>
</table>
{% render 'PropelBundle:Panel:configuration' %}
<div id="propel-stack-trace-{{ i }}" class="sf-toggle-content sf-toggle-hidden">
{{ profiler_dump(query.trace, maxDepth=1) }}
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
{{ render(controller('PropelBundle:Panel:configuration')) }}
{% endblock %}

View file

@ -10,19 +10,11 @@
</tr>
<tr>
<th>Default connection</th>
<td>{{ default_connection }}</td>
<td>{{ configuration.runtime.defaultConnection }}</td>
<tr>
<th>Logging</th>
<td>{{ logging ? 'enabled' : 'disabled' }}</td>
</tr>
<tr>
<th>Propel path</th>
<td>{{ path }}</td>
</tr>
<tr>
<th>Phing path</th>
<td>{{ phing_path }}</td>
</tr>
</tbody>
</table>
@ -36,7 +28,7 @@
</tr>
</thead>
<tbody>
{% for name, config in configuration.datasources %}
{% for name, config in configuration.database.connections %}
<tr>
<th rowspan="5" style="vertical-align: top;">
{{ name }}
@ -46,17 +38,17 @@
</tr>
<tr>
<th>DSN</th>
<td>{{ config.connection.dsn }}</td>
<td>{{ config.dsn }}</td>
</tr>
<tr>
<th>Class</th>
<td>{{ config.connection.classname }}</td>
<td>{{ config.classname }}</td>
</tr>
<tr>
<th>Options</th>
<td>
<ul>
{% for key, value in config.connection.options %}
{% for key, value in config.options %}
<li>{{ key }} : {{ value }}</li>
{% endfor %}
</ul>
@ -66,7 +58,7 @@
<th>Attributes</th>
<td>
<ul>
{% for key, value in config.connection.attributes %}
{% for key, value in config.attributes %}
<li>{{ key }} : {{ value }}</li>
{% endfor %}
</ul>

View file

@ -1,5 +1,3 @@
<h2>Explanation</h2>
<table>
<tr>
{% for label in data[0]|keys %}
@ -13,4 +11,4 @@
{% endfor %}
</tr>
{% endfor %}
</table>
</table>

View file

@ -8,19 +8,18 @@
* @license MIT License
*/
namespace Propel\PropelBundle\Security\Acl;
namespace Propel\Bundle\PropelBundle\Security\Acl;
use Propel\PropelBundle\Model\Acl\EntryQuery;
use Propel\PropelBundle\Model\Acl\ObjectIdentityQuery;
use Propel\PropelBundle\Model\Acl\SecurityIdentity;
use Propel\Runtime\Collection\ObjectCollection;
use Propel\Runtime\Connection\ConnectionInterface;
use Propel\PropelBundle\Security\Acl\Domain\Acl;
use Propel\PropelBundle\Security\Acl\Domain\Entry;
use Propel\PropelBundle\Security\Acl\Domain\FieldEntry;
use Propel\Bundle\PropelBundle\Model\Acl\EntryQuery;
use Propel\Bundle\PropelBundle\Model\Acl\ObjectIdentityQuery;
use Propel\Bundle\PropelBundle\Model\Acl\SecurityIdentity;
use Propel\Bundle\PropelBundle\Security\Acl\Domain\Acl;
use Symfony\Component\Security\Acl\Domain\ObjectIdentity;
use Symfony\Component\Security\Acl\Domain\RoleSecurityIdentity;
use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity;
use Symfony\Component\Security\Acl\Exception\AclNotFoundException;
@ -45,10 +44,10 @@ class AclProvider implements AclProviderInterface
* Constructor.
*
* @param \Symfony\Component\Security\Acl\Model\PermissionGrantingStrategyInterface $permissionGrantingStrategy
* @param \PropelPDO $con
* @param \Symfony\Component\Security\Acl\Model\AclCacheInterface $cache
* @param ConnectionInterface $con
* @param \Symfony\Component\Security\Acl\Model\AclCacheInterface $cache
*/
public function __construct(PermissionGrantingStrategyInterface $permissionGrantingStrategy, \PropelPDO $connection = null, AclCacheInterface $cache = null)
public function __construct(PermissionGrantingStrategyInterface $permissionGrantingStrategy, ConnectionInterface $connection = null, AclCacheInterface $cache = null)
{
$this->permissionGrantingStrategy = $permissionGrantingStrategy;
$this->connection = $connection;
@ -59,7 +58,7 @@ class AclProvider implements AclProviderInterface
* Retrieves all child object identities from the database.
*
* @param \Symfony\Component\Security\Acl\Model\ObjectIdentityInterface $parentObjectIdentity
* @param bool $directChildrenOnly
* @param bool $directChildrenOnly
*
* @return array
*/
@ -90,7 +89,7 @@ class AclProvider implements AclProviderInterface
* @throws \Symfony\Component\Security\Acl\Exception\AclNotFoundException
*
* @param \Symfony\Component\Security\Acl\Model\ObjectIdentityInterface $objectIdentity
* @param array $securityIdentities
* @param array $securityIdentities
*
* @return \Symfony\Component\Security\Acl\Model\AclInterface
*/
@ -149,7 +148,7 @@ class AclProvider implements AclProviderInterface
*
* @throws \Symfony\Component\Security\Acl\Exception\AclNotFoundException When at least one object identity is missing its ACL.
*
* @param array $objectIdentities an array of ObjectIdentityInterface implementations
* @param array $objectIdentities an array of ObjectIdentityInterface implementations
* @param array $securityIdentities an array of SecurityIdentityInterface implementations
*
* @return \SplObjectStorage mapping the passed object identities to ACLs
@ -167,15 +166,15 @@ class AclProvider implements AclProviderInterface
/**
* Create an ACL.
*
* @param \PropelObjectCollection $collection
* @param ObjectCollection $collection
* @param \Symfony\Component\Security\Acl\Model\ObjectIdentityInterface $objectIdentity
* @param array $loadedSecurityIdentities
* @param \Symfony\Component\Security\Acl\Model\AclInterface $parentAcl
* @param bool $inherited
* @param array $loadedSecurityIdentities
* @param \Symfony\Component\Security\Acl\Model\AclInterface $parentAcl
* @param bool $inherited
*
* @return \Propel\PropelBundle\Security\Acl\Domain\Acl
* @return \Propel\Bundle\PropelBundle\Security\Acl\Domain\Acl
*/
protected function getAcl(\PropelObjectCollection $collection, ObjectIdentityInterface $objectIdentity, array $loadedSecurityIdentities = array(), AclInterface $parentAcl = null, $inherited = true)
protected function getAcl(ObjectCollection $collection, ObjectIdentityInterface $objectIdentity, array $loadedSecurityIdentities = array(), AclInterface $parentAcl = null, $inherited = true)
{
return new Acl($collection, $objectIdentity, $this->permissionGrantingStrategy, $loadedSecurityIdentities, $parentAcl, $inherited);
}

View file

@ -8,9 +8,10 @@
* @license MIT License
*/
namespace Propel\PropelBundle\Security\Acl;
namespace Propel\Bundle\PropelBundle\Security\Acl;
use Propel\PropelBundle\Security\Acl\Domain\AuditableAcl;
use Propel\Runtime\Collection\ObjectCollection;
use Propel\Bundle\PropelBundle\Security\Acl\Domain\AuditableAcl;
use Symfony\Component\Security\Acl\Model\AclInterface;
use Symfony\Component\Security\Acl\Model\ObjectIdentityInterface;
@ -23,16 +24,16 @@ class AuditableAclProvider extends MutableAclProvider
/**
* Get an ACL for this provider.
*
* @param \PropelObjectCollection $collection
* @param Propel\Runtime\Collection\ObjectCollection $collection
* @param \Symfony\Component\Security\Acl\Model\ObjectIdentityInterface $objectIdentity
* @param array $loadedSecurityIdentities
* @param \Symfony\Component\Security\Acl\Model\AclInterface $parentAcl
* @param bool $inherited
* @param array $loadedSecurityIdentities
* @param \Symfony\Component\Security\Acl\Model\AclInterface $parentAcl
* @param bool $inherited
*
* @return \Propel\PropelBundle\Security\Acl\Domain\AuditableAcl
* @return \Propel\Bundle\PropelBundle\Security\Acl\Domain\AuditableAcl
*/
protected function getAcl(\PropelObjectCollection $collection, ObjectIdentityInterface $objectIdentity, array $loadedSecurityIdentities = array(), AclInterface $parentAcl = null, $inherited = true)
protected function getAcl(ObjectCollection $collection, ObjectIdentityInterface $objectIdentity, array $loadedSecurityIdentities = array(), AclInterface $parentAcl = null, $inherited = true)
{
return new AuditableAcl($collection, $objectIdentity, $this->permissionGrantingStrategy, $loadedSecurityIdentities, $parentAcl, $inherited, $this->connection);
}
}
}

View file

@ -8,26 +8,25 @@
* @license MIT License
*/
namespace Propel\PropelBundle\Security\Acl\Domain;
namespace Propel\Bundle\PropelBundle\Security\Acl\Domain;
use Propel\PropelBundle\Model\Acl\SecurityIdentity;
use Propel\Runtime\Collection\ObjectCollection;
use Symfony\Component\Security\Acl\Exception\Exception as AclException;
use Symfony\Component\Security\Acl\Model\AclInterface;
use Symfony\Component\Security\Acl\Model\EntryInterface;
use Symfony\Component\Security\Acl\Model\ObjectIdentityInterface;
use Symfony\Component\Security\Acl\Model\PermissionGrantingStrategyInterface;
use Symfony\Component\Security\Acl\Model\SecurityIdentityInterface;
/**
* An ACL implementation that is immutable based on data from a PropelObjectCollection of Propel\PropelBundle\Model\Acl\Entry.
* An ACL implementation that is immutable based on data from a ObjectCollection of Propel\Bundle\PropelBundle\Model\Acl\Entry.
*
* @author Toni Uebernickel <tuebernickel@gmail.com>
*/
class Acl implements AclInterface
{
protected $model = 'Propel\PropelBundle\Model\Acl\Entry';
protected $model = '\Propel\Bundle\PropelBundle\Model\Acl\Entry';
protected $classAces = array();
protected $classFieldAces = array();
@ -51,17 +50,17 @@ class Acl implements AclInterface
/**
* Constructor.
*
* @param \PropelObjectCollection $entries
* @param \Symfony\Component\Security\Acl\Model\ObjectIdentityInterface $objectIdentity
* @param ObjectCollection $entries
* @param \Symfony\Component\Security\Acl\Model\ObjectIdentityInterface $objectIdentity
* @param \Symfony\Component\Security\Acl\Model\PermissionGrantingStrategyInterface $permissionGrantingStrategy
* @param array $loadedSecurityIdentities
* @param \Symfony\Component\Security\Acl\Model\AclInterface $parentAcl
* @param bool $inherited
* @param array $loadedSecurityIdentities
* @param \Symfony\Component\Security\Acl\Model\AclInterface $parentAcl
* @param bool $inherited
*/
public function __construct(\PropelObjectCollection $entries, ObjectIdentityInterface $objectIdentity, PermissionGrantingStrategyInterface $permissionGrantingStrategy, array $loadedSecurityIdentities = array(), AclInterface $parentAcl = null, $inherited = true)
public function __construct(ObjectCollection $entries, ObjectIdentityInterface $objectIdentity, PermissionGrantingStrategyInterface $permissionGrantingStrategy, array $loadedSecurityIdentities = array(), AclInterface $parentAcl = null, $inherited = true)
{
if ($entries->getModel() !== $this->model) {
throw new AclException(sprintf('The given collection does not contain models of class "%s" but of class "%s".', $this->model, $entries->getModel()));
if ($entries->getFullyQualifiedModel() !== $this->model) {
throw new AclException(sprintf('The given collection does not contain models of class "%s" but of class "%s".', $this->model, $entries->getFullyQualifiedModel()));
}
foreach ($entries as $eachEntry) {
@ -178,10 +177,10 @@ class Acl implements AclInterface
/**
* Determines whether field access is granted
*
* @param string $field
* @param array $masks
* @param array $securityIdentities
* @param bool $administrativeMode
* @param string $field
* @param array $masks
* @param array $securityIdentities
* @param bool $administrativeMode
*
* @return bool
*/
@ -195,9 +194,9 @@ class Acl implements AclInterface
*
* @throws \Symfony\Component\Security\Acl\Exception\NoAceFoundException when no ACE was applicable for this request
*
* @param array $masks
* @param array $securityIdentities
* @param bool $administrativeMode
* @param array $masks
* @param array $securityIdentities
* @param bool $administrativeMode
*
* @return bool
*/
@ -304,7 +303,7 @@ class Acl implements AclInterface
*
* @param string $field
*
* @return \Propel\PropelBundle\Security\Acl\Domain\Acl $this
* @return \Propel\Bundle\PropelBundle\Security\Acl\Domain\Acl $this
*/
protected function updateFields($field)
{

View file

@ -8,14 +8,10 @@
* @license MIT License
*/
namespace Propel\PropelBundle\Security\Acl\Domain;
namespace Propel\Bundle\PropelBundle\Security\Acl\Domain;
use Propel\PropelBundle\Model\Acl\Entry as ModelEntry;
use Propel\Bundle\PropelBundle\Model\Acl\Entry as ModelEntry;
use Symfony\Component\Security\Acl\Model\AclInterface;
use Symfony\Component\Security\Acl\Model\ObjectIdentityInterface;
use Symfony\Component\Security\Acl\Model\PermissionGrantingStrategyInterface;
use Symfony\Component\Security\Acl\Model\SecurityIdentityInterface;
use Symfony\Component\Security\Acl\Model\AuditableAclInterface;
/**
@ -27,8 +23,8 @@ class AuditableAcl extends MutableAcl implements AuditableAclInterface
* Updates auditing for class-based ACE
*
* @param integer $index
* @param bool $auditSuccess
* @param bool $auditFailure
* @param bool $auditSuccess
* @param bool $auditFailure
*/
public function updateClassAuditing($index, $auditSuccess, $auditFailure)
{
@ -39,9 +35,9 @@ class AuditableAcl extends MutableAcl implements AuditableAclInterface
* Updates auditing for class-field-based ACE
*
* @param integer $index
* @param string $field
* @param bool $auditSuccess
* @param bool $auditFailure
* @param string $field
* @param bool $auditSuccess
* @param bool $auditFailure
*/
public function updateClassFieldAuditing($index, $field, $auditSuccess, $auditFailure)
{
@ -53,8 +49,8 @@ class AuditableAcl extends MutableAcl implements AuditableAclInterface
* Updates auditing for object-based ACE
*
* @param integer $index
* @param bool $auditSuccess
* @param bool $auditFailure
* @param bool $auditSuccess
* @param bool $auditFailure
*/
public function updateObjectAuditing($index, $auditSuccess, $auditFailure)
{
@ -65,9 +61,9 @@ class AuditableAcl extends MutableAcl implements AuditableAclInterface
* Updates auditing for object-field-based ACE
*
* @param integer $index
* @param string $field
* @param bool $auditSuccess
* @param bool $auditFailure
* @param string $field
* @param bool $auditSuccess
* @param bool $auditFailure
*/
public function updateObjectFieldAuditing($index, $field, $auditSuccess, $auditFailure)
{
@ -81,11 +77,11 @@ class AuditableAcl extends MutableAcl implements AuditableAclInterface
* @throws \InvalidArgumentException
*
* @param array $list
* @param int $index
* @param bool $auditSuccess
* @param bool $auditFailure
* @param int $index
* @param bool $auditSuccess
* @param bool $auditFailure
*
* @return \Propel\PropelBundle\Security\Acl\Domain\AuditableAcl $this
* @return \Propel\Bundle\PropelBundle\Security\Acl\Domain\AuditableAcl $this
*/
protected function updateAuditing(array &$list, $index, $auditSuccess, $auditFailure)
{
@ -104,4 +100,4 @@ class AuditableAcl extends MutableAcl implements AuditableAclInterface
return $this;
}
}
}

View file

@ -8,17 +8,17 @@
* @license MIT License
*/
namespace Propel\PropelBundle\Security\Acl\Domain;
namespace Propel\Bundle\PropelBundle\Security\Acl\Domain;
use Propel\PropelBundle\Model\Acl\Entry as ModelEntry;
use Propel\PropelBundle\Model\Acl\SecurityIdentity;
use Propel\Bundle\PropelBundle\Model\Acl\Entry as ModelEntry;
use Propel\Bundle\PropelBundle\Model\Acl\SecurityIdentity;
use Symfony\Component\Security\Acl\Model\AclInterface;
use Symfony\Component\Security\Acl\Model\AuditableEntryInterface;
use Symfony\Component\Security\Acl\Model\SecurityIdentityInterface;
/**
* An ACE implementation retrieving data from a given Propel\PropelBundle\Model\Acl\Entry.
* An ACE implementation retrieving data from a given Propel\Bundle\PropelBundle\Model\Acl\Entry.
*
* The entry is only used to grab a "snapshot" of its data as an EntryInterface is immutable!
*
@ -41,7 +41,7 @@ class Entry implements AuditableEntryInterface
/**
* Constructor.
*
* @param \Propel\PropelBundle\Model\Acl\Entry $entry
* @param \Propel\Bundle\PropelBundle\Model\Acl\Entry $entry
* @param \Symfony\Component\Security\Acl\Model\AclInterface $acl
*/
public function __construct(ModelEntry $entry, AclInterface $acl)
@ -189,4 +189,4 @@ class Entry implements AuditableEntryInterface
{
return $this->auditSuccess;
}
}
}

View file

@ -8,15 +8,15 @@
* @license MIT License
*/
namespace Propel\PropelBundle\Security\Acl\Domain;
namespace Propel\Bundle\PropelBundle\Security\Acl\Domain;
use Propel\PropelBundle\Model\Acl\Entry as ModelEntry;
use Propel\Bundle\PropelBundle\Model\Acl\Entry as ModelEntry;
use Symfony\Component\Security\Acl\Model\AclInterface;
use Symfony\Component\Security\Acl\Model\FieldEntryInterface;
/**
* An ACE implementation retrieving data from a given \Propel\PropelBundle\Model\Acl\Entry.
* An ACE implementation retrieving data from a given \Propel\Bundle\PropelBundle\Model\Acl\Entry.
*
* The entry is only used to grab a "snapshot" of its data as an \Symfony\Component\Security\Acl\Model\EntryInterface is immutable!
*
@ -31,7 +31,7 @@ class FieldEntry extends Entry implements FieldEntryInterface
/**
* Constructor.
*
* @param \Propel\PropelBundle\Model\Acl\Entry $entry
* @param \Propel\Bundle\PropelBundle\Model\Acl\Entry $entry
* @param \Symfony\Component\Security\Acl\Model\AclInterface $acl
*/
public function __construct(ModelEntry $entry, AclInterface $acl)
@ -98,4 +98,4 @@ class FieldEntry extends Entry implements FieldEntryInterface
return $this;
}
}
}

View file

@ -8,18 +8,20 @@
* @license MIT License
*/
namespace Propel\PropelBundle\Security\Acl\Domain;
namespace Propel\Bundle\PropelBundle\Security\Acl\Domain;
use Propel\PropelBundle\Model\Acl\Entry as ModelEntry;
use Propel\PropelBundle\Model\Acl\SecurityIdentity;
use Propel\PropelBundle\Model\Acl\ObjectIdentity;
use Propel\PropelBundle\Model\Acl\ObjectIdentityQuery;
use Propel\Runtime\Collection\ObjectCollection;
use Propel\Runtime\Connection\ConnectionInterface;
use Propel\Bundle\PropelBundle\Model\Acl\Entry as ModelEntry;
use Propel\Bundle\PropelBundle\Model\Acl\SecurityIdentity;
use Propel\Bundle\PropelBundle\Model\Acl\ObjectIdentity;
use Propel\Bundle\PropelBundle\Model\Acl\ObjectIdentityQuery;
use Symfony\Component\Security\Acl\Domain\PermissionGrantingStrategy;
use Symfony\Component\Security\Acl\Model\AclInterface;
use Symfony\Component\Security\Acl\Model\MutableAclInterface;
use Symfony\Component\Security\Acl\Model\EntryInterface;
use Symfony\Component\Security\Acl\Model\ObjectIdentityInterface;
use Symfony\Component\Security\Acl\Model\PermissionGrantingStrategyInterface;
use Symfony\Component\Security\Acl\Model\SecurityIdentityInterface;
@ -41,29 +43,29 @@ class MutableAcl extends Acl implements MutableAclInterface
/**
* A reference to the ObjectIdentity this ACL is mapped to.
*
* @var \Propel\PropelBundle\Model\Acl\ObjectIdentity
* @var \Propel\Bundle\PropelBundle\Model\Acl\ObjectIdentity
*/
protected $modelObjectIdentity;
/**
* A connection to be used for all changes on the ACL.
*
* @var \PropelPDO
* @var ConnectionInterface
*/
protected $con;
/**
* Constructor.
*
* @param \PropelObjectCollection $entries
* @param \Symfony\Component\Security\Acl\Model\ObjectIdentityInterface $objectIdentity
* @param ObjectCollection $entries
* @param \Symfony\Component\Security\Acl\Model\ObjectIdentityInterface $objectIdentity
* @param \Symfony\Component\Security\Acl\Model\PermissionGrantingStrategyInterface $permissionGrantingStrategy
* @param array $loadedSecurityIdentities
* @param \Symfony\Component\Security\Acl\Model\AclInterface $parentAcl
* @param bool $inherited
* @param \PropelPDO $con
* @param array $loadedSecurityIdentities
* @param \Symfony\Component\Security\Acl\Model\AclInterface $parentAcl
* @param bool $inherited
* @param ConnectionInterface $con
*/
public function __construct(\PropelObjectCollection $entries, ObjectIdentityInterface $objectIdentity, PermissionGrantingStrategyInterface $permissionGrantingStrategy, array $loadedSecurityIdentities = array(), AclInterface $parentAcl = null, $inherited = true, \PropelPDO $con = null)
public function __construct(ObjectCollection $entries, ObjectIdentityInterface $objectIdentity, PermissionGrantingStrategyInterface $permissionGrantingStrategy, array $loadedSecurityIdentities = array(), AclInterface $parentAcl = null, $inherited = true, ConnectionInterface $con = null)
{
parent::__construct($entries, $objectIdentity, $permissionGrantingStrategy, $loadedSecurityIdentities, $parentAcl, $inherited);
@ -125,7 +127,7 @@ class MutableAcl extends Acl implements MutableAclInterface
* Deletes a class-field-based ACE
*
* @param integer $index
* @param string $field
* @param string $field
*/
public function deleteClassFieldAce($index, $field)
{
@ -149,7 +151,7 @@ class MutableAcl extends Acl implements MutableAclInterface
* Deletes an object-field-based ACE
*
* @param integer $index
* @param string $field
* @param string $field
*/
public function deleteObjectFieldAce($index, $field)
{
@ -163,10 +165,10 @@ class MutableAcl extends Acl implements MutableAclInterface
* Inserts a class-based ACE
*
* @param \Symfony\Component\Security\Acl\Model\SecurityIdentityInterface $securityIdentity
* @param integer $mask
* @param integer $index
* @param bool $granting
* @param string $strategy
* @param integer $mask
* @param integer $index
* @param bool $granting
* @param string $strategy
*/
public function insertClassAce(SecurityIdentityInterface $securityIdentity, $mask, $index = 0, $granting = true, $strategy = null)
{
@ -176,12 +178,12 @@ class MutableAcl extends Acl implements MutableAclInterface
/**
* Inserts a class-field-based ACE
*
* @param string $field
* @param string $field
* @param \Symfony\Component\Security\Acl\Model\SecurityIdentityInterface $securityIdentity
* @param integer $mask
* @param integer $index
* @param boolean $granting
* @param string $strategy
* @param integer $mask
* @param integer $index
* @param boolean $granting
* @param string $strategy
*/
public function insertClassFieldAce($field, SecurityIdentityInterface $securityIdentity, $mask, $index = 0, $granting = true, $strategy = null)
{
@ -196,10 +198,10 @@ class MutableAcl extends Acl implements MutableAclInterface
* Inserts an object-based ACE
*
* @param \Symfony\Component\Security\Acl\Model\SecurityIdentityInterface $securityIdentity
* @param integer $mask
* @param integer $index
* @param boolean $granting
* @param string $strategy
* @param integer $mask
* @param integer $index
* @param boolean $granting
* @param string $strategy
*/
public function insertObjectAce(SecurityIdentityInterface $securityIdentity, $mask, $index = 0, $granting = true, $strategy = null)
{
@ -209,12 +211,12 @@ class MutableAcl extends Acl implements MutableAclInterface
/**
* Inserts an object-field-based ACE
*
* @param string $field
* @param string $field
* @param \Symfony\Component\Security\Acl\Model\SecurityIdentityInterface $securityIdentity
* @param integer $mask
* @param integer $index
* @param boolean $granting
* @param string $strategy
* @param integer $mask
* @param integer $index
* @param boolean $granting
* @param string $strategy
*/
public function insertObjectFieldAce($field, SecurityIdentityInterface $securityIdentity, $mask, $index = 0, $granting = true, $strategy = null)
{
@ -230,7 +232,7 @@ class MutableAcl extends Acl implements MutableAclInterface
*
* @param integer $index
* @param integer $mask
* @param string $strategy if null the strategy should not be changed
* @param string $strategy if null the strategy should not be changed
*/
public function updateClassAce($index, $mask, $strategy = null)
{
@ -241,9 +243,9 @@ class MutableAcl extends Acl implements MutableAclInterface
* Updates a class-field-based ACE
*
* @param integer $index
* @param string $field
* @param string $field
* @param integer $mask
* @param string $strategy if null the strategy should not be changed
* @param string $strategy if null the strategy should not be changed
*/
public function updateClassFieldAce($index, $field, $mask, $strategy = null)
{
@ -258,7 +260,7 @@ class MutableAcl extends Acl implements MutableAclInterface
*
* @param integer $index
* @param integer $mask
* @param string $strategy if null the strategy should not be changed
* @param string $strategy if null the strategy should not be changed
*/
public function updateObjectAce($index, $mask, $strategy = null)
{
@ -269,9 +271,9 @@ class MutableAcl extends Acl implements MutableAclInterface
* Updates an object-field-based ACE
*
* @param integer $index
* @param string $field
* @param string $field
* @param integer $mask
* @param string $strategy if null the strategy should not be changed
* @param string $strategy if null the strategy should not be changed
*/
public function updateObjectFieldAce($index, $field, $mask, $strategy = null)
{
@ -336,11 +338,11 @@ class MutableAcl extends Acl implements MutableAclInterface
/**
* Insert a given entry into the list on the given index by shifting all others.
*
* @param array $list
* @param int $index
* @param \Propel\PropelBundle\Model\Acl\Entry\Entry $entry
* @param array $list
* @param int $index
* @param \Propel\Bundle\PropelBundle\Model\Acl\Entry\Entry $entry
*
* @return \Propel\PropelBundle\Security\Acl\Domain\MutableAcl $this
* @return \Propel\Bundle\PropelBundle\Security\Acl\Domain\MutableAcl $this
*/
protected function insertToList(array &$list, $index, Entry $entry)
{
@ -362,13 +364,13 @@ class MutableAcl extends Acl implements MutableAclInterface
/**
* Update a single ACE of this ACL.
*
* @param array $list
* @param int $index
* @param int $mask
* @param array $list
* @param int $index
* @param int $mask
* @param string $strategy
* @param string $field
*
* @return \Propel\PropelBundle\Security\Acl\Domain\MutableAcl $this
* @return \Propel\Bundle\PropelBundle\Security\Acl\Domain\MutableAcl $this
*/
protected function updateAce(array &$list, $index, $mask, $strategy = null)
{
@ -395,7 +397,7 @@ class MutableAcl extends Acl implements MutableAclInterface
* @param array $list
* @param $index
*
* @return \Propel\PropelBundle\Security\Acl\Domain\MutableAcl $this
* @return \Propel\Bundle\PropelBundle\Security\Acl\Domain\MutableAcl $this
*/
protected function deleteIndex(array &$list, $index)
{
@ -412,9 +414,9 @@ class MutableAcl extends Acl implements MutableAclInterface
* @throws \OutOfBoundsException
*
* @param array $list
* @param int $index
* @param int $index
*
* @return \Propel\PropelBundle\Security\Acl\Domain\MutableAcl $this
* @return \Propel\Bundle\PropelBundle\Security\Acl\Domain\MutableAcl $this
*/
protected function isWithinBounds(array &$list, $index)
{
@ -434,7 +436,7 @@ class MutableAcl extends Acl implements MutableAclInterface
* @param array $list
* @param $index
*
* @return \Propel\PropelBundle\Security\Acl\Domain\MutableAcl $this
* @return \Propel\Bundle\PropelBundle\Security\Acl\Domain\MutableAcl $this
*/
protected function validateIndex(array &$list, $index)
{
@ -450,10 +452,10 @@ class MutableAcl extends Acl implements MutableAclInterface
*
* @throws \InvalidArgumentException
*
* @param array $list
* @param array $list
* @param string $field
*
* @return \Propel\PropelBundle\Security\Acl\Domain\MutableAcl $this
* @return \Propel\Bundle\PropelBundle\Security\Acl\Domain\MutableAcl $this
*/
protected function validateField(array &$list, $field)
{
@ -468,9 +470,9 @@ class MutableAcl extends Acl implements MutableAclInterface
* Order the given list to have numeric indexes from 0..x
*
* @param array $list
* @param int $index The right boundary to which the list is valid.
* @param int $index The right boundary to which the list is valid.
*
* @return \Propel\PropelBundle\Security\Acl\Domain\MutableAcl $this
* @return \Propel\Bundle\PropelBundle\Security\Acl\Domain\MutableAcl $this
*/
protected function reorderList(array &$list, $index)
{
@ -485,14 +487,14 @@ class MutableAcl extends Acl implements MutableAclInterface
/**
* Create a new ACL Entry.
*
* @param int $mask
* @param int $index
* @param int $mask
* @param int $index
* @param \Symfony\Component\Security\Acl\Model\SecurityIdentityInterface $securityIdentity
* @param string $strategy
* @param bool $granting
* @param string $field
* @param string $strategy
* @param bool $granting
* @param string $field
*
* @return \Propel\PropelBundle\Security\Acl\Domain\Entry|\Propel\PropelBundle\Security\Acl\Domain\FieldEntry
* @return \Propel\Bundle\PropelBundle\Security\Acl\Domain\Entry|\Propel\Bundle\PropelBundle\Security\Acl\Domain\FieldEntry
*/
protected function createAce($mask, $index, SecurityIdentityInterface $securityIdentity, $strategy = null, $granting = true, $field = null)
{

View file

@ -8,24 +8,28 @@
* @license MIT License
*/
namespace Propel\PropelBundle\Security\Acl;
namespace Propel\Bundle\PropelBundle\Security\Acl;
use Propel\PropelBundle\Model\Acl\Entry as ModelEntry;
use Propel\PropelBundle\Model\Acl\EntryPeer;
use Propel\PropelBundle\Model\Acl\EntryQuery;
use Propel\PropelBundle\Model\Acl\SecurityIdentity;
use Propel\PropelBundle\Model\Acl\ObjectIdentity;
use Propel\PropelBundle\Model\Acl\ObjectIdentityQuery;
use Propel\Runtime\ActiveQuery\Criteria;
use Propel\Runtime\Collection\ObjectCollection;
use Propel\Runtime\Connection\ConnectionInterface;
use Propel\Runtime\Propel;
use Propel\Runtime\ServiceContainer\ServiceContainerInterface;
use Propel\PropelBundle\Security\Acl\Domain\Acl;
use Propel\PropelBundle\Security\Acl\Domain\MutableAcl;
use Propel\PropelBundle\Security\Acl\Domain\Entry;
use Propel\Bundle\PropelBundle\Model\Acl\Entry as ModelEntry;
use Propel\Bundle\PropelBundle\Model\Acl\Map\EntryTableMap;
use Propel\Bundle\PropelBundle\Model\Acl\EntryQuery;
use Propel\Bundle\PropelBundle\Model\Acl\SecurityIdentity;
use Propel\Bundle\PropelBundle\Model\Acl\ObjectIdentity;
use Propel\Bundle\PropelBundle\Model\Acl\ObjectIdentityQuery;
use Propel\Bundle\PropelBundle\Security\Acl\Domain\Acl;
use Propel\Bundle\PropelBundle\Security\Acl\Domain\MutableAcl;
use Propel\Bundle\PropelBundle\Security\Acl\Domain\Entry;
use Symfony\Component\Security\Acl\Exception\AclAlreadyExistsException;
use Symfony\Component\Security\Acl\Exception\Exception as AclException;
use Symfony\Component\Security\Acl\Domain\FieldEntry;
use Symfony\Component\Security\Acl\Model\AclInterface;
use Symfony\Component\Security\Acl\Model\EntryInterface;
use Symfony\Component\Security\Acl\Model\FieldEntryInterface;
@ -47,14 +51,14 @@ class MutableAclProvider extends AclProvider implements MutableAclProviderInterf
* Constructor.
*
* @param \Symfony\Component\Security\Acl\Model\PermissionGrantingStrategyInterface $permissionGrantingStrategy
* @param \PropelPDO $connection
* @param \Symfony\Component\Security\Acl\Model\AclCacheInterface $cache
* @param ConnectionInterface $connection
* @param \Symfony\Component\Security\Acl\Model\AclCacheInterface $cache
*/
public function __construct(PermissionGrantingStrategyInterface $permissionGrantingStrategy, \PropelPDO $connection = null, AclCacheInterface $cache = null)
public function __construct(PermissionGrantingStrategyInterface $permissionGrantingStrategy, ConnectionInterface $connection = null, AclCacheInterface $cache = null)
{
// @codeCoverageIgnoreStart
if (null === $connection) {
$connection = \Propel::getConnection(EntryPeer::DATABASE_NAME, \Propel::CONNECTION_WRITE);
$connection = Propel::getConnection(EntryTableMap::DATABASE_NAME, ServiceContainerInterface::CONNECTION_WRITE);
}
// @codeCoverageIgnoreEnd
@ -68,7 +72,7 @@ class MutableAclProvider extends AclProvider implements MutableAclProviderInterf
*
* @param \Symfony\Component\Security\Acl\Model\ObjectIdentityInterface $objectIdentity
*
* @return \Propel\PropelBundle\Security\Acl\Domain\MutableAcl
* @return \Propel\Bundle\PropelBundle\Security\Acl\Domain\MutableAcl
*/
public function createAcl(ObjectIdentityInterface $objectIdentity)
{
@ -165,7 +169,7 @@ class MutableAclProvider extends AclProvider implements MutableAclProviderInterf
public function updateAcl(MutableAclInterface $acl)
{
if (!$acl instanceof MutableAcl) {
throw new \InvalidArgumentException('The given ACL is not tracked by this provider. Please provide \Propel\PropelBundle\Security\Acl\Domain\MutableAcl only.');
throw new \InvalidArgumentException('The given ACL is not tracked by this provider. Please provide \Propel\Bundle\PropelBundle\Security\Acl\Domain\MutableAcl only.');
}
try {
@ -186,7 +190,7 @@ class MutableAclProvider extends AclProvider implements MutableAclProviderInterf
);
}
foreach ($modelEntries as &$eachEntry) {
foreach ($modelEntries as $eachEntry) {
if (!in_array($eachEntry->getId(), $keepEntries)) {
$eachEntry->delete($this->connection);
}
@ -225,9 +229,9 @@ class MutableAclProvider extends AclProvider implements MutableAclProviderInterf
/**
* Persist the given ACEs.
*
* @param array $accessControlEntries
* @param \Propel\PropelBundle\Model\Acl\ObjectIdentity $objectIdentity
* @param bool $object
* @param array $accessControlEntries
* @param \Propel\Bundle\PropelBundle\Model\Acl\ObjectIdentity $objectIdentity
* @param bool $object
*
* @return array The IDs of the persisted ACEs.
*/
@ -282,7 +286,7 @@ class MutableAclProvider extends AclProvider implements MutableAclProviderInterf
*
* @param \Symfony\Component\Security\Acl\Model\EntryInterface $ace
*
* @return \Propel\PropelBundle\Model\Acl\Entry|null
* @return \Propel\Bundle\PropelBundle\Model\Acl\Entry|null
*/
protected function getPersistedAce(EntryInterface $ace, ObjectIdentity $objectIdentity, $object = false)
{
@ -306,13 +310,13 @@ class MutableAclProvider extends AclProvider implements MutableAclProviderInterf
if (true === $object) {
$ukQuery->filterByObjectIdentity($objectIdentity);
} else {
$ukQuery->filterByObjectIdentityId(null, \Criteria::ISNULL);
$ukQuery->filterByObjectIdentityId(null, Criteria::ISNULL);
}
if ($ace instanceof FieldEntryInterface) {
$ukQuery->filterByFieldName($ace->getField());
} else {
$ukQuery->filterByFieldName(null, \Criteria::ISNULL);
$ukQuery->filterByFieldName(null, Criteria::ISNULL);
}
return $ukQuery->findOne($this->connection);
@ -321,16 +325,16 @@ class MutableAclProvider extends AclProvider implements MutableAclProviderInterf
/**
* Get an ACL for this provider.
*
* @param \PropelObjectCollection $collection
* @param ObjectCollection $collection
* @param \Symfony\Component\Security\Acl\Model\ObjectIdentityInterface $objectIdentity
* @param array $loadedSecurityIdentities
* @param \Symfony\Component\Security\Acl\Model\AclInterface $parentAcl
* @param bool $inherited
* @param array $loadedSecurityIdentities
* @param \Symfony\Component\Security\Acl\Model\AclInterface $parentAcl
* @param bool $inherited
*
* @return \Propel\PropelBundle\Security\Acl\Domain\MutableAcl
* @return \Propel\Bundle\PropelBundle\Security\Acl\Domain\MutableAcl
*/
protected function getAcl(\PropelObjectCollection $collection, ObjectIdentityInterface $objectIdentity, array $loadedSecurityIdentities = array(), AclInterface $parentAcl = null, $inherited = true)
protected function getAcl(ObjectCollection $collection, ObjectIdentityInterface $objectIdentity, array $loadedSecurityIdentities = array(), AclInterface $parentAcl = null, $inherited = true)
{
return new MutableAcl($collection, $objectIdentity, $this->permissionGrantingStrategy, $loadedSecurityIdentities, $parentAcl, $inherited, $this->connection);
}
}
}

View file

@ -0,0 +1,103 @@
<?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 Propel\Bundle\PropelBundle\Security\User;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
/**
* Provides easy to use provisioning for Propel model users.
*
* @author William DURAND <william.durand1@gmail.com>
*/
class PropelUserProvider implements UserProviderInterface
{
/**
* A Model class name.
*
* @var string
*/
protected $class;
/**
* A Query class name.
*
* @var string
*/
protected $queryClass;
/**
* A property to use to retrieve the user.
*
* @var string
*/
protected $property;
/**
* Default constructor
*
* @param string $class The User model class.
* @param string|null $property The property to use to retrieve a user.
*/
public function __construct($class, $property = null)
{
$this->class = $class;
$this->queryClass = $class.'Query';
$this->property = $property;
}
/**
* {@inheritdoc}
*/
public function loadUserByUsername($username)
{
$queryClass = $this->queryClass;
$query = $queryClass::create();
if (null !== $this->property) {
$filter = 'filterBy'.ucfirst($this->property);
$query->$filter($username);
} else {
$query->filterByUsername($username);
}
if (null === $user = $query->findOne()) {
throw new UsernameNotFoundException(sprintf('User "%s" not found.', $username));
}
return $user;
}
/**
* {@inheritdoc}
*/
public function refreshUser(UserInterface $user)
{
if (!$user instanceof $this->class) {
throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user)));
}
$queryClass = $this->queryClass;
return $queryClass::create()->findPk($user->getPrimaryKey());
}
/**
* {@inheritdoc}
*/
public function supportsClass($class)
{
return $class === $this->class;
}
}

90
Service/SchemaLocator.php Normal file
View file

@ -0,0 +1,90 @@
<?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\Bundle\PropelBundle\Service;
use Symfony\Component\Config\FileLocatorInterface;
use Symfony\Component\Finder\Finder;
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
class SchemaLocator
{
protected $fileLocator;
protected $configuration;
public function __construct(FileLocatorInterface $fileLocator, array $configuration)
{
$this->fileLocator = $fileLocator;
$this->configuration = $configuration;
}
public function locateFromBundlesAndConfiguration(array $bundles)
{
$schemas = $this->locateFromBundles($bundles);
$path = $this->configuration['paths']['schemaDir'].'/schema.xml';
if (file_exists($path)) {
$schema = new \SplFileInfo($path);
$schemas[(string) $schema] = array(null, $schema);
}
return $schemas;
}
public function locateFromBundles(array $bundles)
{
$schemas = array();
foreach ($bundles as $bundle) {
$schemas = array_merge($schemas, $this->locateFromBundle($bundle));
}
return $schemas;
}
/**
* @param \Symfony\Component\HttpKernel\Bundle\BundleInterface
*/
public function locateFromBundle(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->fileLocator->locate($logicalName));
$finalSchemas[(string) $finalSchema] = array($bundle, $finalSchema);
}
}
}
return $finalSchemas;
}
/**
* @param \SplFileInfo $schema
* @param BundleInterface $bundle
* @return string
*/
protected function transformToLogicalName(\SplFileInfo $schema, BundleInterface $bundle)
{
$schemaPath = str_replace(
$bundle->getPath(). DIRECTORY_SEPARATOR . 'Resources' . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR,
'',
$schema->getRealPath()
);
return sprintf('%s/Resources/config/%s', $bundle->getName(), $schemaPath);
}
}

View file

@ -8,12 +8,14 @@
* @license MIT License
*/
namespace Propel\PropelBundle\Tests;
namespace Propel\Bundle\PropelBundle\Tests;
use Propel\PropelBundle\Model\Acl\AclClass;
use Propel\PropelBundle\Model\Acl\Entry;
use Propel\PropelBundle\Model\Acl\ObjectIdentity as ModelObjectIdentity;
use Propel\PropelBundle\Security\Acl\MutableAclProvider;
use Propel\Generator\Util\QuickBuilder;
use Propel\Bundle\PropelBundle\Model\Acl\AclClass;
use Propel\Bundle\PropelBundle\Model\Acl\Entry;
use Propel\Bundle\PropelBundle\Model\Acl\ObjectIdentity as ModelObjectIdentity;
use Propel\Bundle\PropelBundle\Security\Acl\MutableAclProvider;
use Symfony\Component\Security\Core\Role\Role;
use Symfony\Component\Security\Acl\Domain\ObjectIdentity;
@ -34,23 +36,22 @@ class AclTestCase extends TestCase
{
parent::setUp();
$this->loadPropelQuickBuilder();
$schema = file_get_contents(__DIR__.'/../Resources/acl_schema.xml');
$builder = new \PropelQuickBuilder();
$builder->setSchema($schema);
if (!class_exists('Propel\PropelBundle\Model\Acl\map\AclClassTableMap')) {
$builder->setClassTargets(array('tablemap', 'peer', 'object', 'query'));
if (!class_exists('Propel\Bundle\PropelBundle\Model\Acl\Map\AclClassTableMap')) {
$classTargets = array('tablemap', 'object', 'query');
} else {
$builder->setClassTargets(array());
$classTargets = array();
}
$this->con = $builder->build();
$builder = new QuickBuilder();
$builder->setSchema($schema);
$this->con = $builder->build($dsn = null, $user = null, $pass = null, $adapter = null, $classTargets);
}
/**
* @return \Propel\PropelBundle\Model\Acl\ObjectIdentity
* @return \Propel\Bundle\PropelBundle\Model\Acl\ObjectIdentity
*/
protected function createModelObjectIdentity($identifier)
{
@ -93,7 +94,7 @@ class AclTestCase extends TestCase
protected function getAclObjectIdentity($identifier = 1)
{
return new ObjectIdentity($identifier, 'Propel\PropelBundle\Tests\Fixtures\Model\Book');
return new ObjectIdentity($identifier, 'Propel\Bundle\PropelBundle\Tests\Fixtures\Model\Book');
}
protected function getRoleSecurityIdentity($role = 'ROLE_USER')

View file

@ -1,269 +0,0 @@
<?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\Tests\Command;
use Propel\PropelBundle\Tests\TestCase;
use Propel\PropelBundle\Command\AbstractCommand;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
use Symfony\Component\HttpKernel\KernelInterface;
/**
* @author William Durand <william.durand1@gmail.com>
*/
class AbstractCommandTest extends TestCase
{
protected $command;
public function setUp()
{
$this->command = new TestableAbstractCommand('testable-command');
}
public function testParseDbName()
{
$dsn = 'mydsn#dbname=foo';
$this->assertEquals('foo', $this->command->parseDbName($dsn));
}
public function testParseDbNameWithoutDbName()
{
$this->assertNull($this->command->parseDbName('foo'));
}
public function testTransformToLogicalName()
{
$bundle = $this->getMock('Symfony\Component\HttpKernel\Bundle\BundleInterface');
$bundle
->expects($this->once())
->method('getName')
->will($this->returnValue('MySuperBundle'));
$bundle
->expects($this->once())
->method('getPath')
->will($this->returnValue('/Users/foo/project/src/My/SuperBundle'));
$schema = $this
->getMockBuilder('\SplFileInfo')
->disableOriginalConstructor()
->getMock();
$schema
->expects($this->once())
->method('getRealPath')
->will($this->returnValue('/Users/foo/project/src/My/SuperBundle/Resources/config/my-schema.xml'));
$expected = '@MySuperBundle/Resources/config/my-schema.xml';
$this->assertEquals($expected, $this->command->transformToLogicalName($schema, $bundle));
}
public function testTransformToLogicalNameWithSubDir()
{
$bundle = $this->getMock('Symfony\Component\HttpKernel\Bundle\BundleInterface');
$bundle
->expects($this->once())
->method('getName')
->will($this->returnValue('MySuperBundle'));
$bundle
->expects($this->once())
->method('getPath')
->will($this->returnValue('/Users/foo/project/src/My/SuperBundle'));
$schema = $this
->getMockBuilder('\SplFileInfo')
->disableOriginalConstructor()
->getMock();
$schema
->expects($this->once())
->method('getRealPath')
->will($this->returnValue('/Users/foo/project/src/My/SuperBundle/Resources/config/propel/my-schema.xml'));
$expected = '@MySuperBundle/Resources/config/propel/my-schema.xml';
$this->assertEquals($expected, $this->command->transformToLogicalName($schema, $bundle));
}
public function testGetSchemasFromBundle()
{
$bundle = $this->getMock('Symfony\Component\HttpKernel\Bundle\BundleInterface');
$bundle
->expects($this->once())
->method('getName')
->will($this->returnValue('MySuperBundle'));
$bundle
->expects($this->exactly(2))
->method('getPath')
->will($this->returnValue(__DIR__ . '/../Fixtures/src/My/SuperBundle'));
$aSchema = realpath(__DIR__ . '/../Fixtures/src/My/SuperBundle/Resources/config/a-schema.xml');
// hack to by pass the file locator
$this->command->setLocateResponse($aSchema);
$schemas = $this->command->getSchemasFromBundle($bundle);
$this->assertNotNull($schemas);
$this->assertTrue(is_array($schemas));
$this->assertCount(1, $schemas);
$this->assertArrayHasKey($aSchema, $schemas);
$this->assertSame($bundle, $schemas[$aSchema][0]);
$this->assertEquals(new \SplFileInfo($aSchema), $schemas[$aSchema][1]);
}
public function testGetSchemasFromBundleWithNoSchema()
{
$bundle = $this->getMock('Symfony\Component\HttpKernel\Bundle\BundleInterface');
$bundle
->expects($this->once())
->method('getPath')
->will($this->returnValue(__DIR__ . '/../Fixtures/src/My/SecondBundle'));
$schemas = $this->command->getSchemasFromBundle($bundle);
$this->assertNotNull($schemas);
$this->assertTrue(is_array($schemas));
$this->assertCount(0, $schemas);
}
public function testGetFinalSchemasWithNoSchemaInBundles()
{
$bundle = $this->getMock('Symfony\Component\HttpKernel\Bundle\BundleInterface');
$kernel = $this->getMock('Symfony\Component\HttpKernel\KernelInterface');
$bundle
->expects($this->once())
->method('getPath')
->will($this->returnValue(__DIR__ . '/../Fixtures/src/My/SecondBundle'));
$kernel
->expects($this->once())
->method('getBundles')
->will($this->returnValue(array($bundle)));
$schemas = $this->command->getFinalSchemas($kernel);
$this->assertNotNull($schemas);
$this->assertTrue(is_array($schemas));
$this->assertCount(0, $schemas);
}
public function testGetFinalSchemas()
{
$bundle = $this->getMock('Symfony\Component\HttpKernel\Bundle\BundleInterface');
$kernel = $this->getMock('Symfony\Component\HttpKernel\KernelInterface');
$bundle
->expects($this->once())
->method('getName')
->will($this->returnValue('MySuperBundle'));
$bundle
->expects($this->exactly(2))
->method('getPath')
->will($this->returnValue(__DIR__ . '/../Fixtures/src/My/SuperBundle'));
$aSchema = realpath(__DIR__ . '/../Fixtures/src/My/SuperBundle/Resources/config/a-schema.xml');
// hack to by pass the file locator
$this->command->setLocateResponse($aSchema);
$kernel
->expects($this->once())
->method('getBundles')
->will($this->returnValue(array($bundle)));
$schemas = $this->command->getFinalSchemas($kernel);
$this->assertNotNull($schemas);
$this->assertTrue(is_array($schemas));
$this->assertCount(1, $schemas);
$this->assertArrayHasKey($aSchema, $schemas);
$this->assertSame($bundle, $schemas[$aSchema][0]);
$this->assertEquals(new \SplFileInfo($aSchema), $schemas[$aSchema][1]);
}
public function testGetFinalSchemasWithGivenBundle()
{
$bundle = $this->getMock('Symfony\Component\HttpKernel\Bundle\BundleInterface');
$kernel = $this->getMock('Symfony\Component\HttpKernel\KernelInterface');
$bundle
->expects($this->once())
->method('getName')
->will($this->returnValue('MySuperBundle'));
$bundle
->expects($this->exactly(2))
->method('getPath')
->will($this->returnValue(__DIR__ . '/../Fixtures/src/My/SuperBundle'));
$aSchema = realpath(__DIR__ . '/../Fixtures/src/My/SuperBundle/Resources/config/a-schema.xml');
// hack to by pass the file locator
$this->command->setLocateResponse($aSchema);
$kernel
->expects($this->never())
->method('getBundles');
$schemas = $this->command->getFinalSchemas($kernel, $bundle);
$this->assertNotNull($schemas);
$this->assertTrue(is_array($schemas));
$this->assertCount(1, $schemas);
$this->assertArrayHasKey($aSchema, $schemas);
$this->assertSame($bundle, $schemas[$aSchema][0]);
$this->assertEquals(new \SplFileInfo($aSchema), $schemas[$aSchema][1]);
}
}
class TestableAbstractCommand extends AbstractCommand
{
private $locate;
public function setLocateResponse($locate)
{
$this->locate = $locate;
}
public function getContainer()
{
return $this;
}
public function get($service)
{
return $this;
}
public function locate($file)
{
return $this->locate;
}
public function parseDbName($dsn)
{
return parent::parseDbName($dsn);
}
public function transformToLogicalName(\SplFileInfo $schema, BundleInterface $bundle)
{
return parent::transformToLogicalName($schema, $bundle);
}
public function getSchemasFromBundle(BundleInterface $bundle)
{
return parent::getSchemasFromBundle($bundle);
}
public function getFinalSchemas(KernelInterface $kernel, BundleInterface $bundle = null)
{
return parent::getFinalSchemas($kernel, $bundle);
}
}

View file

@ -1,77 +0,0 @@
<?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\Tests\Command;
use Symfony\Component\Filesystem\Filesystem;
use Propel\PropelBundle\Tests\TestCase;
use Propel\PropelBundle\Command\FixturesLoadCommand;
/**
* @author Kévin Gomez <contact@kevingomez.fr>
*/
class FixturesLoadCommandTest extends TestCase
{
protected $command;
public function setUp()
{
$this->command = new TestableFixturesLoadCommand('testable-command');
// let's create some dummy fixture files
$this->fixtures_dir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'propel';
$this->fixtures_files = array(
'10_foo.yml', '20_bar.yml', '15_biz.yml', '18_boo.sql', '42_baz.sql'
);
$this->filesystem = new Filesystem();
$this->filesystem->mkdir($this->fixtures_dir);
foreach ($this->fixtures_files as $file)
{
$this->filesystem->touch($this->fixtures_dir . DIRECTORY_SEPARATOR . $file);
}
}
public function tearDown()
{
$this->filesystem->remove($this->fixtures_dir);
}
public function testOrderedFixturesFiles()
{
$this->assertEquals(
array('10_foo.yml', '15_biz.yml', '20_bar.yml',),
$this->cleanFixtureIterator($this->command->getFixtureFiles('yml', $this->fixtures_dir))
);
$this->assertEquals(
array('18_boo.sql', '42_baz.sql',),
$this->cleanFixtureIterator($this->command->getFixtureFiles('sql', $this->fixtures_dir))
);
}
protected function cleanFixtureIterator($file_iterator)
{
$tmp_dir = $this->fixtures_dir;
return array_map(function($file) use($tmp_dir) {
return str_replace($tmp_dir . DIRECTORY_SEPARATOR, '', $file);
}, array_keys(iterator_to_array($file_iterator)));
}
}
class TestableFixturesLoadCommand extends FixturesLoadCommand
{
public function getFixtureFiles($type = 'sql', $in = null)
{
return parent::getFixtureFiles($type, $in);
}
}

View file

@ -1,73 +0,0 @@
<?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\Tests\Command;
use Propel\PropelBundle\Command\GeneratorAwareCommand;
use Propel\PropelBundle\Tests\TestCase;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* @author William Durand <william.durand1@gmail.com>
*/
class GeneratorAwareCommandTest extends TestCase
{
protected $container;
protected function setUp()
{
parent::setUp();
$this->container = $this->getContainer();
$this->container->setParameter('propel.path', __DIR__ . '/../../vendor/propel/propel1');
}
public function testGetDatabasesFromSchema()
{
$command = new GeneratorAwareCommandTestable('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 GeneratorAwareCommandTestable extends GeneratorAwareCommand
{
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);
}
}

Some files were not shown because too many files have changed in this diff Show more