Started to implement the FixturesLoader command (only yaml works for now)
This commit is contained in:
parent
f16c92580f
commit
fcbbff355c
338
Command/FixturesLoadCommand.php
Normal file
338
Command/FixturesLoadCommand.php
Normal file
|
@ -0,0 +1,338 @@
|
|||
<?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\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;
|
||||
|
||||
use Propel\PropelBundle\DataFixtures\Loader\YamlDataLoader;
|
||||
use Propel\PropelBundle\DataFixtures\Loader\XmlDataLoader;
|
||||
|
||||
/**
|
||||
* 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')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Command
|
||||
*
|
||||
* @throws \InvalidArgumentException When the target directory does not exist
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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');
|
||||
//$loader = new XmlDataLoader($this->getApplication()->getKernel()->getRootDir());
|
||||
} 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->getApplication()->getKernel()->getRootDir() . '/cache/propel';
|
||||
$datas = $this->getFixtureFiles('sql');
|
||||
|
||||
$this->prepareCache($tmpdir);
|
||||
|
||||
list($name, $defaultConfig) = $this->getConnection($input, $output);
|
||||
|
||||
// Create a "sqldb.map" file
|
||||
$sqldbContent = '';
|
||||
foreach ($datas as $data) {
|
||||
$output->writeln(sprintf('<info>Loading SQL fixtures from</info> <comment>%s</comment>.', $data));
|
||||
|
||||
$sqldbContent .= $data->getFilename() . '=' . $name . PHP_EOL;
|
||||
$this->filesystem->copy($data, $tmpdir . '/fixtures/' . $data->getFilename(), true);
|
||||
}
|
||||
|
||||
if ('' === $sqldbContent) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
$sqldbFile = $tmpdir . '/fixtures/sqldb.map';
|
||||
file_put_contents($sqldbFile, $sqldbContent);
|
||||
|
||||
if (!$this->insertSql($defaultConfig, $tmpdir . '/fixtures', $tmpdir, $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);
|
||||
|
||||
$fixturesdir = $tmpdir . '/fixtures/';
|
||||
$this->filesystem->remove($fixturesdir);
|
||||
$this->filesystem->mkdir($fixturesdir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert SQL
|
||||
*/
|
||||
protected function insertSql($config, $sqlDir, $schemaDir, $output)
|
||||
{
|
||||
// Insert SQL
|
||||
$ret = $this->callPhing('insert-sql', array(
|
||||
'propel.database.url' => $config['connection']['dsn'],
|
||||
'propel.database.database' => $config['adapter'],
|
||||
'propel.database.user' => $config['connection']['user'],
|
||||
'propel.database.password' => $config['connection']['password'],
|
||||
'propel.schema.dir' => $schemaDir,
|
||||
'propel.sql.dir' => $sqlDir,
|
||||
));
|
||||
|
||||
if (true === $ret) {
|
||||
$output->writeln('All SQL statements have been inserted.');
|
||||
} else {
|
||||
$this->writeTaskError($output, 'insert-sql', false);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
protected function getFixturesPath(BundleInterface $bundle)
|
||||
{
|
||||
return $bundle->getPath() . DIRECTORY_SEPARATOR . 'Resources' . DIRECTORY_SEPARATOR . 'fixtures';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createSubCommandInstance()
|
||||
{
|
||||
// useless here
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getSubCommandArguments(InputInterface $input)
|
||||
{
|
||||
// useless here
|
||||
}
|
||||
}
|
306
DataFixtures/Loader/AbstractDataLoader.php
Normal file
306
DataFixtures/Loader/AbstractDataLoader.php
Normal file
|
@ -0,0 +1,306 @@
|
|||
<?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\Runtime\ActiveRecord\ActiveRecordInterface;
|
||||
use Propel\Runtime\Map\TableMap;
|
||||
|
||||
/**
|
||||
* 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 = $this->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 $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::getFieldnamesForClass($class, 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 (PropelException $e) {
|
||||
// Check whether this is actually an array stored in the object.
|
||||
if ('Cannot fetch TableMap for undefined table: ' . substr($name, 0, -1) === $e->getMessage()) {
|
||||
if (PropelColumnTypes::PHP_ARRAY !== $tableMap->getColumn($name)->getType()
|
||||
&& PropelColumnTypes::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);
|
||||
}
|
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 Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
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 \Symfony\Component\DependencyInjection\ContainerInterface
|
||||
*/
|
||||
private $container;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct($rootDir, ContainerInterface $container)
|
||||
{
|
||||
parent::__construct($rootDir, $container->get('propel'), $container->getParameter('propel.configuration'));
|
||||
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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->container && $this->container->has('faker.generator')) {
|
||||
$generator = $this->container->get('faker.generator');
|
||||
$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);
|
||||
}
|
||||
}
|
|
@ -14,6 +14,7 @@
|
|||
<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.dumper.yaml.class">Propel\PropelBundle\DataFixtures\Dumper\YamlDataDumper</parameter>
|
||||
<parameter key="propel.loader.yaml.class">Propel\PropelBundle\DataFixtures\Loader\YamlDataLoader</parameter>
|
||||
</parameters>
|
||||
|
||||
<services>
|
||||
|
@ -47,5 +48,10 @@
|
|||
<argument type="service" id="propel" />
|
||||
<argument>%propel.configuration%</argument>
|
||||
</service>
|
||||
|
||||
<service id="propel.loader.yaml" class="%propel.loader.yaml.class%">
|
||||
<argument>%kernel.root_dir%</argument>
|
||||
<argument type="service" id="service_container" />
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
||||
|
|
Loading…
Reference in a new issue