commit
3d74fbdb94
20
.travis.yml
20
.travis.yml
|
@ -1,16 +1,18 @@
|
|||
language: php
|
||||
|
||||
php:
|
||||
- 5.3
|
||||
- 5.4
|
||||
- 5.5
|
||||
- 5.4
|
||||
- 5.5
|
||||
- 5.6
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- php: 5.5
|
||||
env:
|
||||
- SYMFONY_VERSION="~2.3.0"
|
||||
- SYMFONY_VERSION="~2.4.0"
|
||||
- SYMFONY_VERSION="~2.5.0"
|
||||
|
||||
before_script:
|
||||
- curl -s http://getcomposer.org/installer | php
|
||||
- php composer.phar install --dev --prefer-source
|
||||
- curl -s http://getcomposer.org/installer | php
|
||||
- php composer.phar require --no-update symfony/symfony:${SYMFONY_VERSION}
|
||||
- php composer.phar install --prefer-source
|
||||
|
||||
script: phpunit --coverage-text
|
||||
script: ./vendor/bin/phpunit --coverage-text
|
||||
|
|
355
Command/AbstractCommand.php
Normal file
355
Command/AbstractCommand.php
Normal file
|
@ -0,0 +1,355 @@
|
|||
<?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\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\HttpKernel\Bundle\Bundle;
|
||||
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
|
||||
/**
|
||||
* @author Kévin Gomez <contact@kevingomez.fr>
|
||||
*/
|
||||
abstract class AbstractCommand extends ContainerAwareCommand
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $cacheDir = null;
|
||||
|
||||
/**
|
||||
* @var Symfony\Component\HttpKernel\Bundle\BundleInterface
|
||||
*/
|
||||
protected $bundle = null;
|
||||
|
||||
/**
|
||||
* @var InputInterface
|
||||
*/
|
||||
protected $input;
|
||||
|
||||
use FormattingHelpers;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function initialize(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$kernel = $this->getApplication()->getKernel();
|
||||
|
||||
$this->input = $input;
|
||||
$this->cacheDir = $kernel->getCacheDir().'/propel';
|
||||
|
||||
if ($input->hasArgument('bundle') && '@' === substr($input->getArgument('bundle'), 0, 1)) {
|
||||
$this->bundle = $this
|
||||
->getContainer()
|
||||
->get('kernel')
|
||||
->getBundle(substr($input->getArgument('bundle'), 1));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create all the files needed by Propel's commands.
|
||||
*/
|
||||
protected function setupBuildTimeFiles()
|
||||
{
|
||||
$kernel = $this->getApplication()->getKernel();
|
||||
|
||||
$fs = new Filesystem();
|
||||
$fs->mkdir($this->cacheDir);
|
||||
|
||||
// collect all schemas
|
||||
$this->copySchemas($kernel, $this->cacheDir);
|
||||
|
||||
// propel.json
|
||||
$this->createPropelConfigurationFile($this->cacheDir.'/propel.json');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param KernelInterface $kernel The application kernel.
|
||||
* @param string $cacheDir The directory in which the schemas will
|
||||
* be copied.
|
||||
*/
|
||||
protected function copySchemas(KernelInterface $kernel, $cacheDir)
|
||||
{
|
||||
$filesystem = new Filesystem();
|
||||
$base = ltrim(realpath($kernel->getRootDir().'/..'), DIRECTORY_SEPARATOR);
|
||||
|
||||
$finalSchemas = $this->getFinalSchemas($kernel, $this->bundle);
|
||||
foreach ($finalSchemas as $schema) {
|
||||
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(),
|
||||
);
|
||||
|
||||
$file = $cacheDir.DIRECTORY_SEPARATOR.$tempSchema;
|
||||
$filesystem->copy((string) $finalSchema, $file, true);
|
||||
|
||||
// the package needs to be set absolute
|
||||
// besides, the automated namespace to package conversion has
|
||||
// not taken place yet so it needs to be done manually
|
||||
$database = simplexml_load_file($file);
|
||||
|
||||
if (isset($database['package'])) {
|
||||
// Do not use the prefix!
|
||||
// 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']);
|
||||
} else {
|
||||
throw new \RuntimeException(
|
||||
sprintf('%s : Please define a `package` attribute or a `namespace` attribute for schema `%s`',
|
||||
$bundle->getName(), $finalSchema->getBaseName())
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->input->hasOption('connection')) {
|
||||
$connections = $this->input->getOption('connection') ?: array($this->getDefaultConnection());
|
||||
|
||||
if (!in_array((string) $database['name'], $connections)) {
|
||||
// we skip this schema because the connection name doesn't
|
||||
// match the input values
|
||||
unset($this->tempSchemas[$tempSchema]);
|
||||
$filesystem->remove($file);
|
||||
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']);
|
||||
} else {
|
||||
$table['package'] = $database['package'];
|
||||
}
|
||||
}
|
||||
|
||||
file_put_contents($file, $database->asXML());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of final schema files that will be processed.
|
||||
*
|
||||
* @param KernelInterface $kernel The application kernel.
|
||||
* @param BundleInterface $bundle If given, only the bundle's schemas will
|
||||
* be returned.
|
||||
*
|
||||
* @return array A list of schemas.
|
||||
*/
|
||||
protected function getFinalSchemas(KernelInterface $kernel, BundleInterface $bundle = null)
|
||||
{
|
||||
if (null !== $bundle) {
|
||||
return $this->getSchemaLocator()->locateFromBundle($bundle);
|
||||
}
|
||||
|
||||
return $this->getSchemaLocator()->locateFromBundles($kernel->getBundles());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 runCommand(Command $command, array $parameters, InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
// add the command's name to the parameters
|
||||
array_unshift($parameters, $this->getName());
|
||||
|
||||
// merge the default parameters
|
||||
$parameters = array_merge(array(
|
||||
'--input-dir' => $this->cacheDir,
|
||||
'--verbose' => $input->getOption('verbose'),
|
||||
), $parameters);
|
||||
|
||||
if ($input->hasOption('platform')) {
|
||||
$parameters['--platform'] = $input->getOption('platform') ?: $this->getPlatform();
|
||||
}
|
||||
|
||||
$command->setApplication($this->getApplication());
|
||||
|
||||
// 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 'propel.json'.
|
||||
*/
|
||||
protected function createPropelConfigurationFile($file)
|
||||
{
|
||||
$config = array(
|
||||
'propel' => $this->getContainer()->getParameter('propel.configuration')
|
||||
);
|
||||
|
||||
file_put_contents($file, json_encode($config, JSON_PRETTY_PRINT));
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates a list of connection names to their DSN equivalents.
|
||||
*
|
||||
* @param array $connections The names.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getConnections(array $connections)
|
||||
{
|
||||
$dsnList = array();
|
||||
foreach ($connections as $connection) {
|
||||
$dsnList[] = sprintf('%s=%s', $connection, $this->getDsn($connection));
|
||||
}
|
||||
|
||||
return $dsnList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 $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);
|
||||
|
||||
return $connection['dsn'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Symfony\Component\Config\FileLocatorInterface
|
||||
*/
|
||||
protected function getSchemaLocator()
|
||||
{
|
||||
return $this->getContainer()->get('propel.schema_locator');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the package prefix for a given bundle.
|
||||
*
|
||||
* @param Bundle $bundle
|
||||
* @param string $baseDirectory The base directory to exclude from prefix.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getPackagePrefix(Bundle $bundle, $baseDirectory = '')
|
||||
{
|
||||
$parts = explode(DIRECTORY_SEPARATOR, realpath($bundle->getPath()));
|
||||
$length = count(explode('\\', $bundle->getNamespace())) * (-1);
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current Propel cache directory.
|
||||
*
|
||||
* @return string The current Propel cache directory.
|
||||
*/
|
||||
protected function getCacheDir()
|
||||
{
|
||||
return $this->cacheDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the database name from a given DSN
|
||||
*
|
||||
* @param string $dsn A DSN
|
||||
* @return string The database name extracted from the given DSN
|
||||
*/
|
||||
protected function parseDbName($dsn)
|
||||
{
|
||||
preg_match('#dbname=([a-zA-Z0-9\_]+)#', $dsn, $matches);
|
||||
|
||||
if (isset($matches[1])) {
|
||||
return $matches[1];
|
||||
}
|
||||
|
||||
// e.g. SQLite
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the migrations table.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getMigrationsTable()
|
||||
{
|
||||
$config = $this->getContainer()->getParameter('propel.configuration');
|
||||
|
||||
return $config['migrations']['tableName'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the default connection.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getDefaultConnection()
|
||||
{
|
||||
$config = $this->getContainer()->getParameter('propel.configuration');
|
||||
|
||||
return $config['generator']['defaultConnection'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the platform class from the configuration
|
||||
*
|
||||
* @return The platform class name.
|
||||
*/
|
||||
protected function getPlatform()
|
||||
{
|
||||
$config = $this->getContainer()->getParameter('propel.configuration');
|
||||
$className = $config['generator']['platformClass'];
|
||||
|
||||
return ($pos = strrpos($className, '\\')) === false ? $className : substr($className, $pos + 1);
|
||||
}
|
||||
}
|
130
Command/AclInitCommand.php
Normal file
130
Command/AclInitCommand.php
Normal file
|
@ -0,0 +1,130 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the PropelBundle package.
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @license MIT License
|
||||
*/
|
||||
|
||||
namespace Propel\PropelBundle\Command;
|
||||
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Output\Output;
|
||||
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
|
||||
/**
|
||||
* @author Toni Uebernickel <tuebernickel@gmail.com>
|
||||
*/
|
||||
class AclInitCommand extends AbstractCommand
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setDescription('Initialize "Access Control Lists" model and SQL')
|
||||
->addOption('force', null, InputOption::VALUE_NONE, 'Set this parameter to execute this action.')
|
||||
->addOption('connection', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, 'Connection to use. Example: default, bookstore')
|
||||
->setHelp(<<<EOT
|
||||
The <info>%command.name%</info> command connects to the database and executes all SQL statements required to setup the ACL database, it also generates the ACL model.
|
||||
|
||||
<info>php %command.full_name%</info>
|
||||
|
||||
The <info>--force</info> parameter has to be used to actually insert SQL.
|
||||
The <info>--connection</info> parameter allows you to change the connection to use.
|
||||
The default connection is the active connection (propel.dbal.default_connection).
|
||||
EOT
|
||||
)
|
||||
->setName('propel:acl:init')
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$outputDir = realpath($this->getApplication()->getKernel()->getRootDir().'/../');
|
||||
|
||||
// Generate ACL model
|
||||
$modelBuildCmd = new \Propel\Generator\Command\ModelBuildCommand();
|
||||
$modelBuildArgs = array(
|
||||
'--output-dir' => $outputDir,
|
||||
);
|
||||
|
||||
if ($this->runCommand($modelBuildCmd, $modelBuildArgs, $input, $output) === 0) {
|
||||
$output->writeln(sprintf(
|
||||
'>> <info>%20s</info> Generated model classes from <comment>%s</comment>',
|
||||
$this->getApplication()->getKernel()->getBundle('PropelBundle')->getName(),
|
||||
'acl_schema.xml'
|
||||
));
|
||||
} else {
|
||||
$this->writeTaskError($output, 'model:build');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Prepare SQL
|
||||
$sqlBuildCmd = new \Propel\Generator\Command\SqlBuildCommand();
|
||||
$sqlBuildArgs = array(
|
||||
'--connection' => $this->getConnections($input->getOption('connection')),
|
||||
'--output-dir' => $this->getCacheDir(),
|
||||
);
|
||||
|
||||
if ($this->runCommand($sqlBuildCmd, $sqlBuildArgs, $input, $output) === 0) {
|
||||
$this->writeSection(
|
||||
$output,
|
||||
'<comment>1</comment> <info>SQL file has been generated.</info>'
|
||||
);
|
||||
} else {
|
||||
$this->writeTaskError($output, 'sql:build');
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
// insert sql
|
||||
$sqlInsertCmd = new \Propel\Generator\Command\SqlInsertCommand();
|
||||
$sqlInsertArgs = array(
|
||||
'--connection' => $this->getConnections($input->getOption('connection')),
|
||||
);
|
||||
|
||||
if ($this->runCommand($sqlBuildCmd, $sqlBuildArgs, $input, $output) === 0) {
|
||||
$this->writeSection(
|
||||
$output,
|
||||
'<comment>1</comment> <info>SQL file has been inserted.</info>'
|
||||
);
|
||||
} else {
|
||||
$this->writeTaskError($output, 'sql:insert');
|
||||
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @note We override this method to only return the acl-related schema
|
||||
*/
|
||||
protected function getFinalSchemas(KernelInterface $kernel, BundleInterface $bundle = null)
|
||||
{
|
||||
$aclSchema = new \SplFileInfo($kernel->locateResource('@PropelBundle/Resources/acl_schema.xml'));
|
||||
|
||||
return array(
|
||||
array($kernel->getBundle('PropelBundle'), $aclSchema)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @note We override this method to modify the cache directory
|
||||
*/
|
||||
protected function initialize(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
parent::initialize($input, $output);
|
||||
|
||||
$this->cacheDir = $this->cacheDir . '/acl';
|
||||
|
||||
$this->setupBuildTimeFiles();
|
||||
}
|
||||
}
|
79
Command/BuildCommand.php
Normal file
79
Command/BuildCommand.php
Normal file
|
@ -0,0 +1,79 @@
|
|||
<?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\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;
|
||||
|
||||
/**
|
||||
* @author Kévin Gomez <contact@kevingomez.fr>
|
||||
*/
|
||||
class BuildCommand extends ContainerAwareCommand
|
||||
{
|
||||
/**
|
||||
* {@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_IS_ARRAY | InputOption::VALUE_OPTIONAL, 'Connection to use. Example: default, bookstore')
|
||||
))
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Command
|
||||
*
|
||||
* @throws \InvalidArgumentException When the target directory does not exist
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
if (!$input->getOption('sql')) {
|
||||
$in = new ArrayInput(array(
|
||||
'command' => 'propel:model:build',
|
||||
'--connection' => $input->getOption('connection'),
|
||||
'--verbose' => $input->getOption('verbose')
|
||||
));
|
||||
$cmd = $this->getApplication()->find('propel:model:build');
|
||||
$cmd->run($in, $output);
|
||||
}
|
||||
|
||||
if (!$input->getOption('classes')) {
|
||||
$in = new ArrayInput(array(
|
||||
'command' => 'propel:build:sql',
|
||||
'--connection' => $input->getOption('connection'),
|
||||
'--verbose' => $input->getOption('verbose'),
|
||||
));
|
||||
$cmd = $this->getApplication()->find('propel:sql:build');
|
||||
$cmd->run($in, $output);
|
||||
}
|
||||
|
||||
if ($input->getOption('insert-sql')) {
|
||||
$in = new ArrayInput(array(
|
||||
'command' => 'propel:sql:insert',
|
||||
'--connection' => $input->getOption('connection'),
|
||||
'--verbose' => $input->getOption('verbose'),
|
||||
));
|
||||
$cmd = $this->getApplication()->find('propel:sql:insert');
|
||||
$cmd->run($in, $output);
|
||||
}
|
||||
}
|
||||
}
|
101
Command/DatabaseCreateCommand.php
Normal file
101
Command/DatabaseCreateCommand.php
Normal file
|
@ -0,0 +1,101 @@
|
|||
<?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\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;
|
||||
|
||||
/**
|
||||
* DatabaseCreateCommand class.
|
||||
* Useful to create a database.
|
||||
*
|
||||
* @author William DURAND
|
||||
*/
|
||||
class DatabaseCreateCommand extends AbstractCommand
|
||||
{
|
||||
/**
|
||||
* @see Command
|
||||
*/
|
||||
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')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Command
|
||||
*
|
||||
* @throws \InvalidArgumentException When the target directory does not exist
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$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>');
|
||||
} else {
|
||||
$query = 'CREATE DATABASE '. $dbName .';';
|
||||
}
|
||||
|
||||
try {
|
||||
$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 created.</info>', $dbName));
|
||||
} catch (\Exception $e) {
|
||||
$this->writeSection($output, array(
|
||||
'[Propel] Exception caught',
|
||||
'',
|
||||
$e->getMessage()
|
||||
), 'fg=white;bg=red');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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='.$dbName.';#',
|
||||
'',
|
||||
$config['dsn']
|
||||
);
|
||||
|
||||
return $config;
|
||||
}
|
||||
}
|
95
Command/DatabaseDropCommand.php
Normal file
95
Command/DatabaseDropCommand.php
Normal file
|
@ -0,0 +1,95 @@
|
|||
<?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\Runtime\Propel;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* DatabaseDropCommand class.
|
||||
* Useful to drop a database.
|
||||
*
|
||||
* @author William DURAND
|
||||
*/
|
||||
class DatabaseDropCommand extends AbstractCommand
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('propel:database:drop')
|
||||
->setDescription('Drop a given database or the default one.')
|
||||
->setHelp(<<<EOT
|
||||
The <info>propel:database:drop</info> command will drop your database.
|
||||
|
||||
<info>php app/console propel:database:drop</info>
|
||||
|
||||
The <info>--force</info> parameter has to be used to actually drop the database.
|
||||
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
|
||||
)
|
||||
|
||||
->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.')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
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($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 .';';
|
||||
}
|
||||
|
||||
try {
|
||||
$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');
|
||||
}
|
||||
}
|
||||
}
|
63
Command/DatabaseReverseCommand.php
Normal file
63
Command/DatabaseReverseCommand.php
Normal 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\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')),
|
||||
);
|
||||
}
|
||||
}
|
92
Command/FixturesDumpCommand.php
Normal file
92
Command/FixturesDumpCommand.php
Normal file
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the PropelBundle package.
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @license MIT License
|
||||
*/
|
||||
|
||||
namespace Propel\PropelBundle\Command;
|
||||
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
|
||||
/**
|
||||
* FixturesDumpCommand.
|
||||
*
|
||||
* @author William DURAND <william.durand1@gmail.com>
|
||||
*/
|
||||
class FixturesDumpCommand extends AbstractCommand
|
||||
{
|
||||
/**
|
||||
* Default fixtures directory.
|
||||
* @var string
|
||||
*/
|
||||
protected $defaultFixturesDir = 'app/propel/fixtures';
|
||||
|
||||
/**
|
||||
* @see Command
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('propel:fixtures:dump')
|
||||
->setDescription('Dump data from database into YAML fixtures file.')
|
||||
->setHelp(<<<EOT
|
||||
The <info>propel:fixtures:dump</info> dumps data from database into YAML fixtures file.
|
||||
|
||||
<info>php app/console propel:fixtures:dump</info>
|
||||
|
||||
The <info>--connection</info> parameter allows you to change the connection to use.
|
||||
The <info>--dir</info> parameter allows you to change the output directory.
|
||||
The default connection is the active connection (propel.dbal.default_connection).
|
||||
EOT
|
||||
)
|
||||
|
||||
->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')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$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]")) {
|
||||
$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');
|
||||
|
||||
try {
|
||||
$dumper->dump($filename, $input->getOption('connection'));
|
||||
} catch (\Exception $e) {
|
||||
$this->writeSection($output, array(
|
||||
'[Propel] Exception',
|
||||
'',
|
||||
$e->getMessage()), 'fg=white;bg=red');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->writeNewFile($output, $filename);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
326
Command/FixturesLoadCommand.php
Normal file
326
Command/FixturesLoadCommand.php
Normal file
|
@ -0,0 +1,326 @@
|
|||
<?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\ArrayInput;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
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\Bundle\BundleInterface;
|
||||
|
||||
/**
|
||||
* FixturesLoadCommand
|
||||
*
|
||||
* @author William DURAND <william.durand1@gmail.com>
|
||||
*/
|
||||
class FixturesLoadCommand extends AbstractCommand
|
||||
{
|
||||
/**
|
||||
* Default fixtures directory.
|
||||
* @var string
|
||||
*/
|
||||
private $defaultFixturesDir = 'app/propel/fixtures';
|
||||
|
||||
/**
|
||||
* Absolute path for fixtures directory
|
||||
* @var string
|
||||
*/
|
||||
private $absoluteFixturesPath = '';
|
||||
|
||||
/**
|
||||
* Filesystem for manipulating files
|
||||
* @var \Symfony\Component\Filesystem\Filesystem
|
||||
*/
|
||||
private $filesystem = null;
|
||||
|
||||
/**
|
||||
* @see Command
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('propel:fixtures:load')
|
||||
->setDescription('Load XML, SQL and/or YAML fixtures')
|
||||
->setHelp(<<<EOT
|
||||
The <info>propel:fixtures:load</info> loads <info>XML</info>, <info>SQL</info> and/or <info>YAML</info> fixtures.
|
||||
|
||||
<info>php app/console propel:fixtures:load</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).
|
||||
|
||||
The <info>--dir</info> parameter allows you to change the directory that contains <info>XML</info> or/and <info>SQL</info> fixtures files <comment>(default: app/propel/fixtures)</comment>.
|
||||
|
||||
The <info>--xml</info> parameter allows you to load only <info>XML</info> fixtures.
|
||||
The <info>--sql</info> parameter allows you to load only <info>SQL</info> fixtures.
|
||||
The <info>--yml</info> parameter allows you to load only <info>YAML</info> fixtures.
|
||||
|
||||
You can mix <info>--xml</info>, <info>--sql</info> and <info>--yml</info> parameters to load XML, YAML and SQL fixtures at the same time.
|
||||
If none of this parameter is set, all XML, YAML and SQL files in the directory will be load.
|
||||
|
||||
XML fixtures files are the same XML files you can get with the command <info>propel:data-dump</info>:
|
||||
<comment>
|
||||
<Fixtures>
|
||||
<Object Namespace="Awesome">
|
||||
<o1 Title="My title" MyFoo="bar" />
|
||||
</Object>
|
||||
<Related Namespace="Awesome">
|
||||
<r1 ObjectId="o1" Description="Hello world !" />
|
||||
</Related>
|
||||
</Fixtures>
|
||||
</comment>
|
||||
|
||||
YAML fixtures are:
|
||||
<comment>
|
||||
\Awesome\Object:
|
||||
o1:
|
||||
Title: My title
|
||||
MyFoo: bar
|
||||
|
||||
\Awesome\Related:
|
||||
r1:
|
||||
ObjectId: o1
|
||||
Description: Hello world !
|
||||
</comment>
|
||||
EOT
|
||||
)
|
||||
|
||||
->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')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$this->filesystem = new Filesystem();
|
||||
|
||||
if (null !== $this->bundle) {
|
||||
$this->absoluteFixturesPath = $this->getFixturesPath($this->bundle);
|
||||
} else {
|
||||
$this->absoluteFixturesPath = realpath($this->getApplication()->getKernel()->getRootDir() . '/../' . $input->getOption('dir'));
|
||||
}
|
||||
|
||||
if (!$this->absoluteFixturesPath && !file_exists($this->absoluteFixturesPath)) {
|
||||
return $this->writeSection($output, array(
|
||||
'The fixtures directory "' . $this->absoluteFixturesPath . '" does not exist.'
|
||||
), 'fg=white;bg=red');
|
||||
}
|
||||
|
||||
$noOptions = !$input->getOption('xml') && !$input->getOption('sql') && !$input->getOption('yml');
|
||||
|
||||
if ($input->getOption('sql') || $noOptions) {
|
||||
if (-1 === $this->loadSqlFixtures($input, $output)) {
|
||||
$output->writeln('No <info>SQL</info> fixtures found.');
|
||||
}
|
||||
}
|
||||
|
||||
if ($input->getOption('xml') || $noOptions) {
|
||||
if (-1 === $this->loadFixtures($input, $output, 'xml')) {
|
||||
$output->writeln('No <info>XML</info> fixtures found.');
|
||||
}
|
||||
}
|
||||
|
||||
if ($input->getOption('yml') || $noOptions) {
|
||||
if (-1 === $this->loadFixtures($input, $output, 'yml')) {
|
||||
$output->writeln('No <info>YML</info> fixtures found.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load fixtures
|
||||
*
|
||||
* @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)
|
||||
{
|
||||
if (null === $type) {
|
||||
return;
|
||||
}
|
||||
|
||||
$datas = $this->getFixtureFiles($type);
|
||||
|
||||
if (count(iterator_to_array($datas)) === 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
$connectionName = $input->getOption('connection');
|
||||
|
||||
if ('yml' === $type) {
|
||||
$loader = $this->getContainer()->get('propel.loader.yaml');
|
||||
} elseif ('xml' === $type) {
|
||||
$loader = $this->getContainer()->get('propel.loader.xml');
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$nb = $loader->load($datas, $connectionName);
|
||||
} catch (\Exception $e) {
|
||||
$this->writeSection($output, array(
|
||||
'[Propel] Exception',
|
||||
'',
|
||||
$e->getMessage()), 'fg=white;bg=red');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$output->writeln(sprintf('<comment>%s</comment> %s fixtures file%s loaded.', $nb, strtoupper($type), $nb > 1 ? 's' : ''));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load SQL fixtures
|
||||
*
|
||||
* @param \Symfony\Component\Console\Input\InputInterface $input
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface $output
|
||||
*/
|
||||
protected function loadSqlFixtures(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$tmpdir = $this->getCacheDir();
|
||||
$datas = $this->getFixtureFiles('sql');
|
||||
|
||||
$this->prepareCache($tmpdir);
|
||||
|
||||
$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() . '=' . $connectionName . PHP_EOL;
|
||||
$this->filesystem->copy($data, $tmpdir . '/' . $data->getFilename(), true);
|
||||
}
|
||||
|
||||
if ('' === $sqldbContent) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
$sqldbFile = $tmpdir . '/sqldb.map';
|
||||
file_put_contents($sqldbFile, $sqldbContent);
|
||||
|
||||
if (!$this->insertSql($connectionName, $input, $output)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
$this->filesystem->remove($tmpdir);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the cache directory
|
||||
*
|
||||
* @param string $tmpdir The temporary directory path.
|
||||
*/
|
||||
protected function prepareCache($tmpdir)
|
||||
{
|
||||
// Recreate a propel directory in cache
|
||||
$this->filesystem->remove($tmpdir);
|
||||
$this->filesystem->mkdir($tmpdir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert SQL
|
||||
*/
|
||||
protected function insertSql($connectionName, InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$parameters = array(
|
||||
'--connection' => array($connectionName),
|
||||
'--verbose' => $input->getOption('verbose'),
|
||||
);
|
||||
|
||||
// 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 $ret === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the fixtures files to load.
|
||||
*
|
||||
* @param string $type The extension of the files.
|
||||
* @param string $in The directory in which we search the files. If null,
|
||||
* we'll use the absoluteFixturesPath property.
|
||||
*
|
||||
* @return \Iterator An iterator through the files.
|
||||
*/
|
||||
protected function getFixtureFiles($type = 'sql', $in = null)
|
||||
{
|
||||
$finder = new Finder();
|
||||
$finder->sortByName()->name('*.' . $type);
|
||||
|
||||
$files = $finder->in(null !== $in ? $in : $this->absoluteFixturesPath);
|
||||
|
||||
if (null === $this->bundle) {
|
||||
return $files;
|
||||
}
|
||||
|
||||
$finalFixtureFiles = array();
|
||||
foreach ($files as $file) {
|
||||
$fixtureFilePath = str_replace($this->getFixturesPath($this->bundle) . DIRECTORY_SEPARATOR, '', $file->getRealPath());
|
||||
$logicalName = sprintf('@%s/Resources/fixtures/%s', $this->bundle->getName(), $fixtureFilePath);
|
||||
$finalFixtureFiles[] = new \SplFileInfo($this->getFileLocator()->locate($logicalName));
|
||||
}
|
||||
|
||||
return new \ArrayIterator($finalFixtureFiles);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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');
|
||||
}
|
||||
}
|
257
Command/FormGenerateCommand.php
Normal file
257
Command/FormGenerateCommand.php
Normal file
|
@ -0,0 +1,257 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the PropelBundle package.
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @license MIT License
|
||||
*/
|
||||
|
||||
namespace Propel\PropelBundle\Command;
|
||||
|
||||
use Propel\Generator\Config\GeneratorConfig;
|
||||
use Propel\Generator\Command\ModelBuildCommand as BaseModelBuildCommand;
|
||||
use Propel\Generator\Model\Database;
|
||||
use Propel\Generator\Model\Table;
|
||||
use Propel\Generator\Manager\ModelManager;
|
||||
use Propel\Runtime\Propel;
|
||||
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
|
||||
|
||||
/**
|
||||
* @author William DURAND <william.durand1@gmail.com>
|
||||
*/
|
||||
class FormGenerateCommand extends AbstractCommand
|
||||
{
|
||||
const DEFAULT_FORM_TYPE_DIRECTORY = '/Form/Type';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('propel:form:generate')
|
||||
->setDescription('Generate Form types stubs based on the schema.xml')
|
||||
|
||||
->addOption('force', 'f', InputOption::VALUE_NONE, 'Overwrite existing Form types')
|
||||
->addOption('platform', null, InputOption::VALUE_REQUIRED, 'The platform')
|
||||
->addArgument('bundle', InputArgument::REQUIRED, '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>
|
||||
|
||||
The <info>--force</info> parameter allows you to overwrite existing files.
|
||||
EOT
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$kernel = $this->getApplication()->getKernel();
|
||||
$models = $input->getArgument('models');
|
||||
$force = $input->getOption('force');
|
||||
|
||||
if (!$this->bundle) {
|
||||
throw new \InvalidArgumentException('No valid bundle given');
|
||||
}
|
||||
|
||||
$this->setupBuildTimeFiles();
|
||||
|
||||
if (!($schemas = $this->getFinalSchemas($kernel, $this->bundle))) {
|
||||
$output->writeln(sprintf('No <comment>*schemas.xml</comment> files found in bundle <comment>%s</comment>.', $this->bundle->getName()));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$manager = $this->getModelManager($input, $schemas);
|
||||
|
||||
foreach ($manager->getDataModels() as $dataModel) {
|
||||
foreach ($dataModel->getDatabases() as $database) {
|
||||
$this->createFormTypeFromDatabase($this->bundle, $database, $models, $output, $force);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
foreach ($database->getTables() as $table) {
|
||||
if (0 < count($models) && !in_array($table->getPhpName(), $models)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$file = new \SplFileInfo(sprintf('%s/%sType.php', $dir, $table->getPhpName()));
|
||||
|
||||
if (!file_exists($file) || true === $force) {
|
||||
$this->writeFormType($bundle, $table, $file, $force, $output);
|
||||
} else {
|
||||
$output->writeln(sprintf('File <comment>%-60s</comment> exists, skipped. Try the <info>--force</info> option.', $this->getRelativeFileName($file)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 (!$fs->exists($dir = $bundle->getPath() . self::DEFAULT_FORM_TYPE_DIRECTORY)) {
|
||||
$fs->mkdir($dir);
|
||||
$this->writeNewDirectory($output, $dir);
|
||||
}
|
||||
|
||||
return $dir;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
file_put_contents($file->getPathName(), $formTypeContent);
|
||||
$this->writeNewFile($output, $this->getRelativeFileName($file) . ($force ? ' (forced)' : ''));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the fields in the FormType.
|
||||
*
|
||||
* @param Table $table Table from which the fields will be extracted.
|
||||
* @param string $formTypeContent FormType skeleton.
|
||||
*
|
||||
* @return string The FormType code.
|
||||
*/
|
||||
protected function addFields(Table $table, $formTypeContent)
|
||||
{
|
||||
$buildCode = '';
|
||||
foreach ($table->getColumns() as $column) {
|
||||
if (!$column->isPrimaryKey()) {
|
||||
$buildCode .= sprintf("\n \$builder->add('%s');", lcfirst($column->getPhpName()));
|
||||
}
|
||||
}
|
||||
|
||||
return str_replace('##BUILD_CODE##', $buildCode, $formTypeContent);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \SplFileInfo $file
|
||||
* @return string
|
||||
*/
|
||||
protected function getRelativeFileName(\SplFileInfo $file)
|
||||
{
|
||||
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 = array(
|
||||
'propel.platform.class' => $input->getOption('platform'),
|
||||
'propel.builder.object.class' => BaseModelBuildCommand::DEFAULT_OBJECT_BUILDER,
|
||||
'propel.builder.objectstub.class' => BaseModelBuildCommand::DEFAULT_OBJECT_STUB_BUILDER,
|
||||
'propel.builder.objectmultiextend.class' => BaseModelBuildCommand::DEFAULT_MULTIEXTEND_OBJECT_BUILDER,
|
||||
'propel.builder.query.class' => BaseModelBuildCommand::DEFAULT_QUERY_BUILDER,
|
||||
'propel.builder.querystub.class' => BaseModelBuildCommand::DEFAULT_QUERY_STUB_BUILDER,
|
||||
'propel.builder.queryinheritance.class' => BaseModelBuildCommand::DEFAULT_QUERY_INHERITANCE_BUILDER,
|
||||
'propel.builder.queryinheritancestub.class' => BaseModelBuildCommand::DEFAULT_QUERY_INHERITANCE_STUB_BUILDER,
|
||||
'propel.builder.tablemap.class' => BaseModelBuildCommand::DEFAULT_TABLEMAP_BUILDER,
|
||||
'propel.builder.pluralizer.class' => BaseModelBuildCommand::DEFAULT_PLURALIZER,
|
||||
'propel.disableIdentifierQuoting' => true,
|
||||
'propel.packageObjectModel' => true,
|
||||
'propel.namespace.autoPackage' => true,
|
||||
'propel.addGenericAccessors' => true,
|
||||
'propel.addGenericMutators' => true,
|
||||
'propel.addSaveMethod' => true,
|
||||
'propel.addTimeStamp' => false,
|
||||
'propel.addValidateMethod' => true,
|
||||
'propel.addHooks' => true,
|
||||
'propel.namespace.map' => 'Map',
|
||||
'propel.useLeftJoinsInDoJoinMethods' => true,
|
||||
'propel.emulateForeignKeyConstraints' => false,
|
||||
'propel.schema.autoPrefix' => false,
|
||||
'propel.dateTimeClass' => '\DateTime',
|
||||
// MySQL specific
|
||||
'propel.mysql.tableType' => BaseModelBuildCommand::DEFAULT_MYSQL_ENGINE,
|
||||
'propel.mysql.tableEngineKeyword' => 'ENGINE',
|
||||
);
|
||||
|
||||
// merge the custom build properties
|
||||
$buildProperties = parse_ini_file($this->getCacheDir().'/build.properties');
|
||||
$generatorConfig = array_merge($generatorConfig, $buildProperties);
|
||||
|
||||
return new GeneratorConfig($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;
|
||||
}
|
||||
}
|
84
Command/FormattingHelpers.php
Normal file
84
Command/FormattingHelpers.php
Normal file
|
@ -0,0 +1,84 @@
|
|||
<?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\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* @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),
|
||||
'',
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 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.' : '';
|
||||
|
||||
return $this->writeSection($output, array(
|
||||
'[Propel] Error',
|
||||
'',
|
||||
'An error has occured during the "' . $taskName . '" task process.' . $moreText
|
||||
), 'fg=white;bg=red');
|
||||
}
|
||||
}
|
55
Command/GraphvizGenerateCommand.php
Normal file
55
Command/GraphvizGenerateCommand.php
Normal file
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the PropelBundle package.
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @license MIT License
|
||||
*/
|
||||
|
||||
namespace Propel\PropelBundle\Command;
|
||||
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
|
||||
use Propel\Generator\Command\GraphvizGenerateCommand as BaseGraphvizGenerateCommand;
|
||||
|
||||
/**
|
||||
* @author Kévin Gomez <contact@kevingomez.fr>
|
||||
*/
|
||||
class GraphvizGenerateCommand extends WrappedCommand
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
parent::configure();
|
||||
|
||||
$this
|
||||
->setName('propel:graphviz:generate')
|
||||
->setDescription('Generate Graphviz files (.dot)')
|
||||
|
||||
->addOption('output-dir', null, InputOption::VALUE_REQUIRED, 'The output directory', BaseGraphvizGenerateCommand::DEFAULT_OUTPUT_DIRECTORY)
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createSubCommandInstance()
|
||||
{
|
||||
return new BaseGraphvizGenerateCommand();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getSubCommandArguments(InputInterface $input)
|
||||
{
|
||||
return array(
|
||||
'--output-dir' => $input->getOption('output-dir'),
|
||||
);
|
||||
}
|
||||
}
|
69
Command/MigrationDiffCommand.php
Normal file
69
Command/MigrationDiffCommand.php
Normal 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\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->getApplication()->getKernel()->getRootDir().'/propel/migrations';
|
||||
|
||||
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'),
|
||||
);
|
||||
}
|
||||
}
|
61
Command/MigrationDownCommand.php
Normal file
61
Command/MigrationDownCommand.php
Normal file
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the PropelBundle package.
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @license MIT License
|
||||
*/
|
||||
|
||||
namespace Propel\PropelBundle\Command;
|
||||
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
|
||||
use 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')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createSubCommandInstance()
|
||||
{
|
||||
return new BaseMigrationCommand();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getSubCommandArguments(InputInterface $input)
|
||||
{
|
||||
$defaultOutputDir = $this->getApplication()->getKernel()->getRootDir().'/propel/migrations';
|
||||
|
||||
return array(
|
||||
'--connection' => $this->getConnections($input->getOption('connection')),
|
||||
'--migration-table' => $input->getOption('migration-table') ?: $this->getMigrationsTable(),
|
||||
'--output-dir' => $input->getOption('output-dir') ?: $defaultOutputDir,
|
||||
);
|
||||
}
|
||||
}
|
61
Command/MigrationStatusCommand.php
Normal file
61
Command/MigrationStatusCommand.php
Normal file
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the PropelBundle package.
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @license MIT License
|
||||
*/
|
||||
|
||||
namespace Propel\PropelBundle\Command;
|
||||
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
|
||||
use Propel\Generator\Command\MigrationStatusCommand as BaseMigrationCommand;
|
||||
|
||||
/**
|
||||
* @author Kévin Gomez <contact@kevingomez.fr>
|
||||
*/
|
||||
class MigrationStatusCommand extends WrappedCommand
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
parent::configure();
|
||||
|
||||
$this
|
||||
->setName('propel:migration:status')
|
||||
->setDescription('Get migration status')
|
||||
|
||||
->addOption('connection', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, 'Connection to use. Example: default, bookstore')
|
||||
->addOption('migration-table', null, InputOption::VALUE_OPTIONAL, 'Migration table name (if none given, the configured table is used)', null)
|
||||
->addOption('output-dir', null, InputOption::VALUE_OPTIONAL, 'The output directory')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createSubCommandInstance()
|
||||
{
|
||||
return new BaseMigrationCommand();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getSubCommandArguments(InputInterface $input)
|
||||
{
|
||||
$defaultOutputDir = $this->getApplication()->getKernel()->getRootDir().'/propel/migrations';
|
||||
|
||||
return array(
|
||||
'--connection' => $this->getConnections($input->getOption('connection')),
|
||||
'--migration-table' => $input->getOption('migration-table') ?: $this->getMigrationsTable(),
|
||||
'--output-dir' => $input->getOption('output-dir') ?: $defaultOutputDir,
|
||||
);
|
||||
}
|
||||
}
|
61
Command/MigrationUpCommand.php
Normal file
61
Command/MigrationUpCommand.php
Normal file
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the PropelBundle package.
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @license MIT License
|
||||
*/
|
||||
|
||||
namespace Propel\PropelBundle\Command;
|
||||
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
|
||||
use 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 migrations up')
|
||||
|
||||
->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')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createSubCommandInstance()
|
||||
{
|
||||
return new BaseMigrationCommand();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getSubCommandArguments(InputInterface $input)
|
||||
{
|
||||
$defaultOutputDir = $this->getApplication()->getKernel()->getRootDir().'/propel/migrations';
|
||||
|
||||
return array(
|
||||
'--connection' => $this->getConnections($input->getOption('connection')),
|
||||
'--migration-table' => $input->getOption('migration-table') ?: $this->getMigrationsTable(),
|
||||
'--output-dir' => $input->getOption('output-dir') ?: $defaultOutputDir,
|
||||
);
|
||||
}
|
||||
}
|
59
Command/ModelBuildCommand.php
Normal file
59
Command/ModelBuildCommand.php
Normal file
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the PropelBundle package.
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @license MIT License
|
||||
*/
|
||||
|
||||
namespace Propel\PropelBundle\Command;
|
||||
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
|
||||
use Propel\Generator\Command\ModelBuildCommand as BaseModelBuildCommand;
|
||||
|
||||
/**
|
||||
* @author Kévin Gomez <contact@kevingomez.fr>
|
||||
*/
|
||||
class ModelBuildCommand extends WrappedCommand
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
parent::configure();
|
||||
|
||||
$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')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createSubCommandInstance()
|
||||
{
|
||||
return new BaseModelBuildCommand();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getSubCommandArguments(InputInterface $input)
|
||||
{
|
||||
$outputDir = $this->getApplication()->getKernel()->getRootDir().'/../';
|
||||
|
||||
return array(
|
||||
'--output-dir' => $outputDir,
|
||||
);
|
||||
}
|
||||
}
|
54
Command/SqlBuildCommand.php
Normal file
54
Command/SqlBuildCommand.php
Normal file
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the PropelBundle package.
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @license MIT License
|
||||
*/
|
||||
|
||||
namespace Propel\PropelBundle\Command;
|
||||
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
|
||||
/**
|
||||
* @author Kévin Gomez <contact@kevingomez.fr>
|
||||
*/
|
||||
class SqlBuildCommand extends WrappedCommand
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
parent::configure();
|
||||
|
||||
$this
|
||||
->setName('propel:sql:build')
|
||||
->setDescription('Build SQL files')
|
||||
|
||||
->addOption('connection', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, 'Connection to use. Example: default, bookstore')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createSubCommandInstance()
|
||||
{
|
||||
return new \Propel\Generator\Command\SqlBuildCommand();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getSubCommandArguments(InputInterface $input)
|
||||
{
|
||||
return array(
|
||||
'--output-dir' => $this->getCacheDir(),
|
||||
'--connection' => $this->getConnections($input->getOption('connection')),
|
||||
);
|
||||
}
|
||||
}
|
51
Command/SqlInsertCommand.php
Normal file
51
Command/SqlInsertCommand.php
Normal file
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the PropelBundle package.
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @license MIT License
|
||||
*/
|
||||
|
||||
namespace Propel\PropelBundle\Command;
|
||||
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
|
||||
/**
|
||||
* @author Kévin Gomez <contact@kevingomez.fr>
|
||||
*/
|
||||
class SqlInsertCommand extends WrappedCommand
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('propel:sql:insert')
|
||||
->setDescription('Insert SQL statements')
|
||||
->addOption('connection', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, 'Connection to use. Example: default, bookstore')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createSubCommandInstance()
|
||||
{
|
||||
return new \Propel\Generator\Command\SqlInsertCommand();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getSubCommandArguments(InputInterface $input)
|
||||
{
|
||||
return array(
|
||||
'--connection' => $this->getConnections($input->getOption('connection')),
|
||||
'--sql-dir' => $this->cacheDir,
|
||||
);
|
||||
}
|
||||
}
|
120
Command/TableDropCommand.php
Normal file
120
Command/TableDropCommand.php
Normal file
|
@ -0,0 +1,120 @@
|
|||
<?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\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;
|
||||
|
||||
/**
|
||||
* @author Kévin Gomez <contact@kevingomez.fr>
|
||||
*/
|
||||
class TableDropCommand extends ContainerAwareCommand
|
||||
{
|
||||
use FormattingHelpers;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('propel:table:drop')
|
||||
->setDescription('Drop a given table or all tables in the database.')
|
||||
|
||||
->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.')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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 ('prod' === $this->getApplication()->getKernel()->getEnvironment()) {
|
||||
$count = $nbTable ?: 'all';
|
||||
|
||||
$this->writeSection(
|
||||
$output,
|
||||
'WARNING: you are about to drop ' . $count . ' table' . $tablePlural . ' in production !',
|
||||
'bg=red;fg=white'
|
||||
);
|
||||
|
||||
if (false === $this->askConfirmation($output, 'Are you sure ? (y/n) ', false)) {
|
||||
$output->writeln('<info>Aborted, nice decision !</info>');
|
||||
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$showStatement = $connection->prepare('SHOW TABLES;');
|
||||
$showStatement->execute();
|
||||
|
||||
$allTables = $showStatement->fetchAll(\PDO::FETCH_COLUMN);
|
||||
|
||||
if ($nbTable) {
|
||||
foreach ($tablesToDelete as $tableToDelete) {
|
||||
if (!array_search($tableToDelete, $allTables)) {
|
||||
throw new \InvalidArgumentException(sprintf('Table %s doesn\'t exist in the database.', $tableToDelete));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$tablesToDelete = $allTables;
|
||||
}
|
||||
|
||||
$connection->exec('SET FOREIGN_KEY_CHECKS = 0;');
|
||||
|
||||
array_walk($tablesToDelete, function (&$table, $key, $dbAdapter) {
|
||||
$table = $dbAdapter->quoteIdentifierTable($table);
|
||||
}, $adapter);
|
||||
|
||||
$tablesToDelete = join(', ', $tablesToDelete);
|
||||
|
||||
if ('' !== $tablesToDelete) {
|
||||
$connection->exec('DROP TABLE ' . $tablesToDelete . ' ;');
|
||||
|
||||
$output->writeln(sprintf('Table' . $tablePlural . ' <info><comment>%s</comment> has been dropped.</info>', $tablesToDelete));
|
||||
} else {
|
||||
$output->writeln('<info>No tables have been dropped</info>');
|
||||
}
|
||||
|
||||
$connection->exec('SET FOREIGN_KEY_CHECKS = 1;');
|
||||
} catch (\Exception $e) {
|
||||
$this->writeSection($output, array(
|
||||
'[Propel] Exception caught',
|
||||
'',
|
||||
$e->getMessage()
|
||||
), 'fg=white;bg=red');
|
||||
}
|
||||
}
|
||||
}
|
58
Command/WrappedCommand.php
Normal file
58
Command/WrappedCommand.php
Normal file
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the PropelBundle package.
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @license MIT License
|
||||
*/
|
||||
|
||||
namespace Propel\PropelBundle\Command;
|
||||
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
}
|
81
Controller/PanelController.php
Normal file
81
Controller/PanelController.php
Normal file
|
@ -0,0 +1,81 @@
|
|||
<?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\Controller;
|
||||
|
||||
use Propel\Runtime\Propel;
|
||||
use Symfony\Component\DependencyInjection\ContainerAware;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* PanelController is designed to display information in the Propel Panel.
|
||||
*
|
||||
* @author William DURAND <william.durand1@gmail.com>
|
||||
*/
|
||||
class PanelController extends ContainerAware
|
||||
{
|
||||
/**
|
||||
* This method renders the global Propel configuration.
|
||||
*/
|
||||
public function configurationAction()
|
||||
{
|
||||
$templating = $this->container->get('templating');
|
||||
|
||||
return $templating->renderResponse(
|
||||
'PropelBundle:Panel:configuration.html.twig',
|
||||
array(
|
||||
'propel_version' => Propel::VERSION,
|
||||
'configuration' => $this->container->getParameter('propel.configuration'),
|
||||
'logging' => $this->container->getParameter('propel.logging'),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the profiler panel for the given token.
|
||||
*
|
||||
* @param string $token The profiler token
|
||||
* @param string $connection The connection name
|
||||
* @param integer $query
|
||||
*
|
||||
* @return Symfony\Component\HttpFoundation\Response A Response instance
|
||||
*/
|
||||
public function explainAction($token, $connection, $query)
|
||||
{
|
||||
$profiler = $this->container->get('profiler');
|
||||
$profiler->disable();
|
||||
|
||||
$profile = $profiler->loadProfile($token);
|
||||
$queries = $profile->getCollector('propel')->getQueries();
|
||||
|
||||
if (!isset($queries[$query])) {
|
||||
return new Response('This query does not exist.');
|
||||
}
|
||||
|
||||
// Open the connection
|
||||
$con = Propel::getConnection($connection);
|
||||
|
||||
try {
|
||||
$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',
|
||||
array(
|
||||
'data' => $results,
|
||||
'query' => $query,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
108
DataCollector/PropelDataCollector.php
Normal file
108
DataCollector/PropelDataCollector.php
Normal file
|
@ -0,0 +1,108 @@
|
|||
<?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\PropelBundle\DataCollector;
|
||||
|
||||
use Propel\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->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());
|
||||
}
|
||||
}
|
160
DataFixtures/AbstractDataHandler.php
Normal file
160
DataFixtures/AbstractDataHandler.php
Normal file
|
@ -0,0 +1,160 @@
|
|||
<?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;
|
||||
|
||||
use Propel\Runtime\Map\DatabaseMap;
|
||||
use Propel\Runtime\Propel;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
|
||||
/**
|
||||
* @author William Durand <william.durand1@gmail.com>
|
||||
*/
|
||||
abstract class AbstractDataHandler
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $rootDir;
|
||||
|
||||
/**
|
||||
* @var \PDO
|
||||
*/
|
||||
protected $con;
|
||||
|
||||
/**
|
||||
* @var DatabaseMap
|
||||
*/
|
||||
protected $dbMap;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $datasources = array();
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*
|
||||
* @param string $rootDir The root directory.
|
||||
* @param array $datasources
|
||||
*/
|
||||
public function __construct($rootDir, array $datasources)
|
||||
{
|
||||
$this->rootDir = $rootDir;
|
||||
$this->datasources = $datasources;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getRootDir()
|
||||
{
|
||||
return $this->rootDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load Map builders.
|
||||
*
|
||||
* @param string $connectionName A connection name.
|
||||
*/
|
||||
protected function loadMapBuilders($connectionName = null)
|
||||
{
|
||||
if (null !== $this->dbMap) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->dbMap = Propel::getDatabaseMap($connectionName);
|
||||
if (0 === count($this->dbMap->getTables())) {
|
||||
$finder = new Finder();
|
||||
$files = $finder
|
||||
->files()->name('*TableMap.php')
|
||||
->in($this->getModelSearchPaths($connectionName))
|
||||
->notName('TableMap.php')
|
||||
->exclude('PropelBundle')
|
||||
->exclude('Tests');
|
||||
|
||||
foreach ($files as $file) {
|
||||
$class = $this->guessFullClassName($file->getRelativePath(), basename($file, '.php'));
|
||||
|
||||
if (null !== $class && $this->isInDatabase($class, $connectionName)) {
|
||||
$this->dbMap->addTableFromMapClass($class);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a table is in a database
|
||||
*
|
||||
* @param string $class
|
||||
* @param string $connectionName
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function isInDatabase($class, $connectionName)
|
||||
{
|
||||
return constant($class.'::DATABASE_NAME') === $connectionName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to find a valid class with its namespace based on the filename.
|
||||
* Based on the PSR-0 standard, the namespace should be the directory structure.
|
||||
*
|
||||
* @param string $path The relative path of the file.
|
||||
* @param string $shortClassName The short class name aka the filename without extension.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
private function guessFullClassName($path, $shortClassName)
|
||||
{
|
||||
$array = array();
|
||||
$path = str_replace('/', '\\', $path);
|
||||
|
||||
$array[] = $path;
|
||||
while ($pos = strpos($path, '\\')) {
|
||||
$path = substr($path, $pos + 1, strlen($path));
|
||||
$array[] = $path;
|
||||
}
|
||||
|
||||
$array = array_reverse($array);
|
||||
while ($ns = array_pop($array)) {
|
||||
|
||||
$class = $ns . '\\' . $shortClassName;
|
||||
if (class_exists($class)) {
|
||||
return $class;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the search path for models out of the configuration.
|
||||
*
|
||||
* @param string $connectionName A connection name.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
protected function getModelSearchPaths($connectionName)
|
||||
{
|
||||
$searchPath = array();
|
||||
|
||||
if (!empty($this->datasources[$connectionName]['connection']['model_paths'])) {
|
||||
$modelPaths = $this->datasources[$connectionName]['connection']['model_paths'];
|
||||
foreach ($modelPaths as $modelPath) {
|
||||
$searchPath[] = $this->getRootDir() . '/../' . $modelPath;
|
||||
}
|
||||
} else {
|
||||
$searchPath[] = $this->getRootDir() . '/../';
|
||||
}
|
||||
|
||||
return $searchPath;
|
||||
}
|
||||
}
|
242
DataFixtures/Dumper/AbstractDataDumper.php
Normal file
242
DataFixtures/Dumper/AbstractDataDumper.php
Normal file
|
@ -0,0 +1,242 @@
|
|||
<?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\Dumper;
|
||||
|
||||
use \PDO;
|
||||
use Propel\PropelBundle\DataFixtures\AbstractDataHandler;
|
||||
use Propel\Generator\Model\PropelTypes;
|
||||
use Propel\Runtime\Propel;
|
||||
|
||||
/**
|
||||
* Abstract class to manage a common logic to dump data.
|
||||
*
|
||||
* @author William Durand <william.durand1@gmail.com>
|
||||
*/
|
||||
abstract class AbstractDataDumper extends AbstractDataHandler implements DataDumperInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function dump($filename, $connectionName = null)
|
||||
{
|
||||
if (null === $filename || '' === $filename) {
|
||||
throw new \Exception('Invalid filename provided.');
|
||||
}
|
||||
|
||||
$this->loadMapBuilders($connectionName);
|
||||
$this->con = Propel::getConnection($connectionName);
|
||||
|
||||
$array = $this->getDataAsArray($connectionName);
|
||||
$data = $this->transformArrayToData($array);
|
||||
|
||||
if (false === file_put_contents($filename, $data)) {
|
||||
throw new \Exception(sprintf('Cannot write file: %s', $filename));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms an array of data to a specific format
|
||||
* depending on the specialized dumper. It should return
|
||||
* a string content ready to write in a file.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function transformArrayToData($data);
|
||||
|
||||
/**
|
||||
* Dumps data to fixture from a given connection and
|
||||
* returns an array.
|
||||
*
|
||||
* @param string $connectionName The connection name
|
||||
* @return array
|
||||
*/
|
||||
protected function getDataAsArray()
|
||||
{
|
||||
$tables = array();
|
||||
foreach ($this->dbMap->getTables() as $table) {
|
||||
$tables[] = $table->getClassname();
|
||||
}
|
||||
|
||||
$tables = $this->fixOrderingOfForeignKeyData($tables);
|
||||
|
||||
$dumpData = array();
|
||||
foreach ($tables as $tableName) {
|
||||
$tableMap = $this->dbMap->getTable(constant(constant($tableName.'::TABLE_MAP').'::TABLE_NAME'));
|
||||
$hasParent = false;
|
||||
$haveParents = false;
|
||||
$fixColumn = null;
|
||||
|
||||
$shortTableName = substr($tableName, strrpos($tableName, '\\') + 1, strlen($tableName));
|
||||
|
||||
foreach ($tableMap->getColumns() as $column) {
|
||||
$col = strtolower($column->getName());
|
||||
if ($column->isForeignKey()) {
|
||||
$relatedTable = $this->dbMap->getTable($column->getRelatedTableName());
|
||||
if ($tableName === $relatedTable->getPhpName()) {
|
||||
if ($hasParent) {
|
||||
$haveParents = true;
|
||||
} else {
|
||||
$fixColumn = $column;
|
||||
$hasParent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($haveParents) {
|
||||
// unable to dump tables having multi-recursive references
|
||||
continue;
|
||||
}
|
||||
|
||||
// get db info
|
||||
$resultsSets = array();
|
||||
if ($hasParent) {
|
||||
$resultsSets[] = $this->fixOrderingOfForeignKeyDataInSameTable($resultsSets, $tableName, $fixColumn);
|
||||
} else {
|
||||
$in = array();
|
||||
foreach ($tableMap->getColumns() as $column) {
|
||||
$in[] = strtolower($column->getName());
|
||||
}
|
||||
$stmt = $this
|
||||
->con
|
||||
->query(sprintf('SELECT %s FROM %s', implode(',', $in), constant(constant($tableName.'::TABLE_MAP').'::TABLE_NAME')));
|
||||
|
||||
$set = array();
|
||||
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$set[] = $row;
|
||||
}
|
||||
$resultsSets[] = $set;
|
||||
$stmt->close();
|
||||
unset($stmt);
|
||||
}
|
||||
|
||||
foreach ($resultsSets as $rows) {
|
||||
if (count($rows) > 0 && !isset($dumpData[$tableName])) {
|
||||
$dumpData[$tableName] = array();
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$pk = $shortTableName;
|
||||
$values = array();
|
||||
$primaryKeys = array();
|
||||
$foreignKeys = array();
|
||||
|
||||
foreach ($tableMap->getColumns() as $column) {
|
||||
$col = strtolower($column->getName());
|
||||
$isPrimaryKey = $column->isPrimaryKey();
|
||||
|
||||
if (null === $row[$col]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($isPrimaryKey) {
|
||||
$value = $row[$col];
|
||||
$pk .= '_'.$value;
|
||||
$primaryKeys[$col] = $value;
|
||||
}
|
||||
|
||||
if ($column->isForeignKey()) {
|
||||
$relatedTable = $this->dbMap->getTable($column->getRelatedTableName());
|
||||
if ($isPrimaryKey) {
|
||||
$foreignKeys[$col] = $row[$col];
|
||||
$primaryKeys[$col] = $relatedTable->getPhpName().'_'.$row[$col];
|
||||
} else {
|
||||
$values[$col] = $relatedTable->getPhpName().'_'.$row[$col];
|
||||
$values[$col] = strlen($row[$col]) ? $relatedTable->getPhpName().'_'.$row[$col] : '';
|
||||
}
|
||||
} elseif (!$isPrimaryKey || ($isPrimaryKey && !$tableMap->isUseIdGenerator())) {
|
||||
if (!empty($row[$col]) && PropelTypes::PHP_ARRAY === $column->getType()) {
|
||||
$serialized = substr($row[$col], 2, -2);
|
||||
$row[$col] = $serialized ? explode(' | ', $serialized) : array();
|
||||
}
|
||||
|
||||
// We did not want auto incremented primary keys
|
||||
$values[$col] = $row[$col];
|
||||
}
|
||||
|
||||
if (PropelTypes::OBJECT === $column->getType()) {
|
||||
$values[$col] = unserialize($row[$col]);
|
||||
}
|
||||
}
|
||||
|
||||
if (count($primaryKeys) > 1 || (count($primaryKeys) > 0 && count($foreignKeys) > 0)) {
|
||||
$values = array_merge($primaryKeys, $values);
|
||||
}
|
||||
|
||||
$dumpData[$tableName][$pk] = $values;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $dumpData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixes the ordering of foreign key data, by outputting data
|
||||
* a foreign key depends on before the table with the foreign key.
|
||||
*
|
||||
* @param array $classes The array with the class names
|
||||
* @return array
|
||||
*/
|
||||
protected function fixOrderingOfForeignKeyData($classes)
|
||||
{
|
||||
// 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.'::TABLE_MAP').'::TABLE_NAME'));
|
||||
|
||||
foreach ($tableMap->getColumns() as $column) {
|
||||
if ($column->isForeignKey()) {
|
||||
$relatedTable = $this->dbMap->getTable($column->getRelatedTableName());
|
||||
$relatedTablePos = array_search($relatedTable->getClassname(), $classes);
|
||||
|
||||
// check if relatedTable is after the current table
|
||||
if ($relatedTablePos > $i) {
|
||||
// move related table 1 position before current table
|
||||
$classes = array_merge(
|
||||
array_slice($classes, 0, $i),
|
||||
array($classes[$relatedTablePos]),
|
||||
array_slice($classes, $i, $relatedTablePos - $i),
|
||||
array_slice($classes, $relatedTablePos + 1)
|
||||
);
|
||||
// we have moved a table, so let's see if we are done
|
||||
return $this->fixOrderingOfForeignKeyData($classes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $classes;
|
||||
}
|
||||
|
||||
protected function fixOrderingOfForeignKeyDataInSameTable($resultsSets, $tableName, $column, $in = null)
|
||||
{
|
||||
$sql = sprintf('SELECT * FROM %s WHERE %s %s',
|
||||
constant(constant($tableName.'::TABLE_MAP').'::TABLE_NAME'),
|
||||
strtolower($column->getName()),
|
||||
null === $in ? 'IS NULL' : 'IN ('.$in.')');
|
||||
|
||||
$stmt = $this->con->prepare($sql);
|
||||
$stmt->execute();
|
||||
|
||||
$in = array();
|
||||
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$in[] = "'".$row[strtolower($column->getRelatedColumnName())]."'";
|
||||
$resultsSets[] = $row;
|
||||
}
|
||||
|
||||
if ($in = implode(', ', $in)) {
|
||||
$resultsSets = $this->fixOrderingOfForeignKeyDataInSameTable($resultsSets, $tableName, $column, $in);
|
||||
}
|
||||
|
||||
return $resultsSets;
|
||||
}
|
||||
}
|
27
DataFixtures/Dumper/DataDumperInterface.php
Normal file
27
DataFixtures/Dumper/DataDumperInterface.php
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?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\Dumper;
|
||||
|
||||
/**
|
||||
* Interface that exposes how Propel data dumpers should work.
|
||||
*
|
||||
* @author William Durand <william.durand1@gmail.com>
|
||||
*/
|
||||
interface DataDumperInterface
|
||||
{
|
||||
/**
|
||||
* Dumps data to fixtures from a given connection.
|
||||
*
|
||||
* @param string $filename The file name to write data.
|
||||
* @param string $connectionName The Propel connection name.
|
||||
*/
|
||||
public function dump($filename, $connectionName = null);
|
||||
}
|
35
DataFixtures/Dumper/YamlDataDumper.php
Normal file
35
DataFixtures/Dumper/YamlDataDumper.php
Normal file
|
@ -0,0 +1,35 @@
|
|||
<?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\Dumper;
|
||||
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
/**
|
||||
* YAML fixtures dumper.
|
||||
*
|
||||
* @author William Durand <william.durand1@gmail.com>
|
||||
*/
|
||||
class YamlDataDumper extends AbstractDataDumper
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function transformArrayToData($data)
|
||||
{
|
||||
return Yaml::dump(
|
||||
$data,
|
||||
$inline = 3,
|
||||
$indent = 4,
|
||||
$exceptionOnInvalidType = false,
|
||||
$objectSupport = true
|
||||
);
|
||||
}
|
||||
}
|
309
DataFixtures/Loader/AbstractDataLoader.php
Normal file
309
DataFixtures/Loader/AbstractDataLoader.php
Normal file
|
@ -0,0 +1,309 @@
|
|||
<?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;
|
||||
|
||||
use Propel\PropelBundle\DataFixtures\AbstractDataHandler;
|
||||
use Propel\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.
|
||||
*
|
||||
* @author William Durand <william.durand1@gmail.com>
|
||||
*/
|
||||
abstract class AbstractDataLoader extends AbstractDataHandler implements DataLoaderInterface
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $deletedClasses = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $object_references = array();
|
||||
|
||||
/**
|
||||
* Transforms a file containing data in an array.
|
||||
*
|
||||
* @param string $file A filename.
|
||||
* @return array
|
||||
*/
|
||||
abstract protected function transformDataToArray($file);
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function load($files = array(), $connectionName)
|
||||
{
|
||||
$nbFiles = 0;
|
||||
$this->deletedClasses = array();
|
||||
|
||||
$this->loadMapBuilders($connectionName);
|
||||
$this->con = Propel::getConnection($connectionName);
|
||||
|
||||
try {
|
||||
$this->con->beginTransaction();
|
||||
|
||||
$datas = array();
|
||||
foreach ($files as $file) {
|
||||
$content = $this->transformDataToArray($file);
|
||||
|
||||
if (count($content) > 0) {
|
||||
$datas = array_merge_recursive($datas, $content);
|
||||
$nbFiles++;
|
||||
}
|
||||
}
|
||||
|
||||
$this->deleteCurrentData($datas);
|
||||
$this->loadDataFromArray($datas);
|
||||
|
||||
$this->con->commit();
|
||||
} catch (\Exception $e) {
|
||||
$this->con->rollBack();
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return $nbFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes current data.
|
||||
*
|
||||
* @param array $data The data to delete
|
||||
*/
|
||||
protected function deleteCurrentData($data = null)
|
||||
{
|
||||
if ($data !== null) {
|
||||
$classes = array_keys($data);
|
||||
foreach (array_reverse($classes) as $class) {
|
||||
$class = trim($class);
|
||||
if (in_array($class, $this->deletedClasses)) {
|
||||
continue;
|
||||
}
|
||||
$this->deleteClassData($class);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete data for a given class, and for its ancestors (if any).
|
||||
*
|
||||
* @param string $class Class name to delete
|
||||
*/
|
||||
protected function deleteClassData($class)
|
||||
{
|
||||
$tableMap = $this->dbMap->getTable(constant(constant($class.'::TABLE_MAP').'::TABLE_NAME'));
|
||||
$tableMap->doDeleteAll($this->con);
|
||||
|
||||
$this->deletedClasses[] = $class;
|
||||
|
||||
// Remove ancestors data
|
||||
if (false !== ($parentClass = get_parent_class(get_parent_class($class)))) {
|
||||
$reflectionClass = new \ReflectionClass($parentClass);
|
||||
if (!$reflectionClass->isAbstract()) {
|
||||
$this->deleteClassData($parentClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the data using the generated data model.
|
||||
*
|
||||
* @param array|null $data The data to be loaded
|
||||
*/
|
||||
protected function loadDataFromArray($data = null)
|
||||
{
|
||||
if ($data === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($data as $class => $datas) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
$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 => $data) {
|
||||
// create a new entry in the database
|
||||
if (!class_exists($class)) {
|
||||
throw new \InvalidArgumentException(sprintf('Unknown class "%s".', $class));
|
||||
}
|
||||
|
||||
$obj = new $class();
|
||||
|
||||
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)) {
|
||||
throw new \InvalidArgumentException(sprintf('You must give a name for each fixture data entry (class %s).', $class));
|
||||
}
|
||||
|
||||
foreach ($data 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 (TableNotFoundException $e) {
|
||||
// Check whether this is actually an array stored in the object.
|
||||
if ('Cannot fetch TableMap for undefined table: ' . substr($name, 0, -1) === $e->getMessage()) {
|
||||
if (PropelTypes::PHP_ARRAY !== $tableMap->getColumn($name)->getType()
|
||||
&& PropelTypes::OBJECT !== $tableMap->getColumn($name)->getType()) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$isARealColumn = true;
|
||||
if ($tableMap->hasColumn($name)) {
|
||||
$column = $tableMap->getColumn($name);
|
||||
} elseif ($tableMap->hasColumnByPhpName($name)) {
|
||||
$column = $tableMap->getColumnByPhpName($name);
|
||||
} else {
|
||||
$isARealColumn = false;
|
||||
}
|
||||
|
||||
// foreign key?
|
||||
if ($isARealColumn) {
|
||||
/*
|
||||
* A column, which is a PrimaryKey (self referencing, e.g. versionable behavior),
|
||||
* but which is not a ForeignKey (e.g. delegatable behavior on 1:1 relation).
|
||||
*/
|
||||
if ($column->isPrimaryKey() && null !== $value && !$column->isForeignKey()) {
|
||||
if (isset($this->object_references[$this->cleanObjectRef($class.'_'.$value)])) {
|
||||
$obj = $this->object_references[$this->cleanObjectRef($class.'_'.$value)];
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ($column->isForeignKey() && null !== $value) {
|
||||
$relatedTable = $this->dbMap->getTable($column->getRelatedTableName());
|
||||
if (!isset($this->object_references[$this->cleanObjectRef($relatedTable->getClassname().'_'.$value)])) {
|
||||
var_dump($this->object_references, $this->cleanObjectRef($relatedTable->getClassname().'_'.$value));
|
||||
throw new \InvalidArgumentException(
|
||||
sprintf('The object "%s" from class "%s" is not defined in your data file.', $value, $relatedTable->getClassname())
|
||||
);
|
||||
}
|
||||
$value = $this
|
||||
->object_references[$this->cleanObjectRef($relatedTable->getClassname().'_'.$value)]
|
||||
->getByName($column->getRelatedName(), TableMap::TYPE_COLNAME);
|
||||
}
|
||||
}
|
||||
|
||||
if (false !== $pos = array_search($name, $column_names)) {
|
||||
$obj->setByPosition($pos, $value);
|
||||
} elseif (is_callable(array($obj, $method = 'set'.ucfirst(PropelInflector::camelize($name))))) {
|
||||
$obj->$method($value);
|
||||
} else {
|
||||
throw new \InvalidArgumentException(sprintf('Column "%s" does not exist for class "%s".', $name, $class));
|
||||
}
|
||||
}
|
||||
|
||||
$obj->save($this->con);
|
||||
|
||||
$this->saveParentReference($class, $key, $obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a reference to the specified object (and its ancestors) before loading them.
|
||||
*
|
||||
* @param string $class Class name of passed object
|
||||
* @param string $key Key identifying specified object
|
||||
* @param ActiveRecordInterface $obj A Propel object
|
||||
*/
|
||||
protected function saveParentReference($class, $key, &$obj)
|
||||
{
|
||||
if (!method_exists($obj, 'getPrimaryKey')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->object_references[$this->cleanObjectRef($class.'_'.$key)] = $obj;
|
||||
|
||||
// Get parent (schema ancestor) of parent (Propel base class) in case of inheritance
|
||||
if (false !== ($parentClass = get_parent_class(get_parent_class($class)))) {
|
||||
|
||||
$reflectionClass = new \ReflectionClass($parentClass);
|
||||
if (!$reflectionClass->isAbstract()) {
|
||||
$parentObj = new $parentClass();
|
||||
$parentObj->fromArray($obj->toArray());
|
||||
$this->saveParentReference($parentClass, $key, $parentObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads many to many objects.
|
||||
*
|
||||
* @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).'::TABLE_MAP').'::TABLE_NAME');
|
||||
|
||||
foreach ($middleTable->getColumns() as $column) {
|
||||
if ($column->isForeignKey()) {
|
||||
if ($tableName !== $column->getRelatedTableName()) {
|
||||
$relatedClass = $this->dbMap->getTable($column->getRelatedTableName())->getClassname();
|
||||
$relatedSetter = 'set' . $column->getRelation()->getName();
|
||||
} else {
|
||||
$setter = 'set' . $column->getRelation()->getName();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($relatedClass)) {
|
||||
throw new \InvalidArgumentException(sprintf('Unable to find the many-to-many relationship for object "%s".', get_class($obj)));
|
||||
}
|
||||
|
||||
foreach ($values as $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)
|
||||
);
|
||||
}
|
||||
|
||||
$middle = new $middleClass();
|
||||
$middle->$setter($obj);
|
||||
$middle->$relatedSetter($this->object_references[$this->cleanObjectRef($relatedClass.'_'.$value)]);
|
||||
$middle->save($this->con);
|
||||
}
|
||||
}
|
||||
|
||||
protected function cleanObjectRef($ref)
|
||||
{
|
||||
return $ref[0] === '\\' ? substr($ref, 1) : $ref;
|
||||
}
|
||||
}
|
27
DataFixtures/Loader/DataLoaderInterface.php
Normal file
27
DataFixtures/Loader/DataLoaderInterface.php
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?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;
|
||||
|
||||
/**
|
||||
* Interface that exposes how Propel data loaders should work.
|
||||
*
|
||||
* @author William Durand <william.durand1@gmail.com>
|
||||
*/
|
||||
interface DataLoaderInterface
|
||||
{
|
||||
/**
|
||||
* Loads data from a set of files.
|
||||
*
|
||||
* @param array $files A set of files containing datas to load.
|
||||
* @param string $connectionName The Propel connection name
|
||||
*/
|
||||
public function load($files = array(), $connectionName);
|
||||
}
|
61
DataFixtures/Loader/XmlDataLoader.php
Normal file
61
DataFixtures/Loader/XmlDataLoader.php
Normal file
|
@ -0,0 +1,61 @@
|
|||
<?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;
|
||||
|
||||
/**
|
||||
* XML fixtures loader.
|
||||
*
|
||||
* @author William Durand <william.durand1@gmail.com>
|
||||
*/
|
||||
class XmlDataLoader extends AbstractDataLoader
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function transformDataToArray($file)
|
||||
{
|
||||
$xml = simplexml_load_file($file);
|
||||
|
||||
return $this->simpleXmlToArray($xml);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SimpleXMLElement $xml
|
||||
* @return array
|
||||
*/
|
||||
protected function simpleXmlToArray($xml)
|
||||
{
|
||||
$array = array();
|
||||
if ($xml instanceof \SimpleXMLElement) {
|
||||
foreach ($xml as $key => $value) {
|
||||
// First make a valid key which is the Ns (Namespace) attribute
|
||||
// + the element name (the class name)
|
||||
foreach ($value->attributes() as $subkey => $subvalue) {
|
||||
if ('Namespace' === (string) $subkey) {
|
||||
$key = $subvalue . '\\' . $key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$array[$key] = array();
|
||||
foreach ($value as $elementKey => $elementValue) {
|
||||
$array[$key][$elementKey] = array();
|
||||
|
||||
foreach ($elementValue->attributes() as $subkey => $subvalue) {
|
||||
$array[$key][$elementKey][$subkey] = (string) $subvalue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
}
|
78
DataFixtures/Loader/YamlDataLoader.php
Normal file
78
DataFixtures/Loader/YamlDataLoader.php
Normal file
|
@ -0,0 +1,78 @@
|
|||
<?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;
|
||||
|
||||
use Faker\Generator;
|
||||
use Symfony\Component\Yaml\ParseException;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
/**
|
||||
* YAML fixtures loader.
|
||||
*
|
||||
* @author William Durand <william.durand1@gmail.com>
|
||||
*/
|
||||
class YamlDataLoader extends AbstractDataLoader
|
||||
{
|
||||
/**
|
||||
* @var \Faker\Generator
|
||||
*/
|
||||
private $faker;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct($rootDir, array $datasources, Generator $faker = null)
|
||||
{
|
||||
parent::__construct($rootDir, $datasources);
|
||||
|
||||
$this->faker = $faker;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function transformDataToArray($file)
|
||||
{
|
||||
if (strpos($file, "\n") === false && is_file($file)) {
|
||||
if (false === is_readable($file)) {
|
||||
throw new ParseException(sprintf('Unable to parse "%s" as the file is not readable.', $file));
|
||||
}
|
||||
|
||||
if (null !== $this->faker) {
|
||||
$generator = $this->faker;
|
||||
$faker = function ($type) use ($generator) {
|
||||
$args = func_get_args();
|
||||
array_shift($args);
|
||||
|
||||
echo Yaml::dump(call_user_func_array(array($generator, $type), $args)) . "\n";
|
||||
};
|
||||
} else {
|
||||
$faker = function ($text) {
|
||||
echo $text . "\n";
|
||||
};
|
||||
}
|
||||
|
||||
ob_start();
|
||||
$retval = include $file;
|
||||
$content = ob_get_clean();
|
||||
|
||||
// if an array is returned by the config file assume it's in plain php form else in YAML
|
||||
$file = is_array($retval) ? $retval : $content;
|
||||
|
||||
// if an array is returned by the config file assume it's in plain php form else in YAML
|
||||
if (is_array($file)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
return Yaml::parse($file);
|
||||
}
|
||||
}
|
111
DependencyInjection/Configuration.php
Normal file
111
DependencyInjection/Configuration.php
Normal file
|
@ -0,0 +1,111 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the PropelBundle package.
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @license MIT License
|
||||
*/
|
||||
|
||||
namespace Propel\PropelBundle\DependencyInjection;
|
||||
|
||||
use Propel\Common\Config\PropelConfiguration;
|
||||
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
|
||||
|
||||
/**
|
||||
* This class contains the configuration information for the bundle
|
||||
*/
|
||||
class Configuration extends PropelConfiguration
|
||||
{
|
||||
private $debug;
|
||||
|
||||
public function __construct($debug = true)
|
||||
{
|
||||
$this->debug = $debug;
|
||||
}
|
||||
|
||||
protected function addDatabaseSection(ArrayNodeDefinition $node)
|
||||
{
|
||||
$node
|
||||
->children()
|
||||
->arrayNode('database')
|
||||
->isRequired()
|
||||
->addDefaultsIfNotSet()
|
||||
->children()
|
||||
->arrayNode('connections')
|
||||
->isRequired()
|
||||
->requiresAtLeastOneElement()
|
||||
->normalizeKeys(false)
|
||||
->prototype('array')
|
||||
->fixXmlConfig('slave')
|
||||
->children()
|
||||
->scalarNode('classname')->defaultValue($this->debug ? '\Propel\Runtime\Connection\DebugPDO' : '\Propel\Runtime\Connection\ConnectionWrapper')->end()
|
||||
->enumNode('adapter')
|
||||
->isRequired()
|
||||
->cannotBeEmpty()
|
||||
->values(array('mysql', 'pgsql', 'sqlite', 'mssql', 'sqlsrv', 'oracle'))
|
||||
->end()
|
||||
->scalarNode('dsn')->isRequired()->cannotBeEmpty()->end()
|
||||
->scalarNode('user')->isRequired()->end()
|
||||
->scalarNode('password')->isRequired()->treatNullLike('')->end()
|
||||
->arrayNode('options')
|
||||
->children()
|
||||
->booleanNode('ATTR_PERSISTENT')->defaultFalse()->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('attributes')
|
||||
->children()
|
||||
->booleanNode('ATTR_EMULATE_PREPARES')->defaultFalse()->end()
|
||||
->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')->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()
|
||||
;
|
||||
}
|
||||
}
|
77
DependencyInjection/PropelExtension.php
Normal file
77
DependencyInjection/PropelExtension.php
Normal file
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the PropelBundle package.
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @license MIT License
|
||||
*/
|
||||
|
||||
namespace Propel\PropelBundle\DependencyInjection;
|
||||
|
||||
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
|
||||
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
use Symfony\Component\Config\FileLocator;
|
||||
use Symfony\Component\Config\Definition\Processor;
|
||||
|
||||
/**
|
||||
* PropelExtension loads the PropelBundle configuration.
|
||||
*
|
||||
* @author William DURAND <william.durand1@gmail.com>
|
||||
*/
|
||||
class PropelExtension extends Extension
|
||||
{
|
||||
/**
|
||||
* Loads the Propel configuration.
|
||||
*
|
||||
* @param array $configs An array of configuration settings
|
||||
* @param ContainerBuilder $container A ContainerBuilder instance
|
||||
*/
|
||||
public function load(array $configs, ContainerBuilder $container)
|
||||
{
|
||||
$processor = new Processor();
|
||||
$configuration = $this->getConfiguration($configs, $container);
|
||||
$config = $processor->processConfiguration($configuration, $configs);
|
||||
|
||||
$container->setParameter('propel.logging', $container->getParameter('kernel.debug'));
|
||||
$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');
|
||||
}
|
||||
}
|
||||
|
||||
public function getConfiguration(array $config, ContainerBuilder $container)
|
||||
{
|
||||
return new Configuration($container->getParameter('kernel.debug'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the base path for the XSD files.
|
||||
*
|
||||
* @return string The XSD base path
|
||||
*/
|
||||
public function getXsdValidationBasePath()
|
||||
{
|
||||
return __DIR__.'/../Resources/config/schema';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the recommended alias to use in XML.
|
||||
*
|
||||
* This alias is also the mandatory prefix to use when using YAML.
|
||||
*
|
||||
* @return string The alias
|
||||
*/
|
||||
public function getAlias()
|
||||
{
|
||||
return 'propel';
|
||||
}
|
||||
}
|
72
Form/BaseAbstractType.php
Normal file
72
Form/BaseAbstractType.php
Normal 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\PropelBundle\Form;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
|
||||
|
||||
/**
|
||||
* @author William DURAND <william.durand1@gmail.com>
|
||||
*/
|
||||
abstract class BaseAbstractType extends AbstractType
|
||||
{
|
||||
protected $options = array(
|
||||
'name' => '',
|
||||
);
|
||||
|
||||
public function __construct($mergeOptions = null)
|
||||
{
|
||||
if ($mergeOptions) {
|
||||
$this->mergeOptions($mergeOptions);
|
||||
}
|
||||
}
|
||||
|
||||
public function setOption($name, $value)
|
||||
{
|
||||
$this->options[$name] = $value;
|
||||
}
|
||||
|
||||
public function getOption($name)
|
||||
{
|
||||
return $this->options[$name];
|
||||
}
|
||||
|
||||
public function setOptions($options)
|
||||
{
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
public function getOptions()
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
public function mergeOptions($options)
|
||||
{
|
||||
$this->options = array_merge($this->options, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setDefaultOptions(OptionsResolverInterface $resolver)
|
||||
{
|
||||
$resolver->setDefaults($this->options);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->getOption('name');
|
||||
}
|
||||
}
|
430
Form/ChoiceList/ModelChoiceList.php
Normal file
430
Form/ChoiceList/ModelChoiceList.php
Normal file
|
@ -0,0 +1,430 @@
|
|||
<?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\PropelBundle\Form\ChoiceList;
|
||||
|
||||
use Propel\Runtime\Map\ColumnMap;
|
||||
use Propel\Runtime\ActiveQuery\ModelCriteria;
|
||||
use Propel\Runtime\ActiveRecord\ActiveRecordInterface;
|
||||
|
||||
use Symfony\Component\Form\Exception\StringCastException;
|
||||
use Symfony\Component\Form\Extension\Core\ChoiceList\ObjectChoiceList;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
||||
|
||||
/**
|
||||
* Widely inspired by the EntityChoiceList.
|
||||
*
|
||||
* @author William Durand <william.durand1@gmail.com>
|
||||
* @author Toni Uebernickel <tuebernickel@gmail.com>
|
||||
*/
|
||||
class ModelChoiceList extends ObjectChoiceList
|
||||
{
|
||||
/**
|
||||
* The fields of which the identifier of the underlying class consists
|
||||
*
|
||||
* This property should only be accessed through identifier.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $identifier = array();
|
||||
|
||||
/**
|
||||
* The query to retrieve the choices of this list.
|
||||
*
|
||||
* @var ModelCriteria
|
||||
*/
|
||||
protected $query;
|
||||
|
||||
/**
|
||||
* The query to retrieve the preferred choices for this list.
|
||||
*
|
||||
* @var ModelCriteria
|
||||
*/
|
||||
protected $preferredQuery;
|
||||
|
||||
/**
|
||||
* Whether the model objects have already been loaded.
|
||||
*
|
||||
* @var Boolean
|
||||
*/
|
||||
protected $loaded = false;
|
||||
|
||||
/**
|
||||
* Whether to use the identifier for index generation
|
||||
*
|
||||
* @var Boolean
|
||||
*/
|
||||
private $identifierAsIndex = false;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @see Symfony\Bridge\Propel1\Form\Type\ModelType How to use the preferred choices.
|
||||
*
|
||||
* @param string $class The FQCN of the model class to be loaded.
|
||||
* @param string $labelPath A property path pointing to the property used for the choice labels.
|
||||
* @param array $choices An optional array to use, rather than fetching the models.
|
||||
* @param ModelCriteria $queryObject The query to use retrieving model data from database.
|
||||
* @param string $groupPath A property path pointing to the property used to group the choices.
|
||||
* @param array|ModelCriteria $preferred The preferred items of this choice.
|
||||
* Either an array if $choices is given,
|
||||
* or a ModelCriteria to be merged with the $queryObject.
|
||||
* @param PropertyAccessorInterface $propertyAccessor The reflection graph for reading property paths.
|
||||
*/
|
||||
public function __construct($class, $labelPath = null, $choices = null, $queryObject = null, $groupPath = null, $preferred = array(), PropertyAccessorInterface $propertyAccessor = null)
|
||||
{
|
||||
$this->class = $class;
|
||||
|
||||
$queryClass = $this->class.'Query';
|
||||
$query = new $queryClass();
|
||||
|
||||
$this->identifier = $query->getTableMap()->getPrimaryKeys();
|
||||
$this->query = $queryObject ?: $query;
|
||||
$this->loaded = is_array($choices) || $choices instanceof \Traversable;
|
||||
|
||||
if ($preferred instanceof ModelCriteria) {
|
||||
$this->preferredQuery = $preferred->mergeWith($this->query);
|
||||
}
|
||||
|
||||
if (!$this->loaded) {
|
||||
// Make sure the constraints of the parent constructor are
|
||||
// fulfilled
|
||||
$choices = array();
|
||||
$preferred = array();
|
||||
}
|
||||
|
||||
if (1 === count($this->identifier) && $this->isInteger(current($this->identifier))) {
|
||||
$this->identifierAsIndex = true;
|
||||
}
|
||||
|
||||
parent::__construct($choices, $labelPath, $preferred, $groupPath, null, $propertyAccessor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the class name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getClass()
|
||||
{
|
||||
return $this->class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of model objects
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @see Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface
|
||||
*/
|
||||
public function getChoices()
|
||||
{
|
||||
if (!$this->loaded) {
|
||||
$this->load();
|
||||
}
|
||||
|
||||
return parent::getChoices();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the values for the model objects
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @see Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface
|
||||
*/
|
||||
public function getValues()
|
||||
{
|
||||
if (!$this->loaded) {
|
||||
$this->load();
|
||||
}
|
||||
|
||||
return parent::getValues();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the choice views of the preferred choices as nested array with
|
||||
* the choice groups as top-level keys.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @see Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface
|
||||
*/
|
||||
public function getPreferredViews()
|
||||
{
|
||||
if (!$this->loaded) {
|
||||
$this->load();
|
||||
}
|
||||
|
||||
return parent::getPreferredViews();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the choice views of the choices that are not preferred as nested
|
||||
* array with the choice groups as top-level keys.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @see Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface
|
||||
*/
|
||||
public function getRemainingViews()
|
||||
{
|
||||
if (!$this->loaded) {
|
||||
$this->load();
|
||||
}
|
||||
|
||||
return parent::getRemainingViews();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the model objects corresponding to the given values.
|
||||
*
|
||||
* @param array $values
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @see Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface
|
||||
*/
|
||||
public function getChoicesForValues(array $values)
|
||||
{
|
||||
if (!$this->loaded) {
|
||||
if (1 === count($this->identifier)) {
|
||||
$filterBy = 'filterBy'.current($this->identifier)->getPhpName();
|
||||
|
||||
return $this->query->create()
|
||||
->$filterBy($values)
|
||||
->find()
|
||||
->getData();
|
||||
}
|
||||
|
||||
$this->load();
|
||||
}
|
||||
|
||||
return parent::getChoicesForValues($values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the values corresponding to the given model objects.
|
||||
*
|
||||
* @param array $models
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @see Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface
|
||||
*/
|
||||
public function getValuesForChoices(array $models)
|
||||
{
|
||||
if (!$this->loaded) {
|
||||
// Optimize performance for single-field identifiers. We already
|
||||
// know that the IDs are used as values
|
||||
|
||||
// Attention: This optimization does not check choices for existence
|
||||
if (1 === count($this->identifier)) {
|
||||
$values = array();
|
||||
foreach ($models as $model) {
|
||||
if ($model instanceof $this->class) {
|
||||
// Make sure to convert to the right format
|
||||
$values[] = $this->fixValue(current($this->getIdentifierValues($model)));
|
||||
}
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
$this->load();
|
||||
}
|
||||
|
||||
return parent::getValuesForChoices($models);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the indices corresponding to the given models.
|
||||
*
|
||||
* @param array $models
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @see Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface
|
||||
*/
|
||||
public function getIndicesForChoices(array $models)
|
||||
{
|
||||
$indices = array();
|
||||
|
||||
if (!$this->loaded) {
|
||||
// Optimize performance for single-field identifiers. We already
|
||||
// know that the IDs are used as indices
|
||||
|
||||
// Attention: This optimization does not check choices for existence
|
||||
if ($this->identifierAsIndex) {
|
||||
foreach ($models as $model) {
|
||||
if ($model instanceof $this->class) {
|
||||
// Make sure to convert to the right format
|
||||
$indices[] = $this->fixIndex(current($this->getIdentifierValues($model)));
|
||||
}
|
||||
}
|
||||
|
||||
return $indices;
|
||||
}
|
||||
|
||||
$this->load();
|
||||
}
|
||||
|
||||
/*
|
||||
* Overwriting default implementation.
|
||||
*
|
||||
* The two objects may represent the same entry in the database,
|
||||
* but if they originated from different queries, there are not the same object within the code.
|
||||
*
|
||||
* This happens when using m:n relations with either sides model as data_class of the form.
|
||||
* The choicelist will retrieve the list of available related models with a different query, resulting in different objects.
|
||||
*/
|
||||
$choices = $this->fixChoices($models);
|
||||
foreach ($this->getChoices() as $i => $choice) {
|
||||
foreach ($choices as $j => $givenChoice) {
|
||||
if (null !== $givenChoice && $this->getIdentifierValues($choice) === $this->getIdentifierValues($givenChoice)) {
|
||||
$indices[] = $i;
|
||||
unset($choices[$j]);
|
||||
|
||||
if (0 === count($choices)) {
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $indices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the models corresponding to the given values.
|
||||
*
|
||||
* @param array $values
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @see Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface
|
||||
*/
|
||||
public function getIndicesForValues(array $values)
|
||||
{
|
||||
if (!$this->loaded) {
|
||||
// Optimize performance for single-field identifiers. We already
|
||||
// know that the IDs are used as indices and values
|
||||
|
||||
// Attention: This optimization does not check values for existence
|
||||
if ($this->identifierAsIndex) {
|
||||
return $this->fixIndices($values);
|
||||
}
|
||||
|
||||
$this->load();
|
||||
}
|
||||
|
||||
return parent::getIndicesForValues($values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new unique index for this model.
|
||||
*
|
||||
* If the model has a single-field identifier, this identifier is used.
|
||||
*
|
||||
* Otherwise a new integer is generated.
|
||||
*
|
||||
* @param mixed $model The choice to create an index for
|
||||
*
|
||||
* @return integer|string A unique index containing only ASCII letters,
|
||||
* digits and underscores.
|
||||
*/
|
||||
protected function createIndex($model)
|
||||
{
|
||||
if ($this->identifierAsIndex) {
|
||||
return current($this->getIdentifierValues($model));
|
||||
}
|
||||
|
||||
return parent::createIndex($model);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new unique value for this model.
|
||||
*
|
||||
* If the model has a single-field identifier, this identifier is used.
|
||||
*
|
||||
* Otherwise a new integer is generated.
|
||||
*
|
||||
* @param mixed $model The choice to create a value for
|
||||
*
|
||||
* @return integer|string A unique value without character limitations.
|
||||
*/
|
||||
protected function createValue($model)
|
||||
{
|
||||
if (1 === count($this->identifier)) {
|
||||
return (string) current($this->getIdentifierValues($model));
|
||||
}
|
||||
|
||||
return parent::createValue($model);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the list with model objects.
|
||||
*/
|
||||
private function load()
|
||||
{
|
||||
$models = $this->query->find()->getData();
|
||||
|
||||
$preferred = array();
|
||||
if ($this->preferredQuery instanceof ModelCriteria) {
|
||||
$preferred = $this->preferredQuery->find()->getData();
|
||||
}
|
||||
|
||||
try {
|
||||
// The second parameter $labels is ignored by ObjectChoiceList
|
||||
parent::initialize($models, array(), $preferred);
|
||||
} catch (StringCastException $e) {
|
||||
throw new StringCastException(str_replace('argument $labelPath', 'option "property"', $e->getMessage()), null, $e);
|
||||
}
|
||||
|
||||
$this->loaded = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the values of the identifier fields of an 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 (null === $model) {
|
||||
return array();
|
||||
}
|
||||
|
||||
if ($model instanceof ActiveRecordInterface) {
|
||||
return array($model->getPrimaryKey());
|
||||
}
|
||||
|
||||
return $model->getPrimaryKeys();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this column in an integer
|
||||
*
|
||||
* @param ColumnMap $column
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
private function isInteger(ColumnMap $column)
|
||||
{
|
||||
return $column->getPdoType() === \PDO::PARAM_INT;
|
||||
}
|
||||
}
|
55
Form/DataTransformer/CollectionToArrayTransformer.php
Normal file
55
Form/DataTransformer/CollectionToArrayTransformer.php
Normal 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\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 \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;
|
||||
}
|
||||
}
|
102
Form/EventListener/TranslationCollectionFormListener.php
Normal file
102
Form/EventListener/TranslationCollectionFormListener.php
Normal file
|
@ -0,0 +1,102 @@
|
|||
<?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\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 ($currentForm->hasParent()) {
|
||||
$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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
81
Form/EventListener/TranslationFormListener.php
Normal file
81
Form/EventListener/TranslationFormListener.php
Normal file
|
@ -0,0 +1,81 @@
|
|||
<?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\PropelBundle\Form\EventListener;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
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 = 'text';
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
37
Form/PropelExtension.php
Normal file
37
Form/PropelExtension.php
Normal file
|
@ -0,0 +1,37 @@
|
|||
<?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\PropelBundle\Form;
|
||||
|
||||
use Symfony\Component\Form\AbstractExtension;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccess;
|
||||
|
||||
/**
|
||||
* Represents the Propel form extension, which loads the Propel functionality.
|
||||
*
|
||||
* @author Joseph Rouff <rouffj@gmail.com>
|
||||
*/
|
||||
class PropelExtension extends AbstractExtension
|
||||
{
|
||||
protected function loadTypes()
|
||||
{
|
||||
return array(
|
||||
new Type\ModelType(PropertyAccess::getPropertyAccessor()),
|
||||
new Type\TranslationCollectionType(),
|
||||
new Type\TranslationType()
|
||||
);
|
||||
}
|
||||
|
||||
protected function loadTypeGuesser()
|
||||
{
|
||||
return new TypeGuesser();
|
||||
}
|
||||
}
|
109
Form/Type/ModelType.php
Normal file
109
Form/Type/ModelType.php
Normal file
|
@ -0,0 +1,109 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Propel\PropelBundle\Form\Type;
|
||||
|
||||
use Propel\PropelBundle\Form\ChoiceList\ModelChoiceList;
|
||||
use Propel\PropelBundle\Form\DataTransformer\CollectionToArrayTransformer;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccess;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
||||
|
||||
/**
|
||||
* 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 PropertyAccessorInterface
|
||||
*/
|
||||
private $propertyAccessor;
|
||||
|
||||
public function __construct(PropertyAccessorInterface $propertyAccessor = null)
|
||||
{
|
||||
$this->propertyAccessor = $propertyAccessor ?: PropertyAccess::getPropertyAccessor();
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
if ($options['multiple']) {
|
||||
$builder->addViewTransformer(new CollectionToArrayTransformer(), true);
|
||||
}
|
||||
}
|
||||
|
||||
public function setDefaultOptions(OptionsResolverInterface $resolver)
|
||||
{
|
||||
$propertyAccessor = $this->propertyAccessor;
|
||||
|
||||
$choiceList = function (Options $options) use ($propertyAccessor) {
|
||||
return new ModelChoiceList(
|
||||
$options['class'],
|
||||
$options['property'],
|
||||
$options['choices'],
|
||||
$options['query'],
|
||||
$options['group_by'],
|
||||
$options['preferred_choices'],
|
||||
$propertyAccessor
|
||||
);
|
||||
};
|
||||
|
||||
$resolver->setDefaults(array(
|
||||
'template' => 'choice',
|
||||
'multiple' => false,
|
||||
'expanded' => false,
|
||||
'class' => null,
|
||||
'property' => null,
|
||||
'query' => null,
|
||||
'choices' => null,
|
||||
'choice_list' => $choiceList,
|
||||
'group_by' => null,
|
||||
'by_reference' => false,
|
||||
));
|
||||
}
|
||||
|
||||
public function getParent()
|
||||
{
|
||||
return 'choice';
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return 'model';
|
||||
}
|
||||
}
|
75
Form/Type/TranslationCollectionType.php
Normal file
75
Form/Type/TranslationCollectionType.php
Normal file
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Propel\PropelBundle\Form\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\OptionsResolver\Exception\MissingOptionsException;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
|
||||
use Propel\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['options']['data_class']) || null === $options['options']['data_class']) {
|
||||
throw new MissingOptionsException('data_class must be set');
|
||||
}
|
||||
if (!isset($options['options']['columns']) || null === $options['options']['columns']) {
|
||||
throw new MissingOptionsException('columns must be set');
|
||||
}
|
||||
|
||||
$listener = new TranslationCollectionFormListener($options['languages'], $options['options']['data_class']);
|
||||
$builder->addEventSubscriber($listener);
|
||||
}
|
||||
|
||||
public function getParent()
|
||||
{
|
||||
return 'collection';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'propel_translation_collection';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setDefaultOptions(OptionsResolverInterface $resolver)
|
||||
{
|
||||
$resolver->setRequired(array(
|
||||
'languages'
|
||||
));
|
||||
|
||||
$resolver->setDefaults(array(
|
||||
'type' => 'propel_translation',
|
||||
'allow_add' => false,
|
||||
'allow_delete' => false,
|
||||
'options' => array(
|
||||
'data_class' => null,
|
||||
'columns' => null
|
||||
)
|
||||
));
|
||||
}
|
||||
}
|
54
Form/Type/TranslationType.php
Normal file
54
Form/Type/TranslationType.php
Normal file
|
@ -0,0 +1,54 @@
|
|||
<?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\PropelBundle\Form\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
|
||||
use Propel\PropelBundle\Form\EventListener\TranslationFormListener;
|
||||
|
||||
/**
|
||||
* 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 getName()
|
||||
{
|
||||
return 'propel_translation';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setDefaultOptions(OptionsResolverInterface $resolver)
|
||||
{
|
||||
$resolver->setRequired(array(
|
||||
'data_class',
|
||||
'columns'
|
||||
));
|
||||
}
|
||||
}
|
182
Form/TypeGuesser.php
Normal file
182
Form/TypeGuesser.php
Normal file
|
@ -0,0 +1,182 @@
|
|||
<?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\PropelBundle\Form;
|
||||
|
||||
use Propel\Runtime\Map\RelationMap;
|
||||
use Propel\Generator\Model\PropelTypes;
|
||||
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('text', 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('model', 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('model', 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('model', array(
|
||||
'class' => $relation->getLocalTable()->getClassName(),
|
||||
'multiple' => true,
|
||||
), Guess::HIGH_CONFIDENCE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$column = $this->getColumn($class, $property)) {
|
||||
return new TypeGuess('text', array(), Guess::LOW_CONFIDENCE);
|
||||
}
|
||||
|
||||
switch ($column->getType()) {
|
||||
case PropelTypes::BOOLEAN:
|
||||
case PropelTypes::BOOLEAN_EMU:
|
||||
return new TypeGuess('checkbox', array(), Guess::HIGH_CONFIDENCE);
|
||||
case PropelTypes::TIMESTAMP:
|
||||
case PropelTypes::BU_TIMESTAMP:
|
||||
return new TypeGuess('datetime', array(), Guess::HIGH_CONFIDENCE);
|
||||
case PropelTypes::DATE:
|
||||
case PropelTypes::BU_DATE:
|
||||
return new TypeGuess('date', array(), Guess::HIGH_CONFIDENCE);
|
||||
case PropelTypes::TIME:
|
||||
return new TypeGuess('time', array(), Guess::HIGH_CONFIDENCE);
|
||||
case PropelTypes::FLOAT:
|
||||
case PropelTypes::REAL:
|
||||
case PropelTypes::DOUBLE:
|
||||
case PropelTypes::DECIMAL:
|
||||
return new TypeGuess('number', array(), Guess::MEDIUM_CONFIDENCE);
|
||||
case PropelTypes::TINYINT:
|
||||
case PropelTypes::SMALLINT:
|
||||
case PropelTypes::INTEGER:
|
||||
case PropelTypes::BIGINT:
|
||||
case PropelTypes::NUMERIC:
|
||||
return new TypeGuess('integer', 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('choice', array('choices' => array_combine($choices, $labels)), Guess::MEDIUM_CONFIDENCE);
|
||||
}
|
||||
case PropelTypes::VARCHAR:
|
||||
return new TypeGuess('text', array(), Guess::MEDIUM_CONFIDENCE);
|
||||
case PropelTypes::LONGVARCHAR:
|
||||
case PropelTypes::BLOB:
|
||||
case PropelTypes::CLOB:
|
||||
case PropelTypes::CLOB_EMU:
|
||||
return new TypeGuess('textarea', array(), Guess::MEDIUM_CONFIDENCE);
|
||||
default:
|
||||
return new TypeGuess('text', 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
103
Logger/PropelLogger.php
Normal file
103
Logger/PropelLogger.php
Normal file
|
@ -0,0 +1,103 @@
|
|||
<?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\Logger;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\LoggerTrait;
|
||||
use Symfony\Component\Stopwatch\Stopwatch;
|
||||
|
||||
/**
|
||||
* @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;
|
||||
|
||||
if (null !== $this->stopwatch) {
|
||||
$trace = debug_backtrace();
|
||||
$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(),
|
||||
);
|
||||
}
|
||||
|
||||
$this->logger->log($level, $message, $context);
|
||||
}
|
||||
|
||||
public function getQueries()
|
||||
{
|
||||
return $this->queries;
|
||||
}
|
||||
}
|
43
Model/Acl/AclClass.php
Normal file
43
Model/Acl/AclClass.php
Normal file
|
@ -0,0 +1,43 @@
|
|||
<?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\Base\AclClass as BaseAclClass;
|
||||
use Propel\Runtime\Connection\ConnectionInterface;
|
||||
|
||||
use Symfony\Component\Security\Acl\Model\ObjectIdentityInterface;
|
||||
|
||||
class AclClass extends BaseAclClass
|
||||
{
|
||||
/**
|
||||
* Return an AclClass for the given ACL ObjectIdentity.
|
||||
*
|
||||
* If none can be found, a new one will be saved.
|
||||
*
|
||||
* @param \Symfony\Component\Security\Acl\Model\ObjectIdentityInterface $objectIdentity
|
||||
* @param ConnectionInterface $con
|
||||
*
|
||||
* @return \Propel\PropelBundle\Model\Acl\AclClass
|
||||
*/
|
||||
public static function fromAclObjectIdentity(ObjectIdentityInterface $objectIdentity, ConnectionInterface $con = null)
|
||||
{
|
||||
$obj = AclClassQuery::create()
|
||||
->filterByType($objectIdentity->getType())
|
||||
->findOneOrCreate($con)
|
||||
;
|
||||
|
||||
if ($obj->isNew()) {
|
||||
$obj->save($con);
|
||||
}
|
||||
|
||||
return $obj;
|
||||
}
|
||||
}
|
18
Model/Acl/AclClassQuery.php
Normal file
18
Model/Acl/AclClassQuery.php
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?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\Base\AclClassQuery as BaseAclClassQuery;
|
||||
|
||||
class AclClassQuery extends BaseAclClassQuery
|
||||
{
|
||||
|
||||
}
|
80
Model/Acl/Entry.php
Normal file
80
Model/Acl/Entry.php
Normal file
|
@ -0,0 +1,80 @@
|
|||
<?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\Base\Entry as BaseEntry;
|
||||
|
||||
use Propel\PropelBundle\Security\Acl\Domain\Entry as AclEntry;
|
||||
use Propel\PropelBundle\Security\Acl\Domain\FieldEntry as AclFieldEntry;
|
||||
|
||||
use Symfony\Component\Security\Acl\Model\AclInterface;
|
||||
use Symfony\Component\Security\Acl\Model\EntryInterface;
|
||||
use Symfony\Component\Security\Acl\Model\AuditableEntryInterface;
|
||||
use Symfony\Component\Security\Acl\Model\FieldEntryInterface;
|
||||
|
||||
class Entry extends BaseEntry
|
||||
{
|
||||
/**
|
||||
* Transform a given ACL entry into a Entry model.
|
||||
*
|
||||
* The entry will not be persisted!
|
||||
*
|
||||
* @param \Symfony\Component\Security\Acl\Model\EntryInterface $aclEntry
|
||||
*
|
||||
* @return \Propel\PropelBundle\Model\Acl\Entry
|
||||
*/
|
||||
public static function fromAclEntry(EntryInterface $aclEntry)
|
||||
{
|
||||
$entry = new self();
|
||||
|
||||
// Already persisted before?
|
||||
if ($aclEntry->getId()) {
|
||||
$entry->setId($aclEntry->getId());
|
||||
}
|
||||
|
||||
$entry
|
||||
->setMask($aclEntry->getMask())
|
||||
->setGranting($aclEntry->isGranting())
|
||||
->setGrantingStrategy($aclEntry->getStrategy())
|
||||
->setSecurityIdentity(SecurityIdentity::fromAclIdentity($aclEntry->getSecurityIdentity()))
|
||||
;
|
||||
|
||||
if ($aclEntry instanceof FieldEntryInterface) {
|
||||
$entry->setFieldName($aclEntry->getField());
|
||||
}
|
||||
|
||||
if ($aclEntry instanceof AuditableEntryInterface) {
|
||||
$entry
|
||||
->setAuditFailure($aclEntry->isAuditFailure())
|
||||
->setAuditSuccess($aclEntry->isAuditSuccess())
|
||||
;
|
||||
}
|
||||
|
||||
return $entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a given model entry into an ACL related Entry (ACE).
|
||||
*
|
||||
* @param \Propel\PropelBundle\Model\Acl\Entry $modelEntry
|
||||
* @param \Symfony\Component\Security\Acl\Model\AclInterface $acl
|
||||
*
|
||||
* @return \Symfony\Component\Security\Acl\Model\EntryInterface
|
||||
*/
|
||||
public static function toAclEntry(Entry $modelEntry, AclInterface $acl)
|
||||
{
|
||||
if (null === $modelEntry->getFieldName()) {
|
||||
return new AclEntry($modelEntry, $acl);
|
||||
}
|
||||
|
||||
return new AclFieldEntry($modelEntry, $acl);
|
||||
}
|
||||
}
|
70
Model/Acl/EntryQuery.php
Normal file
70
Model/Acl/EntryQuery.php
Normal file
|
@ -0,0 +1,70 @@
|
|||
<?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\Base\EntryQuery as BaseEntryQuery;
|
||||
use Propel\PropelBundle\Model\Acl\Map\EntryTableMap;
|
||||
use Propel\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;
|
||||
|
||||
class EntryQuery extends BaseEntryQuery
|
||||
{
|
||||
/**
|
||||
* Return Entry objects filtered by an ACL related ObjectIdentity.
|
||||
*
|
||||
* @see find()
|
||||
*
|
||||
* @param \Symfony\Component\Security\Acl\Model\ObjectIdentityInterface $objectIdentity An ACL related ObjectIdentity.
|
||||
* @param array $securityIdentities A list of SecurityIdentity to filter by.
|
||||
* @param \ConnectionInterface $con
|
||||
*
|
||||
* @return \PropelObjectCollection
|
||||
*/
|
||||
public function findByAclIdentity(ObjectIdentityInterface $objectIdentity, array $securityIdentities = array(), ConnectionInterface $con = null)
|
||||
{
|
||||
$securityIds = array();
|
||||
foreach ($securityIdentities as $eachIdentity) {
|
||||
if (!$eachIdentity instanceof SecurityIdentityInterface) {
|
||||
if (is_object($eachIdentity)) {
|
||||
$errorMessage = sprintf('The list of security identities contains at least one invalid entry of class "%s". Please provide objects of classes implementing "Symfony\Component\Security\Acl\Model\SecurityIdentityInterface" only.', get_class($eachIdentity));
|
||||
} else {
|
||||
$errorMessage = sprintf('The list of security identities contains at least one invalid entry "%s". Please provide objects of classes implementing "Symfony\Component\Security\Acl\Model\SecurityIdentityInterface" only.', $eachIdentity);
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException($errorMessage);
|
||||
}
|
||||
|
||||
if ($securityIdentity = SecurityIdentity::fromAclIdentity($eachIdentity)) {
|
||||
$securityIds[$securityIdentity->getId()] = $securityIdentity->getId();
|
||||
}
|
||||
}
|
||||
|
||||
$this
|
||||
->useAclClassQuery(null, Criteria::INNER_JOIN)
|
||||
->filterByType((string) $objectIdentity->getType())
|
||||
->endUse()
|
||||
->leftJoinObjectIdentity()
|
||||
->add(ObjectIdentityTableMap::COL_OBJECT_IDENTIFIER, (string) $objectIdentity->getIdentifier(), Criteria::EQUAL)
|
||||
->addOr(EntryTableMap::COL_OBJECT_IDENTITY_ID, null, Criteria::ISNULL)
|
||||
;
|
||||
|
||||
if (!empty($securityIdentities)) {
|
||||
$this->filterBySecurityIdentityId($securityIds);
|
||||
}
|
||||
|
||||
return $this->find($con);
|
||||
}
|
||||
}
|
141
Model/Acl/ObjectIdentity.php
Normal file
141
Model/Acl/ObjectIdentity.php
Normal file
|
@ -0,0 +1,141 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the PropelBundle package.
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @license MIT License
|
||||
*/
|
||||
|
||||
namespace Propel\PropelBundle\Model\Acl;
|
||||
|
||||
use Propel\PropelBundle\Model\Acl\Base\ObjectIdentity as BaseObjectIdentity;
|
||||
use Propel\PropelBundle\Model\Acl\Map\ObjectIdentityTableMap;
|
||||
|
||||
use Propel\Runtime\ActiveQuery\Criteria;
|
||||
use Propel\Runtime\Connection\ConnectionInterface;
|
||||
|
||||
class ObjectIdentity extends BaseObjectIdentity
|
||||
{
|
||||
public function preInsert(ConnectionInterface $con = null)
|
||||
{
|
||||
// Compatibility with default implementation.
|
||||
$ancestor = new ObjectIdentityAncestor();
|
||||
$ancestor->setObjectIdentityRelatedByObjectIdentityId($this);
|
||||
$ancestor->setObjectIdentityRelatedByAncestorId($this);
|
||||
|
||||
$this->addObjectIdentityAncestorRelatedByAncestorId($ancestor);
|
||||
|
||||
if ($this->getParentObjectIdentityId()) {
|
||||
$this->updateAncestorsTree($con);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function preUpdate(ConnectionInterface $con = null)
|
||||
{
|
||||
if ($this->isColumnModified(ObjectIdentityTableMap::COL_PARENT_OBJECT_IDENTITY_ID)) {
|
||||
$this->updateAncestorsTree($con);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
$objIds = $children->getPrimaryKeys(false);
|
||||
$objIds[] = $this->getId();
|
||||
|
||||
$children->delete($con);
|
||||
|
||||
// Manually delete those for DBAdapter not capable of cascading the DELETE.
|
||||
ObjectIdentityAncestorQuery::create()
|
||||
->filterByObjectIdentityId($objIds, Criteria::IN)
|
||||
->delete($con)
|
||||
;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update all ancestor entries to reflect changes on this instance.
|
||||
*
|
||||
* @param ConnectionInterface $con
|
||||
*
|
||||
* @return \Propel\PropelBundle\Model\Acl\ObjectIdentity $this
|
||||
*/
|
||||
protected function updateAncestorsTree(ConnectionInterface $con = null)
|
||||
{
|
||||
$con->beginTransaction();
|
||||
|
||||
$oldAncestors = ObjectIdentityQuery::create()->findAncestors($this, $con);
|
||||
|
||||
$children = ObjectIdentityQuery::create()->findGrandChildren($this, $con);
|
||||
$children->append($this);
|
||||
|
||||
if (count($oldAncestors)) {
|
||||
foreach ($children as $eachChild) {
|
||||
/*
|
||||
* Delete only those entries, that are ancestors based on the parent relation.
|
||||
* Ancestors of grand children up to the current node will be kept.
|
||||
*/
|
||||
$query = ObjectIdentityAncestorQuery::create()
|
||||
->filterByObjectIdentityId($eachChild->getId())
|
||||
->filterByObjectIdentityRelatedByAncestorId($oldAncestors, Criteria::IN)
|
||||
;
|
||||
|
||||
if ($eachChild->getId() !== $this->getId()) {
|
||||
$query->filterByAncestorId(array($eachChild->getId(), $this->getId()), Criteria::NOT_IN);
|
||||
} else {
|
||||
$query->filterByAncestorId($this->getId(), Criteria::NOT_EQUAL);
|
||||
}
|
||||
|
||||
$query->delete($con);
|
||||
}
|
||||
}
|
||||
|
||||
// This is the new parent object identity!
|
||||
$parent = $this->getObjectIdentityRelatedByParentObjectIdentityId($con);
|
||||
if (null !== $parent) {
|
||||
$newAncestors = ObjectIdentityQuery::create()->findAncestors($parent, $con);
|
||||
$newAncestors->append($parent);
|
||||
foreach ($newAncestors as $eachAncestor) {
|
||||
// This collection contains the current object identity!
|
||||
foreach ($children as $eachChild) {
|
||||
$ancestor = ObjectIdentityAncestorQuery::create()
|
||||
->filterByObjectIdentityId($eachChild->getId())
|
||||
->filterByAncestorId($eachAncestor->getId())
|
||||
->findOneOrCreate($con)
|
||||
;
|
||||
|
||||
// If the entry already exists, next please.
|
||||
if (!$ancestor->isNew()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($eachChild->getId() === $this->getId()) {
|
||||
// Do not save() here, as it would result in an infinite recursion loop!
|
||||
$this->addObjectIdentityAncestorRelatedByObjectIdentityId($ancestor);
|
||||
} else {
|
||||
// Save the new ancestor to avoid integrity constraint violation.
|
||||
$ancestor->save($con);
|
||||
|
||||
$eachChild
|
||||
->addObjectIdentityAncestorRelatedByObjectIdentityId($ancestor)
|
||||
->save($con)
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$con->commit();
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
18
Model/Acl/ObjectIdentityAncestor.php
Normal file
18
Model/Acl/ObjectIdentityAncestor.php
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?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\Base\ObjectIdentityAncestor as BaseObjectIdentityAncestor;
|
||||
|
||||
class ObjectIdentityAncestor extends BaseObjectIdentityAncestor
|
||||
{
|
||||
|
||||
}
|
18
Model/Acl/ObjectIdentityAncestorQuery.php
Normal file
18
Model/Acl/ObjectIdentityAncestorQuery.php
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?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\Base\ObjectIdentityAncestorQuery as BaseObjectIdentityAncestorQuery;
|
||||
|
||||
class ObjectIdentityAncestorQuery extends BaseObjectIdentityAncestorQuery
|
||||
{
|
||||
|
||||
}
|
114
Model/Acl/ObjectIdentityQuery.php
Normal file
114
Model/Acl/ObjectIdentityQuery.php
Normal file
|
@ -0,0 +1,114 @@
|
|||
<?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\Base\ObjectIdentityQuery as BaseObjectIdentityQuery;
|
||||
|
||||
use Propel\Runtime\ActiveQuery\Criteria;
|
||||
use Propel\Runtime\Connection\ConnectionInterface;
|
||||
|
||||
use Symfony\Component\Security\Acl\Model\ObjectIdentityInterface;
|
||||
|
||||
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 ConnectionInterface $con
|
||||
*
|
||||
* @return \Propel\PropelBundle\Model\Acl\ObjectIdentityQuery $this
|
||||
*/
|
||||
public function filterByAclObjectIdentity(ObjectIdentityInterface $objectIdentity, ConnectionInterface $con = null)
|
||||
{
|
||||
/*
|
||||
* Not using a JOIN here, because the filter may be applied on 'findOneOrCreate',
|
||||
* which is currently (Propel 1.6.4-dev) not working.
|
||||
*/
|
||||
$aclClass = AclClass::fromAclObjectIdentity($objectIdentity, $con);
|
||||
$this
|
||||
->filterByClassId($aclClass->getId())
|
||||
->filterByIdentifier($objectIdentity->getIdentifier())
|
||||
;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an ObjectIdentity object belonging to the given ACL related ObjectIdentity.
|
||||
*
|
||||
* @param \Symfony\Component\Security\Acl\Model\ObjectIdentityInterface $objectIdentity
|
||||
* @param ConnectionInterface $con
|
||||
*
|
||||
* @return \Propel\PropelBundle\Model\Acl\ObjectIdentity
|
||||
*/
|
||||
public function findOneByAclObjectIdentity(ObjectIdentityInterface $objectIdentity, ConnectionInterface $con = null)
|
||||
{
|
||||
return $this
|
||||
->filterByAclObjectIdentity($objectIdentity, $con)
|
||||
->findOne($con)
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all children of the given object identity.
|
||||
*
|
||||
* @param \Propel\PropelBundle\Model\Acl\ObjectIdentity $objectIdentity
|
||||
* @param ConnectionInterface $con
|
||||
*
|
||||
* @return \PropelObjectCollection
|
||||
*/
|
||||
public function findChildren(ObjectIdentity $objectIdentity, ConnectionInterface $con = null)
|
||||
{
|
||||
return $this
|
||||
->filterByObjectIdentityRelatedByParentObjectIdentityId($objectIdentity)
|
||||
->find($con)
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all children and grand-children of the given object identity.
|
||||
*
|
||||
* @param \Propel\PropelBundle\Model\Acl\ObjectIdentity $objectIdentity
|
||||
* @param ConnectionInterface $con
|
||||
*
|
||||
* @return \PropelObjectCollection
|
||||
*/
|
||||
public function findGrandChildren(ObjectIdentity $objectIdentity, ConnectionInterface $con = null)
|
||||
{
|
||||
return $this
|
||||
->useObjectIdentityAncestorRelatedByObjectIdentityIdQuery()
|
||||
->filterByObjectIdentityRelatedByAncestorId($objectIdentity)
|
||||
->filterByObjectIdentityRelatedByObjectIdentityId($objectIdentity, Criteria::NOT_EQUAL)
|
||||
->endUse()
|
||||
->find($con)
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all ancestors of the given object identity.
|
||||
*
|
||||
* @param ObjectIdentity $objectIdentity
|
||||
* @param ConnectionInterface $con
|
||||
*
|
||||
* @return \PropelObjectCollection
|
||||
*/
|
||||
public function findAncestors(ObjectIdentity $objectIdentity, ConnectionInterface $con = null)
|
||||
{
|
||||
return $this
|
||||
->useObjectIdentityAncestorRelatedByAncestorIdQuery()
|
||||
->filterByObjectIdentityRelatedByObjectIdentityId($objectIdentity)
|
||||
->filterByObjectIdentityRelatedByAncestorId($objectIdentity, Criteria::NOT_EQUAL)
|
||||
->endUse()
|
||||
->find($con)
|
||||
;
|
||||
}
|
||||
}
|
87
Model/Acl/SecurityIdentity.php
Normal file
87
Model/Acl/SecurityIdentity.php
Normal file
|
@ -0,0 +1,87 @@
|
|||
<?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\Base\SecurityIdentity as BaseSecurityIdentity;
|
||||
|
||||
use Propel\Runtime\Connection\ConnectionInterface;
|
||||
|
||||
use Symfony\Component\Security\Acl\Domain\RoleSecurityIdentity;
|
||||
use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity;
|
||||
use Symfony\Component\Security\Acl\Model\SecurityIdentityInterface;
|
||||
|
||||
class SecurityIdentity extends BaseSecurityIdentity
|
||||
{
|
||||
/**
|
||||
* Transform a given mode security identity into an ACL related SecurityIdentity.
|
||||
*
|
||||
* @param \Propel\PropelBundle\Model\Acl\SecurityIdentity $securityIdentity
|
||||
*
|
||||
* @return \Symfony\Component\Security\Acl\Model\SecurityIdentityInterface
|
||||
*/
|
||||
public static function toAclIdentity(SecurityIdentity $securityIdentity)
|
||||
{
|
||||
$identifier = $securityIdentity->getIdentifier();
|
||||
|
||||
if ($securityIdentity->getUsername()) {
|
||||
if (false === strpos($identifier, '-')) {
|
||||
throw new \InvalidArgumentException('The given identifier does not resolve to a UserSecurityIdentity.');
|
||||
}
|
||||
|
||||
list($class, $username) = explode('-', $identifier, 2);
|
||||
|
||||
return new UserSecurityIdentity($username, $class);
|
||||
}
|
||||
|
||||
if (0 === strpos($identifier, 'ROLE_') or 0 === strpos($identifier, 'IS_AUTHENTICATED_')) {
|
||||
return new RoleSecurityIdentity($identifier);
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException('The security identity does not resolve to either UserSecurityIdentity or RoleSecurityIdentity.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a given ACL security identity into a SecurityIdentity model.
|
||||
*
|
||||
* If there is no model entry given, a new one will be created and saved to the database.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @param \Symfony\Component\Security\Acl\Model\SecurityIdentityInterface $aclIdentity
|
||||
* @param ConnectionInterface $con
|
||||
*
|
||||
* @return \Propel\PropelBundle\Model\Acl\SecurityIdentity
|
||||
*/
|
||||
public static function fromAclIdentity(SecurityIdentityInterface $aclIdentity, ConnectionInterface $con = null)
|
||||
{
|
||||
if ($aclIdentity instanceof UserSecurityIdentity) {
|
||||
$identifier = $aclIdentity->getClass().'-'.$aclIdentity->getUsername();
|
||||
$username = true;
|
||||
} elseif ($aclIdentity instanceof RoleSecurityIdentity) {
|
||||
$identifier = $aclIdentity->getRole();
|
||||
$username = false;
|
||||
} else {
|
||||
throw new \InvalidArgumentException('The ACL identity must either be an instance of UserSecurityIdentity or RoleSecurityIdentity.');
|
||||
}
|
||||
|
||||
$obj = SecurityIdentityQuery::create()
|
||||
->filterByIdentifier($identifier)
|
||||
->filterByUsername($username)
|
||||
->findOneOrCreate($con)
|
||||
;
|
||||
|
||||
if ($obj->isNew()) {
|
||||
$obj->save($con);
|
||||
}
|
||||
|
||||
return $obj;
|
||||
}
|
||||
}
|
18
Model/Acl/SecurityIdentityQuery.php
Normal file
18
Model/Acl/SecurityIdentityQuery.php
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?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\Base\SecurityIdentityQuery as BaseSecurityIdentityQuery;
|
||||
|
||||
class SecurityIdentityQuery extends BaseSecurityIdentityQuery
|
||||
{
|
||||
|
||||
}
|
|
@ -10,6 +10,10 @@
|
|||
|
||||
namespace Propel\PropelBundle;
|
||||
|
||||
use Propel\Runtime\Propel;
|
||||
use Propel\Runtime\Connection\ConnectionManagerSingle;
|
||||
use Propel\Runtime\Connection\ConnectionManagerMasterSlave;
|
||||
|
||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
|
@ -20,11 +24,63 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
|
|||
*/
|
||||
class PropelBundle extends Bundle
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
try {
|
||||
$this->configureConnections();
|
||||
|
||||
if ($this->container->getParameter('propel.logging')) {
|
||||
$this->configureLogging();
|
||||
}
|
||||
} catch (\Exception $e) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function build(ContainerBuilder $container)
|
||||
{
|
||||
}
|
||||
|
||||
protected function configureConnections()
|
||||
{
|
||||
$config = $this->container->getParameter('propel.configuration');
|
||||
|
||||
$serviceContainer = Propel::getServiceContainer();
|
||||
$serviceContainer->setDefaultDatasource($config['runtime']['defaultConnection']);
|
||||
|
||||
foreach ($config['database']['connections'] as $name => $config) {
|
||||
if (!empty($config['slaves'])) {
|
||||
$manager = new ConnectionManagerMasterSlave();
|
||||
|
||||
// configure the master (write) connection
|
||||
$manager->setWriteConfiguration($config['connection']);
|
||||
// configure the slave (read) connections
|
||||
$manager->setReadConfiguration($config['slaves']);
|
||||
} else {
|
||||
$manager = new ConnectionManagerSingle();
|
||||
$manager->setConfiguration($config);
|
||||
}
|
||||
|
||||
$serviceContainer->setAdapterClass($name, $config['adapter']);
|
||||
$serviceContainer->setConnectionManager($name, $manager);
|
||||
}
|
||||
}
|
||||
|
||||
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')));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
284
Request/ParamConverter/PropelParamConverter.php
Normal file
284
Request/ParamConverter/PropelParamConverter.php
Normal file
|
@ -0,0 +1,284 @@
|
|||
<?php
|
||||
|
||||
namespace Propel\PropelBundle\Request\ParamConverter;
|
||||
|
||||
use Propel\PropelBundle\Util\PropelInflector;
|
||||
use Propel\Runtime\ActiveQuery\Criteria;
|
||||
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
|
||||
*
|
||||
* This convert action parameter to a Propel Object
|
||||
* there is two option for this converter:
|
||||
*
|
||||
* mapping : take an array of routeParam => column
|
||||
* exclude : take an array of routeParam to exclude from the conversion process
|
||||
*
|
||||
*
|
||||
* @author Jérémie Augustin <jeremie.augustin@pixel-cookers.com>
|
||||
*/
|
||||
class PropelParamConverter implements ParamConverterInterface
|
||||
{
|
||||
/**
|
||||
* the pk column (e.g. id)
|
||||
* @var string
|
||||
*/
|
||||
protected $pk;
|
||||
|
||||
/**
|
||||
* list of column/value to use with filterBy
|
||||
* @var array
|
||||
*/
|
||||
protected $filters = array();
|
||||
|
||||
/**
|
||||
* list of route parameters to exclude from the conversion process
|
||||
* @var array
|
||||
*/
|
||||
protected $exclude = array();
|
||||
|
||||
/**
|
||||
* list of with option use to hydrate related object
|
||||
* @var array
|
||||
*/
|
||||
protected $withs;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $hasWith = false;
|
||||
|
||||
/**
|
||||
* @var RouterInterface
|
||||
*/
|
||||
protected $router;
|
||||
|
||||
public function setRouter(RouterInterface $router = null)
|
||||
{
|
||||
$this->router = $router;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param ParamConverter $configuration
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @throws \LogicException
|
||||
* @throws NotFoundHttpException
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function apply(Request $request, ParamConverter $configuration)
|
||||
{
|
||||
$class = $configuration->getClass();
|
||||
$classQuery = $class . 'Query';
|
||||
$classTableMap = $class::TABLE_MAP;
|
||||
$this->filters = array();
|
||||
$this->exclude = array();
|
||||
|
||||
if (!class_exists($classQuery)) {
|
||||
throw new \Exception(sprintf('The %s Query class does not exist', $classQuery));
|
||||
}
|
||||
|
||||
$tableMap = new $classTableMap();
|
||||
$pkColumns = $tableMap->getPrimaryKeys();
|
||||
|
||||
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');
|
||||
if (!empty($converterOption[$configuration->getName()])) {
|
||||
$options = $converterOption[$configuration->getName()];
|
||||
}
|
||||
}
|
||||
if (isset($options['mapping'])) {
|
||||
// We use the mapping for calling findPk or filterBy
|
||||
foreach ($options['mapping'] as $routeParam => $column) {
|
||||
if ($request->attributes->has($routeParam)) {
|
||||
if ($this->pk === $column) {
|
||||
$this->pk = $routeParam;
|
||||
} else {
|
||||
$this->filters[$column] = $request->attributes->get($routeParam);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->exclude = isset($options['exclude']) ? $options['exclude'] : array();
|
||||
$this->filters = $request->attributes->all();
|
||||
}
|
||||
|
||||
if (array_key_exists($configuration->getName(), $this->filters)) {
|
||||
unset($this->filters[$configuration->getName()]);
|
||||
}
|
||||
|
||||
$this->withs = isset($options['with']) ? is_array($options['with']) ? $options['with'] : array($options['with']) : array();
|
||||
|
||||
// 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.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (null === $object && false === $configuration->isOptional()) {
|
||||
throw new NotFoundHttpException(sprintf('%s object not found.', $configuration->getClass()));
|
||||
}
|
||||
|
||||
$request->attributes->set($configuration->getName(), $object);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ParamConverter $configuration
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
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->implementsInterface('\Propel\Runtime\ActiveRecord\ActiveRecordInterface')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to find the object with the id
|
||||
*
|
||||
* @param string $classQuery the query class
|
||||
* @param Request $request
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function findPk($classQuery, Request $request)
|
||||
{
|
||||
if (in_array($this->pk, $this->exclude) || !$request->attributes->has($this->pk)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$query = $this->getQuery($classQuery);
|
||||
|
||||
if (!$this->hasWith) {
|
||||
return $query->findPk($request->attributes->get($this->pk));
|
||||
} else {
|
||||
return $query->filterByPrimaryKey($request->attributes->get($this->pk))->find()->getFirst();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to find the object with all params from the $request
|
||||
*
|
||||
* @param string $classQuery the query class
|
||||
* @param Request $request
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function findOneBy($classQuery, Request $request)
|
||||
{
|
||||
$query = $this->getQuery($classQuery);
|
||||
$hasCriteria = false;
|
||||
foreach ($this->filters as $column => $value) {
|
||||
if (!in_array($column, $this->exclude)) {
|
||||
try {
|
||||
$query->{'filterBy' . PropelInflector::camelize($column)}($value);
|
||||
$hasCriteria = true;
|
||||
} catch (\PropelException $e) { }
|
||||
}
|
||||
}
|
||||
|
||||
if (!$hasCriteria) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$this->hasWith) {
|
||||
return $query->findOne();
|
||||
} else {
|
||||
return $query->find()->getFirst();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Init the query class with optional joinWith
|
||||
*
|
||||
* @param string $classQuery
|
||||
*
|
||||
* @return \ModelCriteria
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function getQuery($classQuery)
|
||||
{
|
||||
$query = $classQuery::create();
|
||||
|
||||
foreach ($this->withs as $with) {
|
||||
if (is_array($with)) {
|
||||
if (2 == count($with)) {
|
||||
$query->joinWith($with[0], $this->getValidJoin($with));
|
||||
$this->hasWith = true;
|
||||
} else {
|
||||
throw new \Exception(sprintf('ParamConverter : "with" parameter "%s" is invalid,
|
||||
only string relation name (e.g. "Book") or an array with two keys (e.g. {"Book", "LEFT_JOIN"}) are allowed',
|
||||
var_export($with, true)));
|
||||
}
|
||||
} else {
|
||||
$query->joinWith($with);
|
||||
$this->hasWith = true;
|
||||
}
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the valid join Criteria base on the with parameter
|
||||
*
|
||||
* @param array $with
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function getValidJoin($with)
|
||||
{
|
||||
switch (trim(str_replace(array('_', 'JOIN'), '', strtoupper($with[1])))) {
|
||||
case 'LEFT':
|
||||
return Criteria::LEFT_JOIN;
|
||||
case 'RIGHT':
|
||||
return Criteria::RIGHT_JOIN;
|
||||
case 'INNER':
|
||||
return Criteria::INNER_JOIN;
|
||||
}
|
||||
|
||||
throw new \Exception(sprintf('ParamConverter : "with" parameter "%s" is invalid,
|
||||
only "left", "right" or "inner" are allowed for join option',
|
||||
var_export($with, true)));
|
||||
}
|
||||
|
||||
}
|
104
Resources/acl_schema.xml
Normal file
104
Resources/acl_schema.xml
Normal file
|
@ -0,0 +1,104 @@
|
|||
<?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">
|
||||
<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" />
|
||||
|
||||
<unique>
|
||||
<unique-column name="class_type" />
|
||||
</unique>
|
||||
</table>
|
||||
|
||||
<table name="acl_security_identities" phpName="SecurityIdentity">
|
||||
<column name="id" type="integer" autoIncrement="true" primaryKey="true" />
|
||||
<column name="identifier" type="varchar" size="200" required="true" />
|
||||
<column name="username" type="boolean" required="true" />
|
||||
|
||||
<unique>
|
||||
<unique-column name="identifier" />
|
||||
<unique-column name="username" />
|
||||
</unique>
|
||||
</table>
|
||||
|
||||
<table name="acl_object_identities" phpName="ObjectIdentity">
|
||||
<column name="id" type="integer" autoIncrement="true" primaryKey="true" />
|
||||
<column name="class_id" type="integer" required="true" />
|
||||
<column name="object_identifier" type="varchar" size="200" required="true" phpName="Identifier" />
|
||||
<column name="parent_object_identity_id" type="integer" required="false" defaultValue="null" />
|
||||
<column name="entries_inheriting" type="boolean" required="true" defaultValue="true" />
|
||||
|
||||
<unique>
|
||||
<unique-column name="class_id" />
|
||||
<unique-column name="object_identifier" />
|
||||
</unique>
|
||||
|
||||
<index>
|
||||
<index-column name="parent_object_identity_id" />
|
||||
</index>
|
||||
|
||||
<foreign-key foreignTable="acl_classes" onDelete="RESTRICT" onUpdate="CASCADE">
|
||||
<reference local="class_id" foreign="id" />
|
||||
</foreign-key>
|
||||
<foreign-key foreignTable="acl_object_identities" onDelete="RESTRICT" onUpdate="CASCADE">
|
||||
<reference local="parent_object_identity_id" foreign="id" />
|
||||
</foreign-key>
|
||||
</table>
|
||||
|
||||
<table name="acl_object_identity_ancestors" phpName="ObjectIdentityAncestor" heavyIndexing="true">
|
||||
<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">
|
||||
<reference local="object_identity_id" foreign="id" />
|
||||
</foreign-key>
|
||||
<foreign-key foreignTable="acl_object_identities" onDelete="CASCADE" onUpdate="CASCADE">
|
||||
<reference local="ancestor_id" foreign="id" />
|
||||
</foreign-key>
|
||||
</table>
|
||||
|
||||
<table name="acl_entries" phpName="Entry">
|
||||
<column name="id" type="integer" autoIncrement="true" primaryKey="true" />
|
||||
<column name="class_id" type="integer" required="true" />
|
||||
<column name="object_identity_id" type="integer" required="false" defaultValue="null" />
|
||||
<column name="security_identity_id" type="integer" required="true" />
|
||||
<column name="field_name" type="varchar" size="50" />
|
||||
<column name="ace_order" type="integer" required="true" />
|
||||
<column name="mask" type="integer" required="true" />
|
||||
<column name="granting" type="boolean" required="true" />
|
||||
<column name="granting_strategy" type="varchar" size="30" required="true" />
|
||||
<column name="audit_success" type="boolean" required="true" defaultValue="false" />
|
||||
<column name="audit_failure" type="boolean" required="true" defaultValue="true" />
|
||||
|
||||
<unique>
|
||||
<unique-column name="class_id" />
|
||||
<unique-column name="object_identity_id" />
|
||||
<unique-column name="field_name" />
|
||||
<unique-column name="ace_order" />
|
||||
</unique>
|
||||
|
||||
<index>
|
||||
<index-column name="class_id" />
|
||||
<index-column name="object_identity_id" />
|
||||
<index-column name="security_identity_id" />
|
||||
</index>
|
||||
<index>
|
||||
<index-column name="class_id" />
|
||||
</index>
|
||||
<index>
|
||||
<index-column name="object_identity_id" />
|
||||
</index>
|
||||
<index>
|
||||
<index-column name="security_identity_id" />
|
||||
</index>
|
||||
|
||||
<foreign-key foreignTable="acl_classes" onDelete="CASCADE" onUpdate="CASCADE">
|
||||
<reference local="class_id" foreign="id" />
|
||||
</foreign-key>
|
||||
<foreign-key foreignTable="acl_object_identities" onDelete="CASCADE" onUpdate="CASCADE">
|
||||
<reference local="object_identity_id" foreign="id" />
|
||||
</foreign-key>
|
||||
<foreign-key foreignTable="acl_security_identities" onDelete="CASCADE" onUpdate="CASCADE">
|
||||
<reference local="security_identity_id" foreign="id" />
|
||||
</foreign-key>
|
||||
</table>
|
||||
</database>
|
20
Resources/config/converters.xml
Normal file
20
Resources/config/converters.xml
Normal file
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" ?>
|
||||
|
||||
<container xmlns="http://symfony.com/schema/dic/services"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
|
||||
<parameters>
|
||||
<parameter key="propel.converter.propel.class">Propel\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>
|
64
Resources/config/propel.xml
Normal file
64
Resources/config/propel.xml
Normal file
|
@ -0,0 +1,64 @@
|
|||
<?xml version="1.0" ?>
|
||||
|
||||
<container xmlns="http://symfony.com/schema/dic/services"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
|
||||
<parameters>
|
||||
<parameter key="propel.dbal.default_connection">default</parameter>
|
||||
|
||||
<parameter key="propel.schema_locator.class">Propel\PropelBundle\Service\SchemaLocator</parameter>
|
||||
<parameter key="propel.data_collector.class">Propel\PropelBundle\DataCollector\PropelDataCollector</parameter>
|
||||
<parameter key="propel.logger.class">Propel\PropelBundle\Logger\PropelLogger</parameter>
|
||||
<parameter key="propel.twig.extension.syntax.class">Propel\PropelBundle\Twig\Extension\SyntaxExtension</parameter>
|
||||
<parameter key="form.type_guesser.propel.class">Propel\PropelBundle\Form\TypeGuesser</parameter>
|
||||
<parameter key="propel.form.type.model.class">Propel\PropelBundle\Form\Type\ModelType</parameter>
|
||||
<parameter key="propel.dumper.yaml.class">Propel\PropelBundle\DataFixtures\Dumper\YamlDataDumper</parameter>
|
||||
<parameter key="propel.loader.yaml.class">Propel\PropelBundle\DataFixtures\Loader\YamlDataLoader</parameter>
|
||||
<parameter key="propel.loader.xml.class">Propel\PropelBundle\DataFixtures\Loader\XmlDataLoader</parameter>
|
||||
</parameters>
|
||||
|
||||
<services>
|
||||
<service id="propel.schema_locator" class="%propel.schema_locator.class%">
|
||||
<argument type="service" id="file_locator" />
|
||||
</service>
|
||||
|
||||
<service id="propel.logger" class="%propel.logger.class%">
|
||||
<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">
|
||||
<argument type="service" id="propel.logger" />
|
||||
<tag name="data_collector" template="PropelBundle:Collector:propel" id="propel" />
|
||||
</service>
|
||||
|
||||
<service id="propel.twig.extension.syntax" class="%propel.twig.extension.syntax.class%">
|
||||
<tag name="twig.extension" />
|
||||
</service>
|
||||
|
||||
<service id="form.type_guesser.propel" class="%form.type_guesser.propel.class%">
|
||||
<tag name="form.type_guesser" />
|
||||
</service>
|
||||
|
||||
<service id="propel.form.type.model" class="%propel.form.type.model.class%">
|
||||
<tag name="form.type" alias="model" />
|
||||
</service>
|
||||
|
||||
<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>
|
21
Resources/config/security.xml
Normal file
21
Resources/config/security.xml
Normal 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\PropelBundle\Security\Acl\AuditableAclProvider</parameter>
|
||||
<parameter key="propel.security.user.provider.class">Propel\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>
|
21
Resources/skeleton/FormType.php
Normal file
21
Resources/skeleton/FormType.php
Normal file
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
namespace ##NAMESPACE##;
|
||||
|
||||
use Propel\PropelBundle\Form\BaseAbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
class ##CLASS## extends BaseAbstractType
|
||||
{
|
||||
protected $options = array(
|
||||
'data_class' => '##FQCN##',
|
||||
'name' => '##TYPE_NAME##',
|
||||
);
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{##BUILD_CODE##
|
||||
}
|
||||
}
|
111
Resources/views/Collector/propel.html.twig
Normal file
111
Resources/views/Collector/propel.html.twig
Normal file
|
@ -0,0 +1,111 @@
|
|||
{% extends 'WebProfilerBundle: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 } %}
|
||||
{% endblock %}
|
||||
|
||||
{% block menu %}
|
||||
{# the menu content #}
|
||||
<span class="label">
|
||||
<span class="icon"><img src="{{ asset('bundles/propel/images/profiler/propel.png') }}" alt="" /></span>
|
||||
<strong>Propel</strong>
|
||||
<span class="count">
|
||||
<span>{{ collector.querycount }}</span>
|
||||
<span>{{ '%0.0f'|format(collector.time * 1000) }} ms</span>
|
||||
</span>
|
||||
</span>
|
||||
{% endblock %}
|
||||
|
||||
{% block panel %}
|
||||
{# the panel content #}
|
||||
<style type="text/css">
|
||||
.SQLKeyword {
|
||||
color: blue;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.SQLName {
|
||||
color: #464646;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.SQLInfo, .SQLComment {
|
||||
color: gray;
|
||||
display: block;
|
||||
font-size: 0.9em;
|
||||
margin: 3px 0;
|
||||
}
|
||||
|
||||
.SQLExplain {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.SQLExplain .error {
|
||||
background-color: #F2DEDE;
|
||||
border-color: #EED3D7;
|
||||
color: #B94A48;
|
||||
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')
|
||||
}) %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="SQLInfo">
|
||||
Time: {{ query.time }} - Memory: {{ query.memory|format_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>
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{% render controller('PropelBundle:Panel:configuration') %}
|
||||
{% endblock %}
|
69
Resources/views/Panel/configuration.html.twig
Normal file
69
Resources/views/Panel/configuration.html.twig
Normal file
|
@ -0,0 +1,69 @@
|
|||
<h2>Propel configuration</h2>
|
||||
|
||||
<table summary="Current Propel configuration">
|
||||
<thead>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Propel version</th>
|
||||
<td>{{ propel_version }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Default connection</th>
|
||||
<td>{{ configuration.runtime.defaultConnection }}</td>
|
||||
<tr>
|
||||
<th>Logging</th>
|
||||
<td>{{ logging ? 'enabled' : 'disabled' }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2>Propel connections</h2>
|
||||
|
||||
<table summary="Current Propel connections">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Connection name</th>
|
||||
<th colspan="2" style="text-align: center;">Configuration parameters</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for name, config in configuration.database.connections %}
|
||||
<tr>
|
||||
<th rowspan="5" style="vertical-align: top;">
|
||||
{{ name }}
|
||||
</th>
|
||||
<th>Adapter</th>
|
||||
<td>{{ config.adapter }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>DSN</th>
|
||||
<td>{{ config.dsn }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Class</th>
|
||||
<td>{{ config.classname }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Options</th>
|
||||
<td>
|
||||
<ul>
|
||||
{% for key, value in config.options %}
|
||||
<li>{{ key }} : {{ value }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Attributes</th>
|
||||
<td>
|
||||
<ul>
|
||||
{% for key, value in config.attributes %}
|
||||
<li>{{ key }} : {{ value }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
16
Resources/views/Panel/explain.html.twig
Normal file
16
Resources/views/Panel/explain.html.twig
Normal file
|
@ -0,0 +1,16 @@
|
|||
<h2>Explanation</h2>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
{% for label in data[0]|keys %}
|
||||
<th>{{ label }}</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% for row in data %}
|
||||
<tr>
|
||||
{% for item in row %}
|
||||
<td>{{ item }}</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
181
Security/Acl/AclProvider.php
Normal file
181
Security/Acl/AclProvider.php
Normal file
|
@ -0,0 +1,181 @@
|
|||
<?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\Security\Acl;
|
||||
|
||||
use Propel\Runtime\Collection\ObjectCollection;
|
||||
use Propel\Runtime\Connection\ConnectionInterface;
|
||||
|
||||
use Propel\PropelBundle\Model\Acl\EntryQuery;
|
||||
use Propel\PropelBundle\Model\Acl\ObjectIdentityQuery;
|
||||
use Propel\PropelBundle\Model\Acl\SecurityIdentity;
|
||||
|
||||
use Propel\PropelBundle\Security\Acl\Domain\Acl;
|
||||
|
||||
use Symfony\Component\Security\Acl\Domain\ObjectIdentity;
|
||||
|
||||
use Symfony\Component\Security\Acl\Exception\AclNotFoundException;
|
||||
|
||||
use Symfony\Component\Security\Acl\Model\AclInterface;
|
||||
use Symfony\Component\Security\Acl\Model\AclCacheInterface;
|
||||
use Symfony\Component\Security\Acl\Model\AclProviderInterface;
|
||||
use Symfony\Component\Security\Acl\Model\ObjectIdentityInterface;
|
||||
use Symfony\Component\Security\Acl\Model\PermissionGrantingStrategyInterface;
|
||||
|
||||
/**
|
||||
* An implementation of the AclProviderInterface using Propel ORM.
|
||||
*
|
||||
* @author Toni Uebernickel <tuebernickel@gmail.com>
|
||||
*/
|
||||
class AclProvider implements AclProviderInterface
|
||||
{
|
||||
protected $permissionGrantingStrategy;
|
||||
protected $connection;
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \Symfony\Component\Security\Acl\Model\PermissionGrantingStrategyInterface $permissionGrantingStrategy
|
||||
* @param ConnectionInterface $con
|
||||
* @param \Symfony\Component\Security\Acl\Model\AclCacheInterface $cache
|
||||
*/
|
||||
public function __construct(PermissionGrantingStrategyInterface $permissionGrantingStrategy, ConnectionInterface $connection = null, AclCacheInterface $cache = null)
|
||||
{
|
||||
$this->permissionGrantingStrategy = $permissionGrantingStrategy;
|
||||
$this->connection = $connection;
|
||||
$this->cache = $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all child object identities from the database.
|
||||
*
|
||||
* @param \Symfony\Component\Security\Acl\Model\ObjectIdentityInterface $parentObjectIdentity
|
||||
* @param bool $directChildrenOnly
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function findChildren(ObjectIdentityInterface $parentObjectIdentity, $directChildrenOnly = false)
|
||||
{
|
||||
$modelIdentity = ObjectIdentityQuery::create()->findOneByAclObjectIdentity($parentObjectIdentity, $this->connection);
|
||||
if (empty($modelIdentity)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
if ($directChildrenOnly) {
|
||||
$collection = ObjectIdentityQuery::create()->findChildren($modelIdentity, $this->connection);
|
||||
} else {
|
||||
$collection = ObjectIdentityQuery::create()->findGrandChildren($modelIdentity, $this->connection);
|
||||
}
|
||||
|
||||
$children = array();
|
||||
foreach ($collection as $eachChild) {
|
||||
$children[] = new ObjectIdentity($eachChild->getIdentifier(), $eachChild->getAclClass($this->connection)->getType());
|
||||
}
|
||||
|
||||
return $children;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ACL that belongs to the given object identity
|
||||
*
|
||||
* @throws \Symfony\Component\Security\Acl\Exception\AclNotFoundException
|
||||
*
|
||||
* @param \Symfony\Component\Security\Acl\Model\ObjectIdentityInterface $objectIdentity
|
||||
* @param array $securityIdentities
|
||||
*
|
||||
* @return \Symfony\Component\Security\Acl\Model\AclInterface
|
||||
*/
|
||||
public function findAcl(ObjectIdentityInterface $objectIdentity, array $securityIdentities = array())
|
||||
{
|
||||
$modelObj = ObjectIdentityQuery::create()->findOneByAclObjectIdentity($objectIdentity, $this->connection);
|
||||
if (null !== $this->cache and null !== $modelObj) {
|
||||
$cachedAcl = $this->cache->getFromCacheById($modelObj->getId());
|
||||
if ($cachedAcl instanceof AclInterface) {
|
||||
return $cachedAcl;
|
||||
}
|
||||
}
|
||||
|
||||
$collection = EntryQuery::create()->findByAclIdentity($objectIdentity, $securityIdentities, $this->connection);
|
||||
|
||||
if (0 === count($collection)) {
|
||||
if (empty($securityIdentities)) {
|
||||
$errorMessage = 'There is no ACL available for this object identity. Please create one using the MutableAclProvider.';
|
||||
} else {
|
||||
$errorMessage = 'There is at least no ACL for this object identity and the given security identities. Try retrieving the ACL without security identity filter and add ACEs for the security identities.';
|
||||
}
|
||||
|
||||
throw new AclNotFoundException($errorMessage);
|
||||
}
|
||||
|
||||
$loadedSecurityIdentities = array();
|
||||
foreach ($collection as $eachEntry) {
|
||||
if (!isset($loadedSecurityIdentities[$eachEntry->getSecurityIdentity()->getId()])) {
|
||||
$loadedSecurityIdentities[$eachEntry->getSecurityIdentity()->getId()] = SecurityIdentity::toAclIdentity($eachEntry->getSecurityIdentity());
|
||||
}
|
||||
}
|
||||
|
||||
$parentAcl = null;
|
||||
$entriesInherited = true;
|
||||
|
||||
if (null !== $modelObj) {
|
||||
$entriesInherited = $modelObj->getEntriesInheriting();
|
||||
if (null !== $modelObj->getParentObjectIdentityId()) {
|
||||
$parentObj = $modelObj->getObjectIdentityRelatedByParentObjectIdentityId($this->connection);
|
||||
try {
|
||||
$parentAcl = $this->findAcl(new ObjectIdentity($parentObj->getIdentifier(), $parentObj->getAclClass($this->connection)->getType()));
|
||||
} catch (AclNotFoundException $e) {
|
||||
/*
|
||||
* This happens e.g. if the parent ACL is created, but does not contain any ACE by now.
|
||||
* The ACEs may be applied later on.
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->getAcl($collection, $objectIdentity, $loadedSecurityIdentities, $parentAcl, $entriesInherited);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ACLs that belong to the given object identities
|
||||
*
|
||||
* @throws \Symfony\Component\Security\Acl\Exception\AclNotFoundException When at least one object identity is missing its ACL.
|
||||
*
|
||||
* @param array $objectIdentities an array of ObjectIdentityInterface implementations
|
||||
* @param array $securityIdentities an array of SecurityIdentityInterface implementations
|
||||
*
|
||||
* @return \SplObjectStorage mapping the passed object identities to ACLs
|
||||
*/
|
||||
public function findAcls(array $objectIdentities, array $securityIdentities = array())
|
||||
{
|
||||
$result = new \SplObjectStorage();
|
||||
foreach ($objectIdentities as $eachIdentity) {
|
||||
$result[$eachIdentity] = $this->findAcl($eachIdentity, $securityIdentities);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an ACL.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
39
Security/Acl/AuditableAclProvider.php
Normal file
39
Security/Acl/AuditableAclProvider.php
Normal file
|
@ -0,0 +1,39 @@
|
|||
<?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\Security\Acl;
|
||||
|
||||
use Propel\Runtime\Collection\ObjectCollection;
|
||||
use Propel\PropelBundle\Security\Acl\Domain\AuditableAcl;
|
||||
|
||||
use Symfony\Component\Security\Acl\Model\AclInterface;
|
||||
use Symfony\Component\Security\Acl\Model\ObjectIdentityInterface;
|
||||
|
||||
/**
|
||||
* @author Toni Uebernickel <tuebernickel@gmail.com>
|
||||
*/
|
||||
class AuditableAclProvider extends MutableAclProvider
|
||||
{
|
||||
/**
|
||||
* Get an ACL for this provider.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
316
Security/Acl/Domain/Acl.php
Normal file
316
Security/Acl/Domain/Acl.php
Normal file
|
@ -0,0 +1,316 @@
|
|||
<?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\Security\Acl\Domain;
|
||||
|
||||
use Propel\Runtime\Collection\ObjectCollection;
|
||||
|
||||
use Symfony\Component\Security\Acl\Exception\Exception as AclException;
|
||||
|
||||
use Symfony\Component\Security\Acl\Model\AclInterface;
|
||||
use Symfony\Component\Security\Acl\Model\ObjectIdentityInterface;
|
||||
use Symfony\Component\Security\Acl\Model\PermissionGrantingStrategyInterface;
|
||||
use Symfony\Component\Security\Acl\Model\SecurityIdentityInterface;
|
||||
|
||||
/**
|
||||
* An ACL implementation that is immutable based on data from a ObjectCollection of Propel\PropelBundle\Model\Acl\Entry.
|
||||
*
|
||||
* @author Toni Uebernickel <tuebernickel@gmail.com>
|
||||
*/
|
||||
class Acl implements AclInterface
|
||||
{
|
||||
protected $model = '\Propel\PropelBundle\Model\Acl\Entry';
|
||||
|
||||
protected $classAces = array();
|
||||
protected $classFieldAces = array();
|
||||
protected $objectAces = array();
|
||||
protected $objectFieldAces = array();
|
||||
|
||||
protected $objectIdentity;
|
||||
protected $parentAcl;
|
||||
protected $permissionGrantingStrategy;
|
||||
protected $inherited;
|
||||
|
||||
protected $loadedSecurityIdentities = array();
|
||||
|
||||
/**
|
||||
* A list of known associated fields on this ACL.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fields = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @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(ObjectCollection $entries, ObjectIdentityInterface $objectIdentity, PermissionGrantingStrategyInterface $permissionGrantingStrategy, array $loadedSecurityIdentities = array(), AclInterface $parentAcl = null, $inherited = true)
|
||||
{
|
||||
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) {
|
||||
if (null === $eachEntry->getFieldName() and null === $eachEntry->getObjectIdentityId()) {
|
||||
$this->classAces[] = new Entry($eachEntry, $this);
|
||||
}
|
||||
|
||||
if (null !== $eachEntry->getFieldName() and null === $eachEntry->getObjectIdentityId()) {
|
||||
if (empty($this->classFieldAces[$eachEntry->getFieldName()])) {
|
||||
$this->classFieldAces[$eachEntry->getFieldName()] = array();
|
||||
$this->updateFields($eachEntry->getFieldName());
|
||||
}
|
||||
|
||||
$this->classFieldAces[$eachEntry->getFieldName()][] = new FieldEntry($eachEntry, $this);
|
||||
}
|
||||
|
||||
if (null === $eachEntry->getFieldName() and null !== $eachEntry->getObjectIdentityId()) {
|
||||
$this->objectAces[] = new Entry($eachEntry, $this);
|
||||
}
|
||||
|
||||
if (null !== $eachEntry->getFieldName() and null !== $eachEntry->getObjectIdentityId()) {
|
||||
if (empty($this->objectFieldAces[$eachEntry->getFieldName()])) {
|
||||
$this->objectFieldAces[$eachEntry->getFieldName()] = array();
|
||||
$this->updateFields($eachEntry->getFieldName());
|
||||
}
|
||||
|
||||
$this->objectFieldAces[$eachEntry->getFieldName()][] = new FieldEntry($eachEntry, $this);
|
||||
}
|
||||
}
|
||||
|
||||
$this->objectIdentity = $objectIdentity;
|
||||
$this->permissionGrantingStrategy = $permissionGrantingStrategy;
|
||||
$this->parentAcl = $parentAcl;
|
||||
$this->inherited = $inherited;
|
||||
$this->loadedSecurityIdentities = $loadedSecurityIdentities;
|
||||
|
||||
$this->fields = array_unique($this->fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all class-based ACEs associated with this ACL
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getClassAces()
|
||||
{
|
||||
return $this->classAces;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all class-field-based ACEs associated with this ACL
|
||||
*
|
||||
* @param string $field
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getClassFieldAces($field)
|
||||
{
|
||||
return isset($this->classFieldAces[$field]) ? $this->classFieldAces[$field] : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all object-based ACEs associated with this ACL
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getObjectAces()
|
||||
{
|
||||
return $this->objectAces;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all object-field-based ACEs associated with this ACL
|
||||
*
|
||||
* @param string $field
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getObjectFieldAces($field)
|
||||
{
|
||||
return isset($this->objectFieldAces[$field]) ? $this->objectFieldAces[$field] : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the object identity associated with this ACL
|
||||
*
|
||||
* @return \Symfony\Component\Security\Acl\Model\ObjectIdentityInterface
|
||||
*/
|
||||
public function getObjectIdentity()
|
||||
{
|
||||
return $this->objectIdentity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent ACL, or null if there is none.
|
||||
*
|
||||
* @return \Symfony\Component\Security\Acl\Model\AclInterface|null
|
||||
*/
|
||||
public function getParentAcl()
|
||||
{
|
||||
return $this->parentAcl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this ACL is inheriting ACEs from a parent ACL.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEntriesInheriting()
|
||||
{
|
||||
return $this->inherited;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether field access is granted
|
||||
*
|
||||
* @param string $field
|
||||
* @param array $masks
|
||||
* @param array $securityIdentities
|
||||
* @param bool $administrativeMode
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isFieldGranted($field, array $masks, array $securityIdentities, $administrativeMode = false)
|
||||
{
|
||||
return $this->permissionGrantingStrategy->isFieldGranted($this, $field, $masks, $securityIdentities, $administrativeMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether access is granted
|
||||
*
|
||||
* @throws \Symfony\Component\Security\Acl\Exception\NoAceFoundException when no ACE was applicable for this request
|
||||
*
|
||||
* @param array $masks
|
||||
* @param array $securityIdentities
|
||||
* @param bool $administrativeMode
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isGranted(array $masks, array $securityIdentities, $administrativeMode = false)
|
||||
{
|
||||
return $this->permissionGrantingStrategy->isGranted($this, $masks, $securityIdentities, $administrativeMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the ACL has loaded ACEs for all of the passed security identities
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @param mixed $securityIdentities an implementation of SecurityIdentityInterface, or an array thereof
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isSidLoaded($securityIdentities)
|
||||
{
|
||||
if (!is_array($securityIdentities)) {
|
||||
$securityIdentities = array($securityIdentities);
|
||||
}
|
||||
|
||||
$found = 0;
|
||||
foreach ($securityIdentities as $eachSecurityIdentity) {
|
||||
if (!$eachSecurityIdentity instanceof SecurityIdentityInterface) {
|
||||
throw new \InvalidArgumentException('At least one entry of the given list is not implementing the "SecurityIdentityInterface".');
|
||||
}
|
||||
|
||||
foreach ($this->loadedSecurityIdentities as $eachLoadedIdentity) {
|
||||
if ($eachSecurityIdentity->equals($eachLoadedIdentity)) {
|
||||
$found++;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ($found === count($securityIdentities));
|
||||
}
|
||||
|
||||
/**
|
||||
* String representation of object
|
||||
*
|
||||
* @link http://php.net/manual/en/serializable.serialize.php
|
||||
*
|
||||
* @return string the string representation of the object or &null;
|
||||
*/
|
||||
public function serialize()
|
||||
{
|
||||
return serialize(array(
|
||||
$this->model,
|
||||
$this->classAces,
|
||||
$this->classFieldAces,
|
||||
$this->objectAces,
|
||||
$this->objectFieldAces,
|
||||
$this->objectIdentity,
|
||||
$this->parentAcl,
|
||||
$this->permissionGrantingStrategy,
|
||||
$this->inherited,
|
||||
$this->loadedSecurityIdentities,
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs the object
|
||||
*
|
||||
* @link http://php.net/manual/en/serializable.unserialize.php
|
||||
*
|
||||
* @param string $serialized
|
||||
*
|
||||
* @return mixed the original value unserialized.
|
||||
*/
|
||||
public function unserialize($serialized)
|
||||
{
|
||||
list(
|
||||
$this->model,
|
||||
$this->classAces,
|
||||
$this->classFieldAces,
|
||||
$this->objectAces,
|
||||
$this->objectFieldAces,
|
||||
$this->objectIdentity,
|
||||
$this->parentAcl,
|
||||
$this->permissionGrantingStrategy,
|
||||
$this->inherited,
|
||||
$this->loadedSecurityIdentities,
|
||||
) = unserialize($serialized);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of associated fields on this ACL.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFields()
|
||||
{
|
||||
return $this->fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the internal list of associated fields on this ACL.
|
||||
*
|
||||
* @param string $field
|
||||
*
|
||||
* @return \Propel\PropelBundle\Security\Acl\Domain\Acl $this
|
||||
*/
|
||||
protected function updateFields($field)
|
||||
{
|
||||
if (!in_array($field, $this->fields)) {
|
||||
$this->fields[] = $field;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
103
Security/Acl/Domain/AuditableAcl.php
Normal file
103
Security/Acl/Domain/AuditableAcl.php
Normal file
|
@ -0,0 +1,103 @@
|
|||
<?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\Security\Acl\Domain;
|
||||
|
||||
use Propel\PropelBundle\Model\Acl\Entry as ModelEntry;
|
||||
|
||||
use Symfony\Component\Security\Acl\Model\AuditableAclInterface;
|
||||
|
||||
/**
|
||||
* @author Toni Uebernickel <tuebernickel@gmail.com>
|
||||
*/
|
||||
class AuditableAcl extends MutableAcl implements AuditableAclInterface
|
||||
{
|
||||
/**
|
||||
* Updates auditing for class-based ACE
|
||||
*
|
||||
* @param integer $index
|
||||
* @param bool $auditSuccess
|
||||
* @param bool $auditFailure
|
||||
*/
|
||||
public function updateClassAuditing($index, $auditSuccess, $auditFailure)
|
||||
{
|
||||
$this->updateAuditing($this->classAces, $index, $auditSuccess, $auditFailure);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates auditing for class-field-based ACE
|
||||
*
|
||||
* @param integer $index
|
||||
* @param string $field
|
||||
* @param bool $auditSuccess
|
||||
* @param bool $auditFailure
|
||||
*/
|
||||
public function updateClassFieldAuditing($index, $field, $auditSuccess, $auditFailure)
|
||||
{
|
||||
$this->validateField($this->classFieldAces, $field);
|
||||
$this->updateAuditing($this->classFieldAces[$field], $index, $auditSuccess, $auditFailure);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates auditing for object-based ACE
|
||||
*
|
||||
* @param integer $index
|
||||
* @param bool $auditSuccess
|
||||
* @param bool $auditFailure
|
||||
*/
|
||||
public function updateObjectAuditing($index, $auditSuccess, $auditFailure)
|
||||
{
|
||||
$this->updateAuditing($this->objectAces, $index, $auditSuccess, $auditFailure);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates auditing for object-field-based ACE
|
||||
*
|
||||
* @param integer $index
|
||||
* @param string $field
|
||||
* @param bool $auditSuccess
|
||||
* @param bool $auditFailure
|
||||
*/
|
||||
public function updateObjectFieldAuditing($index, $field, $auditSuccess, $auditFailure)
|
||||
{
|
||||
$this->validateField($this->objectFieldAces, $field);
|
||||
$this->updateAuditing($this->objectFieldAces[$field], $index, $auditSuccess, $auditFailure);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update auditing on a single ACE.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @param array $list
|
||||
* @param int $index
|
||||
* @param bool $auditSuccess
|
||||
* @param bool $auditFailure
|
||||
*
|
||||
* @return \Propel\PropelBundle\Security\Acl\Domain\AuditableAcl $this
|
||||
*/
|
||||
protected function updateAuditing(array &$list, $index, $auditSuccess, $auditFailure)
|
||||
{
|
||||
if (!is_bool($auditSuccess) or !is_bool($auditFailure)) {
|
||||
throw new \InvalidArgumentException('The given auditing flags are invalid. Please provide boolean only.');
|
||||
}
|
||||
|
||||
$this->validateIndex($list, $index);
|
||||
|
||||
$entry = ModelEntry::fromAclEntry($list[$index])
|
||||
->setAuditSuccess($auditSuccess)
|
||||
->setAuditFailure($auditFailure)
|
||||
;
|
||||
|
||||
$list[$index] = ModelEntry::toAclEntry($entry, $this);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
192
Security/Acl/Domain/Entry.php
Normal file
192
Security/Acl/Domain/Entry.php
Normal file
|
@ -0,0 +1,192 @@
|
|||
<?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\Security\Acl\Domain;
|
||||
|
||||
use Propel\PropelBundle\Model\Acl\Entry as ModelEntry;
|
||||
use Propel\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.
|
||||
*
|
||||
* The entry is only used to grab a "snapshot" of its data as an EntryInterface is immutable!
|
||||
*
|
||||
* @see \Symfony\Component\Security\Acl\Model\EntryInterface
|
||||
*
|
||||
* @author Toni Uebernickel <tuebernickel@gmail.com>
|
||||
*/
|
||||
class Entry implements AuditableEntryInterface
|
||||
{
|
||||
protected $acl;
|
||||
|
||||
protected $id;
|
||||
protected $securityIdentity;
|
||||
protected $mask;
|
||||
protected $isGranting;
|
||||
protected $strategy;
|
||||
protected $auditSuccess;
|
||||
protected $auditFailure;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \Propel\PropelBundle\Model\Acl\Entry $entry
|
||||
* @param \Symfony\Component\Security\Acl\Model\AclInterface $acl
|
||||
*/
|
||||
public function __construct(ModelEntry $entry, AclInterface $acl)
|
||||
{
|
||||
$this->acl = $acl;
|
||||
$this->securityIdentity = SecurityIdentity::toAclIdentity($entry->getSecurityIdentity());
|
||||
|
||||
/*
|
||||
* A new ACE (from a MutableAcl) does not have an ID,
|
||||
* but will be persisted by the MutableAclProvider afterwards, if issued.
|
||||
*/
|
||||
if ($entry->getId()) {
|
||||
$this->id = $entry->getId();
|
||||
}
|
||||
|
||||
$this->mask = $entry->getMask();
|
||||
$this->isGranting = $entry->getGranting();
|
||||
$this->strategy = $entry->getGrantingStrategy();
|
||||
$this->auditFailure = $entry->getAuditFailure();
|
||||
$this->auditSuccess = $entry->getAuditSuccess();
|
||||
}
|
||||
|
||||
/**
|
||||
* String representation of object
|
||||
*
|
||||
* @link http://php.net/manual/en/serializable.serialize.php
|
||||
*
|
||||
* @return string the string representation of the object or &null;
|
||||
*/
|
||||
public function serialize()
|
||||
{
|
||||
return serialize(array(
|
||||
$this->acl,
|
||||
$this->securityIdentity,
|
||||
$this->id,
|
||||
$this->mask,
|
||||
$this->isGranting,
|
||||
$this->strategy,
|
||||
$this->auditFailure,
|
||||
$this->auditSuccess,
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs the object
|
||||
*
|
||||
* @link http://php.net/manual/en/serializable.unserialize.php
|
||||
*
|
||||
* @param string $serialized
|
||||
*
|
||||
* @return mixed the original value unserialized.
|
||||
*/
|
||||
public function unserialize($serialized)
|
||||
{
|
||||
list(
|
||||
$this->acl,
|
||||
$this->securityIdentity,
|
||||
$this->id,
|
||||
$this->mask,
|
||||
$this->isGranting,
|
||||
$this->strategy,
|
||||
$this->auditFailure,
|
||||
$this->auditSuccess,
|
||||
) = unserialize($serialized);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The ACL this ACE is associated with.
|
||||
*
|
||||
* @return \Symfony\Component\Security\Acl\Model\AclInterface
|
||||
*/
|
||||
public function getAcl()
|
||||
{
|
||||
return $this->acl;
|
||||
}
|
||||
|
||||
/**
|
||||
* The security identity associated with this ACE
|
||||
*
|
||||
* @return \Symfony\Component\Security\Acl\Model\SecurityIdentityInterface
|
||||
*/
|
||||
public function getSecurityIdentity()
|
||||
{
|
||||
return $this->securityIdentity;
|
||||
}
|
||||
|
||||
/**
|
||||
* The primary key of this ACE
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The permission mask of this ACE
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getMask()
|
||||
{
|
||||
return $this->mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* The strategy for comparing masks
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getStrategy()
|
||||
{
|
||||
return $this->strategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this ACE is granting, or denying
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isGranting()
|
||||
{
|
||||
return $this->isGranting;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether auditing for successful grants is turned on
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isAuditFailure()
|
||||
{
|
||||
return $this->auditFailure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether auditing for successful denies is turned on
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isAuditSuccess()
|
||||
{
|
||||
return $this->auditSuccess;
|
||||
}
|
||||
}
|
101
Security/Acl/Domain/FieldEntry.php
Normal file
101
Security/Acl/Domain/FieldEntry.php
Normal file
|
@ -0,0 +1,101 @@
|
|||
<?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\Security\Acl\Domain;
|
||||
|
||||
use Propel\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.
|
||||
*
|
||||
* The entry is only used to grab a "snapshot" of its data as an \Symfony\Component\Security\Acl\Model\EntryInterface is immutable!
|
||||
*
|
||||
* @see \Symfony\Component\Security\Acl\Model\EntryInterface
|
||||
*
|
||||
* @author Toni Uebernickel <tuebernickel@gmail.com>
|
||||
*/
|
||||
class FieldEntry extends Entry implements FieldEntryInterface
|
||||
{
|
||||
protected $field;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \Propel\PropelBundle\Model\Acl\Entry $entry
|
||||
* @param \Symfony\Component\Security\Acl\Model\AclInterface $acl
|
||||
*/
|
||||
public function __construct(ModelEntry $entry, AclInterface $acl)
|
||||
{
|
||||
$this->field = $entry->getFieldName();
|
||||
|
||||
parent::__construct($entry, $acl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the field used for this entry.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getField()
|
||||
{
|
||||
return $this->field;
|
||||
}
|
||||
|
||||
/**
|
||||
* String representation of object
|
||||
*
|
||||
* @link http://php.net/manual/en/serializable.serialize.php
|
||||
*
|
||||
* @return string the string representation of the object or &null;
|
||||
*/
|
||||
public function serialize()
|
||||
{
|
||||
return serialize(array(
|
||||
$this->acl,
|
||||
$this->securityIdentity,
|
||||
$this->id,
|
||||
$this->mask,
|
||||
$this->isGranting,
|
||||
$this->strategy,
|
||||
$this->auditFailure,
|
||||
$this->auditSuccess,
|
||||
$this->field,
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs the object
|
||||
*
|
||||
* @link http://php.net/manual/en/serializable.unserialize.php
|
||||
*
|
||||
* @param string $serialized
|
||||
*
|
||||
* @return mixed the original value unserialized.
|
||||
*/
|
||||
public function unserialize($serialized)
|
||||
{
|
||||
list(
|
||||
$this->acl,
|
||||
$this->securityIdentity,
|
||||
$this->id,
|
||||
$this->mask,
|
||||
$this->isGranting,
|
||||
$this->strategy,
|
||||
$this->auditFailure,
|
||||
$this->auditSuccess,
|
||||
$this->field,
|
||||
) = unserialize($serialized);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
531
Security/Acl/Domain/MutableAcl.php
Normal file
531
Security/Acl/Domain/MutableAcl.php
Normal file
|
@ -0,0 +1,531 @@
|
|||
<?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\Security\Acl\Domain;
|
||||
|
||||
use Propel\Runtime\Collection\ObjectCollection;
|
||||
use Propel\Runtime\Connection\ConnectionInterface;
|
||||
|
||||
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 Symfony\Component\Security\Acl\Domain\PermissionGrantingStrategy;
|
||||
|
||||
use Symfony\Component\Security\Acl\Model\AclInterface;
|
||||
use Symfony\Component\Security\Acl\Model\MutableAclInterface;
|
||||
use Symfony\Component\Security\Acl\Model\ObjectIdentityInterface;
|
||||
use Symfony\Component\Security\Acl\Model\PermissionGrantingStrategyInterface;
|
||||
use Symfony\Component\Security\Acl\Model\SecurityIdentityInterface;
|
||||
|
||||
/**
|
||||
* @author Toni Uebernickel <tuebernickel@gmail.com>
|
||||
*/
|
||||
class MutableAcl extends Acl implements MutableAclInterface
|
||||
{
|
||||
/**
|
||||
* The id of the current ACL.
|
||||
*
|
||||
* It's the id of the ObjectIdentity model.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* A reference to the ObjectIdentity this ACL is mapped to.
|
||||
*
|
||||
* @var \Propel\PropelBundle\Model\Acl\ObjectIdentity
|
||||
*/
|
||||
protected $modelObjectIdentity;
|
||||
|
||||
/**
|
||||
* A connection to be used for all changes on the ACL.
|
||||
*
|
||||
* @var ConnectionInterface
|
||||
*/
|
||||
protected $con;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @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 ConnectionInterface $con
|
||||
*/
|
||||
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);
|
||||
|
||||
$this->modelObjectIdentity = ObjectIdentityQuery::create()
|
||||
->filterByAclObjectIdentity($objectIdentity, $con)
|
||||
->findOneOrCreate($con)
|
||||
;
|
||||
|
||||
if ($this->modelObjectIdentity->isNew()) {
|
||||
$this->modelObjectIdentity->save($con);
|
||||
}
|
||||
|
||||
$this->id = $this->modelObjectIdentity->getId();
|
||||
|
||||
$this->con = $con;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the primary key of this ACL
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether entries are inherited
|
||||
*
|
||||
* @param bool $boolean
|
||||
*/
|
||||
public function setEntriesInheriting($boolean)
|
||||
{
|
||||
$this->inherited = $boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the parent ACL
|
||||
*
|
||||
* @param \Symfony\Component\Security\Acl\Model\AclInterface|null $acl
|
||||
*/
|
||||
public function setParentAcl(AclInterface $acl = null)
|
||||
{
|
||||
$this->parentAcl = $acl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a class-based ACE
|
||||
*
|
||||
* @param integer $index
|
||||
*/
|
||||
public function deleteClassAce($index)
|
||||
{
|
||||
$this->deleteIndex($this->classAces, $index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a class-field-based ACE
|
||||
*
|
||||
* @param integer $index
|
||||
* @param string $field
|
||||
*/
|
||||
public function deleteClassFieldAce($index, $field)
|
||||
{
|
||||
$this
|
||||
->validateField($this->classFieldAces, $field)
|
||||
->deleteIndex($this->classFieldAces[$field], $index)
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an object-based ACE
|
||||
*
|
||||
* @param integer $index
|
||||
*/
|
||||
public function deleteObjectAce($index)
|
||||
{
|
||||
$this->deleteIndex($this->objectAces, $index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an object-field-based ACE
|
||||
*
|
||||
* @param integer $index
|
||||
* @param string $field
|
||||
*/
|
||||
public function deleteObjectFieldAce($index, $field)
|
||||
{
|
||||
$this
|
||||
->validateField($this->objectFieldAces, $field)
|
||||
->deleteIndex($this->objectFieldAces[$field], $index)
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a class-based ACE
|
||||
*
|
||||
* @param \Symfony\Component\Security\Acl\Model\SecurityIdentityInterface $securityIdentity
|
||||
* @param integer $mask
|
||||
* @param integer $index
|
||||
* @param bool $granting
|
||||
* @param string $strategy
|
||||
*/
|
||||
public function insertClassAce(SecurityIdentityInterface $securityIdentity, $mask, $index = 0, $granting = true, $strategy = null)
|
||||
{
|
||||
$this->insertToList($this->classAces, $index, $this->createAce($mask, $index, $securityIdentity, $strategy, $granting));
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a class-field-based ACE
|
||||
*
|
||||
* @param string $field
|
||||
* @param \Symfony\Component\Security\Acl\Model\SecurityIdentityInterface $securityIdentity
|
||||
* @param integer $mask
|
||||
* @param integer $index
|
||||
* @param boolean $granting
|
||||
* @param string $strategy
|
||||
*/
|
||||
public function insertClassFieldAce($field, SecurityIdentityInterface $securityIdentity, $mask, $index = 0, $granting = true, $strategy = null)
|
||||
{
|
||||
if (!isset($this->classFieldAces[$field])) {
|
||||
$this->classFieldAces[$field] = array();
|
||||
}
|
||||
|
||||
$this->insertToList($this->classFieldAces[$field], $index, $this->createAce($mask, $index, $securityIdentity, $strategy, $granting, $field));
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts an object-based ACE
|
||||
*
|
||||
* @param \Symfony\Component\Security\Acl\Model\SecurityIdentityInterface $securityIdentity
|
||||
* @param integer $mask
|
||||
* @param integer $index
|
||||
* @param boolean $granting
|
||||
* @param string $strategy
|
||||
*/
|
||||
public function insertObjectAce(SecurityIdentityInterface $securityIdentity, $mask, $index = 0, $granting = true, $strategy = null)
|
||||
{
|
||||
$this->insertToList($this->objectAces, $index, $this->createAce($mask, $index, $securityIdentity, $strategy, $granting));
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts an object-field-based ACE
|
||||
*
|
||||
* @param string $field
|
||||
* @param \Symfony\Component\Security\Acl\Model\SecurityIdentityInterface $securityIdentity
|
||||
* @param integer $mask
|
||||
* @param integer $index
|
||||
* @param boolean $granting
|
||||
* @param string $strategy
|
||||
*/
|
||||
public function insertObjectFieldAce($field, SecurityIdentityInterface $securityIdentity, $mask, $index = 0, $granting = true, $strategy = null)
|
||||
{
|
||||
if (!isset($this->objectFieldAces[$field])) {
|
||||
$this->objectFieldAces[$field] = array();
|
||||
}
|
||||
|
||||
$this->insertToList($this->objectFieldAces[$field], $index, $this->createAce($mask, $index, $securityIdentity, $strategy, $granting, $field));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a class-based ACE
|
||||
*
|
||||
* @param integer $index
|
||||
* @param integer $mask
|
||||
* @param string $strategy if null the strategy should not be changed
|
||||
*/
|
||||
public function updateClassAce($index, $mask, $strategy = null)
|
||||
{
|
||||
$this->updateAce($this->classAces, $index, $mask, $strategy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a class-field-based ACE
|
||||
*
|
||||
* @param integer $index
|
||||
* @param string $field
|
||||
* @param integer $mask
|
||||
* @param string $strategy if null the strategy should not be changed
|
||||
*/
|
||||
public function updateClassFieldAce($index, $field, $mask, $strategy = null)
|
||||
{
|
||||
$this
|
||||
->validateField($this->classFieldAces, $field)
|
||||
->updateAce($this->classFieldAces[$field], $index, $mask, $strategy)
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates an object-based ACE
|
||||
*
|
||||
* @param integer $index
|
||||
* @param integer $mask
|
||||
* @param string $strategy if null the strategy should not be changed
|
||||
*/
|
||||
public function updateObjectAce($index, $mask, $strategy = null)
|
||||
{
|
||||
$this->updateAce($this->objectAces, $index, $mask, $strategy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates an object-field-based ACE
|
||||
*
|
||||
* @param integer $index
|
||||
* @param string $field
|
||||
* @param integer $mask
|
||||
* @param string $strategy if null the strategy should not be changed
|
||||
*/
|
||||
public function updateObjectFieldAce($index, $field, $mask, $strategy = null)
|
||||
{
|
||||
$this->validateField($this->objectFieldAces, $field);
|
||||
$this->updateAce($this->objectFieldAces[$field], $index, $mask, $strategy);
|
||||
}
|
||||
|
||||
/**
|
||||
* String representation of object
|
||||
*
|
||||
* @link http://php.net/manual/en/serializable.serialize.php
|
||||
*
|
||||
* @return string the string representation of the object or &null;
|
||||
*/
|
||||
public function serialize()
|
||||
{
|
||||
return serialize(array(
|
||||
$this->id,
|
||||
$this->modelObjectIdentity,
|
||||
$this->model,
|
||||
$this->classAces,
|
||||
$this->classFieldAces,
|
||||
$this->objectAces,
|
||||
$this->objectFieldAces,
|
||||
$this->objectIdentity,
|
||||
$this->parentAcl,
|
||||
$this->permissionGrantingStrategy,
|
||||
$this->inherited,
|
||||
$this->loadedSecurityIdentities,
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs the object
|
||||
*
|
||||
* @link http://php.net/manual/en/serializable.unserialize.php
|
||||
*
|
||||
* @param string $serialized
|
||||
*
|
||||
* @return mixed the original value unserialized.
|
||||
*/
|
||||
public function unserialize($serialized)
|
||||
{
|
||||
list(
|
||||
$this->id,
|
||||
$this->modelObjectIdentity,
|
||||
$this->model,
|
||||
$this->classAces,
|
||||
$this->classFieldAces,
|
||||
$this->objectAces,
|
||||
$this->objectFieldAces,
|
||||
$this->objectIdentity,
|
||||
$this->parentAcl,
|
||||
$this->permissionGrantingStrategy,
|
||||
$this->inherited,
|
||||
$this->loadedSecurityIdentities,
|
||||
) = unserialize($serialized);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @return \Propel\PropelBundle\Security\Acl\Domain\MutableAcl $this
|
||||
*/
|
||||
protected function insertToList(array &$list, $index, Entry $entry)
|
||||
{
|
||||
$this->isWithinBounds($list, $index);
|
||||
|
||||
if ($entry instanceof FieldEntry) {
|
||||
$this->updateFields($entry->getField());
|
||||
}
|
||||
|
||||
$list = array_merge(
|
||||
array_slice($list, 0, $index),
|
||||
array($entry),
|
||||
array_splice($list, $index)
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a single ACE of this ACL.
|
||||
*
|
||||
* @param array $list
|
||||
* @param int $index
|
||||
* @param int $mask
|
||||
* @param string $strategy
|
||||
* @param string $field
|
||||
*
|
||||
* @return \Propel\PropelBundle\Security\Acl\Domain\MutableAcl $this
|
||||
*/
|
||||
protected function updateAce(array &$list, $index, $mask, $strategy = null)
|
||||
{
|
||||
$this->validateIndex($list, $index);
|
||||
|
||||
$entry = ModelEntry::fromAclEntry($list[$index]);
|
||||
|
||||
// Apply updates
|
||||
$entry->setMask($mask);
|
||||
if (null !== $strategy) {
|
||||
$entry->setGrantingStrategy($strategy);
|
||||
}
|
||||
|
||||
$list[$index] = ModelEntry::toAclEntry($entry, $this);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the ACE of the given list and index.
|
||||
*
|
||||
* The list will be re-ordered to have a valid 0..x list.
|
||||
*
|
||||
* @param array $list
|
||||
* @param $index
|
||||
*
|
||||
* @return \Propel\PropelBundle\Security\Acl\Domain\MutableAcl $this
|
||||
*/
|
||||
protected function deleteIndex(array &$list, $index)
|
||||
{
|
||||
$this->validateIndex($list, $index);
|
||||
unset($list[$index]);
|
||||
$this->reorderList($list, $index-1);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the index on the given list of ACEs.
|
||||
*
|
||||
* @throws \OutOfBoundsException
|
||||
*
|
||||
* @param array $list
|
||||
* @param int $index
|
||||
*
|
||||
* @return \Propel\PropelBundle\Security\Acl\Domain\MutableAcl $this
|
||||
*/
|
||||
protected function isWithinBounds(array &$list, $index)
|
||||
{
|
||||
// No count()-1, the count is one ahead of index, and could create the next valid entry!
|
||||
if ($index < 0 or $index > count($list)) {
|
||||
throw new \OutOfBoundsException(sprintf('The index must be in the interval [0, %d].', count($list)));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the index for existence in the given list.
|
||||
*
|
||||
* @throws \OutOfBoundsException
|
||||
*
|
||||
* @param array $list
|
||||
* @param $index
|
||||
*
|
||||
* @return \Propel\PropelBundle\Security\Acl\Domain\MutableAcl $this
|
||||
*/
|
||||
protected function validateIndex(array &$list, $index)
|
||||
{
|
||||
if (!isset($list[$index])) {
|
||||
throw new \OutOfBoundsException(sprintf('The index "%d" does not exist.', $index));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the given field to be present.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @param array $list
|
||||
* @param string $field
|
||||
*
|
||||
* @return \Propel\PropelBundle\Security\Acl\Domain\MutableAcl $this
|
||||
*/
|
||||
protected function validateField(array &$list, $field)
|
||||
{
|
||||
if (!isset($list[$field])) {
|
||||
throw new \InvalidArgumentException(sprintf('The given field "%s" does not exist.', $field));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Order the given list to have numeric indexes from 0..x
|
||||
*
|
||||
* @param array $list
|
||||
* @param int $index The right boundary to which the list is valid.
|
||||
*
|
||||
* @return \Propel\PropelBundle\Security\Acl\Domain\MutableAcl $this
|
||||
*/
|
||||
protected function reorderList(array &$list, $index)
|
||||
{
|
||||
$list = array_merge(
|
||||
array_slice($list, 0, $index+1), // +1 to get length
|
||||
array_splice($list, $index+1) // +1 to get first index to re-order
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ACL Entry.
|
||||
*
|
||||
* @param int $mask
|
||||
* @param int $index
|
||||
* @param \Symfony\Component\Security\Acl\Model\SecurityIdentityInterface $securityIdentity
|
||||
* @param string $strategy
|
||||
* @param bool $granting
|
||||
* @param string $field
|
||||
*
|
||||
* @return \Propel\PropelBundle\Security\Acl\Domain\Entry|\Propel\PropelBundle\Security\Acl\Domain\FieldEntry
|
||||
*/
|
||||
protected function createAce($mask, $index, SecurityIdentityInterface $securityIdentity, $strategy = null, $granting = true, $field = null)
|
||||
{
|
||||
if (!is_int($mask)) {
|
||||
throw new \InvalidArgumentException('The given mask is not valid. Please provide an integer.');
|
||||
}
|
||||
|
||||
// Compatibility with default implementation
|
||||
if (null === $strategy) {
|
||||
if (true === $granting) {
|
||||
$strategy = PermissionGrantingStrategy::ALL;
|
||||
} else {
|
||||
$strategy = PermissionGrantingStrategy::ANY;
|
||||
}
|
||||
}
|
||||
|
||||
$model = new ModelEntry();
|
||||
$model
|
||||
->setAceOrder($index)
|
||||
->setMask($mask)
|
||||
->setGrantingStrategy($strategy)
|
||||
->setGranting($granting)
|
||||
->setSecurityIdentity(SecurityIdentity::fromAclIdentity($securityIdentity))
|
||||
;
|
||||
|
||||
if (null !== $field) {
|
||||
$model->setFieldName($field);
|
||||
|
||||
return new FieldEntry($model, $this);
|
||||
}
|
||||
|
||||
return new Entry($model, $this);
|
||||
}
|
||||
}
|
340
Security/Acl/MutableAclProvider.php
Normal file
340
Security/Acl/MutableAclProvider.php
Normal file
|
@ -0,0 +1,340 @@
|
|||
<?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\Security\Acl;
|
||||
|
||||
use Propel\Runtime\ActiveQuery\Criteria;
|
||||
use Propel\Runtime\Collection\ObjectCollection;
|
||||
use Propel\Runtime\Connection\ConnectionInterface;
|
||||
use Propel\Runtime\Propel;
|
||||
use Propel\Runtime\ServiceContainer\ServiceContainerInterface;
|
||||
|
||||
use Propel\PropelBundle\Model\Acl\Entry as ModelEntry;
|
||||
use Propel\PropelBundle\Model\Acl\Map\EntryTableMap;
|
||||
use Propel\PropelBundle\Model\Acl\EntryQuery;
|
||||
use Propel\PropelBundle\Model\Acl\SecurityIdentity;
|
||||
use Propel\PropelBundle\Model\Acl\ObjectIdentity;
|
||||
use Propel\PropelBundle\Model\Acl\ObjectIdentityQuery;
|
||||
|
||||
use Propel\PropelBundle\Security\Acl\Domain\Acl;
|
||||
use Propel\PropelBundle\Security\Acl\Domain\MutableAcl;
|
||||
use Propel\PropelBundle\Security\Acl\Domain\Entry;
|
||||
|
||||
use Symfony\Component\Security\Acl\Exception\AclAlreadyExistsException;
|
||||
use Symfony\Component\Security\Acl\Exception\Exception as AclException;
|
||||
|
||||
use Symfony\Component\Security\Acl\Model\AclInterface;
|
||||
use Symfony\Component\Security\Acl\Model\EntryInterface;
|
||||
use Symfony\Component\Security\Acl\Model\FieldEntryInterface;
|
||||
use Symfony\Component\Security\Acl\Model\AuditableEntryInterface;
|
||||
use Symfony\Component\Security\Acl\Model\AclCacheInterface;
|
||||
use Symfony\Component\Security\Acl\Model\MutableAclInterface;
|
||||
use Symfony\Component\Security\Acl\Model\MutableAclProviderInterface;
|
||||
use Symfony\Component\Security\Acl\Model\ObjectIdentityInterface;
|
||||
use Symfony\Component\Security\Acl\Model\PermissionGrantingStrategyInterface;
|
||||
|
||||
/**
|
||||
* An implementation of the MutableAclProviderInterface using Propel ORM.
|
||||
*
|
||||
* @author Toni Uebernickel <tuebernickel@gmail.com>
|
||||
*/
|
||||
class MutableAclProvider extends AclProvider implements MutableAclProviderInterface
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \Symfony\Component\Security\Acl\Model\PermissionGrantingStrategyInterface $permissionGrantingStrategy
|
||||
* @param ConnectionInterface $connection
|
||||
* @param \Symfony\Component\Security\Acl\Model\AclCacheInterface $cache
|
||||
*/
|
||||
public function __construct(PermissionGrantingStrategyInterface $permissionGrantingStrategy, ConnectionInterface $connection = null, AclCacheInterface $cache = null)
|
||||
{
|
||||
// @codeCoverageIgnoreStart
|
||||
if (null === $connection) {
|
||||
$connection = Propel::getConnection(EntryTableMap::DATABASE_NAME, ServiceContainerInterface::CONNECTION_WRITE);
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
parent::__construct($permissionGrantingStrategy, $connection, $cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ACL for the given object identity.
|
||||
*
|
||||
* @throws \Symfony\Component\Security\Acl\Exception\AclAlreadyExistsException When there already is an ACL for the given object identity.
|
||||
*
|
||||
* @param \Symfony\Component\Security\Acl\Model\ObjectIdentityInterface $objectIdentity
|
||||
*
|
||||
* @return \Propel\PropelBundle\Security\Acl\Domain\MutableAcl
|
||||
*/
|
||||
public function createAcl(ObjectIdentityInterface $objectIdentity)
|
||||
{
|
||||
$entries = EntryQuery::create()->findByAclIdentity($objectIdentity, array(), $this->connection);
|
||||
if (count($entries)) {
|
||||
throw new AclAlreadyExistsException('An ACL for the given object identity already exists, find and update that one.');
|
||||
}
|
||||
|
||||
$objIdentity = ObjectIdentityQuery::create()
|
||||
->filterByAclObjectIdentity($objectIdentity, $this->connection)
|
||||
->findOneOrCreate($this->connection)
|
||||
;
|
||||
|
||||
if ($objIdentity->isNew()) {
|
||||
// This is safe to do, it makes the ID available and does not affect changes to any ACL.
|
||||
$objIdentity->save($this->connection);
|
||||
}
|
||||
|
||||
return $this->getAcl($entries, $objectIdentity, array(), null, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the ACL for a given object identity.
|
||||
*
|
||||
* This will automatically trigger a delete for any child ACLs. If you don't
|
||||
* want child ACLs to be deleted, you will have to set their parent ACL to null.
|
||||
*
|
||||
* @throws \Symfony\Component\Security\Acl\Exception\Exception
|
||||
*
|
||||
* @param \Symfony\Component\Security\Acl\Model\ObjectIdentityInterface $objectIdentity
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteAcl(ObjectIdentityInterface $objectIdentity)
|
||||
{
|
||||
try {
|
||||
$objIdentity = ObjectIdentityQuery::create()->findOneByAclObjectIdentity($objectIdentity, $this->connection);
|
||||
if (null === $objIdentity) {
|
||||
// No object identity, no ACL, so deletion is successful (expected result is given).
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->connection->beginTransaction();
|
||||
|
||||
// Retrieve all class and class-field ACEs, if any.
|
||||
$aces = EntryQuery::create()->findByAclIdentity($objectIdentity, array(), $this->connection);
|
||||
if (count($aces)) {
|
||||
// In case this is the last of its kind, delete the class and class-field ACEs.
|
||||
$count = ObjectIdentityQuery::create()->filterByClassId($objIdentity->getClassId())->count($this->connection);
|
||||
if (1 === $count) {
|
||||
$aces->delete($this->connection);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If caching is enabled, retrieve the (grand-)children of this ACL.
|
||||
* Those will be removed from the cache as well, as their parents do not exist anymore.
|
||||
*/
|
||||
if (null !== $this->cache) {
|
||||
$children = ObjectIdentityQuery::create()->findGrandChildren($objIdentity, $this->connection);
|
||||
}
|
||||
|
||||
// This deletes all object and object-field ACEs, too.
|
||||
$objIdentity->delete($this->connection);
|
||||
|
||||
$this->connection->commit();
|
||||
|
||||
if (null !== $this->cache) {
|
||||
$this->cache->evictFromCacheById($objIdentity->getId());
|
||||
foreach ($children as $eachChild) {
|
||||
$this->cache->evictFromCacheById($eachChild->getId());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
// @codeCoverageIgnoreStart
|
||||
} catch (Exception $e) {
|
||||
throw new AclException('An error occurred while deleting the ACL.', 1, $e);
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
/**
|
||||
* Persists any changes which were made to the ACL, or any associated access control entries.
|
||||
*
|
||||
* Changes to parent ACLs are not persisted.
|
||||
*
|
||||
* @throws \Symfony\Component\Security\Acl\Exception\Exception
|
||||
*
|
||||
* @param \Symfony\Component\Security\Acl\Model\MutableAclInterface $acl
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function updateAcl(MutableAclInterface $acl)
|
||||
{
|
||||
if (!$acl instanceof MutableAcl) {
|
||||
throw new \InvalidArgumentException('The given ACL is not tracked by this provider. Please provide \Propel\PropelBundle\Security\Acl\Domain\MutableAcl only.');
|
||||
}
|
||||
|
||||
try {
|
||||
$modelEntries = EntryQuery::create()->findByAclIdentity($acl->getObjectIdentity(), array(), $this->connection);
|
||||
$objectIdentity = ObjectIdentityQuery::create()->findOneByAclObjectIdentity($acl->getObjectIdentity(), $this->connection);
|
||||
|
||||
$this->connection->beginTransaction();
|
||||
|
||||
$keepEntries = array_merge(
|
||||
$this->persistAcl($acl->getClassAces(), $objectIdentity),
|
||||
$this->persistAcl($acl->getObjectAces(), $objectIdentity, true)
|
||||
);
|
||||
|
||||
foreach ($acl->getFields() as $eachField) {
|
||||
$keepEntries = array_merge($keepEntries,
|
||||
$this->persistAcl($acl->getClassFieldAces($eachField), $objectIdentity),
|
||||
$this->persistAcl($acl->getObjectFieldAces($eachField), $objectIdentity, true)
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($modelEntries as $eachEntry) {
|
||||
if (!in_array($eachEntry->getId(), $keepEntries)) {
|
||||
$eachEntry->delete($this->connection);
|
||||
}
|
||||
}
|
||||
|
||||
if (null === $acl->getParentAcl()) {
|
||||
$objectIdentity
|
||||
->setParentObjectIdentityId(null)
|
||||
->save($this->connection)
|
||||
;
|
||||
} else {
|
||||
$objectIdentity
|
||||
->setParentObjectIdentityId($acl->getParentAcl()->getId())
|
||||
->save($this->connection)
|
||||
;
|
||||
}
|
||||
|
||||
$this->connection->commit();
|
||||
|
||||
// After successfully committing the transaction, we are good to update the cache.
|
||||
if (null !== $this->cache) {
|
||||
$this->cache->evictFromCacheById($objectIdentity->getId());
|
||||
$this->cache->putInCache($acl);
|
||||
}
|
||||
|
||||
return true;
|
||||
// @codeCoverageIgnoreStart
|
||||
} catch (Exception $e) {
|
||||
$this->connection->rollBack();
|
||||
|
||||
throw new AclException('An error occurred while updating the ACL.', 0, $e);
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist the given ACEs.
|
||||
*
|
||||
* @param array $accessControlEntries
|
||||
* @param \Propel\PropelBundle\Model\Acl\ObjectIdentity $objectIdentity
|
||||
* @param bool $object
|
||||
*
|
||||
* @return array The IDs of the persisted ACEs.
|
||||
*/
|
||||
protected function persistAcl(array $accessControlEntries, ObjectIdentity $objectIdentity, $object = false)
|
||||
{
|
||||
$entries = array();
|
||||
|
||||
/* @var $eachAce \Symfony\Component\Security\Acl\Model\EntryInterface */
|
||||
foreach ($accessControlEntries as $order => $eachAce) {
|
||||
// If the given ACE has never been persisted, create a new one.
|
||||
if (null === $entry = $this->getPersistedAce($eachAce, $objectIdentity, $object)) {
|
||||
$entry = ModelEntry::fromAclEntry($eachAce);
|
||||
}
|
||||
|
||||
if (in_array($entry->getId(), $entries)) {
|
||||
$entry = ModelEntry::fromAclEntry($eachAce);
|
||||
}
|
||||
|
||||
// Apply possible changes from local ACE.
|
||||
$entry
|
||||
->setAceOrder($order)
|
||||
->setAclClass($objectIdentity->getAclClass())
|
||||
->setMask($eachAce->getMask())
|
||||
;
|
||||
|
||||
if ($eachAce instanceof AuditableEntryInterface) {
|
||||
if (is_bool($eachAce->isAuditSuccess())) {
|
||||
$entry->setAuditSuccess($eachAce->isAuditSuccess());
|
||||
}
|
||||
|
||||
if (is_bool($eachAce->isAuditFailure())) {
|
||||
$entry->setAuditFailure($eachAce->isAuditFailure());
|
||||
}
|
||||
}
|
||||
|
||||
if (true === $object) {
|
||||
$entry->setObjectIdentity($objectIdentity);
|
||||
}
|
||||
|
||||
$entry->save($this->connection);
|
||||
|
||||
$entries[] = $entry->getId();
|
||||
}
|
||||
|
||||
return $entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the persisted model for the given ACE.
|
||||
*
|
||||
* If none is given, null is returned.
|
||||
*
|
||||
* @param \Symfony\Component\Security\Acl\Model\EntryInterface $ace
|
||||
*
|
||||
* @return \Propel\PropelBundle\Model\Acl\Entry|null
|
||||
*/
|
||||
protected function getPersistedAce(EntryInterface $ace, ObjectIdentity $objectIdentity, $object = false)
|
||||
{
|
||||
if (null !== $ace->getId() and null !== $entry = EntryQuery::create()->findPk($ace->getId(), $this->connection)) {
|
||||
$entry->reload(true, $this->connection);
|
||||
|
||||
return $entry;
|
||||
}
|
||||
|
||||
/*
|
||||
* The id is not set, but there may be an ACE in the database.
|
||||
*
|
||||
* This happens if the ACL has created new ACEs, but was not reloaded.
|
||||
* We try to retrieve one by the unique key.
|
||||
*/
|
||||
$ukQuery = EntryQuery::create()
|
||||
->filterByAclClass($objectIdentity->getAclClass($this->connection))
|
||||
->filterBySecurityIdentity(SecurityIdentity::fromAclIdentity($ace->getSecurityIdentity(), $this->connection))
|
||||
;
|
||||
|
||||
if (true === $object) {
|
||||
$ukQuery->filterByObjectIdentity($objectIdentity);
|
||||
} else {
|
||||
$ukQuery->filterByObjectIdentityId(null, Criteria::ISNULL);
|
||||
}
|
||||
|
||||
if ($ace instanceof FieldEntryInterface) {
|
||||
$ukQuery->filterByFieldName($ace->getField());
|
||||
} else {
|
||||
$ukQuery->filterByFieldName(null, Criteria::ISNULL);
|
||||
}
|
||||
|
||||
return $ukQuery->findOne($this->connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an ACL for this provider.
|
||||
*
|
||||
* @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\MutableAcl
|
||||
*/
|
||||
protected function getAcl(ObjectCollection $collection, ObjectIdentityInterface $objectIdentity, array $loadedSecurityIdentities = array(), AclInterface $parentAcl = null, $inherited = true)
|
||||
{
|
||||
return new MutableAcl($collection, $objectIdentity, $this->permissionGrantingStrategy, $loadedSecurityIdentities, $parentAcl, $inherited, $this->connection);
|
||||
}
|
||||
}
|
103
Security/User/PropelUserProvider.php
Normal file
103
Security/User/PropelUserProvider.php
Normal file
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Propel\PropelBundle\Security\User;
|
||||
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
use Symfony\Component\Security\Core\User\UserProviderInterface;
|
||||
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
|
||||
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
|
||||
|
||||
/**
|
||||
* Provides easy to use provisioning for Propel model users.
|
||||
*
|
||||
* @author William DURAND <william.durand1@gmail.com>
|
||||
*/
|
||||
class PropelUserProvider implements UserProviderInterface
|
||||
{
|
||||
/**
|
||||
* A Model class name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $class;
|
||||
|
||||
/**
|
||||
* A Query class name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $queryClass;
|
||||
|
||||
/**
|
||||
* A property to use to retrieve the user.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $property;
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*
|
||||
* @param string $class The User model class.
|
||||
* @param string|null $property The property to use to retrieve a user.
|
||||
*/
|
||||
public function __construct($class, $property = null)
|
||||
{
|
||||
$this->class = $class;
|
||||
$this->queryClass = $class.'Query';
|
||||
$this->property = $property;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function loadUserByUsername($username)
|
||||
{
|
||||
$queryClass = $this->queryClass;
|
||||
$query = $queryClass::create();
|
||||
|
||||
if (null !== $this->property) {
|
||||
$filter = 'filterBy'.ucfirst($this->property);
|
||||
$query->$filter($username);
|
||||
} else {
|
||||
$query->filterByUsername($username);
|
||||
}
|
||||
|
||||
if (null === $user = $query->findOne()) {
|
||||
throw new UsernameNotFoundException(sprintf('User "%s" not found.', $username));
|
||||
}
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function refreshUser(UserInterface $user)
|
||||
{
|
||||
if (!$user instanceof $this->class) {
|
||||
throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user)));
|
||||
}
|
||||
|
||||
$queryClass = $this->queryClass;
|
||||
|
||||
return $queryClass::create()->findPk($user->getPrimaryKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supportsClass($class)
|
||||
{
|
||||
return $class === $this->class;
|
||||
}
|
||||
}
|
75
Service/SchemaLocator.php
Normal file
75
Service/SchemaLocator.php
Normal file
|
@ -0,0 +1,75 @@
|
|||
<?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\Service;
|
||||
|
||||
use Symfony\Component\Config\FileLocatorInterface;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
|
||||
|
||||
class SchemaLocator
|
||||
{
|
||||
protected $fileLocator;
|
||||
|
||||
public function __construct(FileLocatorInterface $fileLocator)
|
||||
{
|
||||
$this->fileLocator = $fileLocator;
|
||||
}
|
||||
|
||||
public function locateFromBundles(array $bundles)
|
||||
{
|
||||
$schemas = array();
|
||||
foreach ($bundles as $bundle) {
|
||||
$schemas = array_merge($schemas, $this->locateFromBundle($bundle));
|
||||
}
|
||||
|
||||
return $schemas;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Symfony\Component\HttpKernel\Bundle\BundleInterface
|
||||
*/
|
||||
public function locateFromBundle(BundleInterface $bundle)
|
||||
{
|
||||
$finalSchemas = array();
|
||||
|
||||
if (is_dir($dir = $bundle->getPath().'/Resources/config')) {
|
||||
$finder = new Finder();
|
||||
$schemas = $finder->files()->name('*schema.xml')->followLinks()->in($dir);
|
||||
|
||||
if (iterator_count($schemas)) {
|
||||
foreach ($schemas as $schema) {
|
||||
$logicalName = $this->transformToLogicalName($schema, $bundle);
|
||||
$finalSchema = new \SplFileInfo($this->fileLocator->locate($logicalName));
|
||||
|
||||
$finalSchemas[(string) $finalSchema] = array($bundle, $finalSchema);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $finalSchemas;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \SplFileInfo $schema
|
||||
* @param BundleInterface $bundle
|
||||
* @return string
|
||||
*/
|
||||
protected function transformToLogicalName(\SplFileInfo $schema, BundleInterface $bundle)
|
||||
{
|
||||
$schemaPath = str_replace(
|
||||
$bundle->getPath(). DIRECTORY_SEPARATOR . 'Resources' . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR,
|
||||
'',
|
||||
$schema->getRealPath()
|
||||
);
|
||||
|
||||
return sprintf('@%s/Resources/config/%s', $bundle->getName(), $schemaPath);
|
||||
}
|
||||
}
|
104
Tests/AclTestCase.php
Normal file
104
Tests/AclTestCase.php
Normal file
|
@ -0,0 +1,104 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the PropelBundle package.
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @license MIT License
|
||||
*/
|
||||
|
||||
namespace Propel\PropelBundle\Tests;
|
||||
|
||||
use Propel\Generator\Util\QuickBuilder;
|
||||
|
||||
use Propel\PropelBundle\Model\Acl\AclClass;
|
||||
use Propel\PropelBundle\Model\Acl\Entry;
|
||||
use Propel\PropelBundle\Model\Acl\ObjectIdentity as ModelObjectIdentity;
|
||||
use Propel\PropelBundle\Security\Acl\MutableAclProvider;
|
||||
|
||||
use Symfony\Component\Security\Core\Role\Role;
|
||||
use Symfony\Component\Security\Acl\Domain\ObjectIdentity;
|
||||
use Symfony\Component\Security\Acl\Domain\RoleSecurityIdentity;
|
||||
use Symfony\Component\Security\Acl\Domain\PermissionGrantingStrategy;
|
||||
|
||||
/**
|
||||
* AclTestCase
|
||||
*
|
||||
* @author Toni Uebernickel <tuebernickel@gmail.com>
|
||||
*/
|
||||
class AclTestCase extends TestCase
|
||||
{
|
||||
protected $con = null;
|
||||
protected $cache = null;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$schema = file_get_contents(__DIR__.'/../Resources/acl_schema.xml');
|
||||
|
||||
if (!class_exists('Propel\PropelBundle\Model\Acl\Map\AclClassTableMap')) {
|
||||
$classTargets = array('tablemap', 'object', 'query');
|
||||
} else {
|
||||
$classTargets = array();
|
||||
}
|
||||
|
||||
$builder = new QuickBuilder();
|
||||
$builder->setSchema($schema);
|
||||
|
||||
$this->con = $builder->build($dsn = null, $user = null, $pass = null, $adapter = null, $classTargets);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Propel\PropelBundle\Model\Acl\ObjectIdentity
|
||||
*/
|
||||
protected function createModelObjectIdentity($identifier)
|
||||
{
|
||||
$aclClass = $this->getAclClass();
|
||||
$objIdentity = new ModelObjectIdentity();
|
||||
|
||||
$this->assertTrue((bool) $objIdentity
|
||||
->setAclClass($aclClass)
|
||||
->setIdentifier($identifier)
|
||||
->save($this->con)
|
||||
);
|
||||
|
||||
return $objIdentity;
|
||||
}
|
||||
|
||||
protected function createEntry()
|
||||
{
|
||||
$entry = new Entry();
|
||||
$entry
|
||||
->setAuditSuccess(false)
|
||||
->setAuditFailure(false)
|
||||
->setMask(64)
|
||||
->setGranting(true)
|
||||
->setGrantingStrategy('all')
|
||||
->setAceOrder(0)
|
||||
;
|
||||
|
||||
return $entry;
|
||||
}
|
||||
|
||||
protected function getAclClass()
|
||||
{
|
||||
return AclClass::fromAclObjectIdentity($this->getAclObjectIdentity(), $this->con);
|
||||
}
|
||||
|
||||
protected function getAclProvider()
|
||||
{
|
||||
return new MutableAclProvider(new PermissionGrantingStrategy(), $this->con, $this->cache);
|
||||
}
|
||||
|
||||
protected function getAclObjectIdentity($identifier = 1)
|
||||
{
|
||||
return new ObjectIdentity($identifier, 'Propel\PropelBundle\Tests\Fixtures\Model\Book');
|
||||
}
|
||||
|
||||
protected function getRoleSecurityIdentity($role = 'ROLE_USER')
|
||||
{
|
||||
return new RoleSecurityIdentity(new Role($role));
|
||||
}
|
||||
}
|
61
Tests/DataFixtures/Dumper/YamlDataDumperTest.php
Normal file
61
Tests/DataFixtures/Dumper/YamlDataDumperTest.php
Normal file
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the PropelBundle package.
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @license MIT License
|
||||
*/
|
||||
|
||||
namespace Propel\PropelBundle\Tests\DataFixtures\Dumper;
|
||||
|
||||
use Propel\Runtime\Propel;
|
||||
use Propel\PropelBundle\Tests\DataFixtures\TestCase;
|
||||
use Propel\PropelBundle\DataFixtures\Dumper\YamlDataDumper;
|
||||
|
||||
/**
|
||||
* @author William Durand <william.durand1@gmail.com>
|
||||
* @author Toni Uebernickel <tuebernickel@gmail.com>
|
||||
*/
|
||||
class YamlDataDumperTest extends TestCase
|
||||
{
|
||||
public function testYamlDump()
|
||||
{
|
||||
$author = new \Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\CoolBookAuthor();
|
||||
$author->setName('A famous one')->save($this->con);
|
||||
|
||||
$complementary = new \stdClass();
|
||||
$complementary->first_word_date = '2012-01-01';
|
||||
|
||||
$book = new \Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\CoolBook();
|
||||
$book
|
||||
->setName('An important one')
|
||||
->setAuthorId(1)
|
||||
->setComplementaryInfos($complementary)
|
||||
->save($this->con)
|
||||
;
|
||||
|
||||
$filename = $this->getTempFile();
|
||||
|
||||
$loader = new YamlDataDumper(__DIR__.'/../../Fixtures/DataFixtures/Loader', array());
|
||||
$loader->dump($filename);
|
||||
|
||||
$expected = <<<YAML
|
||||
\Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\CoolBookAuthor:
|
||||
CoolBookAuthor_1:
|
||||
id: '1'
|
||||
name: 'A famous one'
|
||||
\Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\CoolBook:
|
||||
CoolBook_1:
|
||||
id: '1'
|
||||
name: 'An important one'
|
||||
author_id: CoolBookAuthor_1
|
||||
complementary_infos: !!php/object:O:8:"stdClass":1:{s:15:"first_word_date";s:10:"2012-01-01";}
|
||||
|
||||
YAML;
|
||||
|
||||
$result = file_get_contents($filename);
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
}
|
47
Tests/DataFixtures/Loader/XmlDataLoaderTest.php
Normal file
47
Tests/DataFixtures/Loader/XmlDataLoaderTest.php
Normal file
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the PropelBundle package.
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @license MIT License
|
||||
*/
|
||||
|
||||
namespace Propel\PropelBundle\Tests\DataFixtures\Loader;
|
||||
|
||||
use Propel\Runtime\Propel;
|
||||
use Propel\PropelBundle\Tests\DataFixtures\TestCase;
|
||||
use Propel\PropelBundle\DataFixtures\Loader\XmlDataLoader;
|
||||
|
||||
/**
|
||||
* @author William Durand <william.durand1@gmail.com>
|
||||
* @author Toni Uebernickel <tuebernickel@gmail.com>
|
||||
*/
|
||||
class XmlDataLoaderTest extends TestCase
|
||||
{
|
||||
public function testXmlLoad()
|
||||
{
|
||||
$fixtures = <<<XML
|
||||
<Fixtures>
|
||||
<CoolBookAuthor Namespace="Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader">
|
||||
<CoolBookAuthor_1 id="1" name="A famous one" />
|
||||
</CoolBookAuthor>
|
||||
<CoolBook Namespace="Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader">
|
||||
<CoolBook_1 id="1" name="An important one" author_id="CoolBookAuthor_1" />
|
||||
</CoolBook>
|
||||
</Fixtures>
|
||||
XML;
|
||||
|
||||
$filename = $this->getTempFile($fixtures);
|
||||
|
||||
$loader = new XmlDataLoader(__DIR__.'/../../Fixtures/DataFixtures/Loader', array());
|
||||
$loader->load(array($filename), 'default');
|
||||
|
||||
$books = \Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\CoolBookQuery::create()->find($this->con);
|
||||
$this->assertCount(1, $books);
|
||||
|
||||
$book = $books[0];
|
||||
$this->assertInstanceOf('Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\CoolBookAuthor', $book->getCoolBookAuthor());
|
||||
}
|
||||
}
|
467
Tests/DataFixtures/Loader/YamlDataLoaderTest.php
Normal file
467
Tests/DataFixtures/Loader/YamlDataLoaderTest.php
Normal file
|
@ -0,0 +1,467 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the PropelBundle package.
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @license MIT License
|
||||
*/
|
||||
|
||||
namespace Propel\PropelBundle\Tests\DataFixtures\Loader;
|
||||
|
||||
use Propel\Generator\Util\QuickBuilder;
|
||||
use Propel\Runtime\Propel;
|
||||
use Propel\PropelBundle\Tests\DataFixtures\TestCase;
|
||||
use Propel\PropelBundle\DataFixtures\Loader\YamlDataLoader;
|
||||
|
||||
/**
|
||||
* @author William Durand <william.durand1@gmail.com>
|
||||
* @author Toni Uebernickel <tuebernickel@gmail.com>
|
||||
*/
|
||||
class YamlDataLoaderTest extends TestCase
|
||||
{
|
||||
public function testYamlLoadOneToMany()
|
||||
{
|
||||
$fixtures = <<<YAML
|
||||
\Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\CoolBookAuthor:
|
||||
CoolBookAuthor_1:
|
||||
id: '1'
|
||||
name: 'A famous one'
|
||||
\Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\CoolBook:
|
||||
CoolBook_1:
|
||||
id: '1'
|
||||
name: 'An important one'
|
||||
author_id: CoolBookAuthor_1
|
||||
|
||||
YAML;
|
||||
$filename = $this->getTempFile($fixtures);
|
||||
|
||||
$loader = new YamlDataLoader(__DIR__.'/../../Fixtures/DataFixtures/Loader', array());
|
||||
$loader->load(array($filename), 'default');
|
||||
|
||||
$books = \Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\CoolBookQuery::create()->find($this->con);
|
||||
$this->assertCount(1, $books);
|
||||
|
||||
$book = $books[0];
|
||||
$this->assertInstanceOf('Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\CoolBookAuthor', $book->getCoolBookAuthor());
|
||||
}
|
||||
|
||||
public function testLoadSelfReferencing()
|
||||
{
|
||||
$fixtures = <<<YAML
|
||||
Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\CoolBookAuthor:
|
||||
CoolBookAuthor_1:
|
||||
id: '1'
|
||||
name: 'to be announced'
|
||||
CoolBookAuthor_2:
|
||||
id: CoolBookAuthor_1
|
||||
name: 'A famous one'
|
||||
|
||||
YAML;
|
||||
$filename = $this->getTempFile($fixtures);
|
||||
|
||||
$loader = new YamlDataLoader(__DIR__.'/../../Fixtures/DataFixtures/Loader', array());
|
||||
$loader->load(array($filename), 'default');
|
||||
|
||||
$books = \Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\CoolBookQuery::create()->find($this->con);
|
||||
$this->assertCount(0, $books);
|
||||
|
||||
$authors = \Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\CoolBookAuthorQuery::create()->find($this->con);
|
||||
$this->assertCount(1, $authors);
|
||||
|
||||
$author = $authors[0];
|
||||
$this->assertEquals('A famous one', $author->getName());
|
||||
}
|
||||
|
||||
public function testLoaderWithPhp()
|
||||
{
|
||||
$fixtures = <<<YAML
|
||||
Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\CoolBookAuthor:
|
||||
CoolBookAuthor_1:
|
||||
id: '1'
|
||||
name: <?php echo "to be announced"; ?>
|
||||
|
||||
YAML;
|
||||
$filename = $this->getTempFile($fixtures);
|
||||
|
||||
$loader = new YamlDataLoader(__DIR__.'/../../Fixtures/DataFixtures/Loader', array());
|
||||
$loader->load(array($filename), 'default');
|
||||
|
||||
$books = \Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\CoolBookQuery::create()->find($this->con);
|
||||
$this->assertCount(0, $books);
|
||||
|
||||
$authors = \Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\CoolBookAuthorQuery::create()->find($this->con);
|
||||
$this->assertCount(1, $authors);
|
||||
|
||||
$author = $authors[0];
|
||||
$this->assertEquals('to be announced', $author->getName());
|
||||
}
|
||||
|
||||
public function testLoadWithoutFaker()
|
||||
{
|
||||
$fixtures = <<<YAML
|
||||
Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\CoolBookAuthor:
|
||||
CoolBookAuthor_1:
|
||||
id: '1'
|
||||
name: <?php echo \$faker('word'); ?>
|
||||
|
||||
YAML;
|
||||
$filename = $this->getTempFile($fixtures);
|
||||
|
||||
$loader = new YamlDataLoader(__DIR__.'/../../Fixtures/DataFixtures/Loader', array());
|
||||
$loader->load(array($filename), 'default');
|
||||
|
||||
$books = \Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\CoolBookQuery::create()->find($this->con);
|
||||
$this->assertCount(0, $books);
|
||||
|
||||
$authors = \Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\CoolBookAuthorQuery::create()->find($this->con);
|
||||
$this->assertCount(1, $authors);
|
||||
|
||||
$author = $authors[0];
|
||||
$this->assertEquals('word', $author->getName());
|
||||
}
|
||||
|
||||
public function testLoadWithFaker()
|
||||
{
|
||||
if (!class_exists('Faker\Factory')) {
|
||||
$this->markTestSkipped('Faker is mandatory');
|
||||
}
|
||||
|
||||
$fixtures = <<<YAML
|
||||
Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\CoolBook:
|
||||
CoolBook_1:
|
||||
id: '1'
|
||||
name: <?php \$faker('word'); ?>
|
||||
description: <?php \$faker('sentence'); ?>
|
||||
|
||||
YAML;
|
||||
$filename = $this->getTempFile($fixtures);
|
||||
|
||||
$loader = new YamlDataLoader(__DIR__.'/../../Fixtures/DataFixtures/Loader', array(), \Faker\Factory::create());
|
||||
$loader->load(array($filename), 'default');
|
||||
|
||||
$books = \Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\CoolBookQuery::create()->find($this->con);
|
||||
$this->assertCount(1, $books);
|
||||
|
||||
$book = $books[0];
|
||||
$this->assertNotNull($book->getName());
|
||||
$this->assertNotEquals('null', strtolower($book->getName()));
|
||||
$this->assertRegexp('#[a-z]+#', $book->getName());
|
||||
$this->assertNotNull($book->getDescription());
|
||||
$this->assertNotEquals('null', strtolower($book->getDescription()));
|
||||
$this->assertRegexp('#[\w ]+#', $book->getDescription());
|
||||
}
|
||||
|
||||
public function testYamlLoadManyToMany()
|
||||
{
|
||||
$schema = <<<XML
|
||||
<database name="default" package="vendor.bundles.Propel.PropelBundle.Tests.Fixtures.DataFixtures.Loader" namespace="Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader" defaultIdMethod="native">
|
||||
<table name="table_book" phpName="YamlManyToManyBook">
|
||||
<column name="id" type="integer" primaryKey="true" />
|
||||
<column name="name" type="varchar" size="255" />
|
||||
</table>
|
||||
|
||||
<table name="table_author" phpName="YamlManyToManyAuthor">
|
||||
<column name="id" type="integer" primaryKey="true" />
|
||||
<column name="name" type="varchar" size="255" />
|
||||
</table>
|
||||
|
||||
<table name="table_book_author" phpName="YamlManyToManyBookAuthor" isCrossRef="true">
|
||||
<column name="book_id" type="integer" required="true" primaryKey="true" />
|
||||
<column name="author_id" type="integer" required="true" primaryKey="true" />
|
||||
|
||||
<foreign-key foreignTable="table_book" phpName="Book" onDelete="CASCADE" onUpdate="CASCADE">
|
||||
<reference local="book_id" foreign="id" />
|
||||
</foreign-key>
|
||||
<foreign-key foreignTable="table_author" phpName="Author" onDelete="CASCADE" onUpdate="CASCADE">
|
||||
<reference local="author_id" foreign="id" />
|
||||
</foreign-key>
|
||||
</table>
|
||||
</database>
|
||||
XML;
|
||||
|
||||
$fixtures = <<<YAML
|
||||
\Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\YamlManyToManyBook:
|
||||
Book_1:
|
||||
id: 1
|
||||
name: 'An important one'
|
||||
Book_2:
|
||||
id: 2
|
||||
name: 'Les misérables'
|
||||
|
||||
\Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\YamlManyToManyAuthor:
|
||||
Author_1:
|
||||
id: 1
|
||||
name: 'A famous one'
|
||||
Author_2:
|
||||
id: 2
|
||||
name: 'Victor Hugo'
|
||||
table_book_authors: [ Book_2 ]
|
||||
|
||||
\Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\YamlManyToManyBookAuthor:
|
||||
BookAuthor_1:
|
||||
book_id: Book_1
|
||||
author_id: Author_1
|
||||
YAML;
|
||||
|
||||
$filename = $this->getTempFile($fixtures);
|
||||
|
||||
$builder = new QuickBuilder();
|
||||
$builder->setSchema($schema);
|
||||
|
||||
$con = $builder->build();
|
||||
|
||||
$loader = new YamlDataLoader(__DIR__.'/../../Fixtures/DataFixtures/Loader', array());
|
||||
$loader->load(array($filename), 'default');
|
||||
|
||||
$books = \Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\YamlManyToManyBookQuery::create()->find($con);
|
||||
$this->assertCount(2, $books);
|
||||
$this->assertInstanceOf('Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\YamlManyToManyBook', $books[0]);
|
||||
$this->assertInstanceOf('Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\YamlManyToManyBook', $books[1]);
|
||||
|
||||
$authors = \Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\YamlManyToManyAuthorQuery::create()->find($con);;
|
||||
$this->assertCount(2, $authors);
|
||||
$this->assertInstanceOf('Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\YamlManyToManyAuthor', $authors[0]);
|
||||
$this->assertInstanceOf('Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\YamlManyToManyAuthor', $authors[1]);
|
||||
|
||||
$bookAuthors = \Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\YamlManyToManyBookAuthorQuery::create()->find($con);;
|
||||
$this->assertCount(2, $bookAuthors);
|
||||
$this->assertInstanceOf('Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\YamlManyToManyBookAuthor', $bookAuthors[0]);
|
||||
$this->assertInstanceOf('Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\YamlManyToManyBookAuthor', $bookAuthors[1]);
|
||||
|
||||
$this->assertEquals('Victor Hugo', $authors[1]->getName());
|
||||
$this->assertTrue($authors[1]->getBooks()->contains($books[1]));
|
||||
$this->assertEquals('Les misérables', $authors[1]->getBooks()->get(0)->getName());
|
||||
}
|
||||
|
||||
public function testYamlLoadManyToManyMultipleFiles()
|
||||
{
|
||||
$schema = <<<XML
|
||||
<database name="default" package="vendor.bundles.Propel.PropelBundle.Tests.Fixtures.DataFixtures.Loader" namespace="Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader" defaultIdMethod="native">
|
||||
<table name="table_book_multiple" phpName="YamlManyToManyMultipleFilesBook">
|
||||
<column name="id" type="integer" primaryKey="true" />
|
||||
<column name="name" type="varchar" size="255" />
|
||||
</table>
|
||||
|
||||
<table name="table_author_multiple" phpName="YamlManyToManyMultipleFilesAuthor">
|
||||
<column name="id" type="integer" primaryKey="true" />
|
||||
<column name="name" type="varchar" size="255" />
|
||||
</table>
|
||||
|
||||
<table name="table_book_author_multiple" phpName="YamlManyToManyMultipleFilesBookAuthor" isCrossRef="true">
|
||||
<column name="book_id" type="integer" required="true" primaryKey="true" />
|
||||
<column name="author_id" type="integer" required="true" primaryKey="true" />
|
||||
|
||||
<foreign-key foreignTable="table_book_multiple" phpName="Book" onDelete="CASCADE" onUpdate="CASCADE">
|
||||
<reference local="book_id" foreign="id" />
|
||||
</foreign-key>
|
||||
<foreign-key foreignTable="table_author_multiple" phpName="Author" onDelete="CASCADE" onUpdate="CASCADE">
|
||||
<reference local="author_id" foreign="id" />
|
||||
</foreign-key>
|
||||
</table>
|
||||
</database>
|
||||
XML;
|
||||
|
||||
$fixtures1 = <<<YAML
|
||||
Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\YamlManyToManyMultipleFilesBook:
|
||||
Book_2:
|
||||
id: 2
|
||||
name: 'Les misérables'
|
||||
|
||||
Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\YamlManyToManyMultipleFilesAuthor:
|
||||
Author_1:
|
||||
id: 1
|
||||
name: 'A famous one'
|
||||
|
||||
Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\YamlManyToManyMultipleFilesBookAuthor:
|
||||
BookAuthor_1:
|
||||
book_id: Book_1
|
||||
author_id: Author_1
|
||||
YAML;
|
||||
|
||||
$fixtures2 = <<<YAML
|
||||
Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\YamlManyToManyMultipleFilesBook:
|
||||
Book_1:
|
||||
id: 1
|
||||
name: 'An important one'
|
||||
|
||||
Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\YamlManyToManyMultipleFilesAuthor:
|
||||
Author_2:
|
||||
id: 2
|
||||
name: 'Victor Hugo'
|
||||
table_book_author_multiples: [ Book_2 ]
|
||||
YAML;
|
||||
|
||||
$filename1 = $this->getTempFile($fixtures1);
|
||||
$filename2 = $this->getTempFile($fixtures2);
|
||||
|
||||
$builder = new QuickBuilder();
|
||||
$builder->setSchema($schema);
|
||||
$con = $builder->build();
|
||||
|
||||
$loader = new YamlDataLoader(__DIR__.'/../../Fixtures/DataFixtures/Loader', array());
|
||||
$loader->load(array($filename1, $filename2), 'default');
|
||||
|
||||
$books = \Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\YamlManyToManyMultipleFilesBookQuery::create()->find($con);
|
||||
$this->assertCount(2, $books);
|
||||
$this->assertInstanceOf('Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\YamlManyToManyMultipleFilesBook', $books[0]);
|
||||
$this->assertInstanceOf('Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\YamlManyToManyMultipleFilesBook', $books[1]);
|
||||
|
||||
$authors = \Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\YamlManyToManyMultipleFilesAuthorQuery::create()->find($con);
|
||||
$this->assertCount(2, $authors);
|
||||
$this->assertInstanceOf('Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\YamlManyToManyMultipleFilesAuthor', $authors[0]);
|
||||
$this->assertInstanceOf('Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\YamlManyToManyMultipleFilesAuthor', $authors[1]);
|
||||
|
||||
$bookAuthors = \Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\YamlManyToManyMultipleFilesBookAuthorQuery::create()->find($con);
|
||||
$this->assertCount(2, $bookAuthors);
|
||||
$this->assertInstanceOf('Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\YamlManyToManyMultipleFilesBookAuthor', $bookAuthors[0]);
|
||||
$this->assertInstanceOf('Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\YamlManyToManyMultipleFilesBookAuthor', $bookAuthors[1]);
|
||||
|
||||
$this->assertEquals('Victor Hugo', $authors[1]->getName());
|
||||
$this->assertTrue($authors[1]->getBooks()->contains($books[1]));
|
||||
$this->assertEquals('Les misérables', $authors[1]->getBooks()->get(0)->getName());
|
||||
}
|
||||
|
||||
public function testLoadWithInheritedRelationship()
|
||||
{
|
||||
$schema = <<<XML
|
||||
<database name="default" package="vendor.bundles.Propel.PropelBundle.Tests.Fixtures.DataFixtures.Loader" namespace="Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader" defaultIdMethod="native">
|
||||
|
||||
<table name="table_book_inherited_relationship" phpName="YamlInheritedRelationshipBook">
|
||||
<column name="id" type="integer" primaryKey="true" autoIncrement="true" />
|
||||
<column name="name" type="varchar" size="255" />
|
||||
<column name="author_id" type="integer" required="true" />
|
||||
<foreign-key foreignTable="table_author_inherited_relationship" phpName="Author">
|
||||
<reference local="author_id" foreign="id" />
|
||||
</foreign-key>
|
||||
</table>
|
||||
|
||||
<table name="table_author_inherited_relationship" phpName="YamlInheritedRelationshipAuthor">
|
||||
<column name="id" type="integer" primaryKey="true" autoIncrement="true" />
|
||||
<column name="name" type="varchar" size="255" />
|
||||
</table>
|
||||
|
||||
<table name="table_nobelized_author_inherited_relationship" phpName="YamlInheritedRelationshipNobelizedAuthor">
|
||||
<column name="nobel_year" type="integer" />
|
||||
<behavior name="concrete_inheritance">
|
||||
<parameter name="extends" value="table_author_inherited_relationship" />
|
||||
</behavior>
|
||||
</table>
|
||||
|
||||
</database>
|
||||
XML;
|
||||
|
||||
$fixtures = <<<YAML
|
||||
Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\YamlInheritedRelationshipNobelizedAuthor:
|
||||
NobelizedAuthor_1:
|
||||
nobel_year: 2012
|
||||
|
||||
Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\YamlInheritedRelationshipBook:
|
||||
Book_1:
|
||||
name: 'Supplice du santal'
|
||||
author_id: NobelizedAuthor_1
|
||||
YAML;
|
||||
|
||||
$filename = $this->getTempFile($fixtures);
|
||||
|
||||
$builder = new QuickBuilder();
|
||||
$builder->setSchema($schema);
|
||||
$con = $builder->build();
|
||||
|
||||
$loader = new YamlDataLoader(__DIR__.'/../../Fixtures/DataFixtures/Loader', array());
|
||||
$loader->load(array($filename), 'default');
|
||||
|
||||
$books = \Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\YamlInheritedRelationshipBookQuery::create()->find($con);
|
||||
$this->assertCount(1, $books);
|
||||
|
||||
$book = $books[0];
|
||||
$author = $book->getAuthor();
|
||||
$this->assertInstanceOf('Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\YamlInheritedRelationshipAuthor', $author);
|
||||
}
|
||||
|
||||
public function testLoadArrayToObjectType()
|
||||
{
|
||||
$schema = <<<XML
|
||||
<database name="default" package="vendor.bundles.Propel.PropelBundle.Tests.Fixtures.DataFixtures.Loader" namespace="Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader" defaultIdMethod="native">
|
||||
<table name="table_book_with_object" phpName="YamlBookWithObject">
|
||||
<column name="id" type="integer" primaryKey="true" />
|
||||
<column name="name" type="varchar" size="255" />
|
||||
<column name="options" type="object" />
|
||||
</table>
|
||||
</database>
|
||||
XML;
|
||||
$fixtures = <<<YAML
|
||||
Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\YamlBookWithObject:
|
||||
book1:
|
||||
name: my book
|
||||
options: {opt1: 2012, opt2: 140, inner: {subOpt: 123}}
|
||||
YAML;
|
||||
|
||||
$filename = $this->getTempFile($fixtures);
|
||||
|
||||
$builder = new QuickBuilder();
|
||||
$builder->setSchema($schema);
|
||||
$con = $builder->build();
|
||||
|
||||
$loader = new YamlDataLoader(__DIR__.'/../../Fixtures/DataFixtures/Loader', array());
|
||||
$loader->load(array($filename), 'default');
|
||||
|
||||
$book = \Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\YamlBookWithObjectQuery::create(null)->findOne($con);
|
||||
|
||||
$this->assertInstanceOf('\Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\YamlBookWithObject', $book);
|
||||
$this->assertEquals(array('opt1' => 2012, 'opt2' => 140, 'inner' => array('subOpt' => 123)), $book->getOptions());
|
||||
}
|
||||
|
||||
public function testLoadDelegatedOnPrimaryKey()
|
||||
{
|
||||
$schema = <<<XML
|
||||
<database name="default" package="vendor.bundles.Propel.PropelBundle.Tests.Fixtures.DataFixtures.Loader" namespace="Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader" defaultIdMethod="native">
|
||||
<table name="yaml_delegate_on_primary_key_person" phpName="YamlDelegateOnPrimaryKeyPerson">
|
||||
<column name="id" type="integer" primaryKey="true" autoIncrement="true" />
|
||||
<column name="name" type="varchar" size="255" />
|
||||
</table>
|
||||
|
||||
<table name="yaml_delegate_on_primary_key_author" phpName="YamlDelegateOnPrimaryKeyAuthor">
|
||||
<column name="id" type="integer" primaryKey="true" autoIncrement="false" />
|
||||
<column name="count_books" type="integer" defaultValue="0" required="true" />
|
||||
|
||||
<behavior name="delegate">
|
||||
<parameter name="to" value="yaml_delegate_on_primary_key_person" />
|
||||
</behavior>
|
||||
|
||||
<foreign-key foreignTable="yaml_delegate_on_primary_key_person" onDelete="RESTRICT" onUpdate="CASCADE">
|
||||
<reference local="id" foreign="id" />
|
||||
</foreign-key>
|
||||
</table>
|
||||
</database>
|
||||
XML;
|
||||
|
||||
$fixtures = <<<YAML
|
||||
Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\YamlDelegateOnPrimaryKeyPerson:
|
||||
yaml_delegate_on_primary_key_person_1:
|
||||
name: "Some Persons Name"
|
||||
|
||||
Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\YamlDelegateOnPrimaryKeyAuthor:
|
||||
yaml_delegate_on_primary_key_author_1:
|
||||
id: yaml_delegate_on_primary_key_person_1
|
||||
count_books: 7
|
||||
YAML;
|
||||
|
||||
$filename = $this->getTempFile($fixtures);
|
||||
|
||||
$builder = new QuickBuilder();
|
||||
$builder->setSchema($schema);
|
||||
$con = $builder->build();
|
||||
|
||||
$loader = new YamlDataLoader(__DIR__.'/../../Fixtures/DataFixtures/Loader', array());
|
||||
$loader->load(array($filename), 'default');
|
||||
|
||||
$authors = \Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\YamlDelegateOnPrimaryKeyAuthorQuery::create()->find($con);
|
||||
$this->assertCount(1, $authors);
|
||||
|
||||
$author = $authors[0];
|
||||
$person = $author->getYamlDelegateOnPrimaryKeyPerson();
|
||||
$this->assertInstanceOf('Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\YamlDelegateOnPrimaryKeyPerson', $person);
|
||||
}
|
||||
}
|
106
Tests/DataFixtures/TestCase.php
Normal file
106
Tests/DataFixtures/TestCase.php
Normal file
|
@ -0,0 +1,106 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the PropelBundle package.
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @license MIT License
|
||||
*/
|
||||
|
||||
namespace Propel\PropelBundle\Tests\DataFixtures;
|
||||
|
||||
use Propel\Generator\Util\QuickBuilder;
|
||||
use Propel\Runtime\Propel;
|
||||
use Propel\PropelBundle\Tests\TestCase as BaseTestCase;
|
||||
|
||||
/**
|
||||
* @author Toni Uebernickel <tuebernickel@gmail.com>
|
||||
*/
|
||||
class TestCase extends BaseTestCase
|
||||
{
|
||||
/**
|
||||
* @var \PropelPDO
|
||||
*/
|
||||
protected $con;
|
||||
|
||||
/**
|
||||
* The list of created temp files to be removed.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $tmpFiles = array();
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
if (!class_exists('Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader\CoolBook')) {
|
||||
$schema = <<<XML
|
||||
<database name="default" package="vendor.bundles.Propel.PropelBundle.Tests.Fixtures.DataFixtures.Loader" namespace="Propel\PropelBundle\Tests\Fixtures\DataFixtures\Loader" defaultIdMethod="native">
|
||||
<table name="cool_book">
|
||||
<column name="id" type="integer" primaryKey="true" />
|
||||
<column name="name" type="varchar" size="255" />
|
||||
<column name="description" type="varchar" />
|
||||
<column name="author_id" type="integer" required="false" defaultValue="null" />
|
||||
<column name="complementary_infos" required="false" type="object" description="An object column" />
|
||||
|
||||
<foreign-key foreignTable="cool_book_author" onDelete="CASCADE" onUpdate="CASCADE">
|
||||
<reference local="author_id" foreign="id" />
|
||||
</foreign-key>
|
||||
</table>
|
||||
|
||||
<table name="cool_book_author">
|
||||
<column name="id" type="integer" primaryKey="true" />
|
||||
<column name="name" type="varchar" size="255" />
|
||||
</table>
|
||||
</database>
|
||||
XML;
|
||||
|
||||
QuickBuilder::buildSchema($schema);
|
||||
}
|
||||
|
||||
$this->con = Propel::getServiceContainer()->getConnection('default');
|
||||
$this->con->beginTransaction();
|
||||
}
|
||||
|
||||
protected function tearDown()
|
||||
{
|
||||
foreach ($this->tmpFiles as $eachFile) {
|
||||
@unlink($eachFile);
|
||||
}
|
||||
|
||||
$this->tmpFiles = array();
|
||||
|
||||
// Only commit if the transaction hasn't failed.
|
||||
// This is because tearDown() is also executed on a failed tests,
|
||||
// and we don't want to call ConnectionInterface::commit() in that case
|
||||
// since it will trigger an exception on its own
|
||||
// ('Cannot commit because a nested transaction was rolled back')
|
||||
if (null !== $this->con) {
|
||||
if ($this->con->isCommitable()) {
|
||||
$this->con->commit();
|
||||
}
|
||||
$this->con = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of a created temporary file containing the given content.
|
||||
*
|
||||
* @param string $content
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getTempFile($content = '')
|
||||
{
|
||||
$filename = tempnam(sys_get_temp_dir(), 'propelbundle-datafixtures-test');
|
||||
@unlink($filename);
|
||||
|
||||
file_put_contents($filename, $content);
|
||||
|
||||
$this->tmpFiles[] = $filename;
|
||||
|
||||
return $filename;
|
||||
}
|
||||
}
|
60
Tests/Fixtures/Acl/ArrayCache.php
Normal file
60
Tests/Fixtures/Acl/ArrayCache.php
Normal file
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the PropelBundle package.
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @license MIT License
|
||||
*/
|
||||
|
||||
namespace Propel\PropelBundle\Tests\Fixtures\Acl;
|
||||
|
||||
use Symfony\Component\Security\Acl\Model\AclInterface;
|
||||
use Symfony\Component\Security\Acl\Model\AclCacheInterface;
|
||||
use Symfony\Component\Security\Acl\Model\ObjectIdentityInterface;
|
||||
|
||||
class ArrayCache implements AclCacheInterface
|
||||
{
|
||||
public $content = array();
|
||||
|
||||
public function evictFromCacheById($primaryKey)
|
||||
{
|
||||
if (isset($this->content[$primaryKey])) {
|
||||
unset($this->content[$primaryKey]);
|
||||
}
|
||||
}
|
||||
|
||||
public function evictFromCacheByIdentity(ObjectIdentityInterface $oid)
|
||||
{
|
||||
// Propel ACL does not make use of those.
|
||||
}
|
||||
|
||||
public function getFromCacheById($primaryKey)
|
||||
{
|
||||
if (isset($this->content[$primaryKey])) {
|
||||
return $this->content[$primaryKey];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getFromCacheByIdentity(ObjectIdentityInterface $oid)
|
||||
{
|
||||
// Propel ACL does not make use of those.
|
||||
}
|
||||
|
||||
public function putInCache(AclInterface $acl)
|
||||
{
|
||||
if (null === $acl->getId()) {
|
||||
throw new \InvalidArgumentException('The given ACL does not have an ID.');
|
||||
}
|
||||
|
||||
$this->content[$acl->getId()] = $acl;
|
||||
}
|
||||
|
||||
public function clearCache()
|
||||
{
|
||||
$this->content = array();
|
||||
}
|
||||
}
|
61
Tests/Fixtures/Column.php
Normal file
61
Tests/Fixtures/Column.php
Normal file
|
@ -0,0 +1,61 @@
|
|||
<?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\PropelBundle\Tests\Fixtures;
|
||||
|
||||
use Propel\Generator\Model\PropelTypes;
|
||||
|
||||
class Column
|
||||
{
|
||||
private $name;
|
||||
|
||||
private $type;
|
||||
|
||||
public function __construct($name, $type)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
public function getType()
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function isText()
|
||||
{
|
||||
if (!$this->type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch ($this->type) {
|
||||
case PropelTypes::CHAR:
|
||||
case PropelTypes::VARCHAR:
|
||||
case PropelTypes::LONGVARCHAR:
|
||||
case PropelTypes::BLOB:
|
||||
case PropelTypes::CLOB:
|
||||
case PropelTypes::CLOB_EMU:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getSize()
|
||||
{
|
||||
return $this->isText() ? 255 : 0;
|
||||
}
|
||||
|
||||
public function isNotNull()
|
||||
{
|
||||
return ('id' === $this->name);
|
||||
}
|
||||
}
|
109
Tests/Fixtures/Item.php
Normal file
109
Tests/Fixtures/Item.php
Normal file
|
@ -0,0 +1,109 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Propel\PropelBundle\Tests\Fixtures;
|
||||
|
||||
use Propel\Runtime\ActiveRecord\ActiveRecordInterface;
|
||||
use Propel\Runtime\Connection\ConnectionInterface;
|
||||
|
||||
class Item implements ActiveRecordInterface
|
||||
{
|
||||
private $id;
|
||||
|
||||
private $value;
|
||||
|
||||
private $groupName;
|
||||
|
||||
private $price;
|
||||
|
||||
public function __construct($id = null, $value = null, $groupName = null, $price = null)
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->value = $value;
|
||||
$this->groupName = $groupName;
|
||||
$this->price = $price;
|
||||
}
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setId($id)
|
||||
{
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
public function getValue()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function getGroupName()
|
||||
{
|
||||
return $this->groupName;
|
||||
}
|
||||
|
||||
public function getPrice()
|
||||
{
|
||||
return $this->price;
|
||||
}
|
||||
|
||||
public function getPrimaryKey()
|
||||
{
|
||||
return $this->getId();
|
||||
}
|
||||
|
||||
public function setPrimaryKey($primaryKey)
|
||||
{
|
||||
$this->setId($primaryKey);
|
||||
}
|
||||
|
||||
public function isModified()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function isColumnModified($col)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function isNew()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function setNew($b)
|
||||
{
|
||||
}
|
||||
|
||||
public function resetModified()
|
||||
{
|
||||
}
|
||||
|
||||
public function isDeleted()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function setDeleted($b)
|
||||
{
|
||||
}
|
||||
|
||||
public function delete(ConnectionInterface $con = null)
|
||||
{
|
||||
}
|
||||
|
||||
public function save(ConnectionInterface $con = null)
|
||||
{
|
||||
}
|
||||
}
|
100
Tests/Fixtures/ItemQuery.php
Normal file
100
Tests/Fixtures/ItemQuery.php
Normal file
|
@ -0,0 +1,100 @@
|
|||
<?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\PropelBundle\Tests\Fixtures;
|
||||
|
||||
use Propel\Runtime\Map\ColumnMap;
|
||||
use Propel\Runtime\Map\RelationMap;
|
||||
use Propel\Runtime\Map\TableMap;
|
||||
use Propel\Generator\Model\PropelTypes;
|
||||
|
||||
class ItemQuery
|
||||
{
|
||||
private $map = array(
|
||||
'id' => PropelTypes::INTEGER,
|
||||
'value' => PropelTypes::VARCHAR,
|
||||
'price' => PropelTypes::FLOAT,
|
||||
'is_active' => PropelTypes::BOOLEAN,
|
||||
'enabled' => PropelTypes::BOOLEAN_EMU,
|
||||
'updated_at' => PropelTypes::TIMESTAMP,
|
||||
|
||||
'updated_at' => PropelTypes::TIMESTAMP,
|
||||
'updated_at' => PropelTypes::TIMESTAMP,
|
||||
'updated_at' => PropelTypes::TIMESTAMP,
|
||||
);
|
||||
|
||||
public function getTableMap()
|
||||
{
|
||||
// Allows to define methods in this class
|
||||
// to avoid a lot of mock classes
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPrimaryKeys()
|
||||
{
|
||||
$cm = new ColumnMap('id', new TableMap());
|
||||
$cm->setType('INTEGER');
|
||||
|
||||
return array('id' => $cm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method from the TableMap API
|
||||
*/
|
||||
public function hasColumn($column)
|
||||
{
|
||||
return in_array($column, array_keys($this->map));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method from the TableMap API
|
||||
*/
|
||||
public function getColumn($column)
|
||||
{
|
||||
if ($this->hasColumn($column)) {
|
||||
return new Column($column, $this->map[$column]);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method from the TableMap API
|
||||
*/
|
||||
public function getRelations()
|
||||
{
|
||||
// table maps
|
||||
$authorTable = new TableMap();
|
||||
$authorTable->setClassName('\Foo\Author');
|
||||
|
||||
$resellerTable = new TableMap();
|
||||
$resellerTable->setClassName('\Foo\Reseller');
|
||||
|
||||
// relations
|
||||
$mainAuthorRelation = new RelationMap('MainAuthor');
|
||||
$mainAuthorRelation->setType(RelationMap::MANY_TO_ONE);
|
||||
$mainAuthorRelation->setForeignTable($authorTable);
|
||||
|
||||
$authorRelation = new RelationMap('Author');
|
||||
$authorRelation->setType(RelationMap::ONE_TO_MANY);
|
||||
$authorRelation->setForeignTable($authorTable);
|
||||
|
||||
$resellerRelation = new RelationMap('Reseller');
|
||||
$resellerRelation->setType(RelationMap::MANY_TO_MANY);
|
||||
$resellerRelation->setLocalTable($resellerTable);
|
||||
|
||||
return array(
|
||||
$mainAuthorRelation,
|
||||
$authorRelation,
|
||||
$resellerRelation
|
||||
);
|
||||
}
|
||||
}
|
1330
Tests/Fixtures/Model/Base/Book.php
Normal file
1330
Tests/Fixtures/Model/Base/Book.php
Normal file
File diff suppressed because it is too large
Load diff
489
Tests/Fixtures/Model/Base/BookQuery.php
Normal file
489
Tests/Fixtures/Model/Base/BookQuery.php
Normal file
|
@ -0,0 +1,489 @@
|
|||
<?php
|
||||
|
||||
namespace Propel\PropelBundle\Tests\Fixtures\Model\Base;
|
||||
|
||||
use \Exception;
|
||||
use \PDO;
|
||||
use Propel\PropelBundle\Tests\Fixtures\Model\Book as ChildBook;
|
||||
use Propel\PropelBundle\Tests\Fixtures\Model\BookQuery as ChildBookQuery;
|
||||
use Propel\PropelBundle\Tests\Fixtures\Model\Map\BookTableMap;
|
||||
use Propel\Runtime\Propel;
|
||||
use Propel\Runtime\ActiveQuery\Criteria;
|
||||
use Propel\Runtime\ActiveQuery\ModelCriteria;
|
||||
use Propel\Runtime\ActiveQuery\ModelJoin;
|
||||
use Propel\Runtime\Collection\Collection;
|
||||
use Propel\Runtime\Collection\ObjectCollection;
|
||||
use Propel\Runtime\Connection\ConnectionInterface;
|
||||
use Propel\Runtime\Exception\PropelException;
|
||||
|
||||
/**
|
||||
* Base class that represents a query for the 'book' table.
|
||||
*
|
||||
*
|
||||
*
|
||||
* @method ChildBookQuery orderById($order = Criteria::ASC) Order by the id column
|
||||
* @method ChildBookQuery orderByTitle($order = Criteria::ASC) Order by the title column
|
||||
* @method ChildBookQuery orderByIsbn($order = Criteria::ASC) Order by the ISBN column
|
||||
* @method ChildBookQuery orderByAuthorId($order = Criteria::ASC) Order by the author_id column
|
||||
*
|
||||
* @method ChildBookQuery groupById() Group by the id column
|
||||
* @method ChildBookQuery groupByTitle() Group by the title column
|
||||
* @method ChildBookQuery groupByIsbn() Group by the ISBN column
|
||||
* @method ChildBookQuery groupByAuthorId() Group by the author_id column
|
||||
*
|
||||
* @method ChildBookQuery leftJoin($relation) Adds a LEFT JOIN clause to the query
|
||||
* @method ChildBookQuery rightJoin($relation) Adds a RIGHT JOIN clause to the query
|
||||
* @method ChildBookQuery innerJoin($relation) Adds a INNER JOIN clause to the query
|
||||
*
|
||||
* @method ChildBook findOne(ConnectionInterface $con = null) Return the first ChildBook matching the query
|
||||
* @method ChildBook findOneOrCreate(ConnectionInterface $con = null) Return the first ChildBook matching the query, or a new ChildBook object populated from the query conditions when no match is found
|
||||
*
|
||||
* @method ChildBook findOneById(int $id) Return the first ChildBook filtered by the id column
|
||||
* @method ChildBook findOneByTitle(string $title) Return the first ChildBook filtered by the title column
|
||||
* @method ChildBook findOneByIsbn(string $ISBN) Return the first ChildBook filtered by the ISBN column
|
||||
* @method ChildBook findOneByAuthorId(int $author_id) Return the first ChildBook filtered by the author_id column
|
||||
*
|
||||
* @method array findById(int $id) Return ChildBook objects filtered by the id column
|
||||
* @method array findByTitle(string $title) Return ChildBook objects filtered by the title column
|
||||
* @method array findByIsbn(string $ISBN) Return ChildBook objects filtered by the ISBN column
|
||||
* @method array findByAuthorId(int $author_id) Return ChildBook objects filtered by the author_id column
|
||||
*
|
||||
*/
|
||||
abstract class BookQuery extends ModelCriteria
|
||||
{
|
||||
|
||||
/**
|
||||
* Initializes internal state of \Acme\DemoBundle\Model\Base\BookQuery object.
|
||||
*
|
||||
* @param string $dbName The database name
|
||||
* @param string $modelName The phpName of a model, e.g. 'Book'
|
||||
* @param string $modelAlias The alias for the model in this query, e.g. 'b'
|
||||
*/
|
||||
public function __construct($dbName = 'default', $modelName = '\\Propel\\PropelBundle\\Tests\\Fixtures\\Model\\Book', $modelAlias = null)
|
||||
{
|
||||
parent::__construct($dbName, $modelName, $modelAlias);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new ChildBookQuery object.
|
||||
*
|
||||
* @param string $modelAlias The alias of a model in the query
|
||||
* @param Criteria $criteria Optional Criteria to build the query from
|
||||
*
|
||||
* @return ChildBookQuery
|
||||
*/
|
||||
public static function create($modelAlias = null, $criteria = null)
|
||||
{
|
||||
if ($criteria instanceof \Propel\PropelBundle\Tests\Fixtures\Model\BookQuery) {
|
||||
return $criteria;
|
||||
}
|
||||
$query = new \Propel\PropelBundle\Tests\Fixtures\Model\BookQuery();
|
||||
if (null !== $modelAlias) {
|
||||
$query->setModelAlias($modelAlias);
|
||||
}
|
||||
if ($criteria instanceof Criteria) {
|
||||
$query->mergeWith($criteria);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find object by primary key.
|
||||
* Propel uses the instance pool to skip the database if the object exists.
|
||||
* Go fast if the query is untouched.
|
||||
*
|
||||
* <code>
|
||||
* $obj = $c->findPk(12, $con);
|
||||
* </code>
|
||||
*
|
||||
* @param mixed $key Primary key to use for the query
|
||||
* @param ConnectionInterface $con an optional connection object
|
||||
*
|
||||
* @return ChildBook|array|mixed the result, formatted by the current formatter
|
||||
*/
|
||||
public function findPk($key, ConnectionInterface $con = null)
|
||||
{
|
||||
if ($key === null) {
|
||||
return null;
|
||||
}
|
||||
if ((null !== ($obj = BookTableMap::getInstanceFromPool((string) $key))) && !$this->formatter) {
|
||||
// the object is already in the instance pool
|
||||
return $obj;
|
||||
}
|
||||
if ($con === null) {
|
||||
$con = Propel::getServiceContainer()->getReadConnection(BookTableMap::DATABASE_NAME);
|
||||
}
|
||||
$this->basePreSelect($con);
|
||||
if ($this->formatter || $this->modelAlias || $this->with || $this->select
|
||||
|| $this->selectColumns || $this->asColumns || $this->selectModifiers
|
||||
|| $this->map || $this->having || $this->joins) {
|
||||
return $this->findPkComplex($key, $con);
|
||||
} else {
|
||||
return $this->findPkSimple($key, $con);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find object by primary key using raw SQL to go fast.
|
||||
* Bypass doSelect() and the object formatter by using generated code.
|
||||
*
|
||||
* @param mixed $key Primary key to use for the query
|
||||
* @param ConnectionInterface $con A connection object
|
||||
*
|
||||
* @return ChildBook A model object, or null if the key is not found
|
||||
*/
|
||||
protected function findPkSimple($key, $con)
|
||||
{
|
||||
$sql = 'SELECT ID, TITLE, ISBN, AUTHOR_ID FROM book WHERE ID = :p0';
|
||||
try {
|
||||
$stmt = $con->prepare($sql);
|
||||
$stmt->bindValue(':p0', $key, PDO::PARAM_INT);
|
||||
$stmt->execute();
|
||||
} catch (Exception $e) {
|
||||
Propel::log($e->getMessage(), Propel::LOG_ERR);
|
||||
throw new PropelException(sprintf('Unable to execute SELECT statement [%s]', $sql), 0, $e);
|
||||
}
|
||||
$obj = null;
|
||||
if ($row = $stmt->fetch(\PDO::FETCH_NUM)) {
|
||||
$obj = new ChildBook();
|
||||
$obj->hydrate($row);
|
||||
BookTableMap::addInstanceToPool($obj, (string) $key);
|
||||
}
|
||||
$stmt->closeCursor();
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find object by primary key.
|
||||
*
|
||||
* @param mixed $key Primary key to use for the query
|
||||
* @param ConnectionInterface $con A connection object
|
||||
*
|
||||
* @return ChildBook|array|mixed the result, formatted by the current formatter
|
||||
*/
|
||||
protected function findPkComplex($key, $con)
|
||||
{
|
||||
// As the query uses a PK condition, no limit(1) is necessary.
|
||||
$criteria = $this->isKeepQuery() ? clone $this : $this;
|
||||
$dataFetcher = $criteria
|
||||
->filterByPrimaryKey($key)
|
||||
->doSelect($con);
|
||||
|
||||
return $criteria->getFormatter()->init($criteria)->formatOne($dataFetcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find objects by primary key
|
||||
* <code>
|
||||
* $objs = $c->findPks(array(12, 56, 832), $con);
|
||||
* </code>
|
||||
* @param array $keys Primary keys to use for the query
|
||||
* @param ConnectionInterface $con an optional connection object
|
||||
*
|
||||
* @return ObjectCollection|array|mixed the list of results, formatted by the current formatter
|
||||
*/
|
||||
public function findPks($keys, ConnectionInterface $con = null)
|
||||
{
|
||||
if (null === $con) {
|
||||
$con = Propel::getServiceContainer()->getReadConnection($this->getDbName());
|
||||
}
|
||||
$this->basePreSelect($con);
|
||||
$criteria = $this->isKeepQuery() ? clone $this : $this;
|
||||
$dataFetcher = $criteria
|
||||
->filterByPrimaryKeys($keys)
|
||||
->doSelect($con);
|
||||
|
||||
return $criteria->getFormatter()->init($criteria)->format($dataFetcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the query by primary key
|
||||
*
|
||||
* @param mixed $key Primary key to use for the query
|
||||
*
|
||||
* @return ChildBookQuery The current query, for fluid interface
|
||||
*/
|
||||
public function filterByPrimaryKey($key)
|
||||
{
|
||||
return $this->addUsingAlias(BookTableMap::ID, $key, Criteria::EQUAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the query by a list of primary keys
|
||||
*
|
||||
* @param array $keys The list of primary key to use for the query
|
||||
*
|
||||
* @return ChildBookQuery The current query, for fluid interface
|
||||
*/
|
||||
public function filterByPrimaryKeys($keys)
|
||||
{
|
||||
return $this->addUsingAlias(BookTableMap::ID, $keys, Criteria::IN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the query on the id column
|
||||
*
|
||||
* Example usage:
|
||||
* <code>
|
||||
* $query->filterById(1234); // WHERE id = 1234
|
||||
* $query->filterById(array(12, 34)); // WHERE id IN (12, 34)
|
||||
* $query->filterById(array('min' => 12)); // WHERE id > 12
|
||||
* </code>
|
||||
*
|
||||
* @param mixed $id The value to use as filter.
|
||||
* Use scalar values for equality.
|
||||
* Use array values for in_array() equivalent.
|
||||
* Use associative array('min' => $minValue, 'max' => $maxValue) for intervals.
|
||||
* @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL
|
||||
*
|
||||
* @return ChildBookQuery The current query, for fluid interface
|
||||
*/
|
||||
public function filterById($id = null, $comparison = null)
|
||||
{
|
||||
if (is_array($id)) {
|
||||
$useMinMax = false;
|
||||
if (isset($id['min'])) {
|
||||
$this->addUsingAlias(BookTableMap::ID, $id['min'], Criteria::GREATER_EQUAL);
|
||||
$useMinMax = true;
|
||||
}
|
||||
if (isset($id['max'])) {
|
||||
$this->addUsingAlias(BookTableMap::ID, $id['max'], Criteria::LESS_EQUAL);
|
||||
$useMinMax = true;
|
||||
}
|
||||
if ($useMinMax) {
|
||||
return $this;
|
||||
}
|
||||
if (null === $comparison) {
|
||||
$comparison = Criteria::IN;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->addUsingAlias(BookTableMap::ID, $id, $comparison);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the query on the title column
|
||||
*
|
||||
* Example usage:
|
||||
* <code>
|
||||
* $query->filterByTitle('fooValue'); // WHERE title = 'fooValue'
|
||||
* $query->filterByTitle('%fooValue%'); // WHERE title LIKE '%fooValue%'
|
||||
* </code>
|
||||
*
|
||||
* @param string $title The value to use as filter.
|
||||
* Accepts wildcards (* and % trigger a LIKE)
|
||||
* @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL
|
||||
*
|
||||
* @return ChildBookQuery The current query, for fluid interface
|
||||
*/
|
||||
public function filterByTitle($title = null, $comparison = null)
|
||||
{
|
||||
if (null === $comparison) {
|
||||
if (is_array($title)) {
|
||||
$comparison = Criteria::IN;
|
||||
} elseif (preg_match('/[\%\*]/', $title)) {
|
||||
$title = str_replace('*', '%', $title);
|
||||
$comparison = Criteria::LIKE;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->addUsingAlias(BookTableMap::TITLE, $title, $comparison);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the query on the ISBN column
|
||||
*
|
||||
* Example usage:
|
||||
* <code>
|
||||
* $query->filterByIsbn('fooValue'); // WHERE ISBN = 'fooValue'
|
||||
* $query->filterByIsbn('%fooValue%'); // WHERE ISBN LIKE '%fooValue%'
|
||||
* </code>
|
||||
*
|
||||
* @param string $isbn The value to use as filter.
|
||||
* Accepts wildcards (* and % trigger a LIKE)
|
||||
* @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL
|
||||
*
|
||||
* @return ChildBookQuery The current query, for fluid interface
|
||||
*/
|
||||
public function filterByIsbn($isbn = null, $comparison = null)
|
||||
{
|
||||
if (null === $comparison) {
|
||||
if (is_array($isbn)) {
|
||||
$comparison = Criteria::IN;
|
||||
} elseif (preg_match('/[\%\*]/', $isbn)) {
|
||||
$isbn = str_replace('*', '%', $isbn);
|
||||
$comparison = Criteria::LIKE;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->addUsingAlias(BookTableMap::ISBN, $isbn, $comparison);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the query on the author_id column
|
||||
*
|
||||
* Example usage:
|
||||
* <code>
|
||||
* $query->filterByAuthorId(1234); // WHERE author_id = 1234
|
||||
* $query->filterByAuthorId(array(12, 34)); // WHERE author_id IN (12, 34)
|
||||
* $query->filterByAuthorId(array('min' => 12)); // WHERE author_id > 12
|
||||
* </code>
|
||||
*
|
||||
* @see filterByAuthor()
|
||||
*
|
||||
* @param mixed $authorId The value to use as filter.
|
||||
* Use scalar values for equality.
|
||||
* Use array values for in_array() equivalent.
|
||||
* Use associative array('min' => $minValue, 'max' => $maxValue) for intervals.
|
||||
* @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL
|
||||
*
|
||||
* @return ChildBookQuery The current query, for fluid interface
|
||||
*/
|
||||
public function filterByAuthorId($authorId = null, $comparison = null)
|
||||
{
|
||||
if (is_array($authorId)) {
|
||||
$useMinMax = false;
|
||||
if (isset($authorId['min'])) {
|
||||
$this->addUsingAlias(BookTableMap::AUTHOR_ID, $authorId['min'], Criteria::GREATER_EQUAL);
|
||||
$useMinMax = true;
|
||||
}
|
||||
if (isset($authorId['max'])) {
|
||||
$this->addUsingAlias(BookTableMap::AUTHOR_ID, $authorId['max'], Criteria::LESS_EQUAL);
|
||||
$useMinMax = true;
|
||||
}
|
||||
if ($useMinMax) {
|
||||
return $this;
|
||||
}
|
||||
if (null === $comparison) {
|
||||
$comparison = Criteria::IN;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->addUsingAlias(BookTableMap::AUTHOR_ID, $authorId, $comparison);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a JOIN clause to the query using the Author relation
|
||||
*
|
||||
* @param string $relationAlias optional alias for the relation
|
||||
* @param string $joinType Accepted values are null, 'left join', 'right join', 'inner join'
|
||||
*
|
||||
* @return ChildBookQuery The current query, for fluid interface
|
||||
*/
|
||||
public function joinAuthor($relationAlias = null, $joinType = Criteria::LEFT_JOIN)
|
||||
{
|
||||
$tableMap = $this->getTableMap();
|
||||
$relationMap = $tableMap->getRelation('Author');
|
||||
|
||||
// create a ModelJoin object for this join
|
||||
$join = new ModelJoin();
|
||||
$join->setJoinType($joinType);
|
||||
$join->setRelationMap($relationMap, $this->useAliasInSQL ? $this->getModelAlias() : null, $relationAlias);
|
||||
if ($previousJoin = $this->getPreviousJoin()) {
|
||||
$join->setPreviousJoin($previousJoin);
|
||||
}
|
||||
|
||||
// add the ModelJoin to the current object
|
||||
if ($relationAlias) {
|
||||
$this->addAlias($relationAlias, $relationMap->getRightTable()->getName());
|
||||
$this->addJoinObject($join, $relationAlias);
|
||||
} else {
|
||||
$this->addJoinObject($join, 'Author');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exclude object from result
|
||||
*
|
||||
* @param ChildBook $book Object to remove from the list of results
|
||||
*
|
||||
* @return ChildBookQuery The current query, for fluid interface
|
||||
*/
|
||||
public function prune($book = null)
|
||||
{
|
||||
if ($book) {
|
||||
$this->addUsingAlias(BookTableMap::ID, $book->getId(), Criteria::NOT_EQUAL);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all rows from the book table.
|
||||
*
|
||||
* @param ConnectionInterface $con the connection to use
|
||||
* @return int The number of affected rows (if supported by underlying database driver).
|
||||
*/
|
||||
public function doDeleteAll(ConnectionInterface $con = null)
|
||||
{
|
||||
if (null === $con) {
|
||||
$con = Propel::getServiceContainer()->getWriteConnection(BookTableMap::DATABASE_NAME);
|
||||
}
|
||||
$affectedRows = 0; // initialize var to track total num of affected rows
|
||||
try {
|
||||
// use transaction because $criteria could contain info
|
||||
// for more than one table or we could emulating ON DELETE CASCADE, etc.
|
||||
$con->beginTransaction();
|
||||
$affectedRows += parent::doDeleteAll($con);
|
||||
// Because this db requires some delete cascade/set null emulation, we have to
|
||||
// clear the cached instance *after* the emulation has happened (since
|
||||
// instances get re-added by the select statement contained therein).
|
||||
BookTableMap::clearInstancePool();
|
||||
BookTableMap::clearRelatedInstancePool();
|
||||
|
||||
$con->commit();
|
||||
} catch (PropelException $e) {
|
||||
$con->rollBack();
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return $affectedRows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a DELETE on the database, given a ChildBook or Criteria object OR a primary key value.
|
||||
*
|
||||
* @param mixed $values Criteria or ChildBook object or primary key or array of primary keys
|
||||
* which is used to create the DELETE statement
|
||||
* @param ConnectionInterface $con the connection to use
|
||||
* @return int The number of affected rows (if supported by underlying database driver). This includes CASCADE-related rows
|
||||
* if supported by native driver or if emulated using Propel.
|
||||
* @throws PropelException Any exceptions caught during processing will be
|
||||
* rethrown wrapped into a PropelException.
|
||||
*/
|
||||
public function delete(ConnectionInterface $con = null)
|
||||
{
|
||||
if (null === $con) {
|
||||
$con = Propel::getServiceContainer()->getWriteConnection(BookTableMap::DATABASE_NAME);
|
||||
}
|
||||
|
||||
$criteria = $this;
|
||||
|
||||
// Set the correct dbName
|
||||
$criteria->setDbName(BookTableMap::DATABASE_NAME);
|
||||
|
||||
$affectedRows = 0; // initialize var to track total num of affected rows
|
||||
|
||||
try {
|
||||
// use transaction because $criteria could contain info
|
||||
// for more than one table or we could emulating ON DELETE CASCADE, etc.
|
||||
$con->beginTransaction();
|
||||
|
||||
BookTableMap::removeInstanceFromPool($criteria);
|
||||
|
||||
$affectedRows += ModelCriteria::delete($con);
|
||||
BookTableMap::clearRelatedInstancePool();
|
||||
$con->commit();
|
||||
|
||||
return $affectedRows;
|
||||
} catch (PropelException $e) {
|
||||
$con->rollBack();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
} // BookQuery
|
10
Tests/Fixtures/Model/Book.php
Normal file
10
Tests/Fixtures/Model/Book.php
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace Propel\PropelBundle\Tests\Fixtures\Model;
|
||||
|
||||
use Propel\PropelBundle\Tests\Fixtures\Model\Base\Book as BaseBook;
|
||||
|
||||
class Book extends BaseBook
|
||||
{
|
||||
|
||||
}
|
95
Tests/Fixtures/Model/BookQuery.php
Normal file
95
Tests/Fixtures/Model/BookQuery.php
Normal file
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
|
||||
namespace Propel\PropelBundle\Tests\Fixtures\Model;
|
||||
|
||||
use Propel\Runtime\Connection\ConnectionInterface;
|
||||
|
||||
use Propel\PropelBundle\Tests\Fixtures\Model\Base\BookQuery as BaseBookQuery;
|
||||
use Propel\PropelBundle\Tests\Fixtures\Model\Book;
|
||||
|
||||
/**
|
||||
* Skeleton subclass for performing query and update operations on the 'book' table.
|
||||
*
|
||||
*
|
||||
*
|
||||
* You should add additional methods to this class to meet the
|
||||
* application requirements. This class will only be generated as
|
||||
* long as it does not already exist in the output directory.
|
||||
*
|
||||
*/
|
||||
class BookQuery extends BaseBookQuery
|
||||
{
|
||||
private $bySlug = false;
|
||||
private $byAuthorSlug = false;
|
||||
|
||||
/**
|
||||
* fake for test
|
||||
*/
|
||||
public function findPk($key, ConnectionInterface $con = null)
|
||||
{
|
||||
if (1 === $key) {
|
||||
$book = new Book();
|
||||
$book->setId(1);
|
||||
|
||||
return $book;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* fake for test
|
||||
*/
|
||||
public function filterByAuthorSlug($slug = null, $comparison = null)
|
||||
{
|
||||
if ('my-author' === $slug) {
|
||||
$this->byAuthorSlug = true;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* fake for test
|
||||
*/
|
||||
public function filterBySlug($slug = null, $comparison = null)
|
||||
{
|
||||
if ('my-book' == $slug) {
|
||||
$this->bySlug = true;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* fake for test
|
||||
*/
|
||||
public function filterByName($name = null, $comparison = null)
|
||||
{
|
||||
throw new \Exception('Test should never call this method');
|
||||
}
|
||||
|
||||
/**
|
||||
* fake for test
|
||||
*/
|
||||
public function findOne(ConnectionInterface $con = null)
|
||||
{
|
||||
if (true === $this->bySlug) {
|
||||
$book = new Book();
|
||||
$book->setId(1);
|
||||
$book->setName('My Book');
|
||||
$book->setSlug('my-book');
|
||||
|
||||
return $book;
|
||||
} elseif (true === $this->byAuthorSlug) {
|
||||
$book = new Book();
|
||||
$book->setId(2);
|
||||
$book->setName('My Kewl Book');
|
||||
$book->setSlug('my-kewl-book');
|
||||
|
||||
return $book;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
} // BookQuery
|
430
Tests/Fixtures/Model/Map/BookTableMap.php
Normal file
430
Tests/Fixtures/Model/Map/BookTableMap.php
Normal file
|
@ -0,0 +1,430 @@
|
|||
<?php
|
||||
|
||||
namespace Propel\PropelBundle\Tests\Fixtures\Model\Map;
|
||||
|
||||
use Acme\DemoBundle\Model\Book;
|
||||
use Acme\DemoBundle\Model\BookQuery;
|
||||
use Propel\Runtime\Propel;
|
||||
use Propel\Runtime\ActiveQuery\Criteria;
|
||||
use Propel\Runtime\ActiveQuery\InstancePoolTrait;
|
||||
use Propel\Runtime\Connection\ConnectionInterface;
|
||||
use Propel\Runtime\Exception\PropelException;
|
||||
use Propel\Runtime\Map\RelationMap;
|
||||
use Propel\Runtime\Map\TableMap;
|
||||
use Propel\Runtime\Map\TableMapTrait;
|
||||
|
||||
/**
|
||||
* This class defines the structure of the 'book' table.
|
||||
*
|
||||
*
|
||||
*
|
||||
* This map class is used by Propel to do runtime db structure discovery.
|
||||
* For example, the createSelectSql() method checks the type of a given column used in an
|
||||
* ORDER BY clause to know whether it needs to apply SQL to make the ORDER BY case-insensitive
|
||||
* (i.e. if it's a text column type).
|
||||
*
|
||||
*/
|
||||
class BookTableMap extends TableMap
|
||||
{
|
||||
use InstancePoolTrait;
|
||||
use TableMapTrait;
|
||||
/**
|
||||
* The (dot-path) name of this class
|
||||
*/
|
||||
const CLASS_NAME = 'src.Acme.DemoBundle.Model.Map.BookTableMap';
|
||||
|
||||
/**
|
||||
* The default database name for this class
|
||||
*/
|
||||
const DATABASE_NAME = 'default';
|
||||
|
||||
/**
|
||||
* The table name for this class
|
||||
*/
|
||||
const TABLE_NAME = 'book';
|
||||
|
||||
/**
|
||||
* The related Propel class for this table
|
||||
*/
|
||||
const OM_CLASS = '\\Propel\\PropelBundle\\Tests\\Fixtures\\Model\\Book';
|
||||
|
||||
/**
|
||||
* A class that can be returned by this tableMap
|
||||
*/
|
||||
const CLASS_DEFAULT = 'src.Acme.DemoBundle.Model.Book';
|
||||
|
||||
/**
|
||||
* The total number of columns
|
||||
*/
|
||||
const NUM_COLUMNS = 5;
|
||||
|
||||
/**
|
||||
* The number of lazy-loaded columns
|
||||
*/
|
||||
const NUM_LAZY_LOAD_COLUMNS = 0;
|
||||
|
||||
/**
|
||||
* The number of columns to hydrate (NUM_COLUMNS - NUM_LAZY_LOAD_COLUMNS)
|
||||
*/
|
||||
const NUM_HYDRATE_COLUMNS = 5;
|
||||
|
||||
/**
|
||||
* the column name for the ID field
|
||||
*/
|
||||
const ID = 'book.ID';
|
||||
|
||||
/**
|
||||
* the column name for the NAME field
|
||||
*/
|
||||
const NAME = 'book.NAME';
|
||||
|
||||
/**
|
||||
* the column name for the SLUG field
|
||||
*/
|
||||
const SLUG = 'book.SLUG';
|
||||
|
||||
/**
|
||||
* the column name for the ISBN field
|
||||
*/
|
||||
const ISBN = 'book.ISBN';
|
||||
|
||||
/**
|
||||
* the column name for the AUTHOR_ID field
|
||||
*/
|
||||
const AUTHOR_ID = 'book.AUTHOR_ID';
|
||||
|
||||
/**
|
||||
* The default string format for model objects of the related table
|
||||
*/
|
||||
const DEFAULT_STRING_FORMAT = 'YAML';
|
||||
|
||||
/**
|
||||
* holds an array of fieldnames
|
||||
*
|
||||
* first dimension keys are the type constants
|
||||
* e.g. self::$fieldNames[self::TYPE_PHPNAME][0] = 'Id'
|
||||
*/
|
||||
protected static $fieldNames = array (
|
||||
self::TYPE_PHPNAME => array('Id', 'Name', 'Slug', 'Isbn', 'AuthorId', ),
|
||||
self::TYPE_CAMELNAME => array('id', 'name', 'slug', 'isbn', 'authorId', ),
|
||||
self::TYPE_COLNAME => array(BookTableMap::ID, BookTableMap::NAME, BookTableMap::SLUG, BookTableMap::ISBN, BookTableMap::AUTHOR_ID, ),
|
||||
self::TYPE_RAW_COLNAME => array('ID', 'NAME', 'SLUG', 'ISBN', 'AUTHOR_ID', ),
|
||||
self::TYPE_FIELDNAME => array('id', 'name', 'slug', 'ISBN', 'author_id', ),
|
||||
self::TYPE_NUM => array(0, 1, 2, 3, 4, )
|
||||
);
|
||||
|
||||
/**
|
||||
* holds an array of keys for quick access to the fieldnames array
|
||||
*
|
||||
* first dimension keys are the type constants
|
||||
* e.g. self::$fieldKeys[self::TYPE_PHPNAME]['Id'] = 0
|
||||
*/
|
||||
protected static $fieldKeys = array (
|
||||
self::TYPE_PHPNAME => array('Id' => 0, 'Name' => 1, 'Slug' => 2, 'Isbn' => 3, 'AuthorId' => 4, ),
|
||||
self::TYPE_CAMELNAME => array('id' => 0, 'name' => 1, 'slug' => 2, 'isbn' => 3, 'authorId' => 4, ),
|
||||
self::TYPE_COLNAME => array(BookTableMap::ID => 0, BookTableMap::NAME => 1, BookTableMap::SLUG => 2, BookTableMap::ISBN => 3, BookTableMap::AUTHOR_ID => 4, ),
|
||||
self::TYPE_RAW_COLNAME => array('ID' => 0, 'NAME' => 1, 'SLUG' => 2, 'ISBN' => 3, 'AUTHOR_ID' => 4, ),
|
||||
self::TYPE_FIELDNAME => array('id' => 0, 'name' => 1, 'slug' => 2, 'ISBN' => 3, 'author_id' => 4, ),
|
||||
self::TYPE_NUM => array(0, 1, 2, 3, 4, )
|
||||
);
|
||||
|
||||
/**
|
||||
* Initialize the table attributes and columns
|
||||
* Relations are not initialized by this method since they are lazy loaded
|
||||
*
|
||||
* @return void
|
||||
* @throws PropelException
|
||||
*/
|
||||
public function initialize()
|
||||
{
|
||||
// attributes
|
||||
$this->setName('book');
|
||||
$this->setPhpName('Book');
|
||||
$this->setClassName('\\Propel\\PropelBundle\\Tests\\Fixtures\\Model\\Book');
|
||||
$this->setPackage('src.Acme.DemoBundle.Model');
|
||||
$this->setUseIdGenerator(true);
|
||||
// columns
|
||||
$this->addPrimaryKey('ID', 'Id', 'INTEGER', true, null, null);
|
||||
$this->addColumn('NAME', 'Name', 'VARCHAR', false, 100, null);
|
||||
$this->addColumn('SLUG', 'Slug', 'VARCHAR', false, 100, null);
|
||||
$this->getColumn('NAME', false)->setPrimaryString(true);
|
||||
$this->addColumn('ISBN', 'Isbn', 'VARCHAR', false, 20, null);
|
||||
} // initialize()
|
||||
|
||||
/**
|
||||
* Build the RelationMap objects for this table relationships
|
||||
*/
|
||||
public function buildRelations()
|
||||
{
|
||||
} // buildRelations()
|
||||
|
||||
/**
|
||||
* Retrieves a string version of the primary key from the DB resultset row that can be used to uniquely identify a row in this table.
|
||||
*
|
||||
* For tables with a single-column primary key, that simple pkey value will be returned. For tables with
|
||||
* a multi-column primary key, a serialize()d version of the primary key will be returned.
|
||||
*
|
||||
* @param array $row resultset row.
|
||||
* @param int $offset The 0-based offset for reading from the resultset row.
|
||||
* @param string $indexType One of the class type constants TableMap::TYPE_PHPNAME, TableMap::TYPE_CAMELNAME
|
||||
* TableMap::TYPE_COLNAME, TableMap::TYPE_FIELDNAME, TableMap::TYPE_NUM
|
||||
*/
|
||||
public static function getPrimaryKeyHashFromRow($row, $offset = 0, $indexType = TableMap::TYPE_NUM)
|
||||
{
|
||||
// If the PK cannot be derived from the row, return NULL.
|
||||
if ($row[TableMap::TYPE_NUM == $indexType ? 0 + $offset : static::translateFieldName('Id', TableMap::TYPE_PHPNAME, $indexType)] === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (string) $row[TableMap::TYPE_NUM == $indexType ? 0 + $offset : static::translateFieldName('Id', TableMap::TYPE_PHPNAME, $indexType)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the primary key from the DB resultset row
|
||||
* For tables with a single-column primary key, that simple pkey value will be returned. For tables with
|
||||
* a multi-column primary key, an array of the primary key columns will be returned.
|
||||
*
|
||||
* @param array $row resultset row.
|
||||
* @param int $offset The 0-based offset for reading from the resultset row.
|
||||
* @param string $indexType One of the class type constants TableMap::TYPE_PHPNAME, TableMap::TYPE_CAMELNAME
|
||||
* TableMap::TYPE_COLNAME, TableMap::TYPE_FIELDNAME, TableMap::TYPE_NUM
|
||||
*
|
||||
* @return mixed The primary key of the row
|
||||
*/
|
||||
public static function getPrimaryKeyFromRow($row, $offset = 0, $indexType = TableMap::TYPE_NUM)
|
||||
{
|
||||
return (int) $row[
|
||||
$indexType == TableMap::TYPE_NUM
|
||||
? 0 + $offset
|
||||
: self::translateFieldName('Id', TableMap::TYPE_PHPNAME, $indexType)
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* The class that the tableMap will make instances of.
|
||||
*
|
||||
* If $withPrefix is true, the returned path
|
||||
* uses a dot-path notation which is translated into a path
|
||||
* relative to a location on the PHP include_path.
|
||||
* (e.g. path.to.MyClass -> 'path/to/MyClass.php')
|
||||
*
|
||||
* @param boolean $withPrefix Whether or not to return the path with the class name
|
||||
* @return string path.to.ClassName
|
||||
*/
|
||||
public static function getOMClass($withPrefix = true)
|
||||
{
|
||||
return $withPrefix ? BookTableMap::CLASS_DEFAULT : BookTableMap::OM_CLASS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates an object of the default type or an object that inherit from the default.
|
||||
*
|
||||
* @param array $row row returned by DataFetcher->fetch().
|
||||
* @param int $offset The 0-based offset for reading from the resultset row.
|
||||
* @param string $indexType The index type of $row. Mostly DataFetcher->getIndexType().
|
||||
One of the class type constants TableMap::TYPE_PHPNAME, TableMap::TYPE_CAMELNAME
|
||||
* TableMap::TYPE_COLNAME, TableMap::TYPE_FIELDNAME, TableMap::TYPE_NUM.
|
||||
*
|
||||
* @throws PropelException Any exceptions caught during processing will be
|
||||
* rethrown wrapped into a PropelException.
|
||||
* @return array (Book object, last column rank)
|
||||
*/
|
||||
public static function populateObject($row, $offset = 0, $indexType = TableMap::TYPE_NUM)
|
||||
{
|
||||
$key = BookTableMap::getPrimaryKeyHashFromRow($row, $offset, $indexType);
|
||||
if (null !== ($obj = BookTableMap::getInstanceFromPool($key))) {
|
||||
// We no longer rehydrate the object, since this can cause data loss.
|
||||
// See http://www.propelorm.org/ticket/509
|
||||
// $obj->hydrate($row, $offset, true); // rehydrate
|
||||
$col = $offset + BookTableMap::NUM_HYDRATE_COLUMNS;
|
||||
} else {
|
||||
$cls = BookTableMap::OM_CLASS;
|
||||
$obj = new $cls();
|
||||
$col = $obj->hydrate($row, $offset, false, $indexType);
|
||||
BookTableMap::addInstanceToPool($obj, $key);
|
||||
}
|
||||
|
||||
return array($obj, $col);
|
||||
}
|
||||
|
||||
/**
|
||||
* The returned array will contain objects of the default type or
|
||||
* objects that inherit from the default.
|
||||
*
|
||||
* @param DataFetcherInterface $dataFetcher
|
||||
* @return array
|
||||
* @throws PropelException Any exceptions caught during processing will be
|
||||
* rethrown wrapped into a PropelException.
|
||||
*/
|
||||
public static function populateObjects(DataFetcherInterface $dataFetcher)
|
||||
{
|
||||
$results = array();
|
||||
|
||||
// set the class once to avoid overhead in the loop
|
||||
$cls = static::getOMClass(false);
|
||||
// populate the object(s)
|
||||
while ($row = $dataFetcher->fetch()) {
|
||||
$key = BookTableMap::getPrimaryKeyHashFromRow($row, 0, $dataFetcher->getIndexType());
|
||||
if (null !== ($obj = BookTableMap::getInstanceFromPool($key))) {
|
||||
// We no longer rehydrate the object, since this can cause data loss.
|
||||
// See http://www.propelorm.org/ticket/509
|
||||
// $obj->hydrate($row, 0, true); // rehydrate
|
||||
$results[] = $obj;
|
||||
} else {
|
||||
$obj = new $cls();
|
||||
$obj->hydrate($row);
|
||||
$results[] = $obj;
|
||||
BookTableMap::addInstanceToPool($obj, $key);
|
||||
} // if key exists
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
/**
|
||||
* Add all the columns needed to create a new object.
|
||||
*
|
||||
* Note: any columns that were marked with lazyLoad="true" in the
|
||||
* XML schema will not be added to the select list and only loaded
|
||||
* on demand.
|
||||
*
|
||||
* @param Criteria $criteria object containing the columns to add.
|
||||
* @param string $alias optional table alias
|
||||
* @throws PropelException Any exceptions caught during processing will be
|
||||
* rethrown wrapped into a PropelException.
|
||||
*/
|
||||
public static function addSelectColumns(Criteria $criteria, $alias = null)
|
||||
{
|
||||
if (null === $alias) {
|
||||
$criteria->addSelectColumn(BookTableMap::ID);
|
||||
$criteria->addSelectColumn(BookTableMap::NAME);
|
||||
$criteria->addSelectColumn(BookTableMap::SLUG);
|
||||
$criteria->addSelectColumn(BookTableMap::ISBN);
|
||||
$criteria->addSelectColumn(BookTableMap::AUTHOR_ID);
|
||||
} else {
|
||||
$criteria->addSelectColumn($alias . '.ID');
|
||||
$criteria->addSelectColumn($alias . '.NAME');
|
||||
$criteria->addSelectColumn($alias . '.SLUG');
|
||||
$criteria->addSelectColumn($alias . '.ISBN');
|
||||
$criteria->addSelectColumn($alias . '.AUTHOR_ID');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the TableMap related to this object.
|
||||
* This method is not needed for general use but a specific application could have a need.
|
||||
* @return TableMap
|
||||
* @throws PropelException Any exceptions caught during processing will be
|
||||
* rethrown wrapped into a PropelException.
|
||||
*/
|
||||
public static function getTableMap()
|
||||
{
|
||||
return Propel::getServiceContainer()->getDatabaseMap(BookTableMap::DATABASE_NAME)->getTable(BookTableMap::TABLE_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a TableMap instance to the database for this tableMap class.
|
||||
*/
|
||||
public static function buildTableMap()
|
||||
{
|
||||
$dbMap = Propel::getServiceContainer()->getDatabaseMap(BookTableMap::DATABASE_NAME);
|
||||
if (!$dbMap->hasTable(BookTableMap::TABLE_NAME)) {
|
||||
$dbMap->addTableObject(new BookTableMap());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a DELETE on the database, given a Book or Criteria object OR a primary key value.
|
||||
*
|
||||
* @param mixed $values Criteria or Book object or primary key or array of primary keys
|
||||
* which is used to create the DELETE statement
|
||||
* @param ConnectionInterface $con the connection to use
|
||||
* @return int The number of affected rows (if supported by underlying database driver). This includes CASCADE-related rows
|
||||
* if supported by native driver or if emulated using Propel.
|
||||
* @throws PropelException Any exceptions caught during processing will be
|
||||
* rethrown wrapped into a PropelException.
|
||||
*/
|
||||
public static function doDelete($values, ConnectionInterface $con = null)
|
||||
{
|
||||
if (null === $con) {
|
||||
$con = Propel::getServiceContainer()->getWriteConnection(BookTableMap::DATABASE_NAME);
|
||||
}
|
||||
|
||||
if ($values instanceof Criteria) {
|
||||
// rename for clarity
|
||||
$criteria = $values;
|
||||
} elseif ($values instanceof \Acme\DemoBundle\Model\Book) { // it's a model object
|
||||
// create criteria based on pk values
|
||||
$criteria = $values->buildPkeyCriteria();
|
||||
} else { // it's a primary key, or an array of pks
|
||||
$criteria = new Criteria(BookTableMap::DATABASE_NAME);
|
||||
$criteria->add(BookTableMap::ID, (array) $values, Criteria::IN);
|
||||
}
|
||||
|
||||
$query = BookQuery::create()->mergeWith($criteria);
|
||||
|
||||
if ($values instanceof Criteria) { BookTableMap::clearInstancePool();
|
||||
} elseif (!is_object($values)) { // it's a primary key, or an array of pks
|
||||
foreach ((array) $values as $singleval) { BookTableMap::removeInstanceFromPool($singleval);
|
||||
}
|
||||
}
|
||||
|
||||
return $query->delete($con);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all rows from the book table.
|
||||
*
|
||||
* @param ConnectionInterface $con the connection to use
|
||||
* @return int The number of affected rows (if supported by underlying database driver).
|
||||
*/
|
||||
public static function doDeleteAll(ConnectionInterface $con = null)
|
||||
{
|
||||
return BookQuery::create()->doDeleteAll($con);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs an INSERT on the database, given a Book or Criteria object.
|
||||
*
|
||||
* @param mixed $criteria Criteria or Book object containing data that is used to create the INSERT statement.
|
||||
* @param ConnectionInterface $con the ConnectionInterface connection to use
|
||||
* @return mixed The new primary key.
|
||||
* @throws PropelException Any exceptions caught during processing will be
|
||||
* rethrown wrapped into a PropelException.
|
||||
*/
|
||||
public static function doInsert($criteria, ConnectionInterface $con = null)
|
||||
{
|
||||
if (null === $con) {
|
||||
$con = Propel::getServiceContainer()->getWriteConnection(BookTableMap::DATABASE_NAME);
|
||||
}
|
||||
|
||||
if ($criteria instanceof Criteria) {
|
||||
$criteria = clone $criteria; // rename for clarity
|
||||
} else {
|
||||
$criteria = $criteria->buildCriteria(); // build Criteria from Book object
|
||||
}
|
||||
|
||||
if ($criteria->containsKey(BookTableMap::ID) && $criteria->keyContainsValue(BookTableMap::ID) ) {
|
||||
throw new PropelException('Cannot insert a value for auto-increment primary key ('.BookTableMap::ID.')');
|
||||
}
|
||||
|
||||
// Set the correct dbName
|
||||
$query = BookQuery::create()->mergeWith($criteria);
|
||||
|
||||
try {
|
||||
// use transaction because $criteria could contain info
|
||||
// for more than one table (I guess, conceivably)
|
||||
$con->beginTransaction();
|
||||
$pk = $query->doInsert($con);
|
||||
$con->commit();
|
||||
} catch (PropelException $e) {
|
||||
$con->rollBack();
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return $pk;
|
||||
}
|
||||
|
||||
} // BookTableMap
|
||||
// This is the static code needed to register the TableMap for this table with the main Propel class.
|
||||
//
|
||||
BookTableMap::buildTableMap();
|
13
Tests/Fixtures/Model/User.php
Normal file
13
Tests/Fixtures/Model/User.php
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace Propel\PropelBundle\Tests\Fixtures\Model;
|
||||
|
||||
use Propel\PropelBundle\Tests\Fixtures\Model\Base\User as BaseUser;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
|
||||
class User extends BaseUser implements UserInterface
|
||||
{
|
||||
public function eraseCredentials()
|
||||
{
|
||||
}
|
||||
}
|
27
Tests/Fixtures/ReadOnlyItem.php
Normal file
27
Tests/Fixtures/ReadOnlyItem.php
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?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\PropelBundle\Tests\Fixtures;
|
||||
|
||||
use Propel\Runtime\ActiveRecord\ActiveRecordInterface;
|
||||
|
||||
class ReadOnlyItem implements ActiveRecordInterface
|
||||
{
|
||||
public function getName()
|
||||
{
|
||||
return 'Marvin';
|
||||
}
|
||||
|
||||
public function getPrimaryKey()
|
||||
{
|
||||
return 42;
|
||||
}
|
||||
}
|
33
Tests/Fixtures/ReadOnlyItemQuery.php
Normal file
33
Tests/Fixtures/ReadOnlyItemQuery.php
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?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\PropelBundle\Tests\Fixtures;
|
||||
|
||||
use Propel\Runtime\Map\ColumnMap;
|
||||
use Propel\Runtime\Map\TableMap;
|
||||
|
||||
class ReadOnlyItemQuery
|
||||
{
|
||||
public function getTableMap()
|
||||
{
|
||||
// Allows to define methods in this class
|
||||
// to avoid a lot of mock classes
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPrimaryKeys()
|
||||
{
|
||||
$cm = new ColumnMap('id', new TableMap());
|
||||
$cm->setType('INTEGER');
|
||||
|
||||
return array('id' => $cm);
|
||||
}
|
||||
}
|
131
Tests/Fixtures/TranslatableItem.php
Normal file
131
Tests/Fixtures/TranslatableItem.php
Normal file
|
@ -0,0 +1,131 @@
|
|||
<?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\PropelBundle\Tests\Fixtures;
|
||||
|
||||
use Propel\Runtime\ActiveRecord\ActiveRecordInterface;
|
||||
use Propel\Runtime\Connection\ConnectionInterface;
|
||||
|
||||
class TranslatableItem implements ActiveRecordInterface
|
||||
{
|
||||
private $id;
|
||||
|
||||
private $currentTranslations;
|
||||
|
||||
private $groupName;
|
||||
|
||||
private $price;
|
||||
|
||||
public function __construct($id = null, $translations = array())
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->currentTranslations = $translations;
|
||||
}
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setId($id)
|
||||
{
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
public function getGroupName()
|
||||
{
|
||||
return $this->groupName;
|
||||
}
|
||||
|
||||
public function getPrice()
|
||||
{
|
||||
return $this->price;
|
||||
}
|
||||
|
||||
public function getPrimaryKey()
|
||||
{
|
||||
return $this->getId();
|
||||
}
|
||||
|
||||
public function setPrimaryKey($primaryKey)
|
||||
{
|
||||
$this->setId($primaryKey);
|
||||
}
|
||||
|
||||
public function isModified()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function isColumnModified($col)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function isNew()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function setNew($b)
|
||||
{
|
||||
}
|
||||
|
||||
public function resetModified()
|
||||
{
|
||||
}
|
||||
|
||||
public function isDeleted()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function setDeleted($b)
|
||||
{
|
||||
}
|
||||
|
||||
public function delete(ConnectionInterface $con = null)
|
||||
{
|
||||
}
|
||||
|
||||
public function save(ConnectionInterface $con = null)
|
||||
{
|
||||
}
|
||||
|
||||
public function getTranslation($locale = 'de', ConnectionInterface $con = null)
|
||||
{
|
||||
if (!isset($this->currentTranslations[$locale])) {
|
||||
$translation = new TranslatableItemI18n();
|
||||
$translation->setLocale($locale);
|
||||
$this->currentTranslations[$locale] = $translation;
|
||||
}
|
||||
|
||||
return $this->currentTranslations[$locale];
|
||||
}
|
||||
|
||||
public function addTranslatableItemI18n(TranslatableItemI18n $i)
|
||||
{
|
||||
if (!in_array($i, $this->currentTranslations)) {
|
||||
$this->currentTranslations[$i->getLocale()] = $i;
|
||||
$i->setItem($this);
|
||||
}
|
||||
}
|
||||
|
||||
public function removeTranslatableItemI18n(TranslatableItemI18n $i)
|
||||
{
|
||||
unset($this->currentTranslations[$i->getLocale()]);
|
||||
}
|
||||
|
||||
public function getTranslatableItemI18ns()
|
||||
{
|
||||
return $this->currentTranslations;
|
||||
}
|
||||
}
|
138
Tests/Fixtures/TranslatableItemI18n.php
Normal file
138
Tests/Fixtures/TranslatableItemI18n.php
Normal file
|
@ -0,0 +1,138 @@
|
|||
<?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\PropelBundle\Tests\Fixtures;
|
||||
|
||||
use Propel\Runtime\ActiveRecord\ActiveRecordInterface;
|
||||
use Propel\Runtime\Connection\ConnectionInterface;
|
||||
|
||||
class TranslatableItemI18n implements ActiveRecordInterface
|
||||
{
|
||||
private $id;
|
||||
|
||||
private $locale;
|
||||
|
||||
private $value;
|
||||
|
||||
private $value2;
|
||||
|
||||
private $item;
|
||||
|
||||
public function __construct($id = null, $locale = null, $value = null)
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->locale = $locale;
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setId($id)
|
||||
{
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
public function getPrimaryKey()
|
||||
{
|
||||
return $this->getId();
|
||||
}
|
||||
|
||||
public function setPrimaryKey($primaryKey)
|
||||
{
|
||||
$this->setId($primaryKey);
|
||||
}
|
||||
|
||||
public function isModified()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function isColumnModified($col)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function isNew()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function setNew($b)
|
||||
{
|
||||
}
|
||||
|
||||
public function resetModified()
|
||||
{
|
||||
}
|
||||
|
||||
public function isDeleted()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function setDeleted($b)
|
||||
{
|
||||
}
|
||||
|
||||
public function delete(ConnectionInterface $con = null)
|
||||
{
|
||||
}
|
||||
|
||||
public function save(ConnectionInterface $con = null)
|
||||
{
|
||||
}
|
||||
|
||||
public function setLocale($locale)
|
||||
{
|
||||
|
||||
$this->locale = $locale;
|
||||
}
|
||||
|
||||
public function getLocale()
|
||||
{
|
||||
return $this->locale;
|
||||
}
|
||||
|
||||
public function getItem()
|
||||
{
|
||||
return $this->item;
|
||||
}
|
||||
|
||||
public function setItem($item)
|
||||
{
|
||||
$this->item = $item;
|
||||
}
|
||||
|
||||
public function setValue($value)
|
||||
{
|
||||
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
public function getValue()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function setValue2($value2)
|
||||
{
|
||||
|
||||
$this->value2 = $value2;
|
||||
}
|
||||
|
||||
public function getValue2()
|
||||
{
|
||||
return $this->value2;
|
||||
}
|
||||
}
|
215
Tests/Form/ChoiceList/ModelChoiceListTest.php
Normal file
215
Tests/Form/ChoiceList/ModelChoiceListTest.php
Normal file
|
@ -0,0 +1,215 @@
|
|||
<?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\PropelBundle\Tests\Form\ChoiceList;
|
||||
|
||||
use Propel\PropelBundle\Form\ChoiceList\ModelChoiceList;
|
||||
use Propel\PropelBundle\Tests\Fixtures\Item;
|
||||
use Propel\PropelBundle\Tests\Fixtures\ReadOnlyItem;
|
||||
use Propel\PropelBundle\Tests\TestCase;
|
||||
|
||||
use Symfony\Component\Form\Extension\Core\View\ChoiceView;
|
||||
|
||||
class ModelChoiceListTest extends TestCase
|
||||
{
|
||||
const ITEM_CLASS = '\Propel\PropelBundle\Tests\Fixtures\Item';
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
if (!class_exists('Symfony\Component\Form\Form')) {
|
||||
$this->markTestSkipped('The "Form" component is not available');
|
||||
}
|
||||
|
||||
if (!class_exists('Symfony\Component\PropertyAccess\PropertyAccessor')) {
|
||||
$this->markTestSkipped('The "PropertyAccessor" component is not available');
|
||||
}
|
||||
}
|
||||
|
||||
public function testEmptyChoicesReturnsEmpty()
|
||||
{
|
||||
$choiceList = new ModelChoiceList(
|
||||
self::ITEM_CLASS,
|
||||
'value',
|
||||
array()
|
||||
);
|
||||
|
||||
$this->assertSame(array(), $choiceList->getChoices());
|
||||
}
|
||||
|
||||
public function testReadOnlyIsValidChoice()
|
||||
{
|
||||
$item = new ReadOnlyItem();
|
||||
$choiceList = new ModelChoiceList(
|
||||
'\Propel\PropelBundle\Tests\Fixtures\ReadOnlyItem',
|
||||
'name',
|
||||
array(
|
||||
$item,
|
||||
)
|
||||
);
|
||||
|
||||
$this->assertSame(array(42 => $item), $choiceList->getChoices());
|
||||
}
|
||||
|
||||
public function testFlattenedChoices()
|
||||
{
|
||||
$item1 = new Item(1, 'Foo');
|
||||
$item2 = new Item(2, 'Bar');
|
||||
|
||||
$choiceList = new ModelChoiceList(
|
||||
self::ITEM_CLASS,
|
||||
'value',
|
||||
array(
|
||||
$item1,
|
||||
$item2,
|
||||
)
|
||||
);
|
||||
|
||||
$this->assertSame(array(1 => $item1, 2 => $item2), $choiceList->getChoices());
|
||||
}
|
||||
|
||||
public function testFlattenedPreferredChoices()
|
||||
{
|
||||
$item1 = new Item(1, 'Foo');
|
||||
$item2 = new Item(2, 'Bar');
|
||||
|
||||
$choiceList = new ModelChoiceList(
|
||||
self::ITEM_CLASS,
|
||||
'value',
|
||||
array(
|
||||
$item1,
|
||||
$item2,
|
||||
),
|
||||
null,
|
||||
null,
|
||||
array(
|
||||
$item1
|
||||
)
|
||||
);
|
||||
|
||||
$this->assertSame(array(1 => $item1, 2 => $item2), $choiceList->getChoices());
|
||||
$this->assertEquals(array(1 => new ChoiceView($item1, '1', 'Foo')), $choiceList->getPreferredViews());
|
||||
}
|
||||
|
||||
public function testNestedChoices()
|
||||
{
|
||||
$item1 = new Item(1, 'Foo');
|
||||
$item2 = new Item(2, 'Bar');
|
||||
|
||||
$choiceList = new ModelChoiceList(
|
||||
self::ITEM_CLASS,
|
||||
'value',
|
||||
array(
|
||||
'group1' => array($item1),
|
||||
'group2' => array($item2),
|
||||
)
|
||||
);
|
||||
|
||||
$this->assertSame(array(1 => $item1, 2 => $item2), $choiceList->getChoices());
|
||||
$this->assertEquals(array(
|
||||
'group1' => array(1 => new ChoiceView($item1, '1', 'Foo')),
|
||||
'group2' => array(2 => new ChoiceView($item2, '2', 'Bar'))
|
||||
), $choiceList->getRemainingViews());
|
||||
}
|
||||
|
||||
public function testGroupBySupportsString()
|
||||
{
|
||||
$item1 = new Item(1, 'Foo', 'Group1');
|
||||
$item2 = new Item(2, 'Bar', 'Group1');
|
||||
$item3 = new Item(3, 'Baz', 'Group2');
|
||||
$item4 = new Item(4, 'Boo!', null);
|
||||
|
||||
$choiceList = new ModelChoiceList(
|
||||
self::ITEM_CLASS,
|
||||
'value',
|
||||
array(
|
||||
$item1,
|
||||
$item2,
|
||||
$item3,
|
||||
$item4,
|
||||
),
|
||||
null,
|
||||
'groupName'
|
||||
);
|
||||
|
||||
$this->assertEquals(array(1 => $item1, 2 => $item2, 3 => $item3, 4 => $item4), $choiceList->getChoices());
|
||||
$this->assertEquals(array(
|
||||
'Group1' => array(1 => new ChoiceView($item1, '1', 'Foo'), 2 => new ChoiceView($item2, '2', 'Bar')),
|
||||
'Group2' => array(3 => new ChoiceView($item3, '3', 'Baz')),
|
||||
4 => new ChoiceView($item4, '4', 'Boo!')
|
||||
), $choiceList->getRemainingViews());
|
||||
}
|
||||
|
||||
public function testGroupByInvalidPropertyPathReturnsFlatChoices()
|
||||
{
|
||||
$item1 = new Item(1, 'Foo', 'Group1');
|
||||
$item2 = new Item(2, 'Bar', 'Group1');
|
||||
|
||||
$choiceList = new ModelChoiceList(
|
||||
self::ITEM_CLASS,
|
||||
'value',
|
||||
array(
|
||||
$item1,
|
||||
$item2,
|
||||
),
|
||||
null,
|
||||
'child.that.does.not.exist'
|
||||
);
|
||||
|
||||
$this->assertEquals(array(
|
||||
1 => $item1,
|
||||
2 => $item2
|
||||
), $choiceList->getChoices());
|
||||
}
|
||||
|
||||
public function testGetValuesForChoices()
|
||||
{
|
||||
$item1 = new Item(1, 'Foo');
|
||||
$item2 = new Item(2, 'Bar');
|
||||
|
||||
$choiceList = new ModelChoiceList(
|
||||
self::ITEM_CLASS,
|
||||
'value',
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
$this->assertEquals(array(1, 2), $choiceList->getValuesForChoices(array($item1, $item2)));
|
||||
$this->assertEquals(array(1, 2), $choiceList->getIndicesForChoices(array($item1, $item2)));
|
||||
}
|
||||
|
||||
public function testDifferentEqualObjectsAreChoosen()
|
||||
{
|
||||
$item = new Item(1, 'Foo');
|
||||
$choiceList = new ModelChoiceList(
|
||||
self::ITEM_CLASS,
|
||||
'value',
|
||||
array($item)
|
||||
);
|
||||
|
||||
$choosenItem = new Item(1, 'Foo');
|
||||
|
||||
$this->assertEquals(array(1), $choiceList->getIndicesForChoices(array($choosenItem)));
|
||||
}
|
||||
|
||||
public function testGetIndicesForNullChoices()
|
||||
{
|
||||
$item = new Item(1, 'Foo');
|
||||
$choiceList = new ModelChoiceList(
|
||||
self::ITEM_CLASS,
|
||||
'value',
|
||||
array($item)
|
||||
);
|
||||
|
||||
$this->assertEquals(array(), $choiceList->getIndicesForChoices(array(null)));
|
||||
}
|
||||
}
|
111
Tests/Form/DataTransformer/CollectionToArrayTransformerTest.php
Normal file
111
Tests/Form/DataTransformer/CollectionToArrayTransformerTest.php
Normal file
|
@ -0,0 +1,111 @@
|
|||
<?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\PropelBundle\Tests\Form\Form\DataTransformer;
|
||||
|
||||
use Propel\Runtime\Collection\ObjectCollection;
|
||||
use Propel\PropelBundle\Tests\TestCase;
|
||||
use Propel\PropelBundle\Form\DataTransformer\CollectionToArrayTransformer;
|
||||
|
||||
class CollectionToArrayTransformerTest extends TestCase
|
||||
{
|
||||
private $transformer;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
if (!class_exists('Symfony\Component\Form\Form')) {
|
||||
$this->markTestSkipped('The "Form" component is not available');
|
||||
}
|
||||
|
||||
parent::setUp();
|
||||
|
||||
$this->transformer = new CollectionToArrayTransformer();
|
||||
}
|
||||
|
||||
public function testTransform()
|
||||
{
|
||||
$result = $this->transformer->transform(new ObjectCollection());
|
||||
|
||||
$this->assertTrue(is_array($result));
|
||||
$this->assertEquals(0, count($result));
|
||||
}
|
||||
|
||||
public function testTransformWithNull()
|
||||
{
|
||||
$result = $this->transformer->transform(null);
|
||||
|
||||
$this->assertTrue(is_array($result));
|
||||
$this->assertEquals(0, count($result));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Form\Exception\TransformationFailedException
|
||||
*/
|
||||
public function testTransformThrowsExceptionIfNotObjectCollection()
|
||||
{
|
||||
$this->transformer->transform(new DummyObject());
|
||||
}
|
||||
|
||||
public function testTransformWithData()
|
||||
{
|
||||
$coll = new ObjectCollection();
|
||||
$coll->setData(array('foo', 'bar'));
|
||||
|
||||
$result = $this->transformer->transform($coll);
|
||||
|
||||
$this->assertTrue(is_array($result));
|
||||
$this->assertEquals(2, count($result));
|
||||
$this->assertEquals('foo', $result[0]);
|
||||
$this->assertEquals('bar', $result[1]);
|
||||
}
|
||||
|
||||
public function testReverseTransformWithNull()
|
||||
{
|
||||
$result = $this->transformer->reverseTransform(null);
|
||||
|
||||
$this->assertInstanceOf('\Propel\Runtime\Collection\ObjectCollection', $result);
|
||||
$this->assertEquals(0, count($result->getData()));
|
||||
}
|
||||
|
||||
public function testReverseTransformWithEmptyString()
|
||||
{
|
||||
$result = $this->transformer->reverseTransform('');
|
||||
|
||||
$this->assertInstanceOf('\Propel\Runtime\Collection\ObjectCollection', $result);
|
||||
$this->assertEquals(0, count($result->getData()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Form\Exception\TransformationFailedException
|
||||
*/
|
||||
public function testReverseTransformThrowsExceptionIfNotArray()
|
||||
{
|
||||
$this->transformer->reverseTransform(new DummyObject());
|
||||
}
|
||||
|
||||
public function testReverseTransformWithData()
|
||||
{
|
||||
$inputData = array('foo', 'bar');
|
||||
|
||||
$result = $this->transformer->reverseTransform($inputData);
|
||||
$data = $result->getData();
|
||||
|
||||
$this->assertInstanceOf('\Propel\Runtime\Collection\ObjectCollection', $result);
|
||||
|
||||
$this->assertTrue(is_array($data));
|
||||
$this->assertEquals(2, count($data));
|
||||
$this->assertEquals('foo', $data[0]);
|
||||
$this->assertEquals('bar', $data[1]);
|
||||
$this->assertsame($inputData, $data);
|
||||
}
|
||||
}
|
||||
|
||||
class DummyObject {}
|
158
Tests/Form/Type/TranslationCollectionTypeTest.php
Normal file
158
Tests/Form/Type/TranslationCollectionTypeTest.php
Normal file
|
@ -0,0 +1,158 @@
|
|||
<?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\PropelBundle\Tests\Form\Form\Type;
|
||||
|
||||
use Propel\PropelBundle\Tests\Fixtures\Item;
|
||||
use Propel\PropelBundle\Form\PropelExtension;
|
||||
use Propel\PropelBundle\Tests\Fixtures\TranslatableItemI18n;
|
||||
use Propel\PropelBundle\Tests\Fixtures\TranslatableItem;
|
||||
use Symfony\Component\Form\Tests\Extension\Core\Type\TypeTestCase;
|
||||
|
||||
class TranslationCollectionTypeTest extends TypeTestCase
|
||||
{
|
||||
const TRANSLATION_CLASS = 'Propel\PropelBundle\Tests\Fixtures\TranslatableItem';
|
||||
const TRANSLATABLE_I18N_CLASS = 'Propel\PropelBundle\Tests\Fixtures\TranslatableItemI18n';
|
||||
const NON_TRANSLATION_CLASS = 'Propel\PropelBundle\Tests\Fixtures\Item';
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
protected function getExtensions()
|
||||
{
|
||||
return array_merge(parent::getExtensions(), array(
|
||||
new PropelExtension(),
|
||||
));
|
||||
}
|
||||
|
||||
public function testTranslationsAdded()
|
||||
{
|
||||
$item = new TranslatableItem();
|
||||
$item->addTranslatableItemI18n(new TranslatableItemI18n(1, 'fr', 'val1'));
|
||||
$item->addTranslatableItemI18n(new TranslatableItemI18n(2, 'en', 'val2'));
|
||||
|
||||
$builder = $this->factory->createBuilder('form', null, array(
|
||||
'data_class' => self::TRANSLATION_CLASS
|
||||
));
|
||||
|
||||
$builder->add('translatableItemI18ns', 'propel_translation_collection', array(
|
||||
'languages' => array('en', 'fr'),
|
||||
'options' => array(
|
||||
'data_class' => self::TRANSLATABLE_I18N_CLASS,
|
||||
'columns' => array('value', 'value2' => array('label' => 'Label', 'type' => 'textarea'))
|
||||
)
|
||||
));
|
||||
$form = $builder->getForm();
|
||||
$form->setData($item);
|
||||
$translations = $form->get('translatableItemI18ns');
|
||||
|
||||
$this->assertCount(2, $translations);
|
||||
$this->assertInstanceOf('Symfony\Component\Form\Form', $translations['en']);
|
||||
$this->assertInstanceOf('Symfony\Component\Form\Form', $translations['fr']);
|
||||
|
||||
$this->assertInstanceOf(self::TRANSLATABLE_I18N_CLASS, $translations['en']->getData());
|
||||
$this->assertInstanceOf(self::TRANSLATABLE_I18N_CLASS, $translations['fr']->getData());
|
||||
|
||||
$this->assertEquals($item->getTranslation('en'), $translations['en']->getData());
|
||||
$this->assertEquals($item->getTranslation('fr'), $translations['fr']->getData());
|
||||
|
||||
$columnOptions = $translations['fr']->getConfig()->getOption('columns');
|
||||
$this->assertEquals('value', $columnOptions[0]);
|
||||
$this->assertEquals('textarea', $columnOptions['value2']['type']);
|
||||
$this->assertEquals('Label', $columnOptions['value2']['label']);
|
||||
}
|
||||
|
||||
public function testNotPresentTranslationsAdded()
|
||||
{
|
||||
$item = new TranslatableItem();
|
||||
|
||||
$this->assertCount(0, $item->getTranslatableItemI18ns());
|
||||
|
||||
$builder = $this->factory->createBuilder('form', null, array(
|
||||
'data_class' => self::TRANSLATION_CLASS
|
||||
));
|
||||
$builder->add('translatableItemI18ns', 'propel_translation_collection', array(
|
||||
'languages' => array('en', 'fr'),
|
||||
'options' => array(
|
||||
'data_class' => self::TRANSLATABLE_I18N_CLASS,
|
||||
'columns' => array('value', 'value2' => array('label' => 'Label', 'type' => 'textarea'))
|
||||
)
|
||||
));
|
||||
|
||||
$form = $builder->getForm();
|
||||
$form->setData($item);
|
||||
|
||||
$this->assertCount(2, $item->getTranslatableItemI18ns());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException
|
||||
*/
|
||||
public function testNoArrayGiven()
|
||||
{
|
||||
$item = new Item(null, 'val');
|
||||
|
||||
$builder = $this->factory->createBuilder('form', null, array(
|
||||
'data_class' => self::NON_TRANSLATION_CLASS
|
||||
));
|
||||
$builder->add('value', 'propel_translation_collection', array(
|
||||
'languages' => array('en', 'fr'),
|
||||
'options' => array(
|
||||
'data_class' => self::TRANSLATABLE_I18N_CLASS,
|
||||
'columns' => array('value', 'value2' => array('label' => 'Label', 'type' => 'textarea'))
|
||||
)
|
||||
));
|
||||
|
||||
$form = $builder->getForm();
|
||||
$form->setData($item);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\OptionsResolver\Exception\MissingOptionsException
|
||||
*/
|
||||
public function testNoDataClassAdded()
|
||||
{
|
||||
$this->factory->createNamed('itemI18ns', 'propel_translation_collection', null, array(
|
||||
'languages' => array('en', 'fr'),
|
||||
'options' => array(
|
||||
'columns' => array('value', 'value2')
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\OptionsResolver\Exception\MissingOptionsException
|
||||
*/
|
||||
public function testNoLanguagesAdded()
|
||||
{
|
||||
$this->factory->createNamed('itemI18ns', 'propel_translation_collection', null, array(
|
||||
'options' => array(
|
||||
'data_class' => self::TRANSLATABLE_I18N_CLASS,
|
||||
'columns' => array('value', 'value2')
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\OptionsResolver\Exception\MissingOptionsException
|
||||
*/
|
||||
public function testNoColumnsAdded()
|
||||
{
|
||||
$this->factory->createNamed('itemI18ns', 'propel_translation_collection', null, array(
|
||||
'languages' => array('en', 'fr'),
|
||||
'options' => array(
|
||||
'data_class' => self::TRANSLATABLE_I18N_CLASS
|
||||
)
|
||||
));
|
||||
}
|
||||
}
|
129
Tests/Form/TypeGuesserTest.php
Normal file
129
Tests/Form/TypeGuesserTest.php
Normal file
|
@ -0,0 +1,129 @@
|
|||
<?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\PropelBundle\Tests\Form;
|
||||
|
||||
use Propel\PropelBundle\Form\TypeGuesser;
|
||||
use Propel\PropelBundle\Tests\TestCase;
|
||||
use Symfony\Component\Form\Guess\Guess;
|
||||
|
||||
class TypeGuesserTest extends TestCase
|
||||
{
|
||||
const CLASS_NAME = 'Propel\PropelBundle\Tests\Fixtures\Item';
|
||||
|
||||
const UNKNOWN_CLASS_NAME = 'Propel\PropelBundle\Tests\Fixtures\UnknownItem';
|
||||
|
||||
private $guesser;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->guesser = new TypeGuesser();
|
||||
}
|
||||
|
||||
public function testGuessMaxLengthWithText()
|
||||
{
|
||||
$value = $this->guesser->guessMaxLength(self::CLASS_NAME, 'value');
|
||||
|
||||
$this->assertNotNull($value);
|
||||
$this->assertEquals(255, $value->getValue());
|
||||
}
|
||||
|
||||
public function testGuessMaxLengthWithFloat()
|
||||
{
|
||||
$value = $this->guesser->guessMaxLength(self::CLASS_NAME, 'price');
|
||||
|
||||
$this->assertNotNull($value);
|
||||
$this->assertNull($value->getValue());
|
||||
}
|
||||
|
||||
public function testGuessMinLengthWithText()
|
||||
{
|
||||
$value = $this->guesser->guessPattern(self::CLASS_NAME, 'value');
|
||||
|
||||
$this->assertNull($value);
|
||||
}
|
||||
|
||||
public function testGuessMinLengthWithFloat()
|
||||
{
|
||||
$value = $this->guesser->guessPattern(self::CLASS_NAME, 'price');
|
||||
|
||||
$this->assertNotNull($value);
|
||||
$this->assertNull($value->getValue());
|
||||
}
|
||||
|
||||
public function testGuessRequired()
|
||||
{
|
||||
$value = $this->guesser->guessRequired(self::CLASS_NAME, 'id');
|
||||
|
||||
$this->assertNotNull($value);
|
||||
$this->assertTrue($value->getValue());
|
||||
}
|
||||
|
||||
public function testGuessRequiredWithNullableColumn()
|
||||
{
|
||||
$value = $this->guesser->guessRequired(self::CLASS_NAME, 'value');
|
||||
|
||||
$this->assertNotNull($value);
|
||||
$this->assertFalse($value->getValue());
|
||||
}
|
||||
|
||||
public function testGuessTypeWithoutTable()
|
||||
{
|
||||
$value = $this->guesser->guessType(self::UNKNOWN_CLASS_NAME, 'property');
|
||||
|
||||
$this->assertNotNull($value);
|
||||
$this->assertEquals('text', $value->getType());
|
||||
$this->assertEquals(Guess::LOW_CONFIDENCE, $value->getConfidence());
|
||||
}
|
||||
|
||||
public function testGuessTypeWithoutColumn()
|
||||
{
|
||||
$value = $this->guesser->guessType(self::CLASS_NAME, 'property');
|
||||
|
||||
$this->assertNotNull($value);
|
||||
$this->assertEquals('text', $value->getType());
|
||||
$this->assertEquals(Guess::LOW_CONFIDENCE, $value->getConfidence());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataProviderForGuessType
|
||||
*/
|
||||
public function testGuessType($property, $type, $confidence, $multiple = null)
|
||||
{
|
||||
$value = $this->guesser->guessType(self::CLASS_NAME, $property);
|
||||
|
||||
$this->assertNotNull($value);
|
||||
$this->assertEquals($type, $value->getType());
|
||||
$this->assertEquals($confidence, $value->getConfidence());
|
||||
|
||||
if ($type === 'model') {
|
||||
$options = $value->getOptions();
|
||||
|
||||
$this->assertSame($multiple, $options['multiple']);
|
||||
}
|
||||
}
|
||||
|
||||
public static function dataProviderForGuessType()
|
||||
{
|
||||
return array(
|
||||
array('is_active', 'checkbox', Guess::HIGH_CONFIDENCE),
|
||||
array('enabled', 'checkbox', Guess::HIGH_CONFIDENCE),
|
||||
array('id', 'integer', Guess::MEDIUM_CONFIDENCE),
|
||||
array('value', 'text', Guess::MEDIUM_CONFIDENCE),
|
||||
array('price', 'number', Guess::MEDIUM_CONFIDENCE),
|
||||
array('updated_at', 'datetime', Guess::HIGH_CONFIDENCE),
|
||||
|
||||
array('Authors', 'model', Guess::HIGH_CONFIDENCE, true),
|
||||
array('Resellers', 'model', Guess::HIGH_CONFIDENCE, true),
|
||||
array('MainAuthor', 'model', Guess::HIGH_CONFIDENCE, false),
|
||||
);
|
||||
}
|
||||
}
|
39
Tests/Model/AclClassTest.php
Normal file
39
Tests/Model/AclClassTest.php
Normal file
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the PropelBundle package.
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @license MIT License
|
||||
*/
|
||||
|
||||
namespace Propel\PropelBundle\Tests\Model\Acl;
|
||||
|
||||
use Propel\PropelBundle\Model\Acl\AclClass;
|
||||
use Propel\PropelBundle\Model\Acl\AclClassQuery;
|
||||
|
||||
use Symfony\Component\Security\Acl\Domain\ObjectIdentity;
|
||||
|
||||
use Propel\PropelBundle\Tests\AclTestCase;
|
||||
|
||||
/**
|
||||
* @author Toni Uebernickel <tuebernickel@gmail.com>
|
||||
*/
|
||||
class AclClassTest extends AclTestCase
|
||||
{
|
||||
public function testFromAclObjectIdentity()
|
||||
{
|
||||
$type = 'Merchant';
|
||||
|
||||
$aclClass = AclClass::fromAclObjectIdentity(new ObjectIdentity(5, $type), $this->con);
|
||||
$this->assertInstanceOf('Propel\PropelBundle\Model\Acl\AclClass', $aclClass);
|
||||
$this->assertEquals($type, $aclClass->getType());
|
||||
|
||||
$dbEntry = AclClassQuery::create()->findOne($this->con);
|
||||
$this->assertInstanceOf('Propel\PropelBundle\Model\Acl\AclClass', $dbEntry);
|
||||
$this->assertEquals($type, $dbEntry->getType());
|
||||
|
||||
$this->assertEquals($dbEntry->getId(), $aclClass->getId());
|
||||
}
|
||||
}
|
135
Tests/Model/EntryQueryTest.php
Normal file
135
Tests/Model/EntryQueryTest.php
Normal file
|
@ -0,0 +1,135 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the PropelBundle package.
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @license MIT License
|
||||
*/
|
||||
|
||||
namespace Propel\PropelBundle\Tests\Model\Acl;
|
||||
|
||||
use Propel\PropelBundle\Model\Acl\Entry;
|
||||
use Propel\PropelBundle\Model\Acl\EntryQuery;
|
||||
use Propel\PropelBundle\Model\Acl\SecurityIdentity;
|
||||
|
||||
use Symfony\Component\Security\Acl\Domain\ObjectIdentity;
|
||||
|
||||
use Propel\PropelBundle\Tests\AclTestCase;
|
||||
|
||||
/**
|
||||
* @author Toni Uebernickel <tuebernickel@gmail.com>
|
||||
*/
|
||||
class EntryQueryTest extends AclTestCase
|
||||
{
|
||||
public function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$obj = $this->createModelObjectIdentity(1);
|
||||
$entry = $this->createEntry();
|
||||
$entry
|
||||
->setSecurityIdentity(SecurityIdentity::fromAclIdentity($this->getRoleSecurityIdentity('ROLE_USER')))
|
||||
->setAclClass($obj->getAclClass())
|
||||
->setMask(64)
|
||||
;
|
||||
$obj->addEntry($entry)->save($this->con);
|
||||
}
|
||||
|
||||
public function testFindByAclIdentityInvalidSecurityIdentity()
|
||||
{
|
||||
$this->setExpectedException('InvalidArgumentException');
|
||||
EntryQuery::create()->findByAclIdentity($this->getAclObjectIdentity(), array('foo'), $this->con);
|
||||
}
|
||||
|
||||
public function testFindByAclIdentityInvalidSecurityIdentityObject()
|
||||
{
|
||||
$this->setExpectedException('InvalidArgumentException');
|
||||
EntryQuery::create()->findByAclIdentity($this->getAclObjectIdentity(), array(new \stdClass()), $this->con);
|
||||
}
|
||||
|
||||
public function testFindByAclIdentityNotExists()
|
||||
{
|
||||
$this->assertCount(0, EntryQuery::create()->findByAclIdentity($this->getAclObjectIdentity(2), array(), $this->con));
|
||||
}
|
||||
|
||||
public function testFindByAclIdentitySecurityIdentityNotFound()
|
||||
{
|
||||
$this->assertCount(0, EntryQuery::create()->findByAclIdentity($this->getAclObjectIdentity(1), array($this->getRoleSecurityIdentity('ROLE_ADMIN')), $this->con));
|
||||
}
|
||||
|
||||
public function testFindByAclIdentity()
|
||||
{
|
||||
// Another Entry, should not be found (different ObjectIdentity).
|
||||
$obj = $this->createModelObjectIdentity(2);
|
||||
$entry = $this->createEntry();
|
||||
$entry
|
||||
->setSecurityIdentity(SecurityIdentity::fromAclIdentity($this->getRoleSecurityIdentity()))
|
||||
->setAclClass($obj->getAclClass())
|
||||
->setMask(64)
|
||||
;
|
||||
$obj->addEntry($entry)->save($this->con);
|
||||
|
||||
$entries = EntryQuery::create()->findByAclIdentity($this->getAclObjectIdentity(1), array(), $this->con);
|
||||
$this->assertCount(1, $entries);
|
||||
$this->assertEquals(1, $entries[0]->getObjectIdentityId());
|
||||
|
||||
// A class based entry for the wrong ObjectIdentity.
|
||||
$classEntry = $this->createEntry();
|
||||
$classEntry
|
||||
->setObjectIdentityId(2)
|
||||
->setSecurityIdentity(SecurityIdentity::fromAclIdentity($this->getRoleSecurityIdentity()))
|
||||
->setAclClass($obj->getAclClass())
|
||||
->setMask(64)
|
||||
->save($this->con)
|
||||
;
|
||||
|
||||
// A class based entry for the correct ObjectIdentity.
|
||||
$classEntry = $this->createEntry();
|
||||
$classEntry
|
||||
->setObjectIdentityId(null)
|
||||
->setSecurityIdentity(SecurityIdentity::fromAclIdentity($this->getRoleSecurityIdentity()))
|
||||
->setAclClass($this->getAclClass())
|
||||
->setMask(64)
|
||||
->save($this->con)
|
||||
;
|
||||
|
||||
$this->assertEquals(4, EntryQuery::create()->count($this->con));
|
||||
|
||||
$entries = EntryQuery::create()->findByAclIdentity($this->getAclObjectIdentity(1), array(), $this->con);
|
||||
$this->assertCount(2, $entries);
|
||||
$this->assertEquals($obj->getClassId(), $entries[0]->getClassId());
|
||||
$this->assertEquals($obj->getClassId(), $entries[1]->getClassId());
|
||||
}
|
||||
|
||||
public function testFindByAclIdentityFilterSecurityIdentity()
|
||||
{
|
||||
// Another Entry, should not be found (different SecurityIdentity).
|
||||
$entry = $this->createEntry();
|
||||
$entry
|
||||
->setObjectIdentityId(1)
|
||||
->setSecurityIdentity(SecurityIdentity::fromAclIdentity($this->getRoleSecurityIdentity('ROLE_ADMIN')))
|
||||
->setAclClass($this->getAclClass())
|
||||
->setMask(64)
|
||||
->save($this->con)
|
||||
;
|
||||
|
||||
$this->assertEquals(2, EntryQuery::create()->count($this->con));
|
||||
|
||||
$entries = EntryQuery::create()->findByAclIdentity($this->getAclObjectIdentity(1), array($this->getRoleSecurityIdentity('ROLE_USER')), $this->con);
|
||||
$this->assertCount(1, $entries);
|
||||
$this->assertEquals(SecurityIdentity::fromAclIdentity($this->getRoleSecurityIdentity('ROLE_USER'))->getId(), $entries[0]->getSecurityIdentityId());
|
||||
}
|
||||
|
||||
public function testFindByAclIdentityOnlyClassEntries()
|
||||
{
|
||||
$this->assertEquals(1, EntryQuery::create()->count($this->con));
|
||||
EntryQuery::create()->findOne($this->con)
|
||||
->setObjectIdentity(null)
|
||||
->save($this->con);
|
||||
|
||||
$entries = EntryQuery::create()->findByAclIdentity($this->getAclObjectIdentity(1), array(), $this->con);
|
||||
$this->assertCount(1, $entries);
|
||||
}
|
||||
}
|
88
Tests/Model/EntryTest.php
Normal file
88
Tests/Model/EntryTest.php
Normal file
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the PropelBundle package.
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @license MIT License
|
||||
*/
|
||||
|
||||
namespace Propel\PropelBundle\Tests\Model\Acl;
|
||||
|
||||
use Propel\PropelBundle\Model\Acl\Entry as ModelEntry;
|
||||
use Propel\PropelBundle\Model\Acl\SecurityIdentity;
|
||||
|
||||
use Propel\PropelBundle\Security\Acl\Domain\Entry as AclEntry;
|
||||
|
||||
use Propel\PropelBundle\Tests\AclTestCase;
|
||||
|
||||
/**
|
||||
* @author Toni Uebernickel <tuebernickel@gmail.com>
|
||||
*/
|
||||
class EntryTest extends AclTestCase
|
||||
{
|
||||
public function testToAclEntry()
|
||||
{
|
||||
$acl = $this->getMock('Propel\PropelBundle\Security\Acl\Domain\AuditableAcl', array(), array(), '', false, false);
|
||||
$entry = $this->createModelEntry();
|
||||
|
||||
$aclEntry = ModelEntry::toAclEntry($entry, $acl);
|
||||
$this->assertInstanceOf('Propel\PropelBundle\Security\Acl\Domain\Entry', $aclEntry);
|
||||
$this->assertSame($acl, $aclEntry->getAcl());
|
||||
$this->assertEquals(42, $aclEntry->getId());
|
||||
$this->assertTrue($aclEntry->isAuditFailure());
|
||||
$this->assertFalse($aclEntry->isAuditSuccess());
|
||||
$this->assertEquals('all', $aclEntry->getStrategy());
|
||||
$this->assertTrue($aclEntry->isGranting());
|
||||
$this->assertEquals(64, $aclEntry->getMask());
|
||||
|
||||
return $aclEntry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testToAclEntry
|
||||
*/
|
||||
public function testToAclEntryFieldEntry()
|
||||
{
|
||||
$acl = $this->getMock('Propel\PropelBundle\Security\Acl\Domain\AuditableAcl', array(), array(), '', false, false);
|
||||
$entry = $this->createModelEntry();
|
||||
$entry->setFieldName('name');
|
||||
|
||||
$aclEntry = ModelEntry::toAclEntry($entry, $acl);
|
||||
$this->assertInstanceOf('Propel\PropelBundle\Security\Acl\Domain\FieldEntry', $aclEntry);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testToAclEntry
|
||||
*/
|
||||
public function testFromAclEntry($aclEntry)
|
||||
{
|
||||
$modelEntry = ModelEntry::fromAclEntry($aclEntry);
|
||||
|
||||
$this->assertInstanceOf('Propel\PropelBundle\Model\Acl\Entry', $modelEntry);
|
||||
$this->assertEquals(42, $modelEntry->getId());
|
||||
$this->assertTrue($modelEntry->getAuditFailure());
|
||||
$this->assertFalse($modelEntry->getAuditSuccess());
|
||||
$this->assertEquals('all', $modelEntry->getGrantingStrategy());
|
||||
$this->assertTrue($modelEntry->getGranting());
|
||||
$this->assertEquals(64, $modelEntry->getMask());
|
||||
}
|
||||
|
||||
protected function createModelEntry()
|
||||
{
|
||||
$entry = new ModelEntry();
|
||||
$entry
|
||||
->setId(42)
|
||||
->setAclClass($this->getAclClass())
|
||||
->setSecurityIdentity(SecurityIdentity::fromAclIdentity($this->getRoleSecurityIdentity()))
|
||||
->setAuditFailure(true)
|
||||
->setAuditSuccess(false)
|
||||
->setGrantingStrategy('all')
|
||||
->setGranting(true)
|
||||
->setMask(64)
|
||||
;
|
||||
|
||||
return $entry;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue