Compare commits

...

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

169 changed files with 8036 additions and 7093 deletions

14
.gitignore vendored
View file

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

View file

@ -1,22 +1,33 @@
sudo: false
language: php
php:
- 5.3
- 5.4
- 5.5
- 7.0
- 7.1
- 7.2
cache:
directories:
- $HOME/.composer/cache/files
env:
- SYMFONY_VERSION=2.2.*
- SYMFONY_VERSION=2.3.*
- SYMFONY_VERSION=dev-master
- SYMFONY_VERSION="^3.0"
- SYMFONY_VERSION="^4.0"
matrix:
allow_failures:
- php: 5.5
fast_finish: true
before_script:
- curl -s http://getcomposer.org/installer | php
- php composer.phar require symfony/symfony:${SYMFONY_VERSION} --no-update
- php composer.phar --dev install
exclude:
- php: 7.0
env: SYMFONY_VERSION="^4.0"
script: phpunit --coverage-text
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,101 +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;
/**
* @var Boolean
*/
private $alreadyWroteConnection = false;
/**
*
* @var InputInterface
*/
protected $input;
/**
* Return the package prefix for a given bundle.
*
* @param Bundle $bundle
* @param string $baseDirectory The base directory to exclude from prefix.
*
* @return string
* @var OutputInterface
*/
protected function getPackagePrefix(Bundle $bundle, $baseDirectory = '')
{
$parts = explode(DIRECTORY_SEPARATOR, realpath($bundle->getPath()));
$length = count(explode('\\', $bundle->getNamespace())) * (-1);
protected $output;
$prefix = implode(DIRECTORY_SEPARATOR, array_slice($parts, 0, $length));
$prefix = ltrim(str_replace($baseDirectory, '', $prefix), DIRECTORY_SEPARATOR);
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);
if ($input->getOption('verbose')) {
$this->additionalPhingArgs[] = 'verbose';
}
$kernel = $this->getApplication()->getKernel();
$this->input = $input;
$this->checkConfiguration();
$this->output = $output;
$this->cacheDir = $kernel->getCacheDir().'/propel';
if ($input->hasArgument('bundle') && '@' === substr($input->getArgument('bundle'), 0, 1)) {
$this->bundle = $this
@ -113,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 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
@ -231,27 +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 && $this->input->hasOption('connection') && $this->input->getOption('connection')
&& $database['name'] != $this->input->getOption('connection')) {
//we skip this schema because the connection name doesn't match the input value
unset($this->tempSchemas[$tempSchema]);
$filesystem->remove($file);
continue;
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'];
}
@ -264,167 +168,204 @@ 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;
} elseif ("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.
*/
protected function getCacheDir()
@ -433,47 +374,29 @@ 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));
}
if (false === $this->alreadyWroteConnection) {
$output->writeln(sprintf('Use connection named <comment>%s</comment> in <comment>%s</comment> environment.',
$name, $this->getApplication()->getKernel()->getEnvironment())
);
$this->alreadyWroteConnection = true;
$result = '';
while ($to && false === strpos($from, $to)) {
$result .= '../';
$to = substr($to, 0, strrpos($to, '/'));
}
// prevent errors
if (!isset($defaultConfig['connection']['password'])) {
$defaultConfig['connection']['password'] = null;
}
return array($name, $defaultConfig);
return !$to /*we reached root*/ ? $result . substr($from, 1) : $result. substr($from, strlen($to) + 1);
}
/**
@ -484,10 +407,10 @@ EOT;
*/
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
@ -495,158 +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,
'propel.useDateTimeClass' => true,
'propel.dateTimeClass' => 'DateTime',
'propel.defaultTimeFormat' => '',
'propel.defaultDateFormat' => '',
'propel.addClassLevelComment' => false,
'propel.defaultTimeStampFormat' => '',
'propel.builder.pluralizer.class' => 'builder.util.StandardEnglishPluralizer',
), $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'];
}
}

View file

@ -8,27 +8,26 @@
* @license MIT License
*/
namespace Propel\PropelBundle\Command;
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\Filesystem\Filesystem;
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
use Symfony\Component\HttpKernel\KernelInterface;
/**
* @author Toni Uebernickel <tuebernickel@gmail.com>
*/
class AclInitCommand extends SqlInsertCommand
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_OPTIONAL, 'Set this parameter to define a connection to use')
->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.
@ -45,51 +44,93 @@ EOT
protected function execute(InputInterface $input, OutputInterface $output)
{
$outputDir = realpath($this->getApplication()->getKernel()->getRootDir().'/../');
// Generate ACL model
if (true == $result = $this->callPhing('om')) {
$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, 'om');
$this->writeTaskError($output, 'model:build');
return 1;
}
// Prepare SQL directory
$sqlDirectory = $this->getSqlDir();
$filesystem = new Filesystem();
$filesystem->remove($sqlDirectory);
$filesystem->mkdir($sqlDirectory);
// Prepare SQL
$sqlBuildCmd = new \Propel\Generator\Command\SqlBuildCommand();
$sqlBuildArgs = array(
'--connection' => $this->getConnections($input->getOption('connection')),
'--output-dir' => $this->getCacheDir(),
);
if (true == $result = $this->callPhing('build-sql', array('propel.sql.dir' => $sqlDirectory))) {
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, 'build-sql');
$this->writeTaskError($output, 'sql:build');
return 2;
}
return parent::execute($input, $output);
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((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()
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,38 +8,35 @@
* @license MIT License
*/
namespace Propel\PropelBundle\Command;
use Propel\PropelBundle\Command\AbstractCommand;
namespace Propel\Bundle\PropelBundle\Command;
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');
;
}
/**
@ -51,33 +48,33 @@ class BuildCommand extends AbstractCommand
{
if (!$input->getOption('sql')) {
$in = new ArrayInput(array(
'command' => 'propel:model:build',
'--connection' => $input->getOption('connection'),
'--verbose' => $input->getOption('verbose'),
'command' => 'propel:model:build',
'--connection' => $input->getOption('connection'),
'--verbose' => $input->getOption('verbose')
));
$modelCommand = $this->getApplication()->find('propel:model:build');
$res = $modelCommand->run($in, $output);
$cmd = $this->getApplication()->find('propel:model:build');
$cmd->run($in, $output);
}
if (!$input->getOption('classes')) {
$in = new ArrayInput(array(
'command' => 'propel:build:sql',
'--connection' => $input->getOption('connection'),
'--verbose' => $input->getOption('verbose'),
'command' => 'propel:build:sql',
'--connection' => $input->getOption('connection'),
'--verbose' => $input->getOption('verbose'),
));
$sqlCommand = $this->getApplication()->find('propel:sql:build');
$sqlCommand->run($in, $output);
$cmd = $this->getApplication()->find('propel:sql:build');
$cmd->run($in, $output);
}
if ($input->getOption('insert-sql')) {
$in = new ArrayInput(array(
'command' => 'propel:sql:insert',
'--connection' => $input->getOption('connection'),
'--verbose' => $input->getOption('verbose'),
'--force' => true,
'command' => 'propel:sql:insert',
'--connection' => $input->getOption('connection'),
'--force' => true,
'--verbose' => $input->getOption('verbose'),
));
$insertCommand = $this->getApplication()->find('propel:sql:insert');
$insertCommand->run($in, $output);
$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,8 +44,9 @@ class DatabaseCreateCommand extends AbstractCommand
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
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>');
@ -50,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 caught',
'',
$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));
}
/**
@ -73,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.';#',
$config['dsn'] = preg_replace(
'#;?(dbname|Database)='.$dbName.'#',
'',
$config['connection']['dsn']
$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,51 +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)
{
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 caught',
'',
$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,10 +8,8 @@
* @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;
@ -28,7 +26,7 @@ class FixturesDumpCommand extends AbstractCommand
* Default fixtures directory.
* @var string
*/
private $defaultFixturesDir = 'app/propel/fixtures';
protected $defaultFixturesDir = 'app/propel/fixtures';
/**
* @see Command
@ -36,9 +34,8 @@ 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')
->addOption('dir', null, InputOption::VALUE_OPTIONAL, 'Set this parameter to define a fixture directory')
->setHelp(<<<EOT
The <info>propel:fixtures:dump</info> dumps data from database into YAML fixtures file.
@ -49,46 +46,35 @@ 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)
{
list($name, $defaultConfig) = $this->getConnection($input, $output);
$fixtureDir = $input->getOption('dir') ? $input->getOption('dir') : $this->defaultFixturesDir;
$fixtureDir = $input->getOption('dir') ?: $this->defaultFixturesDir;
$path = realpath($this->getApplication()->getKernel()->getRootDir() . '/../') . '/' . $fixtureDir;
if (!file_exists($path)) {
$output->writeln("<info>The $path folder does not exists.</info>");
if ($this->askConfirmation($output, "<question>Do you want me to create it for you ?</question> [Yes]")) {
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));
}
}
$filename = $path . '/fixtures_' . time() . '.yml';
$dumper = $this->getContainer()->get('propel.dumper.yaml');
$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;
}
$dumper->dump($filename, $input->getOption('connection'));
$this->writeNewFile($output, $filename);

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,14 +95,22 @@ 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)
{
@ -132,7 +128,7 @@ EOT
), '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)) {
@ -156,9 +152,9 @@ EOT
/**
* Load fixtures
*
* @param \Symfony\Component\Console\Input\InputInterface $input
* @param \Symfony\Component\Console\Output\OutputInterface $output
* @return void
* @param \Symfony\Component\Console\Input\InputInterface $input
* @param \Symfony\Component\Console\Output\OutputInterface $output
* @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)
{
@ -172,26 +168,17 @@ 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(), $this->getContainer());
$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');
return false;
}
$nb = $loader->load($datas, $connectionName);
$output->writeln(sprintf('<comment>%s</comment> %s fixtures file%s loaded.', $nb, strtoupper($type), $nb > 1 ? 's' : ''));
@ -201,36 +188,35 @@ EOT
/**
* Load SQL fixtures
*
* @param \Symfony\Component\Console\Input\InputInterface $input
* @param \Symfony\Component\Console\Output\OutputInterface $output
* @return void
* @param \Symfony\Component\Console\Input\InputInterface $input
* @param \Symfony\Component\Console\Output\OutputInterface $output
*/
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) {
$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;
}
@ -249,36 +235,36 @@ EOT
// 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) {
// 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;
}
/**
@ -286,14 +272,16 @@ EOT
*
* @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.
* 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);
@ -314,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

@ -8,7 +8,13 @@
* @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\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
@ -20,50 +26,73 @@ 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);
@ -82,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);
}
@ -94,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

@ -8,49 +8,48 @@
* @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\GraphvizGenerateCommand as BaseGraphvizGenerateCommand;
/**
* GraphvizGenerateCommand.
*
* @author William DURAND <william.durand1@gmail.com>
* @author Kévin Gomez <contact@kevingomez.fr>
*/
class GraphvizGenerateCommand extends AbstractCommand
class GraphvizGenerateCommand extends WrappedCommand
{
/**
* @see Command
* {@inheritdoc}
*/
protected function configure()
{
$this
->setDescription('Generates Graphviz file for your project')
->setHelp(<<<EOT
The <info>propel:graphviz</info> generates Graphviz file for your project.
parent::configure();
<info>php app/console propel:graphviz</info>
EOT
)
$this
->setName('propel:graphviz:generate')
->setDescription('Generate Graphviz files (.dot)')
->addOption('output-dir', null, InputOption::VALUE_REQUIRED, 'The output directory', BaseGraphvizGenerateCommand::DEFAULT_OUTPUT_DIRECTORY)
;
}
/**
* @see Command
*
* @throws \InvalidArgumentException When the target directory does not exist
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
protected function createSubCommandInstance()
{
$dest = $this->getApplication()->getKernel()->getRootDir() . '/propel/graph/';
return new BaseGraphvizGenerateCommand();
}
$this->callPhing('graphviz', array(
'propel.graph.dir' => $dest,
));
$this->writeNewDirectory($output, $dest);
/**
* {@inheritdoc}
*/
protected function getSubCommandArguments(InputInterface $input)
{
return array(
'--output-dir' => $input->getOption('output-dir'),
);
}
}

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,62 +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\Input\InputOption;
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')
->addOption('connection', null, InputOption::VALUE_OPTIONAL, 'Set this parameter to define a connection to use')
->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)
{
if (true === $this->callPhing('diff')) {
$this->writeSummary($output, 'propel-sql-diff');
} elseif ( strpos( $this->buffer, 'Uncommitted migrations have been found' ) ) {
$this->writeSection($output, array(
'[Propel] Error',
'',
'Uncommitted migrations have been found. You should either execute or delete them before rerunning the propel:migration:generate-diff command.'
), 'fg=white;bg=red');
} else {
$this->writeTaskError($output, 'propel-sql-diff');
}
}
}

View file

@ -8,62 +8,58 @@
* @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\Output\OutputInterface;
use Symfony\Component\Console\Input\InputInterface;
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');
} elseif ($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,58 +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\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
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')
->addOption('connection', null, InputOption::VALUE_OPTIONAL, 'Set this parameter to define a connection to use')
->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()
{
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 new BaseModelBuildCommand();
}
/**
* {@inheritdoc}
*/
protected function getSubCommandArguments(InputInterface $input)
{
$outputDir = $this->getApplication()->getKernel()->getRootDir().'/../';
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
{
public static 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,83 +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;
/**
* 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)
{
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,123 +8,51 @@
* @license MIT License
*/
namespace Propel\PropelBundle\Command;
namespace Propel\Bundle\PropelBundle\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
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;
use Symfony\Component\Console\Input\InputInterface;
/**
* 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
)
->addOption('connection', null, InputOption::VALUE_OPTIONAL, 'Set this parameter to define a connection to use')
$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()
{
$finder = new Finder();
$filesystem = new Filesystem();
$sqlDir = $this->getApplication()->getKernel()->getRootDir(). DIRECTORY_SEPARATOR . 'propel'. DIRECTORY_SEPARATOR . 'sql';
$cacheDir = $this->getApplication()->getKernel()->getCacheDir(). DIRECTORY_SEPARATOR . 'sql';
$filesystem->remove($cacheDir);
$filesystem->mkdir($cacheDir);
if (!$filesystem->exists($sqlDir)) {
$filesystem->mkdir($sqlDir);
}
// Execute the task
$ret = $this->callPhing('build-sql', array(
'propel.sql.dir' => $cacheDir
));
// Show the list of generated files
if (true === $ret) {
$files = $finder->name('*')->in($cacheDir);
$nbFiles = 0;
foreach ($files as $file) {
$fileExt = pathinfo($file->getFilename(), PATHINFO_EXTENSION);
$finalLocation = $sqlDir. DIRECTORY_SEPARATOR. $file->getFilename();
if ($fileExt === 'map' && $filesystem->exists($finalLocation)) {
$this->mergeMapFiles($finalLocation, (string) $file);
} else {
$filesystem->remove($finalLocation);
$filesystem->rename((string) $file, $finalLocation);
}
$this->writeNewFile($output, (string) $file);
if ('sql' === $fileExt) {
$nbFiles++;
}
}
$output->writeln(sprintf('<comment>%d</comment> <info>SQL file%s ha%s been generated.</info>',
$nbFiles, $nbFiles > 1 ? 's' : '', $nbFiles > 1 ? 've' : 's'
));
} 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 new \Propel\Generator\Command\SqlBuildCommand();
}
/**
* Reads the existing target and the generated map files, and adds to the
* target the missing lines that are in the generated file.
*
* @param string $target target map filename
* @param string $generated generated map filename
*
* @return boolean result
* {@inheritdoc}
*/
protected function mergeMapFiles($target, $generated)
protected function getSubCommandArguments(InputInterface $input)
{
if(($targetContent = file($target)) === false)
$defaultSqlDir = $this->getContainer()->getParameter('propel.configuration')['paths']['sqlDir'];
return false;
if(($generatedContent = file($generated)) === false)
return false;
$targetContent = array_merge($generatedContent, array_diff($targetContent, $generatedContent));
return file_put_contents($target, $targetContent);
return array(
'--connection' => $this->getConnections($input->getOption('connection')),
'--output-dir' => $input->getOption('sql-dir') ?: $defaultSqlDir,
'--overwrite' => $input->getOption('overwrite')
);
}
}

View file

@ -8,117 +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';
if ($input->getOption('force')) {
$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) {
$output->writeln('<info>All SQL statements have been inserted.</info>');
} else {
$output->writeln('<comment>No SQL statements found.</comment>');
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,119 +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)
{
$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;
}
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();
$showStatement = $connection->prepare('SHOW TABLES;');
$showStatement->execute();
$allTables = $showStatement->fetchAll(\PDO::FETCH_COLUMN);
$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;
if ($nbTable) {
foreach ($tablesToDelete as $tableToDelete) {
if (!array_search($tableToDelete, $allTables)) {
throw new \InvalidArgumentException(sprintf('Table %s doesn\'t exist in the database.', $tableToDelete));
}
$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 caught',
'',
$e->getMessage()
), 'fg=white;bg=red');
}
} else {
$output->writeln('<error>You have to use the "--force" option to drop some tables.</error>');
$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,9 +8,12 @@
* @license MIT License
*/
namespace Propel\PropelBundle\Controller;
namespace Propel\Bundle\PropelBundle\Controller;
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;
/**
@ -18,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'),
)
);
}
@ -47,11 +45,11 @@ class PanelController extends ContainerAware
* @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);
@ -62,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,9 +8,10 @@
* @license MIT License
*/
namespace Propel\PropelBundle\DataFixtures;
namespace Propel\Bundle\PropelBundle\DataFixtures;
use \Propel;
use Propel\Runtime\Map\DatabaseMap;
use Propel\Runtime\Propel;
use Symfony\Component\Finder\Finder;
/**
@ -22,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;
}
/**
@ -63,8 +73,10 @@ abstract class AbstractDataHandler
$this->dbMap = Propel::getDatabaseMap($connectionName);
if (0 === count($this->dbMap->getTables())) {
$finder = new Finder();
$files = $finder->files()->name('*TableMap.php')
$files = $finder
->files()->name('*TableMap.php')
->in($this->getModelSearchPaths($connectionName))
->notName('TableMap.php')
->exclude('PropelBundle')
->exclude('Tests');
@ -80,15 +92,15 @@ abstract class AbstractDataHandler
/**
* Check if a table is in a database
* @param string $class
* @param string $connectionName
*
* @param string $class
* @param string $connectionName
*
* @return boolean
*/
protected function isInDatabase($class, $connectionName)
{
$table = new $class();
return constant($table->getPeerClassname().'::DATABASE_NAME') == $connectionName;
return constant($class.'::DATABASE_NAME') === $connectionName;
}
/**
@ -97,6 +109,8 @@ abstract class AbstractDataHandler
*
* @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)
{
@ -128,12 +142,12 @@ abstract class AbstractDataHandler
*
* @return string[]
*/
protected function getModelSearchPaths($connectionName) {
$configuration = Propel::getConfiguration();
protected function getModelSearchPaths($connectionName)
{
$searchPath = array();
if (!empty($configuration['datasources'][$connectionName]['connection']['model_paths'])) {
$modelPaths = $configuration['datasources'][$connectionName]['connection']['model_paths'];
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;
}

View file

@ -8,12 +8,12 @@
* @license MIT License
*/
namespace Propel\PropelBundle\DataFixtures\Dumper;
namespace Propel\Bundle\PropelBundle\DataFixtures\Dumper;
use Propel\PropelBundle\DataFixtures\AbstractDataHandler;
use \PDO;
use \Propel;
use \PropelColumnTypes;
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.
@ -69,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;
@ -107,10 +107,14 @@ 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);
}
@ -148,7 +152,7 @@ abstract class AbstractDataDumper extends AbstractDataHandler implements DataDum
$values[$col] = strlen($row[$col]) ? $relatedTable->getPhpName().'_'.$row[$col] : '';
}
} elseif (!$isPrimaryKey || ($isPrimaryKey && !$tableMap->isUseIdGenerator())) {
if (!empty($row[$col]) && PropelColumnTypes::PHP_ARRAY === $column->getType()) {
if (!empty($row[$col]) && PropelTypes::PHP_ARRAY === $column->getType()) {
$serialized = substr($row[$col], 2, -2);
$row[$col] = $serialized ? explode(' | ', $serialized) : array();
}
@ -157,7 +161,7 @@ abstract class AbstractDataDumper extends AbstractDataHandler implements DataDum
$values[$col] = $row[$col];
}
if (PropelColumnTypes::OBJECT === $column->getType()) {
if (PropelTypes::OBJECT === $column->getType()) {
$values[$col] = unserialize($row[$col]);
}
}
@ -187,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()) {
@ -216,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.

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;
@ -28,8 +28,7 @@ class YamlDataDumper extends AbstractDataDumper
$data,
$inline = 3,
$indent = 4,
$exceptionOnInvalidType = false,
$objectSupport = true
Yaml::DUMP_OBJECT
);
}
}

View file

@ -8,15 +8,15 @@
* @license MIT License
*/
namespace Propel\PropelBundle\DataFixtures\Loader;
namespace Propel\Bundle\PropelBundle\DataFixtures\Loader;
use \BasePeer;
use \BaseObject;
use \Propel;
use \PropelColumnTypes;
use \PropelException;
use Propel\PropelBundle\DataFixtures\AbstractDataHandler;
use Propel\PropelBundle\Util\PropelInflector;
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.
@ -105,15 +105,8 @@ abstract class AbstractDataLoader extends AbstractDataHandler implements DataLoa
*/
protected function 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));
}
// bypass the soft_delete behavior if enabled
$deleteMethod = method_exists($peerClass, 'doForceDeleteAll') ? 'doForceDeleteAll' : 'doDeleteAll';
call_user_func(array($peerClass, $deleteMethod), $this->con);
$tableMap = $this->dbMap->getTable(constant(constant($class.'::TABLE_MAP').'::TABLE_NAME'));
$tableMap->doDeleteAll($this->con);
$this->deletedClasses[] = $class;
@ -129,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)
{
@ -138,20 +131,20 @@ abstract class AbstractDataLoader extends AbstractDataHandler implements DataLoa
}
foreach ($data as $class => $datas) {
$class = trim($class);
if ('\\' == $class[0]) {
$class = substr($class, 1);
}
$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));
@ -159,28 +152,28 @@ 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) {
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) {
} 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 (PropelColumnTypes::PHP_ARRAY !== $tableMap->getColumn($name)->getType()
&& PropelColumnTypes::OBJECT !== $tableMap->getColumn($name)->getType()) {
if (PropelTypes::PHP_ARRAY !== $tableMap->getColumn($name)->getType()
&& PropelTypes::OBJECT !== $tableMap->getColumn($name)->getType()) {
throw $e;
}
}
@ -203,8 +196,8 @@ abstract class AbstractDataLoader extends AbstractDataHandler implements DataLoa
* 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[$class.'_'.$value])) {
$obj = $this->object_references[$class.'_'.$value];
if (isset($this->object_references[$this->cleanObjectRef($class.'_'.$value)])) {
$obj = $this->object_references[$this->cleanObjectRef($class.'_'.$value)];
continue;
}
@ -212,14 +205,19 @@ 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);
}
}
@ -242,43 +240,42 @@ abstract class AbstractDataLoader extends AbstractDataHandler implements DataLoa
/**
* 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 BaseObject $obj A Propel object
* @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')) {
if (!method_exists($obj, 'getPrimaryKey')) {
return;
}
$this->object_references[$class.'_'.$key] = $obj;
$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);
}
// 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);
}
}
}
/**
* 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->getClassname();
$tableName = constant(constant(get_class($obj).'::PEER').'::TABLE_NAME');
$tableName = constant(constant(get_class($obj).'::TABLE_MAP').'::TABLE_NAME');
foreach ($middleTable->getColumns() as $column) {
if ($column->isForeignKey()) {
@ -296,7 +293,7 @@ abstract class AbstractDataLoader extends AbstractDataHandler implements DataLoa
}
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)
);
@ -304,8 +301,13 @@ abstract class AbstractDataLoader extends AbstractDataHandler implements DataLoa
$middle = new $middleClass();
$middle->$setter($obj);
$middle->$relatedSetter($this->object_references[$relatedClass.'_'.$value]);
$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.

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.

View file

@ -8,9 +8,9 @@
* @license MIT License
*/
namespace Propel\PropelBundle\DataFixtures\Loader;
namespace Propel\Bundle\PropelBundle\DataFixtures\Loader;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Faker\Generator;
use Symfony\Component\Yaml\ParseException;
use Symfony\Component\Yaml\Yaml;
@ -22,18 +22,18 @@ use Symfony\Component\Yaml\Yaml;
class YamlDataLoader extends AbstractDataLoader
{
/**
* @var \Symfony\Component\DependencyInjection\ContainerInterface
* @var \Faker\Generator
*/
private $container;
private $faker;
/**
* {@inheritdoc}
*/
public function __construct($rootDir, ContainerInterface $container = null)
public function __construct($rootDir, array $datasources, Generator $faker = null)
{
parent::__construct($rootDir);
parent::__construct($rootDir, $datasources);
$this->container = $container;
$this->faker = $faker;
}
/**
@ -46,22 +46,22 @@ class YamlDataLoader extends AbstractDataLoader
throw new ParseException(sprintf('Unable to parse "%s" as the file is not readable.', $file));
}
if (null !== $this->container && $this->container->has('faker.generator')) {
$generator = $this->container->get('faker.generator');
$faker = function($type) use ($generator) {
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) {
$faker = function ($text) {
echo $text . "\n";
};
}
ob_start();
$retval = include($file);
$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

View file

@ -8,256 +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
* ...
* behaviors:
* fooable: My\FooableBehavior
* barable: src.barable.BarableBehavior
*/
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()
->arrayNode('behaviors')
->useAttributeAsKey('key')
->prototype('scalar')->end()
->end()
;
}
/**
* Adds 'dbal' configuration.
*
* propel:
* dbal:
* driver: mysql
* user: root
* password: null
* dsn: xxxxxxxx
* options: {}
* attributes: {}
* settings: {}
* default_connection: xxxxxx
*/
private function addDbalSection(ArrayNodeDefinition $node)
{
$node
->children()
->arrayNode('dbal')
->beforeNormalization()
->ifNull()
->then(function($v) { return array ('connections' => array('default' => array())); })
->end()
->children()
->scalarNode('default_connection')->defaultValue('default')->end()
->scalarNode('driver')
->beforeNormalization()
->always()
->then(function($v) { return str_replace('pdo_', '', $v); })
->end()
->defaultValue('mysql')
->end()
->scalarNode('user')->defaultValue('root')->end()
->scalarNode('password')->defaultValue('')->end()
->scalarNode('dsn')
->beforeNormalization()
->always()
->then(function($v) { return str_replace('pdo_', '', $v); })
->end()
->defaultValue('')
->end()
->scalarNode('classname')->defaultValue($this->debug ? 'DebugPDO' : 'PropelPDO')->end()
->end()
->fixXmlConfig('option')
->children()
->arrayNode('options')
->useAttributeAsKey('key')
->prototype('scalar')->end()
->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('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')
->beforeNormalization()
->always()
->then(function($v) { return str_replace('pdo_', '', $v); })
->children()
->arrayNode('runtime')
->addDefaultsIfNotSet()
->fixXmlConfig('connection')
->children()
->scalarNode('defaultConnection')->end()
->arrayNode('connections')
->prototype('scalar')->end()
->end()
->defaultValue('mysql')
->end()
->scalarNode('user')->defaultValue('root')->end()
->scalarNode('password')->defaultValue('')->end()
->scalarNode('dsn')
->beforeNormalization()
->always()
->then(function($v) { return str_replace('pdo_', '', $v); })
->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()
->defaultValue('')
->end()
->scalarNode('classname')->defaultValue($this->debug ? 'DebugPDO' : 'PropelPDO')->end()
->arrayNode('slaves')
->useAttributeAsKey('name')
->prototype('array')
->arrayNode('profiler')
->children()
->scalarNode('driver')
->beforeNormalization()
->always()
->then(function($v) { return str_replace('pdo_', '', $v); })
->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()
->defaultValue('mysql')
->end()
->scalarNode('user')->defaultValue('root')->end()
->scalarNode('password')->defaultValue('')->end()
->scalarNode('dsn')
->beforeNormalization()
->always()
->then(function($v) { return str_replace('pdo_', '', $v); })
->end()
->defaultValue('')
->end()
->scalarNode('classname')->defaultValue($this->debug ? 'DebugPDO' : 'PropelPDO')->end()
->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('model_path')
->children()
->arrayNode('model_paths')
->defaultValue(array('src/', 'vendor/'))
->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,14 +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\Config\FileLocator;
use Symfony\Component\Config\Definition\Processor;
/**
* PropelExtension loads the PropelBundle configuration.
@ -32,115 +31,35 @@ class PropelExtension extends Extension
*/
public function load(array $configs, ContainerBuilder $container)
{
$processor = new Processor();
$configuration = $this->getConfiguration($configs, $container);
$config = $processor->processConfiguration($configuration, $configs);
$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');
$loader->load('security.xml');
$loader->load('console.xml');
}
// build properties
if (isset($config['build_properties']) && is_array($config['build_properties'])) {
$buildProperties = $config['build_properties'];
} else {
$buildProperties = array();
}
// behaviors
if (isset($config['behaviors']) && is_array($config['behaviors'])) {
foreach ($config['behaviors'] as $name => $class) {
$buildProperties[sprintf('propel.behavior.%s.class', $name)] = $class;
}
}
$container->getDefinition('propel.build_properties')->setArguments(array($buildProperties));
if (!empty($config['dbal'])) {
$this->dbalLoad($config['dbal'], $container);
}
}
/**
* Loads the DBAL configuration.
*
* @param array $config An array of configuration settings
* @param ContainerBuilder $container A ContainerBuilder instance
*/
protected function dbalLoad(array $config, ContainerBuilder $container)
{
if (empty($config['default_connection'])) {
$keys = array_keys($config['connections']);
$config['default_connection'] = reset($keys);
}
$connectionName = $config['default_connection'];
$container->setParameter('propel.dbal.default_connection', $connectionName);
if (0 === count($config['connections'])) {
$config['connections'] = array($connectionName => $config);
}
$c = array();
foreach ($config['connections'] as $name => $conf) {
$c['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', 'model_paths') 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));
}
public function getConfiguration(array $config, ContainerBuilder $container)
{
return new Configuration($container->getParameter('kernel.debug'));
return new Configuration($container->getParameter('kernel.debug'), $container->getParameter('kernel.root_dir'));
}
/**

View file

@ -1,46 +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()
;
}
}

View file

@ -1,15 +1,26 @@
<?php
namespace Propel\PropelBundle\Form;
/**
* 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(
'name' => '',
);
protected $options = array();
public function __construct($mergeOptions = null)
{
@ -44,18 +55,18 @@ abstract class BaseAbstractType extends AbstractType
}
/**
* {@inheritdoc}
* {@inheritdoc}
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults($this->options);
}
/**
* {@inheritdoc}
* {@inheritdoc}
*/
public function getName()
{
return $this->getOption('name');
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
*/
public static 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,7 +29,7 @@ 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
*/
public static function fromAclEntry(EntryInterface $aclEntry)
{
@ -64,7 +64,7 @@ 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

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;
@ -25,11 +29,11 @@ class EntryQuery extends BaseEntryQuery
*
* @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 \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,7 +23,7 @@ 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
*/
@ -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
*/
public static 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,9 +1,9 @@
PropelBundle
============
[![Build Status](https://secure.travis-ci.org/propelorm/PropelBundle.png)](http://travis-ci.org/propelorm/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 Symfony2.
This is the official implementation of [Propel](http://www.propelorm.org/) in Symfony.
## Branching model
@ -11,18 +11,10 @@ As `Propel2` will be released in the near future, we are migrating the branching
* 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.1* (*currently 2.2, 2.3 and 2.4 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 Symfony `2.2`, `2.3` or `master` branch, switch to the `1.2` branch.
* If you depend on Symfony `2.1` branch, switch to the `1.1` branch.
* If you depend on Symfony `2.0` branch, switch to the `1.0` branch.
**Note:** the `master`, and `2.0` branches won't be updated anymore, and will trigger a `E_USER_DEPRECATED` error to notice people.
* 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
@ -30,20 +22,16 @@ As `Propel2` will be released in the near future, we are migrating the branching
* 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/wiki/Documentation/1.6/Existing-Database);
* Integration to the Symfony2 Profiler;
* [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.
For documentation, see:
Resources/doc/
[Read the documentation](https://github.com/propelorm/PropelBundle/blob/1.1/Resources/doc/index.markdown)
[Read the documentation](http://propelorm.org/documentation/)
For license, see:

View file

@ -1,14 +1,14 @@
<?php
namespace Propel\PropelBundle\Request\ParamConverter;
namespace Propel\Bundle\PropelBundle\Request\ParamConverter;
use Propel\PropelBundle\Util\PropelInflector;
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;
use Symfony\Component\Routing\RouterInterface;
/**
* PropelParamConverter
@ -48,24 +48,20 @@ class PropelParamConverter implements ParamConverterInterface
*/
protected $withs;
/**
* name of method use to call a query method
* @var string
*/
protected $queryMethod;
/**
* @var bool
*/
protected $hasWith = false;
/**
* @var RouterInterface
*/
protected $router;
public function setRouter(RouterInterface $router = null)
{
$this->router = $router;
}
/**
* @param Request $request
* @param ConfigurationInterface $configuration
* @param Request $request
* @param ParamConverter $configuration
*
* @return bool
*
@ -73,10 +69,11 @@ class PropelParamConverter implements ParamConverterInterface
* @throws NotFoundHttpException
* @throws \Exception
*/
public function apply(Request $request, ConfigurationInterface $configuration)
public function apply(Request $request, ParamConverter $configuration)
{
$classQuery = $configuration->getClass() . 'Query';
$classPeer = $configuration->getClass() . 'Peer';
$class = $configuration->getClass();
$classQuery = $class . 'Query';
$classTableMap = $class::TABLE_MAP;
$this->filters = array();
$this->exclude = array();
@ -84,23 +81,23 @@ class PropelParamConverter implements ParamConverterInterface
throw new \Exception(sprintf('The %s Query class does not exist', $classQuery));
}
$tableMap = $classPeer::getTableMap();
$pkColumns = $tableMap->getPrimaryKeyColumns();
$tableMap = new $classTableMap();
$pkColumns = $tableMap->getPrimaryKeys();
if (count($pkColumns) == 1) {
$this->pk = strtolower($pkColumns[0]->getName());
if (count($pkColumns) === 1) {
$pk = array_pop($pkColumns);
$this->pk = strtolower($pk->getName());
}
$options = $configuration->getOptions();
// Check route options for converter options, if there are non provided.
if (empty($options) && $request->attributes->has('_route') && $this->router && $configuration instanceof ParamConverter) {
$converterOption = $this->router->getRouteCollection()->get($request->attributes->get('_route'))->getOption('propel_converter');
// 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) {
@ -113,21 +110,34 @@ class PropelParamConverter implements ParamConverterInterface
}
}
} else {
$this->exclude = isset($options['exclude'])? $options['exclude'] : array();
$this->exclude = isset($options['exclude']) ? $options['exclude'] : array();
$this->filters = $request->attributes->all();
}
$this->withs = isset($options['with'])? is_array($options['with'])? $options['with'] : array($options['with']) : array();
if (array_key_exists($configuration->getName(), $this->filters)) {
unset($this->filters[$configuration->getName()]);
}
// 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.');
$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.');
}
}
}
}
@ -142,21 +152,23 @@ class PropelParamConverter implements ParamConverterInterface
}
/**
* @param ConfigurationInterface $configuration
* @param ParamConverter $configuration
*
* @return bool
*/
public function supports(ConfigurationInterface $configuration)
public function supports(ParamConverter $configuration)
{
if (null === ($classname = $configuration->getClass())) {
return false;
}
if (!class_exists($classname)) {
return false;
}
// Propel Class?
$class = new \ReflectionClass($configuration->getClass());
if ($class->isSubclassOf('BaseObject')) {
if ($class->implementsInterface('\Propel\Runtime\ActiveRecord\ActiveRecordInterface')) {
return true;
}
@ -203,7 +215,7 @@ class PropelParamConverter implements ParamConverterInterface
try {
$query->{'filterBy' . PropelInflector::camelize($column)}($value);
$hasCriteria = true;
} catch (\PropelException $e) { }
} catch (\Exception $e) { }
}
}
@ -223,7 +235,7 @@ class PropelParamConverter implements ParamConverterInterface
*
* @param string $classQuery
*
* @return \ModelCriteria
* @return ModelCriteria
*
* @throws \Exception
*/
@ -263,11 +275,11 @@ class PropelParamConverter implements ParamConverterInterface
{
switch (trim(str_replace(array('_', 'JOIN'), '', strtoupper($with[1])))) {
case 'LEFT':
return \Criteria::LEFT_JOIN;
return Criteria::LEFT_JOIN;
case 'RIGHT':
return \Criteria::RIGHT_JOIN;
return Criteria::RIGHT_JOIN;
case 'INNER':
return \Criteria::INNER_JOIN;
return Criteria::INNER_JOIN;
}
throw new \Exception(sprintf('ParamConverter : "with" parameter "%s" is invalid,

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,16 +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" converter="propel" priority="1" />
<call method="setRouter">
<argument type="service" id="router" on-invalid="null" />
</call>
</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,39 +0,0 @@
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:acl:init` 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.
[Back to index](index.markdown)

View file

@ -1,97 +0,0 @@
The Commands
============
The PropelBundle provides a lot of commands to manage migrations, database/table manipulations,
and so on.
## Database Manipulations ##
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.
## 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 @AcmeDemoBundle Book Author
## Graphviz ##
You can generate **Graphviz** file for your project by using the following command line:
> php app/console propel:graphviz:generate
It will write files in `app/propel/graph/`.
## Migrations ##
Generates SQL diff between the XML schemas and the current database structure:
> php app/console propel:migration:generate-diff [--connection[=""]]
As usual, `--connection` allows to specify a connection.
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
## Table Manipulations ##
You can drop one or several **tables**:
> 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.
## 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
[Back to index](index.markdown)

View file

@ -1,132 +0,0 @@
Configuration
=============
In order to use Propel, you have to configure few parameters in your `app/config/config.yml` file.
If you are **not** using Composer, add this configuration:
``` yaml
# in app/config/config.yml
propel:
path: "%kernel.root_dir%/../vendor/propel"
phing_path: "%kernel.root_dir%/../vendor/phing"
```
Now, you can configure your application.
## Basic Configuration ##
If you have just one database connection, your configuration will look like as following:
``` yaml
# app/config/config*.yml
propel:
dbal:
driver: mysql
user: root
password: null
dsn: mysql:host=localhost;dbname=test;charset=UTF8
options: {}
attributes: {}
```
The recommended way to fill in these information is to use parameters:
``` yaml
# app/config/config*.yml
propel:
dbal:
driver: %database_driver%
user: %database_user%
password: %database_password%
dsn: %database_driver%:host=%database_host%;dbname=%database_name%;charset=UTF8
options: {}
attributes: {}
```
## Configure Multiple Connection ##
If you have more than one connection, or want to use a named connection, the configuration
will look like:
``` yaml
# app/config/config*.yml
propel:
dbal:
default_connection: conn1
connections:
conn1:
driver: mysql
user: root
password: null
dsn: mysql:host=localhost;dbname=db1
conn2:
driver: mysql
user: root
password: null
dsn: mysql:host=localhost;dbname=db2
```
## Configure Master/Slaves ##
You can also configure Master/Slaves:
``` yaml
# app/config/config*.yml
propel:
dbal:
default_connection: default
connections:
default:
driver: mysql
user: root
password: null
dsn: mysql:host=localhost;dbname=master
slaves:
slave_1:
user: root
password: null
dsn: mysql:host=localhost;dbname=slave_1
```
## Attributes, Options, Settings ##
``` yaml
# app/config/config*.yml
propel:
dbal:
default_connection: default
connections:
default:
# ...
options:
ATTR_PERSISTENT: false
attributes:
ATTR_EMULATE_PREPARES: true
settings:
charset: { value: UTF8 }
queries: { query: 'INSERT INTO BAR ('hey', 'there')' }
model_paths:
- /src/Acme/DemoBundle/Model/
- /vendor/
```
`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.
`model_paths` can be defined to speed up searching for model data. By default it searches in the whole project from project root.
## Logging ##
You can disable the logging by changing the `logging` parameter value:
``` yaml
# in app/config/config.yml
propel:
logging: %kernel.debug%
```
[Back to index](index.markdown) | [Configure Propel](propel_configuration.markdown)

View file

@ -1,104 +0,0 @@
The Fixtures
============
Fixtures are data you usually write to populate your database during the development, or static content
like menus, labels, ... you need by default in your database in production.
## Loading Fixtures ##
The following command is designed to load fixtures:
> php app/console propel:fixtures:load [-d|--dir[="..."]] [--xml] [--sql] [--yml] [--connection[="..."]] [bundle]
As you can see, there are many options to allow you to easily load fixtures.
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 at the same time.
If none of this parameter are set all files YAML, XML and SQL in the directory will be load.
You can pass a bundle name to load fixtures from it. A bundle's name starts with `@` like `@AcmeDemoBundle`.
> php app/console propel:fixtures:load @AcmeDemoBundle
### XML Fixtures ###
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>
```
### YAML Fixtures ###
A valid _YAML fixtures file_ is:
``` yaml
Awesome\Object:
o1:
Title: My title
MyFoo: bar
Awesome\Related:
r1:
ObjectId: o1
Description: Hello world !
Awesome\Tag:
t1:
name: Foo
t2:
name: Baz
Awesome\Post:
p1:
title: A Post with tags (N-N relation)
tags: [ t1, t2 ]
```
#### Using Faker in YAML Fixtures ####
If you use [Faker](https://github.com/fzaninotto/Faker) with its [Symfony2 integration](https://github.com/willdurand/BazingaFakerBundle),
then the PropelBundle offers a facility to use the Faker generator in your YAML files:
``` yml
Acme\DemoBundle\Model\Book:
Book1:
name: "Awesome Feature"
description: <?php $faker('text', 500); ?>
```
The aim of this feature is to be able to mix real, and fake data in the same file. Fake data are interesting to quickly
add data tou your application, but most of the time you need to rely on real data. To integrate Faker in the YAML files
allows to write strong fixtures efficiently.
## Dumping data ##
You can dump data from your database 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 these files by using the `propel:fixtures:load` command.
[Back to index](index.markdown)

View file

@ -1,129 +0,0 @@
PropelBundle
============
This is the official implementation of [Propel](http://www.propelorm.org/) in Symfony2.
## Installation ##
The recommended way to install this bundle is to rely on [Composer](http://getcomposer.org):
``` javascript
{
"require": {
// ...
"propel/propel-bundle": "1.1.*"
}
}
```
Otherwise you can use Git, SVN, Git submodules, or the Symfony vendor management (deps file):
* 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
The second step is to register this bundle in the `AppKernel` class:
``` php
public function registerBundles()
{
$bundles = array(
// ...
new Propel\PropelBundle\PropelBundle(),
);
// ...
}
```
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',
));
```
You are almost ready, the next steps are:
* to [configure the bundle](configuration.markdown);
* to [configure Propel](propel_configuration.markdown);
* to [write an XML schema](schema.markdown).
Now, you can build your model classes, and SQL by running the following command:
> php app/console propel:build [--classes] [--sql] [--insert-sql] [--connection[=""]]
To insert SQL statements, use the `propel:sql:insert` command:
> php app/console propel:sql:insert [--force] [--connection[=""]]
Note that the `--force` option is needed to actually execute the SQL statements.
Congratulation! You're done, just use the Model classes as any other class in Symfony2:
``` php
<?php
class HelloController extends Controller
{
public function indexAction($name)
{
$author = new \Acme\DemoBundle\Model\Author();
$author->setFirstName($name);
$author->save();
return $this->render('AcmeDemoBundle:Hello:index.html.twig', array(
'name' => $name, 'author' => $author)
);
}
}
```
Now you can read more about:
* [The ACL Implementation](acl.markdown);
* [The Commands](commands.markdown);
* [The Fixtures](fixtures.markdown);
* [The PropelParamConverter](param_converter.markdown);
* [The UniqueObjectValidator](unique_object_validator.markdown).
* [The ModelTranslation](model_translation.markdown).
## 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.

View file

@ -1,83 +0,0 @@
ModelTranslation
================
The `PropelBundle` provides a model-based implementation of the Translation components' loader and dumper.
To make us of this `ModelTranslation` you only need to add the translation resource.
``` yaml
services:
translation.loader.propel:
class: Propel\PropelBundle\Translation\ModelTranslation
arguments:
# The model to be used.
- 'Acme\Model\Translation\Translation'
# The column mappings to interact with the model.
-
columns:
key: 'key'
translation: 'translation'
locale: 'locale'
domain: 'domain'
updated_at: 'updated_at'
calls:
- [ 'registerResources', [ '@translator' ] ]
tags:
- { name: 'translation.loader', alias: 'propel' }
# The dumper tag is optional.
- { name: 'translation.dumper', alias: 'propel' }
```
This will add another resource to the translator to be scanned for translations.
## Translation model
An example model schema for the translation model:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<database name="translation" defaultIdMethod="native" namespace="Propel\PropelBundle\Tests\Fixtures\Model">
<table name="translation">
<column name="id" type="integer" autoIncrement="true" primaryKey="true" />
<column name="key" type="varchar" size="255" required="true" primaryString="true" />
<column name="translation" type="longvarchar" lazyLoad="true" required="true" />
<column name="locale" type="varchar" size="255" required="true" />
<column name="domain" type="varchar" size="255" required="true" />
<column name="updated_at" type="timestamp" />
<index>
<index-column name="domain" />
</index>
<index>
<index-column name="locale" />
<index-column name="domain" />
</index>
<unique>
<unique-column name="key" />
<unique-column name="locale" />
<unique-column name="domain" />
</unique>
</table>
</database>
```
### VersionableBehavior
In order to make use of the `VersionableBehavior` (or similar), you can map the `updated_at` column to the `version_created_at` column:
``` yaml
services:
translation.loader.propel:
class: Propel\PropelBundle\Translation\ModelTranslation
arguments:
- 'Acme\Model\Translation\Translation'
-
columns:
updated_at: 'version_created_at'
calls:
- [ 'registerResources', [ '@translator' ] ]
tags:
- { name: 'translation.loader', alias: 'propel' }
```
[Back to index](index.markdown)

View file

@ -1,133 +0,0 @@
The PropelParamConverter
========================
You can use the `PropelParamConverter` with the [SensioFrameworkExtraBundle](http://github.com/sensio/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)
{
}
```
**New** with last version of `SensioFrameworkExtraBundle`,
you can ommit the `class` parameter if your controller parameter is typed,
this is usefull when you need to set extra `options`.
``` php
<?php
use BlogBundle\Model\Post;
/**
* @ParamConverter("post")
*/
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)
{
}
```
#### Custom mapping ####
You can map route parameters directly to model column to be use for filtering.
If you have a route like `/my-route/{postUniqueName}/{AuthorId}`
Mapping option overwrite any other automatic mapping.
``` php
<?php
/**
* @ParamConverter("post", class="BlogBundle\Model\Post", options={"mapping"={"postUniqueName":"name"}})
* @ParamConverter("author", class="BlogBundle\Model\Author", options={"mapping"={"AuthorId":"id"}})
*/
public function myAction(Post $post, $author)
{
}
```
#### Hydrate related object ####
You could hydrate related object with the "with" option:
``` php
<?php
/**
* @ParamConverter("post", class="BlogBundle\Model\Post", options={"with"={"Comments"}})
*/
public function myAction(Post $post)
{
}
```
You can set multiple with ```"with"={"Comments", "Author", "RelatedPosts"}```.
The default join is an "inner join" but you can configure it to be a left join, right join or inner join :
``` php
<?php
/**
* @ParamConverter("post", class="BlogBundle\Model\Post", options={"with"={ {"Comments", "left join" } }})
*/
public function myAction(Post $post)
{
}
```
Accepted parameters for join :
* left, LEFT, left join, LEFT JOIN, left_join, LEFT_JOIN
* right, RIGHT, right join, RIGHT JOIN, right_join, RIGHT_JOIN
* inner, INNER, inner join, INNER JOIN, inner_join, INNER_JOIN
#### Named converter ####
If you have a conflict with another ParamConverter you can force the `PropelParamConverter` with the `converter` option.
``` php
<?php
/**
* @ParamConverter("post", converter="propel")
*/
public function myAction(Post $post)
{
}
```
[Back to index](index.markdown)

View file

@ -1,69 +0,0 @@
Propel Configuration
====================
You can add a `app/config/propel.ini` file in your project to specify some
configuration parameters. See the [Build properties Reference](
http://www.propelorm.org/reference/buildtime-configuration.html) to get more
information. However, **the recommended way** to configure Propel is to rely
on **build properties**, see the section below.
By default the PropelBundle is configured with the default parameters:
``` ini
# Enable full use of the DateTime class.
# Setting this to true means that getter methods for date/time/timestamp
# columns will return a DateTime object when the default format is empty.
propel.useDateTimeClass = true
# Specify a custom DateTime subclass that you wish to have Propel use
# for temporal values.
propel.dateTimeClass = DateTime
# These are the default formats that will be used when fetching values from
# temporal columns in Propel. You can always specify these when calling the
# methods directly, but for methods like getByName() it is nice to change
# the defaults.
# To have these methods return DateTime objects instead, you should set these
# to empty values
propel.defaultTimeStampFormat =
propel.defaultTimeFormat =
propel.defaultDateFormat =
# A better Pluralizer
propel.builder.pluralizer.class = builder.util.StandardEnglishPluralizer
```
## Build properties ##
You can define _build properties_ by creating a `propel.ini` file in `app/config` like below, but you can also follow
the Symfony2 convention by adding build properties in `app/config/config.yml`:
``` yaml
# app/config/config.yml
propel:
build_properties:
xxxxx.xxxx.xxxxx: XXXX
xxxxx.xxxx.xxxxx: XXXX
// ...
```
## Behaviors ##
You can register Propel behaviors using the following syntax:
``` yaml
# app/config/config.yml
propel:
behaviors:
behavior_name: My\Bundle\Behavior\BehaviorClassName
```
If you rely on third party behaviors, most of them are autoloaded so you don't
need to register them. But, for your own behaviors, you can either configure the
autoloader to autoload them, or register them in this section (this is the
recommended way when you namespace your behaviors).
[Configure the bundle](configuration.markdown) | [Back to index](index.markdown) | [Write an XML Schema](schema.markdown)

View file

@ -1,30 +0,0 @@
XML Schema
==========
Place the following schema in `src/Acme/DemoBundle/Resources/config/schema.xml`:
``` xml
<?xml version="1.0" encoding="UTF-8"?>
<database name="default" namespace="Acme\DemoBundle\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>
```
[Configure Propel](propel_configuration.markdown) | [Back to index](index.markdown)

View file

@ -1,124 +0,0 @@
The UniqueObjectValidator
=========================
In a form, if you want to validate the uniqueness of a field in a table you have to use the `UniqueObjectValidator`.
You may use as many validators of this type as you want.
The validator has 1 required parameter:
* `fields` : a field or an array of fields to test for uniqueness
and 3 optionals parameters:
* `message` : the error message with two variable `{{ object_class }}` and `{{ fields }}`
* `messageFieldSeparator` : the field separator ` and `
* `errorPath` : the relative path where the error will be attached, if none is set the error is global.
YAML
----
You can specify this using the `validation.yml` file, like this:
``` yaml
Acme\DemoBundle\Model\User:
constraints:
- Propel\PropelBundle\Validator\Constraints\UniqueObject:
fields: username
```
If you want to validate the uniqueness of more than just one field:
``` yaml
Acme\DemoBundle\Model\User:
constraints:
- Propel\PropelBundle\Validator\Constraints\UniqueObject:
fields: [username, login]
```
Full configuration :
``` yaml
Acme\DemoBundle\Model\User:
constraints:
- Propel\PropelBundle\Validator\Constraints\UniqueObject:
fields: [username, login]
message: We already have a user with {{ fields }}
messageFieldSeparator: " and "
errorPath: username
```
PHP
---
You can also specify this using php. Fields can be specified as a string if there is only one field
``` php
use Propel\PropelBundle\Validator\Constraint\UniqueObject;
...
/**
* Load the Validation Constraints
*
* @param ClassMetadata $metadata
*/
public static function loadValidatorMetadata(ClassMetadata $metadata)
{
$metadata->addConstraint(
new UniqueObject(
array(
'fields' => 'username',
'message' => 'We already have a user with {{ fields }}',
'messageFieldSeparator' => ' and '
'errorPath' => 'username',
)
)
);
}
```
If there is more than one field you must use an array
``` php
...
$metadata->addConstraint(
new UniqueObject(
array(
'fields' => array('username', 'login'),
'message' => 'We already have a user with {{ fields }}',
'messageFieldSeparator' => ' and ',
'errorPath' => 'username'
)
)
);
...
```
XML
---
You can also specify this using xml
```xml
<class name="Acme\DemoBundle\Model\User">
<constraint name="Propel\PropelBundle\Validator\Constraints\UniqueObject">
<option name="fields">username</option>
<option name="message">We already have a user with {{ fields }}</option>
<option name="messageFieldSeparator"> and </option>
<option name="errorPath">username</option>
</constraint>
</class>
```
[Back to index](index.markdown)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

View file

@ -2,20 +2,27 @@
namespace ##NAMESPACE##;
use Propel\PropelBundle\Form\BaseAbstractType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ##CLASS## extends BaseAbstractType
class ##CLASS## extends AbstractType
{
protected $options = array(
'data_class' => '##FQCN##',
'name' => '##TYPE_NAME##',
);
/**
* {@inheritdoc}
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{##BUILD_CODE##
}
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => '##FQCN##',
'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 controller('PropelBundle:Panel:explain', {
'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 controller('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 %}

View file

@ -8,13 +8,16 @@
* @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\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;
@ -41,10 +44,10 @@ class AclProvider implements AclProviderInterface
* Constructor.
*
* @param \Symfony\Component\Security\Acl\Model\PermissionGrantingStrategyInterface $permissionGrantingStrategy
* @param \PropelPDO $con
* @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;
@ -163,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
*
* @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,15 +24,15 @@ 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
*
* @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,7 +8,9 @@
* @license MIT License
*/
namespace Propel\PropelBundle\Security\Acl\Domain;
namespace Propel\Bundle\PropelBundle\Security\Acl\Domain;
use Propel\Runtime\Collection\ObjectCollection;
use Symfony\Component\Security\Acl\Exception\Exception as AclException;
@ -18,13 +20,13 @@ 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();
@ -48,17 +50,17 @@ class Acl implements AclInterface
/**
* Constructor.
*
* @param \PropelObjectCollection $entries
* @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
*/
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) {
@ -301,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,9 +8,9 @@
* @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\AuditableAclInterface;
@ -81,7 +81,7 @@ class AuditableAcl extends MutableAcl implements AuditableAclInterface
* @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)
{

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)

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)

View file

@ -8,12 +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\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;
@ -40,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 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 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);
@ -335,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)
{
@ -367,7 +370,7 @@ class MutableAcl extends Acl implements MutableAclInterface
* @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)
{
@ -394,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)
{
@ -413,7 +416,7 @@ class MutableAcl extends Acl implements MutableAclInterface
* @param array $list
* @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)
{
@ -433,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)
{
@ -452,7 +455,7 @@ class MutableAcl extends Acl implements MutableAclInterface
* @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)
{
@ -469,7 +472,7 @@ class MutableAcl extends Acl implements MutableAclInterface
* @param array $list
* @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)
{
@ -491,7 +494,7 @@ class MutableAcl extends Acl implements MutableAclInterface
* @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)
{

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