Initial commit with Symfony 2.1+Vendors

Signed-off-by: Gergely POLONKAI (W00d5t0ck) <polesz@w00d5t0ck.info>
This commit is contained in:
Polonkai Gergely
2012-07-01 09:52:20 +02:00
commit 082a0130c2
5381 changed files with 416709 additions and 0 deletions

View File

@@ -0,0 +1,104 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools\Console\Command\ClearCache;
use Symfony\Component\Console\Input\InputArgument,
Symfony\Component\Console\Input\InputOption,
Symfony\Component\Console,
Doctrine\Common\Cache;
/**
* Command to clear the metadata cache of the various cache drivers.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class MetadataCommand extends Console\Command\Command
{
/**
* @see Console\Command\Command
*/
protected function configure()
{
$this
->setName('orm:clear-cache:metadata')
->setDescription('Clear all metadata cache of the various cache drivers.')
->setDefinition(array(
new InputOption(
'flush', null, InputOption::VALUE_NONE,
'If defined, cache entries will be flushed instead of deleted/invalidated.'
)
));
$fullName = $this->getName();
$this->setHelp(<<<EOT
The <info>$fullName</info> command is meant to clear the metadata cache of associated Entity Manager.
It is possible to invalidate all cache entries at once - called delete -, or flushes the cache provider
instance completely.
The execution type differ on how you execute the command.
If you want to invalidate the entries (and not delete from cache instance), this command would do the work:
<info>$fullName</info>
Alternatively, if you want to flush the cache provider using this command:
<info>$fullName --flush</info>
Finally, be aware that if <info>--flush</info> option is passed, not all cache providers are able to flush entries,
because of a limitation of its execution nature.
EOT
);
}
/**
* @see Console\Command\Command
*/
protected function execute(Console\Input\InputInterface $input, Console\Output\OutputInterface $output)
{
$em = $this->getHelper('em')->getEntityManager();
$cacheDriver = $em->getConfiguration()->getMetadataCacheImpl();
if ( ! $cacheDriver) {
throw new \InvalidArgumentException('No Metadata cache driver is configured on given EntityManager.');
}
if ($cacheDriver instanceof Cache\ApcCache) {
throw new \LogicException("Cannot clear APC Cache from Console, its shared in the Webserver memory and not accessible from the CLI.");
}
$output->write('Clearing ALL Metadata cache entries' . PHP_EOL);
$result = $cacheDriver->deleteAll();
$message = ($result) ? 'Successfully deleted cache entries.' : 'No cache entries were deleted.';
if (true === $input->getOption('flush')) {
$result = $cacheDriver->flushAll();
$message = ($result) ? 'Successfully flushed cache entries.' : $message;
}
$output->write($message . PHP_EOL);
}
}

View File

@@ -0,0 +1,104 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools\Console\Command\ClearCache;
use Symfony\Component\Console\Input\InputArgument,
Symfony\Component\Console\Input\InputOption,
Symfony\Component\Console,
Doctrine\Common\Cache;
/**
* Command to clear the query cache of the various cache drivers.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class QueryCommand extends Console\Command\Command
{
/**
* @see Console\Command\Command
*/
protected function configure()
{
$this
->setName('orm:clear-cache:query')
->setDescription('Clear all query cache of the various cache drivers.')
->setDefinition(array(
new InputOption(
'flush', null, InputOption::VALUE_NONE,
'If defined, cache entries will be flushed instead of deleted/invalidated.'
)
));
$fullName = $this->getName();
$this->setHelp(<<<EOT
The <info>$fullName</info> command is meant to clear the query cache of associated Entity Manager.
It is possible to invalidate all cache entries at once - called delete -, or flushes the cache provider
instance completely.
The execution type differ on how you execute the command.
If you want to invalidate the entries (and not delete from cache instance), this command would do the work:
<info>$fullName</info>
Alternatively, if you want to flush the cache provider using this command:
<info>$fullName --flush</info>
Finally, be aware that if <info>--flush</info> option is passed, not all cache providers are able to flush entries,
because of a limitation of its execution nature.
EOT
);
}
/**
* @see Console\Command\Command
*/
protected function execute(Console\Input\InputInterface $input, Console\Output\OutputInterface $output)
{
$em = $this->getHelper('em')->getEntityManager();
$cacheDriver = $em->getConfiguration()->getQueryCacheImpl();
if ( ! $cacheDriver) {
throw new \InvalidArgumentException('No Query cache driver is configured on given EntityManager.');
}
if ($cacheDriver instanceof Cache\ApcCache) {
throw new \LogicException("Cannot clear APC Cache from Console, its shared in the Webserver memory and not accessible from the CLI.");
}
$output->write('Clearing ALL Query cache entries' . PHP_EOL);
$result = $cacheDriver->deleteAll();
$message = ($result) ? 'Successfully deleted cache entries.' : 'No cache entries were deleted.';
if (true === $input->getOption('flush')) {
$result = $cacheDriver->flushAll();
$message = ($result) ? 'Successfully flushed cache entries.' : $message;
}
$output->write($message . PHP_EOL);
}
}

View File

@@ -0,0 +1,104 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools\Console\Command\ClearCache;
use Symfony\Component\Console\Input\InputArgument,
Symfony\Component\Console\Input\InputOption,
Symfony\Component\Console,
Doctrine\Common\Cache;
/**
* Command to clear the result cache of the various cache drivers.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class ResultCommand extends Console\Command\Command
{
/**
* @see Console\Command\Command
*/
protected function configure()
{
$this
->setName('orm:clear-cache:result')
->setDescription('Clear all result cache of the various cache drivers.')
->setDefinition(array(
new InputOption(
'flush', null, InputOption::VALUE_NONE,
'If defined, cache entries will be flushed instead of deleted/invalidated.'
)
));
$fullName = $this->getName();
$this->setHelp(<<<EOT
The <info>$fullName</info> command is meant to clear the result cache of associated Entity Manager.
It is possible to invalidate all cache entries at once - called delete -, or flushes the cache provider
instance completely.
The execution type differ on how you execute the command.
If you want to invalidate the entries (and not delete from cache instance), this command would do the work:
<info>$fullName</info>
Alternatively, if you want to flush the cache provider using this command:
<info>$fullName --flush</info>
Finally, be aware that if <info>--flush</info> option is passed, not all cache providers are able to flush entries,
because of a limitation of its execution nature.
EOT
);
}
/**
* @see Console\Command\Command
*/
protected function execute(Console\Input\InputInterface $input, Console\Output\OutputInterface $output)
{
$em = $this->getHelper('em')->getEntityManager();
$cacheDriver = $em->getConfiguration()->getResultCacheImpl();
if ( ! $cacheDriver) {
throw new \InvalidArgumentException('No Result cache driver is configured on given EntityManager.');
}
if ($cacheDriver instanceof Cache\ApcCache) {
throw new \LogicException("Cannot clear APC Cache from Console, its shared in the Webserver memory and not accessible from the CLI.");
}
$output->write('Clearing ALL Result cache entries' . PHP_EOL);
$result = $cacheDriver->deleteAll();
$message = ($result) ? 'Successfully deleted cache entries.' : 'No cache entries were deleted.';
if (true === $input->getOption('flush')) {
$result = $cacheDriver->flushAll();
$message = ($result) ? 'Successfully flushed cache entries.' : $message;
}
$output->write($message . PHP_EOL);
}
}

View File

@@ -0,0 +1,223 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools\Console\Command;
use Symfony\Component\Console\Input\InputArgument,
Symfony\Component\Console\Input\InputOption,
Symfony\Component\Console,
Doctrine\ORM\Tools\Export\ClassMetadataExporter,
Doctrine\ORM\Tools\ConvertDoctrine1Schema,
Doctrine\ORM\Tools\EntityGenerator;
/**
* Command to convert a Doctrine 1 schema to a Doctrine 2 mapping file.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class ConvertDoctrine1SchemaCommand extends Console\Command\Command
{
/**
* @var EntityGenerator
*/
private $entityGenerator = null;
/**
* @var ClassMetadataExporter
*/
private $metadataExporter = null;
/**
* @return EntityGenerator
*/
public function getEntityGenerator()
{
if ($this->entityGenerator == null) {
$this->entityGenerator = new EntityGenerator();
}
return $this->entityGenerator;
}
/**
* @param EntityGenerator $entityGenerator
*/
public function setEntityGenerator(EntityGenerator $entityGenerator)
{
$this->entityGenerator = $entityGenerator;
}
/**
* @return ClassMetadataExporter
*/
public function getMetadataExporter()
{
if ($this->metadataExporter == null) {
$this->metadataExporter = new ClassMetadataExporter();
}
return $this->metadataExporter;
}
/**
* @param ClassMetadataExporter $metadataExporter
*/
public function setMetadataExporter(ClassMetadataExporter $metadataExporter)
{
$this->metadataExporter = $metadataExporter;
}
/**
* @see Console\Command\Command
*/
protected function configure()
{
$this
->setName('orm:convert-d1-schema')
->setDescription('Converts Doctrine 1.X schema into a Doctrine 2.X schema.')
->setDefinition(array(
new InputArgument(
'from-path', InputArgument::REQUIRED, 'The path of Doctrine 1.X schema information.'
),
new InputArgument(
'to-type', InputArgument::REQUIRED, 'The destination Doctrine 2.X mapping type.'
),
new InputArgument(
'dest-path', InputArgument::REQUIRED,
'The path to generate your Doctrine 2.X mapping information.'
),
new InputOption(
'from', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
'Optional paths of Doctrine 1.X schema information.',
array()
),
new InputOption(
'extend', null, InputOption::VALUE_OPTIONAL,
'Defines a base class to be extended by generated entity classes.'
),
new InputOption(
'num-spaces', null, InputOption::VALUE_OPTIONAL,
'Defines the number of indentation spaces', 4
)
))
->setHelp(<<<EOT
Converts Doctrine 1.X schema into a Doctrine 2.X schema.
EOT
);
}
/**
* @see Console\Command\Command
*/
protected function execute(Console\Input\InputInterface $input, Console\Output\OutputInterface $output)
{
$em = $this->getHelper('em')->getEntityManager();
// Process source directories
$fromPaths = array_merge(array($input->getArgument('from-path')), $input->getOption('from'));
// Process destination directory
$destPath = realpath($input->getArgument('dest-path'));
$toType = $input->getArgument('to-type');
$extend = $input->getOption('extend');
$numSpaces = $input->getOption('num-spaces');
$this->convertDoctrine1Schema($em, $fromPaths, $destPath, $toType, $numSpaces, $extend, $output);
}
/**
* @param \Doctrine\ORM\EntityManager $em
* @param array $fromPaths
* @param string $destPath
* @param string $toType
* @param int $numSpaces
* @param string|null $extend
* @param Console\Output\OutputInterface $output
*/
public function convertDoctrine1Schema($em, $fromPaths, $destPath, $toType, $numSpaces, $extend, $output)
{
foreach ($fromPaths as &$dirName) {
$dirName = realpath($dirName);
if ( ! file_exists($dirName)) {
throw new \InvalidArgumentException(
sprintf("Doctrine 1.X schema directory '<info>%s</info>' does not exist.", $dirName)
);
} else if ( ! is_readable($dirName)) {
throw new \InvalidArgumentException(
sprintf("Doctrine 1.X schema directory '<info>%s</info>' does not have read permissions.", $dirName)
);
}
}
if ( ! file_exists($destPath)) {
throw new \InvalidArgumentException(
sprintf("Doctrine 2.X mapping destination directory '<info>%s</info>' does not exist.", $destPath)
);
} else if ( ! is_writable($destPath)) {
throw new \InvalidArgumentException(
sprintf("Doctrine 2.X mapping destination directory '<info>%s</info>' does not have write permissions.", $destPath)
);
}
$cme = $this->getMetadataExporter();
$exporter = $cme->getExporter($toType, $destPath);
if (strtolower($toType) === 'annotation') {
$entityGenerator = $this->getEntityGenerator();
$exporter->setEntityGenerator($entityGenerator);
$entityGenerator->setNumSpaces($numSpaces);
if ($extend !== null) {
$entityGenerator->setClassToExtend($extend);
}
}
$converter = new ConvertDoctrine1Schema($fromPaths);
$metadata = $converter->getMetadata();
if ($metadata) {
$output->write(PHP_EOL);
foreach ($metadata as $class) {
$output->write(sprintf('Processing entity "<info>%s</info>"', $class->name) . PHP_EOL);
}
$exporter->setMetadata($metadata);
$exporter->export();
$output->write(PHP_EOL . sprintf(
'Converting Doctrine 1.X schema to "<info>%s</info>" mapping type in "<info>%s</info>"', $toType, $destPath
));
} else {
$output->write('No Metadata Classes to process.' . PHP_EOL);
}
}
}

View File

@@ -0,0 +1,186 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools\Console\Command;
use Symfony\Component\Console\Input\InputArgument,
Symfony\Component\Console\Input\InputOption,
Symfony\Component\Console,
Doctrine\ORM\Tools\Console\MetadataFilter,
Doctrine\ORM\Tools\Export\ClassMetadataExporter,
Doctrine\ORM\Tools\EntityGenerator,
Doctrine\ORM\Tools\DisconnectedClassMetadataFactory;
/**
* Command to convert your mapping information between the various formats.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class ConvertMappingCommand extends Console\Command\Command
{
/**
* @see Console\Command\Command
*/
protected function configure()
{
$this
->setName('orm:convert-mapping')
->setDescription('Convert mapping information between supported formats.')
->setDefinition(array(
new InputOption(
'filter', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
'A string pattern used to match entities that should be processed.'
),
new InputArgument(
'to-type', InputArgument::REQUIRED, 'The mapping type to be converted.'
),
new InputArgument(
'dest-path', InputArgument::REQUIRED,
'The path to generate your entities classes.'
),
new InputOption(
'force', null, InputOption::VALUE_NONE,
'Force to overwrite existing mapping files.'
),
new InputOption(
'from-database', null, null, 'Whether or not to convert mapping information from existing database.'
),
new InputOption(
'extend', null, InputOption::VALUE_OPTIONAL,
'Defines a base class to be extended by generated entity classes.'
),
new InputOption(
'num-spaces', null, InputOption::VALUE_OPTIONAL,
'Defines the number of indentation spaces', 4
),
new InputOption(
'namespace', null, InputOption::VALUE_OPTIONAL,
'Defines a namespace for the generated entity classes, if converted from database.'
),
))
->setHelp(<<<EOT
Convert mapping information between supported formats.
This is an execute <info>one-time</info> command. It should not be necessary for
you to call this method multiple times, escpecially when using the <comment>--from-database</comment>
flag.
Converting an existing databsae schema into mapping files only solves about 70-80%
of the necessary mapping information. Additionally the detection from an existing
database cannot detect inverse associations, inheritance types,
entities with foreign keys as primary keys and many of the
semantical operations on associations such as cascade.
<comment>Hint:</comment> There is no need to convert YAML or XML mapping files to annotations
every time you make changes. All mapping drivers are first class citizens
in Doctrine 2 and can be used as runtime mapping for the ORM.
EOT
);
}
/**
* @see Console\Command\Command
*/
protected function execute(Console\Input\InputInterface $input, Console\Output\OutputInterface $output)
{
$em = $this->getHelper('em')->getEntityManager();
if ($input->getOption('from-database') === true) {
$databaseDriver = new \Doctrine\ORM\Mapping\Driver\DatabaseDriver(
$em->getConnection()->getSchemaManager()
);
$em->getConfiguration()->setMetadataDriverImpl(
$databaseDriver
);
if (($namespace = $input->getOption('namespace')) !== null) {
$databaseDriver->setNamespace($namespace);
}
}
$cmf = new DisconnectedClassMetadataFactory();
$cmf->setEntityManager($em);
$metadata = $cmf->getAllMetadata();
$metadata = MetadataFilter::filter($metadata, $input->getOption('filter'));
// Process destination directory
if ( ! is_dir($destPath = $input->getArgument('dest-path'))) {
mkdir($destPath, 0777, true);
}
$destPath = realpath($destPath);
if ( ! file_exists($destPath)) {
throw new \InvalidArgumentException(
sprintf("Mapping destination directory '<info>%s</info>' does not exist.", $input->getArgument('dest-path'))
);
} else if ( ! is_writable($destPath)) {
throw new \InvalidArgumentException(
sprintf("Mapping destination directory '<info>%s</info>' does not have write permissions.", $destPath)
);
}
$toType = strtolower($input->getArgument('to-type'));
$exporter = $this->getExporter($toType, $destPath);
$exporter->setOverwriteExistingFiles( ($input->getOption('force') !== false) );
if ($toType == 'annotation') {
$entityGenerator = new EntityGenerator();
$exporter->setEntityGenerator($entityGenerator);
$entityGenerator->setNumSpaces($input->getOption('num-spaces'));
if (($extend = $input->getOption('extend')) !== null) {
$entityGenerator->setClassToExtend($extend);
}
}
if (count($metadata)) {
foreach ($metadata as $class) {
$output->write(sprintf('Processing entity "<info>%s</info>"', $class->name) . PHP_EOL);
}
$exporter->setMetadata($metadata);
$exporter->export();
$output->write(PHP_EOL . sprintf(
'Exporting "<info>%s</info>" mapping information to "<info>%s</info>"' . PHP_EOL, $toType, $destPath
));
} else {
$output->write('No Metadata Classes to process.' . PHP_EOL);
}
}
protected function getExporter($toType, $destPath)
{
$cme = new ClassMetadataExporter();
return $cme->getExporter($toType, $destPath);
}
}

View File

@@ -0,0 +1,85 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools\Console\Command;
use Symfony\Component\Console\Input\InputArgument,
Symfony\Component\Console\Input\InputOption,
Symfony\Component\Console;
/**
* Command to ensure that Doctrine is properly configured for a production environment.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class EnsureProductionSettingsCommand extends Console\Command\Command
{
/**
* @see Console\Command\Command
*/
protected function configure()
{
$this
->setName('orm:ensure-production-settings')
->setDescription('Verify that Doctrine is properly configured for a production environment.')
->setDefinition(array(
new InputOption(
'complete', null, InputOption::VALUE_NONE,
'Flag to also inspect database connection existance.'
)
))
->setHelp(<<<EOT
Verify that Doctrine is properly configured for a production environment.
EOT
);
}
/**
* @see Console\Command\Command
*/
protected function execute(Console\Input\InputInterface $input, Console\Output\OutputInterface $output)
{
$em = $this->getHelper('em')->getEntityManager();
$error = false;
try {
$em->getConfiguration()->ensureProductionSettings();
if ($input->getOption('complete') !== null) {
$em->getConnection()->connect();
}
} catch (\Exception $e) {
$error = true;
$output->writeln('<error>' . $e->getMessage() . '</error>');
}
if ($error === false) {
$output->write('<info>Environment is correctly configured for production.</info>' . PHP_EOL);
}
}
}

View File

@@ -0,0 +1,163 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools\Console\Command;
use Symfony\Component\Console\Input\InputArgument,
Symfony\Component\Console\Input\InputOption,
Symfony\Component\Console,
Doctrine\ORM\Tools\Console\MetadataFilter,
Doctrine\ORM\Tools\EntityGenerator,
Doctrine\ORM\Tools\DisconnectedClassMetadataFactory;
/**
* Command to generate entity classes and method stubs from your mapping information.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class GenerateEntitiesCommand extends Console\Command\Command
{
/**
* @see Console\Command\Command
*/
protected function configure()
{
$this
->setName('orm:generate-entities')
->setDescription('Generate entity classes and method stubs from your mapping information.')
->setDefinition(array(
new InputOption(
'filter', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
'A string pattern used to match entities that should be processed.'
),
new InputArgument(
'dest-path', InputArgument::REQUIRED, 'The path to generate your entity classes.'
),
new InputOption(
'generate-annotations', null, InputOption::VALUE_OPTIONAL,
'Flag to define if generator should generate annotation metadata on entities.', false
),
new InputOption(
'generate-methods', null, InputOption::VALUE_OPTIONAL,
'Flag to define if generator should generate stub methods on entities.', true
),
new InputOption(
'regenerate-entities', null, InputOption::VALUE_OPTIONAL,
'Flag to define if generator should regenerate entity if it exists.', false
),
new InputOption(
'update-entities', null, InputOption::VALUE_OPTIONAL,
'Flag to define if generator should only update entity if it exists.', true
),
new InputOption(
'extend', null, InputOption::VALUE_OPTIONAL,
'Defines a base class to be extended by generated entity classes.'
),
new InputOption(
'num-spaces', null, InputOption::VALUE_OPTIONAL,
'Defines the number of indentation spaces', 4
)
))
->setHelp(<<<EOT
Generate entity classes and method stubs from your mapping information.
If you use the <comment>--update-entities</comment> or <comment>--regenerate-entities</comment> flags your exisiting
code gets overwritten. The EntityGenerator will only append new code to your
file and will not delete the old code. However this approach may still be prone
to error and we suggest you use code repositories such as GIT or SVN to make
backups of your code.
It makes sense to generate the entity code if you are using entities as Data
Access Objects only and dont put much additional logic on them. If you are
however putting much more logic on the entities you should refrain from using
the entity-generator and code your entities manually.
<error>Important:</error> Even if you specified Inheritance options in your
XML or YAML Mapping files the generator cannot generate the base and
child classes for you correctly, because it doesn't know which
class is supposed to extend which. You have to adjust the entity
code manually for inheritance to work!
EOT
);
}
/**
* @see Console\Command\Command
*/
protected function execute(Console\Input\InputInterface $input, Console\Output\OutputInterface $output)
{
$em = $this->getHelper('em')->getEntityManager();
$cmf = new DisconnectedClassMetadataFactory();
$cmf->setEntityManager($em);
$metadatas = $cmf->getAllMetadata();
$metadatas = MetadataFilter::filter($metadatas, $input->getOption('filter'));
// Process destination directory
$destPath = realpath($input->getArgument('dest-path'));
if ( ! file_exists($destPath)) {
throw new \InvalidArgumentException(
sprintf("Entities destination directory '<info>%s</info>' does not exist.", $input->getArgument('dest-path'))
);
} else if ( ! is_writable($destPath)) {
throw new \InvalidArgumentException(
sprintf("Entities destination directory '<info>%s</info>' does not have write permissions.", $destPath)
);
}
if (count($metadatas)) {
// Create EntityGenerator
$entityGenerator = new EntityGenerator();
$entityGenerator->setGenerateAnnotations($input->getOption('generate-annotations'));
$entityGenerator->setGenerateStubMethods($input->getOption('generate-methods'));
$entityGenerator->setRegenerateEntityIfExists($input->getOption('regenerate-entities'));
$entityGenerator->setUpdateEntityIfExists($input->getOption('update-entities'));
$entityGenerator->setNumSpaces($input->getOption('num-spaces'));
if (($extend = $input->getOption('extend')) !== null) {
$entityGenerator->setClassToExtend($extend);
}
foreach ($metadatas as $metadata) {
$output->write(
sprintf('Processing entity "<info>%s</info>"', $metadata->name) . PHP_EOL
);
}
// Generating Entities
$entityGenerator->generate($metadatas, $destPath);
// Outputting information message
$output->write(PHP_EOL . sprintf('Entity classes generated to "<info>%s</INFO>"', $destPath) . PHP_EOL);
} else {
$output->write('No Metadata Classes to process.' . PHP_EOL);
}
}
}

View File

@@ -0,0 +1,115 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools\Console\Command;
use Symfony\Component\Console\Input\InputArgument,
Symfony\Component\Console\Input\InputOption,
Symfony\Component\Console,
Doctrine\ORM\Tools\Console\MetadataFilter;
/**
* Command to (re)generate the proxy classes used by doctrine.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class GenerateProxiesCommand extends Console\Command\Command
{
/**
* @see Console\Command\Command
*/
protected function configure()
{
$this
->setName('orm:generate-proxies')
->setDescription('Generates proxy classes for entity classes.')
->setDefinition(array(
new InputOption(
'filter', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
'A string pattern used to match entities that should be processed.'
),
new InputArgument(
'dest-path', InputArgument::OPTIONAL,
'The path to generate your proxy classes. If none is provided, it will attempt to grab from configuration.'
),
))
->setHelp(<<<EOT
Generates proxy classes for entity classes.
EOT
);
}
/**
* @see Console\Command\Command
*/
protected function execute(Console\Input\InputInterface $input, Console\Output\OutputInterface $output)
{
$em = $this->getHelper('em')->getEntityManager();
$metadatas = $em->getMetadataFactory()->getAllMetadata();
$metadatas = MetadataFilter::filter($metadatas, $input->getOption('filter'));
// Process destination directory
if (($destPath = $input->getArgument('dest-path')) === null) {
$destPath = $em->getConfiguration()->getProxyDir();
}
if ( ! is_dir($destPath)) {
mkdir($destPath, 0777, true);
}
$destPath = realpath($destPath);
if ( ! file_exists($destPath)) {
throw new \InvalidArgumentException(
sprintf("Proxies destination directory '<info>%s</info>' does not exist.", $em->getConfiguration()->getProxyDir())
);
} else if ( ! is_writable($destPath)) {
throw new \InvalidArgumentException(
sprintf("Proxies destination directory '<info>%s</info>' does not have write permissions.", $destPath)
);
}
if ( count($metadatas)) {
foreach ($metadatas as $metadata) {
$output->write(
sprintf('Processing entity "<info>%s</info>"', $metadata->name) . PHP_EOL
);
}
// Generating Proxies
$em->getProxyFactory()->generateProxyClasses($metadatas, $destPath);
// Outputting information message
$output->write(PHP_EOL . sprintf('Proxy classes generated to "<info>%s</INFO>"', $destPath) . PHP_EOL);
} else {
$output->write('No Metadata Classes to process.' . PHP_EOL);
}
}
}

View File

@@ -0,0 +1,116 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools\Console\Command;
use Symfony\Component\Console\Input\InputArgument,
Symfony\Component\Console\Input\InputOption,
Symfony\Component\Console,
Doctrine\ORM\Tools\Console\MetadataFilter,
Doctrine\ORM\Tools\EntityRepositoryGenerator;
/**
* Command to generate repository classes for mapping information.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class GenerateRepositoriesCommand extends Console\Command\Command
{
/**
* @see Console\Command\Command
*/
protected function configure()
{
$this
->setName('orm:generate-repositories')
->setDescription('Generate repository classes from your mapping information.')
->setDefinition(array(
new InputOption(
'filter', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
'A string pattern used to match entities that should be processed.'
),
new InputArgument(
'dest-path', InputArgument::REQUIRED, 'The path to generate your repository classes.'
)
))
->setHelp(<<<EOT
Generate repository classes from your mapping information.
EOT
);
}
/**
* @see Console\Command\Command
*/
protected function execute(Console\Input\InputInterface $input, Console\Output\OutputInterface $output)
{
$em = $this->getHelper('em')->getEntityManager();
$metadatas = $em->getMetadataFactory()->getAllMetadata();
$metadatas = MetadataFilter::filter($metadatas, $input->getOption('filter'));
// Process destination directory
$destPath = realpath($input->getArgument('dest-path'));
if ( ! file_exists($destPath)) {
throw new \InvalidArgumentException(
sprintf("Entities destination directory '<info>%s</info>' does not exist.", $input->getArgument('dest-path'))
);
} else if ( ! is_writable($destPath)) {
throw new \InvalidArgumentException(
sprintf("Entities destination directory '<info>%s</info>' does not have write permissions.", $destPath)
);
}
if (count($metadatas)) {
$numRepositories = 0;
$generator = new EntityRepositoryGenerator();
foreach ($metadatas as $metadata) {
if ($metadata->customRepositoryClassName) {
$output->write(
sprintf('Processing repository "<info>%s</info>"', $metadata->customRepositoryClassName) . PHP_EOL
);
$generator->writeEntityRepositoryClass($metadata->customRepositoryClassName, $destPath);
$numRepositories++;
}
}
if ($numRepositories) {
// Outputting information message
$output->write(PHP_EOL . sprintf('Repository classes generated to "<info>%s</INFO>"', $destPath) . PHP_EOL);
} else {
$output->write('No Repository classes were found to be processed.' . PHP_EOL);
}
} else {
$output->write('No Metadata Classes to process.' . PHP_EOL);
}
}
}

View File

@@ -0,0 +1,80 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools\Console\Command;
use Doctrine\ORM\Mapping\MappingException;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Command\Command;
/**
* Show information about mapped entities
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.1
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class InfoCommand extends Command
{
protected function configure()
{
$this
->setName('orm:info')
->setDescription('Show basic information about all mapped entities')
->setHelp(<<<EOT
The <info>doctrine:mapping:info</info> shows basic information about which
entities exist and possibly if their mapping information contains errors or
not.
EOT
);
}
protected function execute(InputInterface $input, OutputInterface $output)
{
/* @var $entityManager \Doctrine\ORM\EntityManager */
$entityManager = $this->getHelper('em')->getEntityManager();
$entityClassNames = $entityManager->getConfiguration()
->getMetadataDriverImpl()
->getAllClassNames();
if (!$entityClassNames) {
throw new \Exception(
'You do not have any mapped Doctrine ORM entities according to the current configuration. '.
'If you have entities or mapping files you should check your mapping configuration for errors.'
);
}
$output->writeln(sprintf("Found <info>%d</info> mapped entities:", count($entityClassNames)));
foreach ($entityClassNames as $entityClassName) {
try {
$cm = $entityManager->getClassMetadata($entityClassName);
$output->writeln(sprintf("<info>[OK]</info> %s", $entityClassName));
} catch (MappingException $e) {
$output->writeln("<error>[FAIL]</error> ".$entityClassName);
$output->writeln(sprintf("<comment>%s</comment>", $e->getMessage()));
$output->writeln('');
}
}
}
}

View File

@@ -0,0 +1,124 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools\Console\Command;
use Symfony\Component\Console\Input\InputArgument,
Symfony\Component\Console\Input\InputOption,
Symfony\Component\Console;
/**
* Command to execute DQL queries in a given EntityManager.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class RunDqlCommand extends Console\Command\Command
{
/**
* @see Console\Command\Command
*/
protected function configure()
{
$this
->setName('orm:run-dql')
->setDescription('Executes arbitrary DQL directly from the command line.')
->setDefinition(array(
new InputArgument('dql', InputArgument::REQUIRED, 'The DQL to execute.'),
new InputOption(
'hydrate', null, InputOption::VALUE_REQUIRED,
'Hydration mode of result set. Should be either: object, array, scalar or single-scalar.',
'object'
),
new InputOption(
'first-result', null, InputOption::VALUE_REQUIRED,
'The first result in the result set.'
),
new InputOption(
'max-result', null, InputOption::VALUE_REQUIRED,
'The maximum number of results in the result set.'
),
new InputOption(
'depth', null, InputOption::VALUE_REQUIRED,
'Dumping depth of Entity graph.', 7
)
))
->setHelp(<<<EOT
Executes arbitrary DQL directly from the command line.
EOT
);
}
/**
* @see Console\Command\Command
*/
protected function execute(Console\Input\InputInterface $input, Console\Output\OutputInterface $output)
{
$em = $this->getHelper('em')->getEntityManager();
if (($dql = $input->getArgument('dql')) === null) {
throw new \RuntimeException("Argument 'DQL' is required in order to execute this command correctly.");
}
$depth = $input->getOption('depth');
if ( ! is_numeric($depth)) {
throw new \LogicException("Option 'depth' must contains an integer value");
}
$hydrationModeName = $input->getOption('hydrate');
$hydrationMode = 'Doctrine\ORM\Query::HYDRATE_' . strtoupper(str_replace('-', '_', $hydrationModeName));
if ( ! defined($hydrationMode)) {
throw new \RuntimeException(
"Hydration mode '$hydrationModeName' does not exist. It should be either: object. array, scalar or single-scalar."
);
}
$query = $em->createQuery($dql);
if (($firstResult = $input->getOption('first-result')) !== null) {
if ( ! is_numeric($firstResult)) {
throw new \LogicException("Option 'first-result' must contains an integer value");
}
$query->setFirstResult((int) $firstResult);
}
if (($maxResult = $input->getOption('max-result')) !== null) {
if ( ! is_numeric($maxResult)) {
throw new \LogicException("Option 'max-result' must contains an integer value");
}
$query->setMaxResults((int) $maxResult);
}
$resultSet = $query->execute(array(), constant($hydrationMode));
\Doctrine\Common\Util\Debug::dump($resultSet, $input->getOption('depth'));
}
}

View File

@@ -0,0 +1,64 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools\Console\Command\SchemaTool;
use Symfony\Component\Console\Input\InputArgument,
Symfony\Component\Console\Input\InputOption,
Symfony\Component\Console\Input\InputInterface,
Symfony\Component\Console\Output\OutputInterface,
Symfony\Component\Console\Command\Command,
Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper,
Doctrine\ORM\Tools\SchemaTool,
Doctrine\ORM\Mapping\Driver\AbstractFileDriver;
abstract class AbstractCommand extends Command
{
/**
* @param InputInterface $input
* @param OutputInterface $output
* @param SchemaTool $schemaTool
* @param array $metadatas
*/
abstract protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas);
/**
* @see Console\Command\Command
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$emHelper = $this->getHelper('em');
/* @var $em \Doctrine\ORM\EntityManager */
$em = $emHelper->getEntityManager();
$metadatas = $em->getMetadataFactory()->getAllMetadata();
if ( ! empty($metadatas)) {
// Create SchemaTool
$tool = new \Doctrine\ORM\Tools\SchemaTool($em);
$this->executeSchemaCommand($input, $output, $tool, $metadatas);
} else {
$output->write('No Metadata Classes to process.' . PHP_EOL);
}
}
}

View File

@@ -0,0 +1,79 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools\Console\Command\SchemaTool;
use Symfony\Component\Console\Input\InputArgument,
Symfony\Component\Console\Input\InputOption,
Symfony\Component\Console\Input\InputInterface,
Symfony\Component\Console\Output\OutputInterface,
Doctrine\ORM\Tools\SchemaTool;
/**
* Command to create the database schema for a set of classes based on their mappings.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class CreateCommand extends AbstractCommand
{
/**
* @see Console\Command\Command
*/
protected function configure()
{
$this
->setName('orm:schema-tool:create')
->setDescription(
'Processes the schema and either create it directly on EntityManager Storage Connection or generate the SQL output.'
)
->setDefinition(array(
new InputOption(
'dump-sql', null, InputOption::VALUE_NONE,
'Instead of try to apply generated SQLs into EntityManager Storage Connection, output them.'
)
))
->setHelp(<<<EOT
Processes the schema and either create it directly on EntityManager Storage Connection or generate the SQL output.
EOT
);
}
protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas)
{
$output->write('ATTENTION: This operation should not be executed in a production environment.' . PHP_EOL . PHP_EOL);
if ($input->getOption('dump-sql') === true) {
$sqls = $schemaTool->getCreateSchemaSql($metadatas);
$output->write(implode(';' . PHP_EOL, $sqls) . PHP_EOL);
} else {
$output->write('Creating database schema...' . PHP_EOL);
$schemaTool->createSchema($metadatas);
$output->write('Database schema created successfully!' . PHP_EOL);
}
}
}

View File

@@ -0,0 +1,111 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools\Console\Command\SchemaTool;
use Symfony\Component\Console\Input\InputArgument,
Symfony\Component\Console\Input\InputOption,
Symfony\Component\Console\Input\InputInterface,
Symfony\Component\Console\Output\OutputInterface,
Doctrine\ORM\Tools\SchemaTool;
/**
* Command to drop the database schema for a set of classes based on their mappings.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class DropCommand extends AbstractCommand
{
/**
* @see Console\Command\Command
*/
protected function configure()
{
$this
->setName('orm:schema-tool:drop')
->setDescription(
'Drop the complete database schema of EntityManager Storage Connection or generate the corresponding SQL output.'
)
->setDefinition(array(
new InputOption(
'dump-sql', null, InputOption::VALUE_NONE,
'Instead of try to apply generated SQLs into EntityManager Storage Connection, output them.'
),
new InputOption(
'force', null, InputOption::VALUE_NONE,
"Don't ask for the deletion of the database, but force the operation to run."
),
new InputOption(
'full-database', null, InputOption::VALUE_NONE,
'Instead of using the Class Metadata to detect the database table schema, drop ALL assets that the database contains.'
),
))
->setHelp(<<<EOT
Processes the schema and either drop the database schema of EntityManager Storage Connection or generate the SQL output.
Beware that the complete database is dropped by this command, even tables that are not relevant to your metadata model.
EOT
);
}
protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas)
{
$isFullDatabaseDrop = ($input->getOption('full-database'));
if ($input->getOption('dump-sql') === true) {
if ($isFullDatabaseDrop) {
$sqls = $schemaTool->getDropDatabaseSQL();
} else {
$sqls = $schemaTool->getDropSchemaSQL($metadatas);
}
$output->write(implode(';' . PHP_EOL, $sqls) . PHP_EOL);
} else if ($input->getOption('force') === true) {
$output->write('Dropping database schema...' . PHP_EOL);
if ($isFullDatabaseDrop) {
$schemaTool->dropDatabase();
} else {
$schemaTool->dropSchema($metadatas);
}
$output->write('Database schema dropped successfully!' . PHP_EOL);
} else {
$output->write('ATTENTION: This operation should not be executed in a production environment.' . PHP_EOL . PHP_EOL);
if ($isFullDatabaseDrop) {
$sqls = $schemaTool->getDropDatabaseSQL();
} else {
$sqls = $schemaTool->getDropSchemaSQL($metadatas);
}
if (count($sqls)) {
$output->write('Schema-Tool would execute ' . count($sqls) . ' queries to drop the database.' . PHP_EOL);
$output->write('Please run the operation with --force to execute these queries or use --dump-sql to see them.' . PHP_EOL);
} else {
$output->write('Nothing to drop. The database is empty!' . PHP_EOL);
}
}
}
}

View File

@@ -0,0 +1,135 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools\Console\Command\SchemaTool;
use Symfony\Component\Console\Input\InputArgument,
Symfony\Component\Console\Input\InputOption,
Symfony\Component\Console\Input\InputInterface,
Symfony\Component\Console\Output\OutputInterface,
Doctrine\ORM\Tools\SchemaTool;
/**
* Command to generate the SQL needed to update the database schema to match
* the current mapping information.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
* @author Ryan Weaver <ryan@thatsquality.com>
*/
class UpdateCommand extends AbstractCommand
{
protected $name = 'orm:schema-tool:update';
/**
* @see Console\Command\Command
*/
protected function configure()
{
$this
->setName($this->name)
->setDescription(
'Executes (or dumps) the SQL needed to update the database schema to match the current mapping metadata.'
)
->setDefinition(array(
new InputOption(
'complete', null, InputOption::VALUE_NONE,
'If defined, all assets of the database which are not relevant to the current metadata will be dropped.'
),
new InputOption(
'dump-sql', null, InputOption::VALUE_NONE,
'Dumps the generated SQL statements to the screen (does not execute them).'
),
new InputOption(
'force', null, InputOption::VALUE_NONE,
'Causes the generated SQL statements to be physically executed against your database.'
),
));
$fullName = $this->getName();
$this->setHelp(<<<EOT
The <info>$fullName</info> command generates the SQL needed to
synchronize the database schema with the current mapping metadata of the
default entity manager.
For example, if you add metadata for a new column to an entity, this command
would generate and output the SQL needed to add the new column to the database:
<info>$fullName --dump-sql</info>
Alternatively, you can execute the generated queries:
<info>$fullName --force</info>
Finally, be aware that if the <info>--complete</info> option is passed, this
task will drop all database assets (e.g. tables, etc) that are *not* described
by the current metadata. In other words, without this option, this task leaves
untouched any "extra" tables that exist in the database, but which aren't
described by any metadata.
EOT
);
}
protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas)
{
// Defining if update is complete or not (--complete not defined means $saveMode = true)
$saveMode = ($input->getOption('complete') !== true);
$sqls = $schemaTool->getUpdateSchemaSql($metadatas, $saveMode);
if (0 == count($sqls)) {
$output->writeln('Nothing to update - your database is already in sync with the current entity metadata.');
return;
}
$dumpSql = (true === $input->getOption('dump-sql'));
$force = (true === $input->getOption('force'));
if ($dumpSql && $force) {
throw new \InvalidArgumentException('You can pass either the --dump-sql or the --force option (but not both simultaneously).');
}
if ($dumpSql) {
$output->writeln(implode(';' . PHP_EOL, $sqls));
} else if ($force) {
$output->writeln('Updating database schema...');
$schemaTool->updateSchema($metadatas, $saveMode);
$output->writeln(sprintf('Database schema updated successfully! "<info>%s</info>" queries were executed', count($sqls)));
} else {
$output->writeln('<comment>ATTENTION</comment>: This operation should not be executed in a production environment.');
$output->writeln(' Use the incremental update to detect changes during development and use');
$output->writeln(' the SQL DDL provided to manually update your database in production.');
$output->writeln('');
$output->writeln(sprintf('The Schema-Tool would execute <info>"%s"</info> queries to update the database.', count($sqls)));
$output->writeln('Please run the operation by passing one of the following options:');
$output->writeln(sprintf(' <info>%s --force</info> to execute the command', $this->getName()));
$output->writeln(sprintf(' <info>%s --dump-sql</info> to dump the SQL statements to the screen', $this->getName()));
}
}
}

View File

@@ -0,0 +1,89 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools\Console\Command;
use Symfony\Component\Console\Input\InputArgument,
Symfony\Component\Console\Input\InputOption,
Symfony\Component\Console;
/**
* Validate that the current mapping is valid
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.com
* @since 1.0
* @version $Revision$
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class ValidateSchemaCommand extends Console\Command\Command
{
/**
* @see Console\Command\Command
*/
protected function configure()
{
$this
->setName('orm:validate-schema')
->setDescription('Validate the mapping files.')
->setHelp(<<<EOT
'Validate that the mapping files are correct and in sync with the database.'
EOT
);
}
/**
* @see Console\Command\Command
*/
protected function execute(Console\Input\InputInterface $input, Console\Output\OutputInterface $output)
{
$em = $this->getHelper('em')->getEntityManager();
$validator = new \Doctrine\ORM\Tools\SchemaValidator($em);
$errors = $validator->validateMapping();
$exit = 0;
if ($errors) {
foreach ($errors AS $className => $errorMessages) {
$output->write("<error>[Mapping] FAIL - The entity-class '" . $className . "' mapping is invalid:</error>\n");
foreach ($errorMessages AS $errorMessage) {
$output->write('* ' . $errorMessage . "\n");
}
$output->write("\n");
}
$exit += 1;
} else {
$output->write('<info>[Mapping] OK - The mapping files are correct.</info>' . "\n");
}
if (!$validator->schemaInSyncWithMetadata()) {
$output->write('<error>[Database] FAIL - The database schema is not in sync with the current mapping file.</error>' . "\n");
$exit += 2;
} else {
$output->write('<info>[Database] OK - The database schema is in sync with the mapping files.</info>' . "\n");
}
return $exit;
}
}

View File

@@ -0,0 +1,70 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools\Console;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Helper\HelperSet;
class ConsoleRunner
{
/**
* Run console with the given helperset.
*
* @param \Symfony\Component\Console\Helper\HelperSet $helperSet
* @return void
*/
static public function run(HelperSet $helperSet)
{
$cli = new Application('Doctrine Command Line Interface', \Doctrine\ORM\Version::VERSION);
$cli->setCatchExceptions(true);
$cli->setHelperSet($helperSet);
self::addCommands($cli);
$cli->run();
}
/**
* @param Application $cli
*/
static public function addCommands(Application $cli)
{
$cli->addCommands(array(
// DBAL Commands
new \Doctrine\DBAL\Tools\Console\Command\RunSqlCommand(),
new \Doctrine\DBAL\Tools\Console\Command\ImportCommand(),
// ORM Commands
new \Doctrine\ORM\Tools\Console\Command\ClearCache\MetadataCommand(),
new \Doctrine\ORM\Tools\Console\Command\ClearCache\ResultCommand(),
new \Doctrine\ORM\Tools\Console\Command\ClearCache\QueryCommand(),
new \Doctrine\ORM\Tools\Console\Command\SchemaTool\CreateCommand(),
new \Doctrine\ORM\Tools\Console\Command\SchemaTool\UpdateCommand(),
new \Doctrine\ORM\Tools\Console\Command\SchemaTool\DropCommand(),
new \Doctrine\ORM\Tools\Console\Command\EnsureProductionSettingsCommand(),
new \Doctrine\ORM\Tools\Console\Command\ConvertDoctrine1SchemaCommand(),
new \Doctrine\ORM\Tools\Console\Command\GenerateRepositoriesCommand(),
new \Doctrine\ORM\Tools\Console\Command\GenerateEntitiesCommand(),
new \Doctrine\ORM\Tools\Console\Command\GenerateProxiesCommand(),
new \Doctrine\ORM\Tools\Console\Command\ConvertMappingCommand(),
new \Doctrine\ORM\Tools\Console\Command\RunDqlCommand(),
new \Doctrine\ORM\Tools\Console\Command\ValidateSchemaCommand(),
new \Doctrine\ORM\Tools\Console\Command\InfoCommand()
));
}
}

View File

@@ -0,0 +1,74 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools\Console\Helper;
use Symfony\Component\Console\Helper\Helper,
Doctrine\ORM\EntityManager;
/**
* Doctrine CLI Connection Helper.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class EntityManagerHelper extends Helper
{
/**
* Doctrine ORM EntityManager
* @var EntityManager
*/
protected $_em;
/**
* Constructor
*
* @param Connection $connection Doctrine Database Connection
*/
public function __construct(EntityManager $em)
{
$this->_em = $em;
}
/**
* Retrieves Doctrine ORM EntityManager
*
* @return EntityManager
*/
public function getEntityManager()
{
return $this->_em;
}
/**
* @see Helper
*/
public function getName()
{
return 'entityManager';
}
}

View File

@@ -0,0 +1,80 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools\Console;
/**
* Used by CLI Tools to restrict entity-based commands to given patterns.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.com
* @since 1.0
* @version $Revision$
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class MetadataFilter extends \FilterIterator implements \Countable
{
/**
* Filter Metadatas by one or more filter options.
*
* @param array $metadatas
* @param array|string $filter
* @return array
*/
static public function filter(array $metadatas, $filter)
{
$metadatas = new MetadataFilter(new \ArrayIterator($metadatas), $filter);
return iterator_to_array($metadatas);
}
private $_filter = array();
public function __construct(\ArrayIterator $metadata, $filter)
{
$this->_filter = (array)$filter;
parent::__construct($metadata);
}
public function accept()
{
if (count($this->_filter) == 0) {
return true;
}
$it = $this->getInnerIterator();
$metadata = $it->current();
foreach ($this->_filter AS $filter) {
if (strpos($metadata->name, $filter) !== false) {
return true;
}
}
return false;
}
public function count()
{
return count($this->getInnerIterator());
}
}

View File

@@ -0,0 +1,274 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools;
use Doctrine\ORM\Mapping\ClassMetadataInfo,
Doctrine\ORM\Tools\Export\Driver\AbstractExporter,
Doctrine\Common\Util\Inflector;
/**
* Class to help with converting Doctrine 1 schema files to Doctrine 2 mapping files
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class ConvertDoctrine1Schema
{
private $_legacyTypeMap = array(
// TODO: This list may need to be updated
'clob' => 'text',
'timestamp' => 'datetime',
'enum' => 'string'
);
/**
* Constructor passes the directory or array of directories
* to convert the Doctrine 1 schema files from
*
* @param array $from
* @author Jonathan Wage
*/
public function __construct($from)
{
$this->_from = (array) $from;
}
/**
* Get an array of ClassMetadataInfo instances from the passed
* Doctrine 1 schema
*
* @return array $metadatas An array of ClassMetadataInfo instances
*/
public function getMetadata()
{
$schema = array();
foreach ($this->_from as $path) {
if (is_dir($path)) {
$files = glob($path . '/*.yml');
foreach ($files as $file) {
$schema = array_merge($schema, (array) \Symfony\Component\Yaml\Yaml::parse($file));
}
} else {
$schema = array_merge($schema, (array) \Symfony\Component\Yaml\Yaml::parse($path));
}
}
$metadatas = array();
foreach ($schema as $className => $mappingInformation) {
$metadatas[] = $this->_convertToClassMetadataInfo($className, $mappingInformation);
}
return $metadatas;
}
private function _convertToClassMetadataInfo($className, $mappingInformation)
{
$metadata = new ClassMetadataInfo($className);
$this->_convertTableName($className, $mappingInformation, $metadata);
$this->_convertColumns($className, $mappingInformation, $metadata);
$this->_convertIndexes($className, $mappingInformation, $metadata);
$this->_convertRelations($className, $mappingInformation, $metadata);
return $metadata;
}
private function _convertTableName($className, array $model, ClassMetadataInfo $metadata)
{
if (isset($model['tableName']) && $model['tableName']) {
$e = explode('.', $model['tableName']);
if (count($e) > 1) {
$metadata->table['schema'] = $e[0];
$metadata->table['name'] = $e[1];
} else {
$metadata->table['name'] = $e[0];
}
}
}
private function _convertColumns($className, array $model, ClassMetadataInfo $metadata)
{
$id = false;
if (isset($model['columns']) && $model['columns']) {
foreach ($model['columns'] as $name => $column) {
$fieldMapping = $this->_convertColumn($className, $name, $column, $metadata);
if (isset($fieldMapping['id']) && $fieldMapping['id']) {
$id = true;
}
}
}
if ( ! $id) {
$fieldMapping = array(
'fieldName' => 'id',
'columnName' => 'id',
'type' => 'integer',
'id' => true
);
$metadata->mapField($fieldMapping);
$metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_AUTO);
}
}
private function _convertColumn($className, $name, $column, ClassMetadataInfo $metadata)
{
if (is_string($column)) {
$string = $column;
$column = array();
$column['type'] = $string;
}
if ( ! isset($column['name'])) {
$column['name'] = $name;
}
// check if a column alias was used (column_name as field_name)
if (preg_match("/(\w+)\sas\s(\w+)/i", $column['name'], $matches)) {
$name = $matches[1];
$column['name'] = $name;
$column['alias'] = $matches[2];
}
if (preg_match("/([a-zA-Z]+)\(([0-9]+)\)/", $column['type'], $matches)) {
$column['type'] = $matches[1];
$column['length'] = $matches[2];
}
$column['type'] = strtolower($column['type']);
// check if legacy column type (1.x) needs to be mapped to a 2.0 one
if (isset($this->_legacyTypeMap[$column['type']])) {
$column['type'] = $this->_legacyTypeMap[$column['type']];
}
if ( ! \Doctrine\DBAL\Types\Type::hasType($column['type'])) {
throw ToolsException::couldNotMapDoctrine1Type($column['type']);
}
$fieldMapping = array();
if (isset($column['primary'])) {
$fieldMapping['id'] = true;
}
$fieldMapping['fieldName'] = isset($column['alias']) ? $column['alias'] : $name;
$fieldMapping['columnName'] = $column['name'];
$fieldMapping['type'] = $column['type'];
if (isset($column['length'])) {
$fieldMapping['length'] = $column['length'];
}
$allowed = array('precision', 'scale', 'unique', 'options', 'notnull', 'version');
foreach ($column as $key => $value) {
if (in_array($key, $allowed)) {
$fieldMapping[$key] = $value;
}
}
$metadata->mapField($fieldMapping);
if (isset($column['autoincrement'])) {
$metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_AUTO);
} else if (isset($column['sequence'])) {
$metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_SEQUENCE);
$definition = array(
'sequenceName' => is_array($column['sequence']) ? $column['sequence']['name']:$column['sequence']
);
if (isset($column['sequence']['size'])) {
$definition['allocationSize'] = $column['sequence']['size'];
}
if (isset($column['sequence']['value'])) {
$definition['initialValue'] = $column['sequence']['value'];
}
$metadata->setSequenceGeneratorDefinition($definition);
}
return $fieldMapping;
}
private function _convertIndexes($className, array $model, ClassMetadataInfo $metadata)
{
if (isset($model['indexes']) && $model['indexes']) {
foreach ($model['indexes'] as $name => $index) {
$type = (isset($index['type']) && $index['type'] == 'unique')
? 'uniqueConstraints' : 'indexes';
$metadata->table[$type][$name] = array(
'columns' => $index['fields']
);
}
}
}
private function _convertRelations($className, array $model, ClassMetadataInfo $metadata)
{
if (isset($model['relations']) && $model['relations']) {
foreach ($model['relations'] as $name => $relation) {
if ( ! isset($relation['alias'])) {
$relation['alias'] = $name;
}
if ( ! isset($relation['class'])) {
$relation['class'] = $name;
}
if ( ! isset($relation['local'])) {
$relation['local'] = Inflector::tableize($relation['class']);
}
if ( ! isset($relation['foreign'])) {
$relation['foreign'] = 'id';
}
if ( ! isset($relation['foreignAlias'])) {
$relation['foreignAlias'] = $className;
}
if (isset($relation['refClass'])) {
$type = 'many';
$foreignType = 'many';
$joinColumns = array();
} else {
$type = isset($relation['type']) ? $relation['type'] : 'one';
$foreignType = isset($relation['foreignType']) ? $relation['foreignType'] : 'many';
$joinColumns = array(
array(
'name' => $relation['local'],
'referencedColumnName' => $relation['foreign'],
'onDelete' => isset($relation['onDelete']) ? $relation['onDelete'] : null,
)
);
}
if ($type == 'one' && $foreignType == 'one') {
$method = 'mapOneToOne';
} else if ($type == 'many' && $foreignType == 'many') {
$method = 'mapManyToMany';
} else {
$method = 'mapOneToMany';
}
$associationMapping = array();
$associationMapping['fieldName'] = $relation['alias'];
$associationMapping['targetEntity'] = $relation['class'];
$associationMapping['mappedBy'] = $relation['foreignAlias'];
$associationMapping['joinColumns'] = $joinColumns;
$metadata->$method($associationMapping);
}
}
}
}

View File

@@ -0,0 +1,151 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools;
use Doctrine\ORM\Event\OnFlushEventArgs;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\UnitOfWork;
/**
* Use this logger to dump the identity map during the onFlush event. This is useful for debugging
* weird UnitOfWork behavior with complex operations.
*/
class DebugUnitOfWorkListener
{
private $file;
private $context;
/**
* Pass a stream and contet information for the debugging session.
*
* The stream can be php://output to print to the screen.
*
* @param string $file
* @param string $context
*/
public function __construct($file = 'php://output', $context = '')
{
$this->file = $file;
$this->context = $context;
}
public function onFlush(OnFlushEventArgs $args)
{
$this->dumpIdentityMap($args->getEntityManager());
}
/**
* Dump the contents of the identity map into a stream.
*
* @param EntityManager $em
* @return void
*/
public function dumpIdentityMap(EntityManager $em)
{
$uow = $em->getUnitOfWork();
$identityMap = $uow->getIdentityMap();
$fh = fopen($this->file, "x+");
if (count($identityMap) == 0) {
fwrite($fh, "Flush Operation [".$this->context."] - Empty identity map.\n");
return;
}
fwrite($fh, "Flush Operation [".$this->context."] - Dumping identity map:\n");
foreach ($identityMap AS $className => $map) {
fwrite($fh, "Class: ". $className . "\n");
foreach ($map AS $idHash => $entity) {
fwrite($fh, " Entity: " . $this->getIdString($entity, $uow) . " " . spl_object_hash($entity)."\n");
fwrite($fh, " Associations:\n");
$cm = $em->getClassMetadata($className);
foreach ($cm->associationMappings AS $field => $assoc) {
fwrite($fh, " " . $field . " ");
$value = $cm->reflFields[$field]->getValue($entity);
if ($assoc['type'] & ClassMetadata::TO_ONE) {
if ($value === null) {
fwrite($fh, " NULL\n");
} else {
if ($value instanceof Proxy && !$value->__isInitialized__) {
fwrite($fh, "[PROXY] ");
}
fwrite($fh, $this->getIdString($value, $uow) . " " . spl_object_hash($value) . "\n");
}
} else {
$initialized = !($value instanceof PersistentCollection) || $value->isInitialized();
if ($value === null) {
fwrite($fh, " NULL\n");
} else if ($initialized) {
fwrite($fh, "[INITIALIZED] " . $this->getType($value). " " . count($value) . " elements\n");
foreach ($value AS $obj) {
fwrite($fh, " " . $this->getIdString($obj, $uow) . " " . spl_object_hash($obj)."\n");
}
} else {
fwrite($fh, "[PROXY] " . $this->getType($value) . " unknown element size\n");
foreach ($value->unwrap() AS $obj) {
fwrite($fh, " " . $this->getIdString($obj, $uow) . " " . spl_object_hash($obj)."\n");
}
}
}
}
}
}
fclose($fh);
}
private function getType($var)
{
if (is_object($var)) {
$refl = new \ReflectionObject($var);
return $refl->getShortname();
} else {
return gettype($var);
}
}
private function getIdString($entity, $uow)
{
if ($uow->isInIdentityMap($entity)) {
$ids = $uow->getEntityIdentifier($entity);
$idstring = "";
foreach ($ids AS $k => $v) {
$idstring .= $k."=".$v;
}
} else {
$idstring = "NEWOBJECT ";
}
$state = $uow->getEntityState($entity);
if ($state == UnitOfWork::STATE_NEW) {
$idstring .= " [NEW]";
} else if ($state == UnitOfWork::STATE_REMOVED) {
$idstring .= " [REMOVED]";
} else if ($state == UnitOfWork::STATE_MANAGED) {
$idstring .= " [MANAGED]";
} else if ($state == UnitOfwork::STATE_DETACHED) {
$idstring .= " [DETACHED]";
}
return $idstring;
}
}

View File

@@ -0,0 +1,45 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools;
use Doctrine\ORM\Mapping\ClassMetadataFactory;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
/**
* The DisconnectedClassMetadataFactory is used to create ClassMetadataInfo objects
* that do not require the entity class actually exist. This allows us to
* load some mapping information and use it to do things like generate code
* from the mapping information.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class DisconnectedClassMetadataFactory extends ClassMetadataFactory
{
public function getReflectionService()
{
return new \Doctrine\Common\Persistence\Mapping\StaticReflectionService;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,83 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools;
/**
* Class to generate entity repository classes
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class EntityRepositoryGenerator
{
protected static $_template =
'<?php
namespace <namespace>;
use Doctrine\ORM\EntityRepository;
/**
* <className>
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class <className> extends EntityRepository
{
}';
public function generateEntityRepositoryClass($fullClassName)
{
$namespace = substr($fullClassName, 0, strrpos($fullClassName, '\\'));
$className = substr($fullClassName, strrpos($fullClassName, '\\') + 1, strlen($fullClassName));
$variables = array(
'<namespace>' => $namespace,
'<className>' => $className
);
return str_replace(array_keys($variables), array_values($variables), self::$_template);
}
public function writeEntityRepositoryClass($fullClassName, $outputDirectory)
{
$code = $this->generateEntityRepositoryClass($fullClassName);
$path = $outputDirectory . DIRECTORY_SEPARATOR
. str_replace('\\', \DIRECTORY_SEPARATOR, $fullClassName) . '.php';
$dir = dirname($path);
if ( ! is_dir($dir)) {
mkdir($dir, 0777, true);
}
if ( ! file_exists($path)) {
file_put_contents($path, $code);
}
}
}

View File

@@ -0,0 +1,65 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools\Event;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\ORM\EntityManager;
/**
* Event Args used for the Events::postGenerateSchema event.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.com
* @since 1.0
* @version $Revision$
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class GenerateSchemaEventArgs extends \Doctrine\Common\EventArgs
{
private $_em = null;
private $_schema = null;
/**
* @param ClassMetadata $classMetadata
* @param Schema $schema
* @param Table $classTable
*/
public function __construct(EntityManager $em, Schema $schema)
{
$this->_em = $em;
$this->_schema = $schema;
}
/**
* @return EntityManager
*/
public function getEntityManager() {
return $this->_em;
}
/**
* @return Schema
*/
public function getSchema() {
return $this->_schema;
}
}

View File

@@ -0,0 +1,75 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools\Event;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Schema\Table;
/**
* Event Args used for the Events::postGenerateSchemaTable event.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.com
* @since 1.0
* @version $Revision$
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class GenerateSchemaTableEventArgs extends \Doctrine\Common\EventArgs
{
private $_classMetadata = null;
private $_schema = null;
private $_classTable = null;
/**
* @param ClassMetadata $classMetadata
* @param Schema $schema
* @param Table $classTable
*/
public function __construct(ClassMetadata $classMetadata, Schema $schema, Table $classTable)
{
$this->_classMetadata = $classMetadata;
$this->_schema = $schema;
$this->_classTable = $classTable;
}
/**
* @return ClassMetadata
*/
public function getClassMetadata() {
return $this->_classMetadata;
}
/**
* @return Schema
*/
public function getSchema() {
return $this->_schema;
}
/**
* @return Table
*/
public function getClassTable() {
return $this->_classTable;
}
}

View File

@@ -0,0 +1,76 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools\Export;
use Doctrine\ORM\Tools\Export\ExportException,
Doctrine\ORM\EntityManager;
/**
* Class used for converting your mapping information between the
* supported formats: yaml, xml, and php/annotation.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Jonathan Wage <jonwage@gmail.com>
*/
class ClassMetadataExporter
{
private static $_exporterDrivers = array(
'xml' => 'Doctrine\ORM\Tools\Export\Driver\XmlExporter',
'yaml' => 'Doctrine\ORM\Tools\Export\Driver\YamlExporter',
'yml' => 'Doctrine\ORM\Tools\Export\Driver\YamlExporter',
'php' => 'Doctrine\ORM\Tools\Export\Driver\PhpExporter',
'annotation' => 'Doctrine\ORM\Tools\Export\Driver\AnnotationExporter'
);
/**
* Register a new exporter driver class under a specified name
*
* @param string $name
* @param string $class
*/
public static function registerExportDriver($name, $class)
{
self::$_exporterDrivers[$name] = $class;
}
/**
* Get a exporter driver instance
*
* @param string $type The type to get (yml, xml, etc.)
* @param string $source The directory where the exporter will export to
* @return AbstractExporter $exporter
*/
public function getExporter($type, $dest = null)
{
if ( ! isset(self::$_exporterDrivers[$type])) {
throw ExportException::invalidExporterDriverType($type);
}
$class = self::$_exporterDrivers[$type];
return new $class($dest);
}
}

View File

@@ -0,0 +1,217 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools\Export\Driver;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\ORM\Tools\Export\ExportException;
/**
* Abstract base class which is to be used for the Exporter drivers
* which can be found in \Doctrine\ORM\Tools\Export\Driver
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Jonathan Wage <jonwage@gmail.com>
*/
abstract class AbstractExporter
{
protected $_metadata = array();
protected $_outputDir;
protected $_extension;
protected $_overwriteExistingFiles = false;
public function __construct($dir = null)
{
$this->_outputDir = $dir;
}
public function setOverwriteExistingFiles($overwrite)
{
$this->_overwriteExistingFiles = $overwrite;
}
/**
* Converts a single ClassMetadata instance to the exported format
* and returns it
*
* @param ClassMetadataInfo $metadata
* @return mixed $exported
*/
abstract public function exportClassMetadata(ClassMetadataInfo $metadata);
/**
* Set the array of ClassMetadataInfo instances to export
*
* @param array $metadata
* @return void
*/
public function setMetadata(array $metadata)
{
$this->_metadata = $metadata;
}
/**
* Get the extension used to generated the path to a class
*
* @return string $extension
*/
public function getExtension()
{
return $this->_extension;
}
/**
* Set the directory to output the mapping files to
*
* [php]
* $exporter = new YamlExporter($metadata);
* $exporter->setOutputDir(__DIR__ . '/yaml');
* $exporter->export();
*
* @param string $dir
* @return void
*/
public function setOutputDir($dir)
{
$this->_outputDir = $dir;
}
/**
* Export each ClassMetadata instance to a single Doctrine Mapping file
* named after the entity
*
* @return void
*/
public function export()
{
if ( ! is_dir($this->_outputDir)) {
mkdir($this->_outputDir, 0777, true);
}
foreach ($this->_metadata as $metadata) {
//In case output is returned, write it to a file, skip otherwise
if($output = $this->exportClassMetadata($metadata)){
$path = $this->_generateOutputPath($metadata);
$dir = dirname($path);
if ( ! is_dir($dir)) {
mkdir($dir, 0777, true);
}
if (file_exists($path) && !$this->_overwriteExistingFiles) {
throw ExportException::attemptOverwriteExistingFile($path);
}
file_put_contents($path, $output);
}
}
}
/**
* Generate the path to write the class for the given ClassMetadataInfo instance
*
* @param ClassMetadataInfo $metadata
* @return string $path
*/
protected function _generateOutputPath(ClassMetadataInfo $metadata)
{
return $this->_outputDir . '/' . str_replace('\\', '.', $metadata->name) . $this->_extension;
}
/**
* Set the directory to output the mapping files to
*
* [php]
* $exporter = new YamlExporter($metadata, __DIR__ . '/yaml');
* $exporter->setExtension('.yml');
* $exporter->export();
*
* @param string $extension
* @return void
*/
public function setExtension($extension)
{
$this->_extension = $extension;
}
protected function _getInheritanceTypeString($type)
{
switch ($type)
{
case ClassMetadataInfo::INHERITANCE_TYPE_NONE:
return 'NONE';
break;
case ClassMetadataInfo::INHERITANCE_TYPE_JOINED:
return 'JOINED';
break;
case ClassMetadataInfo::INHERITANCE_TYPE_SINGLE_TABLE:
return 'SINGLE_TABLE';
break;
case ClassMetadataInfo::INHERITANCE_TYPE_TABLE_PER_CLASS:
return 'PER_CLASS';
break;
}
}
protected function _getChangeTrackingPolicyString($policy)
{
switch ($policy)
{
case ClassMetadataInfo::CHANGETRACKING_DEFERRED_IMPLICIT:
return 'DEFERRED_IMPLICIT';
break;
case ClassMetadataInfo::CHANGETRACKING_DEFERRED_EXPLICIT:
return 'DEFERRED_EXPLICIT';
break;
case ClassMetadataInfo::CHANGETRACKING_NOTIFY:
return 'NOTIFY';
break;
}
}
protected function _getIdGeneratorTypeString($type)
{
switch ($type)
{
case ClassMetadataInfo::GENERATOR_TYPE_AUTO:
return 'AUTO';
break;
case ClassMetadataInfo::GENERATOR_TYPE_SEQUENCE:
return 'SEQUENCE';
break;
case ClassMetadataInfo::GENERATOR_TYPE_TABLE:
return 'TABLE';
break;
case ClassMetadataInfo::GENERATOR_TYPE_IDENTITY:
return 'IDENTITY';
break;
}
}
}

View File

@@ -0,0 +1,72 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools\Export\Driver;
use Doctrine\ORM\Mapping\ClassMetadataInfo,
Doctrine\ORM\Mapping\AssociationMapping,
Doctrine\ORM\Tools\EntityGenerator;
/**
* ClassMetadata exporter for PHP classes with annotations
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Jonathan Wage <jonwage@gmail.com>
*/
class AnnotationExporter extends AbstractExporter
{
protected $_extension = '.php';
private $_entityGenerator;
/**
* Converts a single ClassMetadata instance to the exported format
* and returns it
*
* @param ClassMetadataInfo $metadata
* @return string $exported
*/
public function exportClassMetadata(ClassMetadataInfo $metadata)
{
if ( ! $this->_entityGenerator) {
throw new \RuntimeException('For the AnnotationExporter you must set an EntityGenerator instance with the setEntityGenerator() method.');
}
$this->_entityGenerator->setGenerateAnnotations(true);
$this->_entityGenerator->setGenerateStubMethods(false);
$this->_entityGenerator->setRegenerateEntityIfExists(false);
$this->_entityGenerator->setUpdateEntityIfExists(false);
return $this->_entityGenerator->generateEntityClass($metadata);
}
protected function _generateOutputPath(ClassMetadataInfo $metadata)
{
return $this->_outputDir . '/' . str_replace('\\', '/', $metadata->name) . $this->_extension;
}
public function setEntityGenerator(EntityGenerator $entityGenerator)
{
$this->_entityGenerator = $entityGenerator;
}
}

View File

@@ -0,0 +1,169 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools\Export\Driver;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
/**
* ClassMetadata exporter for PHP code
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Jonathan Wage <jonwage@gmail.com>
*/
class PhpExporter extends AbstractExporter
{
protected $_extension = '.php';
/**
* Converts a single ClassMetadata instance to the exported format
* and returns it
*
* @param ClassMetadataInfo $metadata
* @return mixed $exported
*/
public function exportClassMetadata(ClassMetadataInfo $metadata)
{
$lines = array();
$lines[] = '<?php';
$lines[] = null;
$lines[] = 'use Doctrine\ORM\Mapping\ClassMetadataInfo;';
$lines[] = null;
if ($metadata->isMappedSuperclass) {
$lines[] = '$metadata->isMappedSuperclass = true;';
}
if ($metadata->inheritanceType) {
$lines[] = '$metadata->setInheritanceType(ClassMetadataInfo::INHERITANCE_TYPE_' . $this->_getInheritanceTypeString($metadata->inheritanceType) . ');';
}
if ($metadata->customRepositoryClassName) {
$lines[] = "\$metadata->customRepositoryClassName = '" . $metadata->customRepositoryClassName . "';";
}
if ($metadata->table) {
$lines[] = '$metadata->setPrimaryTable(' . $this->_varExport($metadata->table) . ');';
}
if ($metadata->discriminatorColumn) {
$lines[] = '$metadata->setDiscriminatorColumn(' . $this->_varExport($metadata->discriminatorColumn) . ');';
}
if ($metadata->discriminatorMap) {
$lines[] = '$metadata->setDiscriminatorMap(' . $this->_varExport($metadata->discriminatorMap) . ');';
}
if ($metadata->changeTrackingPolicy) {
$lines[] = '$metadata->setChangeTrackingPolicy(ClassMetadataInfo::CHANGETRACKING_' . $this->_getChangeTrackingPolicyString($metadata->changeTrackingPolicy) . ');';
}
if ($metadata->lifecycleCallbacks) {
foreach ($metadata->lifecycleCallbacks as $event => $callbacks) {
foreach ($callbacks as $callback) {
$lines[] = "\$metadata->addLifecycleCallback('$callback', '$event');";
}
}
}
foreach ($metadata->fieldMappings as $fieldMapping) {
$lines[] = '$metadata->mapField(' . $this->_varExport($fieldMapping) . ');';
}
if ( ! $metadata->isIdentifierComposite && $generatorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) {
$lines[] = '$metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_' . $generatorType . ');';
}
foreach ($metadata->associationMappings as $associationMapping) {
$cascade = array('remove', 'persist', 'refresh', 'merge', 'detach');
foreach ($cascade as $key => $value) {
if ( ! $associationMapping['isCascade'.ucfirst($value)]) {
unset($cascade[$key]);
}
}
$associationMappingArray = array(
'fieldName' => $associationMapping['fieldName'],
'targetEntity' => $associationMapping['targetEntity'],
'cascade' => $cascade,
);
if ($associationMapping['type'] & ClassMetadataInfo::TO_ONE) {
$method = 'mapOneToOne';
$oneToOneMappingArray = array(
'mappedBy' => $associationMapping['mappedBy'],
'inversedBy' => $associationMapping['inversedBy'],
'joinColumns' => $associationMapping['joinColumns'],
'orphanRemoval' => $associationMapping['orphanRemoval'],
);
$associationMappingArray = array_merge($associationMappingArray, $oneToOneMappingArray);
} else if ($associationMapping['type'] == ClassMetadataInfo::ONE_TO_MANY) {
$method = 'mapOneToMany';
$potentialAssociationMappingIndexes = array(
'mappedBy',
'orphanRemoval',
'orderBy',
);
foreach ($potentialAssociationMappingIndexes as $index) {
if (isset($associationMapping[$index])) {
$oneToManyMappingArray[$index] = $associationMapping[$index];
}
}
$associationMappingArray = array_merge($associationMappingArray, $oneToManyMappingArray);
} else if ($associationMapping['type'] == ClassMetadataInfo::MANY_TO_MANY) {
$method = 'mapManyToMany';
$potentialAssociationMappingIndexes = array(
'mappedBy',
'joinTable',
'orderBy',
);
foreach ($potentialAssociationMappingIndexes as $index) {
if (isset($associationMapping[$index])) {
$manyToManyMappingArray[$index] = $associationMapping[$index];
}
}
$associationMappingArray = array_merge($associationMappingArray, $manyToManyMappingArray);
}
$lines[] = '$metadata->' . $method . '(' . $this->_varExport($associationMappingArray) . ');';
}
return implode("\n", $lines);
}
protected function _varExport($var)
{
$export = var_export($var, true);
$export = str_replace("\n", PHP_EOL . str_repeat(' ', 8), $export);
$export = str_replace(' ', ' ', $export);
$export = str_replace('array (', 'array(', $export);
$export = str_replace('array( ', 'array(', $export);
$export = str_replace(',)', ')', $export);
$export = str_replace(', )', ')', $export);
$export = str_replace(' ', ' ', $export);
return $export;
}
}

View File

@@ -0,0 +1,320 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools\Export\Driver;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
/**
* ClassMetadata exporter for Doctrine XML mapping files
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Jonathan Wage <jonwage@gmail.com>
*/
class XmlExporter extends AbstractExporter
{
protected $_extension = '.dcm.xml';
/**
* Converts a single ClassMetadata instance to the exported format
* and returns it
*
* @param ClassMetadataInfo $metadata
* @return mixed $exported
*/
public function exportClassMetadata(ClassMetadataInfo $metadata)
{
$xml = new \SimpleXmlElement("<?xml version=\"1.0\" encoding=\"utf-8\"?><doctrine-mapping ".
"xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\" " .
"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ".
"xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd\" />");
/*$xml->addAttribute('xmlns', 'http://doctrine-project.org/schemas/orm/doctrine-mapping');
$xml->addAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
$xml->addAttribute('xsi:schemaLocation', 'http://doctrine-project.org/schemas/orm/doctrine-mapping http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd');*/
if ($metadata->isMappedSuperclass) {
$root = $xml->addChild('mapped-superclass');
} else {
$root = $xml->addChild('entity');
}
if ($metadata->customRepositoryClassName) {
$root->addAttribute('repository-class', $metadata->customRepositoryClassName);
}
$root->addAttribute('name', $metadata->name);
if (isset($metadata->table['name'])) {
$root->addAttribute('table', $metadata->table['name']);
}
if (isset($metadata->table['schema'])) {
$root->addAttribute('schema', $metadata->table['schema']);
}
if (isset($metadata->table['inheritance-type'])) {
$root->addAttribute('inheritance-type', $metadata->table['inheritance-type']);
}
if ($metadata->discriminatorColumn) {
$discriminatorColumnXml = $root->addChild('discriminiator-column');
$discriminatorColumnXml->addAttribute('name', $metadata->discriminatorColumn['name']);
$discriminatorColumnXml->addAttribute('type', $metadata->discriminatorColumn['type']);
$discriminatorColumnXml->addAttribute('length', $metadata->discriminatorColumn['length']);
}
if ($metadata->discriminatorMap) {
$discriminatorMapXml = $root->addChild('discriminator-map');
foreach ($metadata->discriminatorMap as $value => $className) {
$discriminatorMappingXml = $discriminatorMapXml->addChild('discriminator-mapping');
$discriminatorMappingXml->addAttribute('value', $value);
$discriminatorMappingXml->addAttribute('class', $className);
}
}
$root->addChild('change-tracking-policy', $this->_getChangeTrackingPolicyString($metadata->changeTrackingPolicy));
if (isset($metadata->table['indexes'])) {
$indexesXml = $root->addChild('indexes');
foreach ($metadata->table['indexes'] as $name => $index) {
$indexXml = $indexesXml->addChild('index');
$indexXml->addAttribute('name', $name);
$indexXml->addAttribute('columns', implode(',', $index['columns']));
}
}
if (isset($metadata->table['uniqueConstraints'])) {
$uniqueConstraintsXml = $root->addChild('unique-constraints');
foreach ($metadata->table['uniqueConstraints'] as $unique) {
$uniqueConstraintXml = $uniqueConstraintsXml->addChild('unique-constraint');
$uniqueConstraintXml->addAttribute('name', $unique['name']);
$uniqueConstraintXml->addAttribute('columns', implode(',', $unique['columns']));
}
}
$fields = $metadata->fieldMappings;
$id = array();
foreach ($fields as $name => $field) {
if (isset($field['id']) && $field['id']) {
$id[$name] = $field;
unset($fields[$name]);
}
}
if ( ! $metadata->isIdentifierComposite && $idGeneratorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) {
$id[$metadata->getSingleIdentifierFieldName()]['generator']['strategy'] = $idGeneratorType;
}
if ($id) {
foreach ($id as $field) {
$idXml = $root->addChild('id');
$idXml->addAttribute('name', $field['fieldName']);
$idXml->addAttribute('type', $field['type']);
if (isset($field['columnName'])) {
$idXml->addAttribute('column', $field['columnName']);
}
if (isset($field['associationKey']) && $field['associationKey']) {
$idXml->addAttribute('association-key', 'true');
}
if ($idGeneratorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) {
$generatorXml = $idXml->addChild('generator');
$generatorXml->addAttribute('strategy', $idGeneratorType);
}
}
}
if ($fields) {
foreach ($fields as $field) {
$fieldXml = $root->addChild('field');
$fieldXml->addAttribute('name', $field['fieldName']);
$fieldXml->addAttribute('type', $field['type']);
if (isset($field['columnName'])) {
$fieldXml->addAttribute('column', $field['columnName']);
}
if (isset($field['length'])) {
$fieldXml->addAttribute('length', $field['length']);
}
if (isset($field['precision'])) {
$fieldXml->addAttribute('precision', $field['precision']);
}
if (isset($field['scale'])) {
$fieldXml->addAttribute('scale', $field['scale']);
}
if (isset($field['unique']) && $field['unique']) {
$fieldXml->addAttribute('unique', $field['unique']);
}
if (isset($field['options'])) {
$optionsXml = $fieldXml->addChild('options');
foreach ($field['options'] as $key => $value) {
$optionsXml->addAttribute($key, $value);
}
}
if (isset($field['version'])) {
$fieldXml->addAttribute('version', $field['version']);
}
if (isset($field['columnDefinition'])) {
$fieldXml->addAttribute('column-definition', $field['columnDefinition']);
}
}
}
foreach ($metadata->associationMappings as $name => $associationMapping) {
if ($associationMapping['type'] == ClassMetadataInfo::ONE_TO_ONE) {
$associationMappingXml = $root->addChild('one-to-one');
} else if ($associationMapping['type'] == ClassMetadataInfo::MANY_TO_ONE) {
$associationMappingXml = $root->addChild('many-to-one');
} else if ($associationMapping['type'] == ClassMetadataInfo::ONE_TO_MANY) {
$associationMappingXml = $root->addChild('one-to-many');
} else if ($associationMapping['type'] == ClassMetadataInfo::MANY_TO_MANY) {
$associationMappingXml = $root->addChild('many-to-many');
}
$associationMappingXml->addAttribute('field', $associationMapping['fieldName']);
$associationMappingXml->addAttribute('target-entity', $associationMapping['targetEntity']);
if (isset($associationMapping['mappedBy'])) {
$associationMappingXml->addAttribute('mapped-by', $associationMapping['mappedBy']);
}
if (isset($associationMapping['inversedBy'])) {
$associationMappingXml->addAttribute('inversed-by', $associationMapping['inversedBy']);
}
if (isset($associationMapping['orphanRemoval'])) {
$associationMappingXml->addAttribute('orphan-removal', $associationMapping['orphanRemoval']);
}
if (isset($associationMapping['joinTable']) && $associationMapping['joinTable']) {
$joinTableXml = $associationMappingXml->addChild('join-table');
$joinTableXml->addAttribute('name', $associationMapping['joinTable']['name']);
$joinColumnsXml = $joinTableXml->addChild('join-columns');
foreach ($associationMapping['joinTable']['joinColumns'] as $joinColumn) {
$joinColumnXml = $joinColumnsXml->addChild('join-column');
$joinColumnXml->addAttribute('name', $joinColumn['name']);
$joinColumnXml->addAttribute('referenced-column-name', $joinColumn['referencedColumnName']);
if (isset($joinColumn['onDelete'])) {
$joinColumnXml->addAttribute('on-delete', $joinColumn['onDelete']);
}
}
$inverseJoinColumnsXml = $joinTableXml->addChild('inverse-join-columns');
foreach ($associationMapping['joinTable']['inverseJoinColumns'] as $inverseJoinColumn) {
$inverseJoinColumnXml = $inverseJoinColumnsXml->addChild('join-column');
$inverseJoinColumnXml->addAttribute('name', $inverseJoinColumn['name']);
$inverseJoinColumnXml->addAttribute('referenced-column-name', $inverseJoinColumn['referencedColumnName']);
if (isset($inverseJoinColumn['onDelete'])) {
$inverseJoinColumnXml->addAttribute('on-delete', $inverseJoinColumn['onDelete']);
}
if (isset($inverseJoinColumn['columnDefinition'])) {
$inverseJoinColumnXml->addAttribute('column-definition', $inverseJoinColumn['columnDefinition']);
}
if (isset($inverseJoinColumn['nullable'])) {
$inverseJoinColumnXml->addAttribute('nullable', $inverseJoinColumn['nullable']);
}
if (isset($inverseJoinColumn['orderBy'])) {
$inverseJoinColumnXml->addAttribute('order-by', $inverseJoinColumn['orderBy']);
}
}
}
if (isset($associationMapping['joinColumns'])) {
$joinColumnsXml = $associationMappingXml->addChild('join-columns');
foreach ($associationMapping['joinColumns'] as $joinColumn) {
$joinColumnXml = $joinColumnsXml->addChild('join-column');
$joinColumnXml->addAttribute('name', $joinColumn['name']);
$joinColumnXml->addAttribute('referenced-column-name', $joinColumn['referencedColumnName']);
if (isset($joinColumn['onDelete'])) {
$joinColumnXml->addAttribute('on-delete', $joinColumn['onDelete']);
}
if (isset($joinColumn['columnDefinition'])) {
$joinColumnXml->addAttribute('column-definition', $joinColumn['columnDefinition']);
}
if (isset($joinColumn['nullable'])) {
$joinColumnXml->addAttribute('nullable', $joinColumn['nullable']);
}
}
}
if (isset($associationMapping['orderBy'])) {
$orderByXml = $associationMappingXml->addChild('order-by');
foreach ($associationMapping['orderBy'] as $name => $direction) {
$orderByFieldXml = $orderByXml->addChild('order-by-field');
$orderByFieldXml->addAttribute('name', $name);
$orderByFieldXml->addAttribute('direction', $direction);
}
}
$cascade = array();
if ($associationMapping['isCascadeRemove']) {
$cascade[] = 'cascade-remove';
}
if ($associationMapping['isCascadePersist']) {
$cascade[] = 'cascade-persist';
}
if ($associationMapping['isCascadeRefresh']) {
$cascade[] = 'cascade-refresh';
}
if ($associationMapping['isCascadeMerge']) {
$cascade[] = 'cascade-merge';
}
if ($associationMapping['isCascadeDetach']) {
$cascade[] = 'cascade-detach';
}
if (count($cascade) === 5) {
$cascade = array('cascade-all');
}
if ($cascade) {
$cascadeXml = $associationMappingXml->addChild('cascade');
foreach ($cascade as $type) {
$cascadeXml->addChild($type);
}
}
}
if (isset($metadata->lifecycleCallbacks)) {
$lifecycleCallbacksXml = $root->addChild('lifecycle-callbacks');
foreach ($metadata->lifecycleCallbacks as $name => $methods) {
foreach ($methods as $method) {
$lifecycleCallbackXml = $lifecycleCallbacksXml->addChild('lifecycle-callback');
$lifecycleCallbackXml->addAttribute('type', $name);
$lifecycleCallbackXml->addAttribute('method', $method);
}
}
}
return $this->_asXml($xml);
}
/**
* @param \SimpleXMLElement $simpleXml
* @return string $xml
*/
private function _asXml($simpleXml)
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->loadXML($simpleXml->asXML());
$dom->formatOutput = true;
$result = $dom->saveXML();
return $result;
}
}

View File

@@ -0,0 +1,210 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools\Export\Driver;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
/**
* ClassMetadata exporter for Doctrine YAML mapping files
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Jonathan Wage <jonwage@gmail.com>
*/
class YamlExporter extends AbstractExporter
{
protected $_extension = '.dcm.yml';
/**
* Converts a single ClassMetadata instance to the exported format
* and returns it
*
* TODO: Should this code be pulled out in to a toArray() method in ClassMetadata
*
* @param ClassMetadataInfo $metadata
* @return mixed $exported
*/
public function exportClassMetadata(ClassMetadataInfo $metadata)
{
$array = array();
if ($metadata->isMappedSuperclass) {
$array['type'] = 'mappedSuperclass';
} else {
$array['type'] = 'entity';
}
$array['table'] = $metadata->table['name'];
if (isset($metadata->table['schema'])) {
$array['schema'] = $metadata->table['schema'];
}
$inheritanceType = $metadata->inheritanceType;
if ($inheritanceType !== ClassMetadataInfo::INHERITANCE_TYPE_NONE) {
$array['inheritanceType'] = $this->_getInheritanceTypeString($inheritanceType);
}
if ($column = $metadata->discriminatorColumn) {
$array['discriminatorColumn'] = $column;
}
if ($map = $metadata->discriminatorMap) {
$array['discriminatorMap'] = $map;
}
if ($metadata->changeTrackingPolicy !== ClassMetadataInfo::CHANGETRACKING_DEFERRED_IMPLICIT) {
$array['changeTrackingPolicy'] = $this->_getChangeTrackingPolicyString($metadata->changeTrackingPolicy);
}
if (isset($metadata->table['indexes'])) {
$array['indexes'] = $metadata->table['indexes'];
}
if ($metadata->customRepositoryClassName) {
$array['repositoryClass'] = $metadata->customRepositoryClassName;
}
if (isset($metadata->table['uniqueConstraints'])) {
$array['uniqueConstraints'] = $metadata->table['uniqueConstraints'];
}
$fieldMappings = $metadata->fieldMappings;
$ids = array();
foreach ($fieldMappings as $name => $fieldMapping) {
$fieldMapping['column'] = $fieldMapping['columnName'];
unset(
$fieldMapping['columnName'],
$fieldMapping['fieldName']
);
if ($fieldMapping['column'] == $name) {
unset($fieldMapping['column']);
}
if (isset($fieldMapping['id']) && $fieldMapping['id']) {
$ids[$name] = $fieldMapping;
unset($fieldMappings[$name]);
continue;
}
$fieldMappings[$name] = $fieldMapping;
}
if ( ! $metadata->isIdentifierComposite && $idGeneratorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) {
$ids[$metadata->getSingleIdentifierFieldName()]['generator']['strategy'] = $idGeneratorType;
}
if ($ids) {
$array['fields'] = $ids;
}
if ($fieldMappings) {
if ( ! isset($array['fields'])) {
$array['fields'] = array();
}
$array['fields'] = array_merge($array['fields'], $fieldMappings);
}
$associations = array();
foreach ($metadata->associationMappings as $name => $associationMapping) {
$cascade = array();
if ($associationMapping['isCascadeRemove']) {
$cascade[] = 'remove';
}
if ($associationMapping['isCascadePersist']) {
$cascade[] = 'persist';
}
if ($associationMapping['isCascadeRefresh']) {
$cascade[] = 'refresh';
}
if ($associationMapping['isCascadeMerge']) {
$cascade[] = 'merge';
}
if ($associationMapping['isCascadeDetach']) {
$cascade[] = 'detach';
}
if (count($cascade) === 5) {
$cascade = array('all');
}
$associationMappingArray = array(
'targetEntity' => $associationMapping['targetEntity'],
'cascade' => $cascade,
);
if ($associationMapping['type'] & ClassMetadataInfo::TO_ONE) {
$joinColumns = $associationMapping['joinColumns'];
$newJoinColumns = array();
foreach ($joinColumns as $joinColumn) {
$newJoinColumns[$joinColumn['name']]['referencedColumnName'] = $joinColumn['referencedColumnName'];
if (isset($joinColumn['onDelete'])) {
$newJoinColumns[$joinColumn['name']]['onDelete'] = $joinColumn['onDelete'];
}
}
$oneToOneMappingArray = array(
'mappedBy' => $associationMapping['mappedBy'],
'inversedBy' => $associationMapping['inversedBy'],
'joinColumns' => $newJoinColumns,
'orphanRemoval' => $associationMapping['orphanRemoval'],
);
$associationMappingArray = array_merge($associationMappingArray, $oneToOneMappingArray);
if ($associationMapping['type'] & ClassMetadataInfo::ONE_TO_ONE) {
$array['oneToOne'][$name] = $associationMappingArray;
} else {
$array['manyToOne'][$name] = $associationMappingArray;
}
} else if ($associationMapping['type'] == ClassMetadataInfo::ONE_TO_MANY) {
$oneToManyMappingArray = array(
'mappedBy' => $associationMapping['mappedBy'],
'inversedBy' => $associationMapping['inversedBy'],
'orphanRemoval' => $associationMapping['orphanRemoval'],
'orderBy' => isset($associationMapping['orderBy']) ? $associationMapping['orderBy'] : null
);
$associationMappingArray = array_merge($associationMappingArray, $oneToManyMappingArray);
$array['oneToMany'][$name] = $associationMappingArray;
} else if ($associationMapping['type'] == ClassMetadataInfo::MANY_TO_MANY) {
$manyToManyMappingArray = array(
'mappedBy' => $associationMapping['mappedBy'],
'inversedBy' => $associationMapping['inversedBy'],
'joinTable' => isset($associationMapping['joinTable']) ? $associationMapping['joinTable'] : null,
'orderBy' => isset($associationMapping['orderBy']) ? $associationMapping['orderBy'] : null
);
$associationMappingArray = array_merge($associationMappingArray, $manyToManyMappingArray);
$array['manyToMany'][$name] = $associationMappingArray;
}
}
if (isset($metadata->lifecycleCallbacks)) {
$array['lifecycleCallbacks'] = $metadata->lifecycleCallbacks;
}
return \Symfony\Component\Yaml\Yaml::dump(array($metadata->name => $array), 10);
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace Doctrine\ORM\Tools\Export;
use Doctrine\ORM\ORMException;
class ExportException extends ORMException
{
public static function invalidExporterDriverType($type)
{
return new self("The specified export driver '$type' does not exist");
}
public static function invalidMappingDriverType($type)
{
return new self("The mapping driver '$type' does not exist");
}
public static function attemptOverwriteExistingFile($file)
{
return new self("Attempting to overwrite an existing file '".$file."'.");
}
}

View File

@@ -0,0 +1,80 @@
<?php
/**
* Doctrine ORM
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to kontakt@beberlei.de so I can send you a copy immediately.
*/
namespace Doctrine\ORM\Tools\Pagination;
use Doctrine\ORM\Query\TreeWalkerAdapter,
Doctrine\ORM\Query\AST\SelectStatement,
Doctrine\ORM\Query\AST\SelectExpression,
Doctrine\ORM\Query\AST\PathExpression,
Doctrine\ORM\Query\AST\AggregateExpression;
/**
* Replaces the selectClause of the AST with a COUNT statement
*
* @category DoctrineExtensions
* @package DoctrineExtensions\Paginate
* @author David Abdemoulaie <dave@hobodave.com>
* @copyright Copyright (c) 2010 David Abdemoulaie (http://hobodave.com/)
* @license http://hobodave.com/license.txt New BSD License
*/
class CountWalker extends TreeWalkerAdapter
{
/**
* Distinct mode hint name
*/
const HINT_DISTINCT = 'doctrine_paginator.distinct';
/**
* Walks down a SelectStatement AST node, modifying it to retrieve a COUNT
*
* @param SelectStatement $AST
* @return void
*/
public function walkSelectStatement(SelectStatement $AST)
{
$rootComponents = array();
foreach ($this->_getQueryComponents() AS $dqlAlias => $qComp) {
$isParent = array_key_exists('parent', $qComp)
&& $qComp['parent'] === null
&& $qComp['nestingLevel'] == 0
;
if ($isParent) {
$rootComponents[] = array($dqlAlias => $qComp);
}
}
if (count($rootComponents) > 1) {
throw new \RuntimeException("Cannot count query which selects two FROM components, cannot make distinction");
}
$root = reset($rootComponents);
$parentName = key($root);
$parent = current($root);
$pathExpression = new PathExpression(
PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $parentName,
$parent['metadata']->getSingleIdentifierFieldName()
);
$pathExpression->type = PathExpression::TYPE_STATE_FIELD;
$distinct = $this->_getQuery()->getHint(self::HINT_DISTINCT);
$AST->selectClause->selectExpressions = array(
new SelectExpression(
new AggregateExpression('count', $pathExpression, $distinct), null
)
);
// ORDER BY is not needed, only increases query execution through unnecessary sorting.
$AST->orderByClause = null;
}
}

View File

@@ -0,0 +1,115 @@
<?php
/**
* Doctrine ORM
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE. This license can also be viewed
* at http://hobodave.com/license.txt
*
* @category DoctrineExtensions
* @package DoctrineExtensions\Paginate
* @author David Abdemoulaie <dave@hobodave.com>
* @copyright Copyright (c) 2010 David Abdemoulaie (http://hobodave.com/)
* @license http://hobodave.com/license.txt New BSD License
*/
namespace Doctrine\ORM\Tools\Pagination;
use Doctrine\DBAL\Types\Type,
Doctrine\ORM\Query\TreeWalkerAdapter,
Doctrine\ORM\Query\AST\SelectStatement,
Doctrine\ORM\Query\AST\SelectExpression,
Doctrine\ORM\Query\AST\PathExpression,
Doctrine\ORM\Query\AST\AggregateExpression;
/**
* Replaces the selectClause of the AST with a SELECT DISTINCT root.id equivalent
*
* @category DoctrineExtensions
* @package DoctrineExtensions\Paginate
* @author David Abdemoulaie <dave@hobodave.com>
* @copyright Copyright (c) 2010 David Abdemoulaie (http://hobodave.com/)
* @license http://hobodave.com/license.txt New BSD License
*/
class LimitSubqueryWalker extends TreeWalkerAdapter
{
/**
* ID type hint
*/
const IDENTIFIER_TYPE = 'doctrine_paginator.id.type';
/**
* @var int Counter for generating unique order column aliases
*/
private $_aliasCounter = 0;
/**
* Walks down a SelectStatement AST node, modifying it to retrieve DISTINCT ids
* of the root Entity
*
* @param SelectStatement $AST
* @return void
*/
public function walkSelectStatement(SelectStatement $AST)
{
$parent = null;
$parentName = null;
$selectExpressions = array();
foreach ($this->_getQueryComponents() AS $dqlAlias => $qComp) {
// preserve mixed data in query for ordering
if (isset($qComp['resultVariable'])) {
$selectExpressions[] = new SelectExpression($qComp['resultVariable'], $dqlAlias);
continue;
}
if ($qComp['parent'] === null && $qComp['nestingLevel'] == 0) {
$parent = $qComp;
$parentName = $dqlAlias;
continue;
}
}
$identifier = $parent['metadata']->getSingleIdentifierFieldName();
$this->_getQuery()->setHint(
self::IDENTIFIER_TYPE,
Type::getType($parent['metadata']->getTypeOfField($identifier))
);
$pathExpression = new PathExpression(
PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION,
$parentName,
$identifier
);
$pathExpression->type = PathExpression::TYPE_STATE_FIELD;
array_unshift($selectExpressions, new SelectExpression($pathExpression, '_dctrn_id'));
$AST->selectClause->selectExpressions = $selectExpressions;
if (isset($AST->orderByClause)) {
foreach ($AST->orderByClause->orderByItems as $item) {
if ($item->expression instanceof PathExpression) {
$pathExpression = new PathExpression(
PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION,
$item->expression->identificationVariable,
$item->expression->field
);
$pathExpression->type = PathExpression::TYPE_STATE_FIELD;
$AST->selectClause->selectExpressions[] = new SelectExpression(
$pathExpression,
'_dctrn_ord' . $this->_aliasCounter++
);
}
}
}
$AST->selectClause->isDistinct = true;
}
}

View File

@@ -0,0 +1,180 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools\Pagination;
use Doctrine\ORM\QueryBuilder;
use Doctrine\ORM\Query;
use Doctrine\ORM\NoResultException;
use Doctrine\ORM\Tools\Pagination\WhereInWalker;
use Doctrine\ORM\Tools\Pagination\CountWalker;
use Countable;
use IteratorAggregate;
use ArrayIterator;
/**
* Paginator
*
* The paginator can handle various complex scenarios with DQL.
*
* @author Pablo Díez <pablodip@gmail.com>
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @license New BSD
*/
class Paginator implements \Countable, \IteratorAggregate
{
/**
* @var Query
*/
private $query;
/**
* @var bool
*/
private $fetchJoinCollection;
/**
* @var int
*/
private $count;
/**
* Constructor.
*
* @param Query|QueryBuilder $query A Doctrine ORM query or query builder.
* @param Boolean $fetchJoinCollection Whether the query joins a collection (true by default).
*/
public function __construct($query, $fetchJoinCollection = true)
{
if ($query instanceof QueryBuilder) {
$query = $query->getQuery();
}
$this->query = $query;
$this->fetchJoinCollection = (Boolean) $fetchJoinCollection;
}
/**
* Returns the query
*
* @return Query
*/
public function getQuery()
{
return $this->query;
}
/**
* Returns whether the query joins a collection.
*
* @return Boolean Whether the query joins a collection.
*/
public function getFetchJoinCollection()
{
return $this->fetchJoinCollection;
}
/**
* {@inheritdoc}
*/
public function count()
{
if ($this->count === null) {
/* @var $countQuery Query */
$countQuery = $this->cloneQuery($this->query);
if ( ! $countQuery->getHint(CountWalker::HINT_DISTINCT)) {
$countQuery->setHint(CountWalker::HINT_DISTINCT, true);
}
$countQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\CountWalker'));
$countQuery->setFirstResult(null)->setMaxResults(null);
try {
$data = $countQuery->getScalarResult();
$data = array_map('current', $data);
$this->count = array_sum($data);
} catch(NoResultException $e) {
$this->count = 0;
}
}
return $this->count;
}
/**
* {@inheritdoc}
*/
public function getIterator()
{
$offset = $this->query->getFirstResult();
$length = $this->query->getMaxResults();
if ($this->fetchJoinCollection) {
$subQuery = $this->cloneQuery($this->query);
$subQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\LimitSubqueryWalker'))
->setFirstResult($offset)
->setMaxResults($length);
$ids = array_map('current', $subQuery->getScalarResult());
$whereInQuery = $this->cloneQuery($this->query);
// don't do this for an empty id array
if (count($ids) > 0) {
$namespace = WhereInWalker::PAGINATOR_ID_ALIAS;
$whereInQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\WhereInWalker'));
$whereInQuery->setHint(WhereInWalker::HINT_PAGINATOR_ID_COUNT, count($ids));
$whereInQuery->setFirstResult(null)->setMaxResults(null);
foreach ($ids as $i => $id) {
$i++;
$whereInQuery->setParameter("{$namespace}_{$i}", $id);
}
}
$result = $whereInQuery->getResult($this->query->getHydrationMode());
} else {
$result = $this->cloneQuery($this->query)
->setMaxResults($length)
->setFirstResult($offset)
->getResult($this->query->getHydrationMode())
;
}
return new \ArrayIterator($result);
}
/**
* Clones a query.
*
* @param Query $query The query.
*
* @return Query The cloned query.
*/
private function cloneQuery(Query $query)
{
/* @var $cloneQuery Query */
$cloneQuery = clone $query;
$cloneQuery->setParameters($query->getParameters());
foreach ($query->getHints() as $name => $value) {
$cloneQuery->setHint($name, $value);
}
return $cloneQuery;
}
}

View File

@@ -0,0 +1,142 @@
<?php
/**
* Doctrine ORM
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE. This license can also be viewed
* at http://hobodave.com/license.txt
*
* @category DoctrineExtensions
* @package DoctrineExtensions\Paginate
* @author David Abdemoulaie <dave@hobodave.com>
* @copyright Copyright (c) 2010 David Abdemoulaie (http://hobodave.com/)
* @license http://hobodave.com/license.txt New BSD License
*/
namespace Doctrine\ORM\Tools\Pagination;
use Doctrine\ORM\Query\AST\ArithmeticExpression,
Doctrine\ORM\Query\AST\SimpleArithmeticExpression,
Doctrine\ORM\Query\TreeWalkerAdapter,
Doctrine\ORM\Query\AST\SelectStatement,
Doctrine\ORM\Query\AST\PathExpression,
Doctrine\ORM\Query\AST\InExpression,
Doctrine\ORM\Query\AST\NullComparisonExpression,
Doctrine\ORM\Query\AST\InputParameter,
Doctrine\ORM\Query\AST\ConditionalPrimary,
Doctrine\ORM\Query\AST\ConditionalTerm,
Doctrine\ORM\Query\AST\ConditionalExpression,
Doctrine\ORM\Query\AST\ConditionalFactor,
Doctrine\ORM\Query\AST\WhereClause;
/**
* Replaces the whereClause of the AST with a WHERE id IN (:foo_1, :foo_2) equivalent
*
* @category DoctrineExtensions
* @package DoctrineExtensions\Paginate
* @author David Abdemoulaie <dave@hobodave.com>
* @copyright Copyright (c) 2010 David Abdemoulaie (http://hobodave.com/)
* @license http://hobodave.com/license.txt New BSD License
*/
class WhereInWalker extends TreeWalkerAdapter
{
/**
* ID Count hint name
*/
const HINT_PAGINATOR_ID_COUNT = 'doctrine.id.count';
/**
* Primary key alias for query
*/
const PAGINATOR_ID_ALIAS = 'dpid';
/**
* Replaces the whereClause in the AST
*
* Generates a clause equivalent to WHERE IN (:dpid_1, :dpid_2, ...)
*
* The parameter namespace (dpid) is defined by
* the PAGINATOR_ID_ALIAS
*
* The total number of parameters is retrieved from
* the HINT_PAGINATOR_ID_COUNT query hint
*
* @param SelectStatement $AST
* @return void
*/
public function walkSelectStatement(SelectStatement $AST)
{
$rootComponents = array();
foreach ($this->_getQueryComponents() AS $dqlAlias => $qComp) {
$isParent = array_key_exists('parent', $qComp)
&& $qComp['parent'] === null
&& $qComp['nestingLevel'] == 0
;
if ($isParent) {
$rootComponents[] = array($dqlAlias => $qComp);
}
}
if (count($rootComponents) > 1) {
throw new \RuntimeException("Cannot count query which selects two FROM components, cannot make distinction");
}
$root = reset($rootComponents);
$parentName = key($root);
$parent = current($root);
$pathExpression = new PathExpression(
PathExpression::TYPE_STATE_FIELD, $parentName, $parent['metadata']->getSingleIdentifierFieldName()
);
$pathExpression->type = PathExpression::TYPE_STATE_FIELD;
$count = $this->_getQuery()->getHint(self::HINT_PAGINATOR_ID_COUNT);
if ($count > 0) {
$arithmeticExpression = new ArithmeticExpression();
$arithmeticExpression->simpleArithmeticExpression = new SimpleArithmeticExpression(
array($pathExpression)
);
$expression = new InExpression($arithmeticExpression);
$ns = self::PAGINATOR_ID_ALIAS;
for ($i = 1; $i <= $count; $i++) {
$expression->literals[] = new InputParameter(":{$ns}_$i");
}
} else {
$expression = new NullComparisonExpression($pathExpression);
$expression->not = false;
}
$conditionalPrimary = new ConditionalPrimary;
$conditionalPrimary->simpleConditionalExpression = $expression;
if ($AST->whereClause) {
if ($AST->whereClause->conditionalExpression instanceof ConditionalTerm) {
$AST->whereClause->conditionalExpression->conditionalFactors[] = $conditionalPrimary;
} elseif ($AST->whereClause->conditionalExpression instanceof ConditionalPrimary) {
$AST->whereClause->conditionalExpression = new ConditionalExpression(array(
new ConditionalTerm(array(
$AST->whereClause->conditionalExpression,
$conditionalPrimary
))
));
} elseif ($AST->whereClause->conditionalExpression instanceof ConditionalExpression) {
$tmpPrimary = new ConditionalPrimary;
$tmpPrimary->conditionalExpression = $AST->whereClause->conditionalExpression;
$AST->whereClause->conditionalExpression = new ConditionalTerm(array(
$tmpPrimary,
$conditionalPrimary
));
}
} else {
$AST->whereClause = new WhereClause(
new ConditionalExpression(array(
new ConditionalTerm(array(
$conditionalPrimary
))
))
);
}
}
}

View File

@@ -0,0 +1,94 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools;
use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
use Doctrine\ORM\Mapping\ClassMetadata;
/**
* ResolveTargetEntityListener
*
* Mechanism to overwrite interfaces or classes specified as association
* targets.
*
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @since 2.2
*/
class ResolveTargetEntityListener
{
/**
* @var array
*/
private $resolveTargetEntities = array();
/**
* Add a target-entity class name to resolve to a new class name.
*
* @param string $originalEntity
* @param string $newEntity
* @param array $mapping
* @return void
*/
public function addResolveTargetEntity($originalEntity, $newEntity, array $mapping)
{
$mapping['targetEntity'] = ltrim($newEntity, "\\");
$this->resolveTargetEntities[ltrim($originalEntity, "\\")] = $mapping;
}
/**
* Process event and resolve new target entity names.
*
* @param LoadClassMetadataEventArgs $args
* @return void
*/
public function loadClassMetadata(LoadClassMetadataEventArgs $args)
{
$cm = $args->getClassMetadata();
foreach ($cm->associationMappings as $assocName => $mapping) {
if (isset($this->resolveTargetEntities[$mapping['targetEntity']])) {
$this->remapAssociation($cm, $mapping);
}
}
}
private function remapAssociation($classMetadata, $mapping)
{
$newMapping = $this->resolveTargetEntities[$mapping['targetEntity']];
$newMapping = array_replace_recursive($mapping, $newMapping);
$newMapping['fieldName'] = $mapping['fieldName'];
unset($classMetadata->associationMappings[$mapping['fieldName']]);
switch ($mapping['type']) {
case ClassMetadata::MANY_TO_MANY:
$classMetadata->mapManyToMany($newMapping);
break;
case ClassMetadata::MANY_TO_ONE:
$classMetadata->mapManyToOne($newMapping);
break;
case ClassMetadata::ONE_TO_MANY:
$classMetadata->mapOneToMany($newMapping);
break;
case ClassMetadata::ONE_TO_ONE:
$classMetadata->mapOneToOne($newMapping);
break;
}
}
}

View File

@@ -0,0 +1,701 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools;
use Doctrine\ORM\ORMException,
Doctrine\DBAL\Types\Type,
Doctrine\DBAL\Schema\Schema,
Doctrine\DBAL\Schema\Visitor\RemoveNamespacedAssets,
Doctrine\ORM\EntityManager,
Doctrine\ORM\Mapping\ClassMetadata,
Doctrine\ORM\Internal\CommitOrderCalculator,
Doctrine\ORM\Tools\Event\GenerateSchemaTableEventArgs,
Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs;
/**
* The SchemaTool is a tool to create/drop/update database schemas based on
* <tt>ClassMetadata</tt> class descriptors.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class SchemaTool
{
/**
* @var \Doctrine\ORM\EntityManager
*/
private $_em;
/**
* @var \Doctrine\DBAL\Platforms\AbstractPlatform
*/
private $_platform;
/**
* Initializes a new SchemaTool instance that uses the connection of the
* provided EntityManager.
*
* @param \Doctrine\ORM\EntityManager $em
*/
public function __construct(EntityManager $em)
{
$this->_em = $em;
$this->_platform = $em->getConnection()->getDatabasePlatform();
}
/**
* Creates the database schema for the given array of ClassMetadata instances.
*
* @throws ToolsException
* @param array $classes
* @return void
*/
public function createSchema(array $classes)
{
$createSchemaSql = $this->getCreateSchemaSql($classes);
$conn = $this->_em->getConnection();
foreach ($createSchemaSql as $sql) {
try {
$conn->executeQuery($sql);
} catch(\Exception $e) {
throw ToolsException::schemaToolFailure($sql, $e);
}
}
}
/**
* Gets the list of DDL statements that are required to create the database schema for
* the given list of ClassMetadata instances.
*
* @param array $classes
* @return array $sql The SQL statements needed to create the schema for the classes.
*/
public function getCreateSchemaSql(array $classes)
{
$schema = $this->getSchemaFromMetadata($classes);
return $schema->toSql($this->_platform);
}
/**
* Some instances of ClassMetadata don't need to be processed in the SchemaTool context. This method detects them.
*
* @param ClassMetadata $class
* @param array $processedClasses
* @return bool
*/
private function processingNotRequired($class, array $processedClasses)
{
return (
isset($processedClasses[$class->name]) ||
$class->isMappedSuperclass ||
($class->isInheritanceTypeSingleTable() && $class->name != $class->rootEntityName)
);
}
/**
* From a given set of metadata classes this method creates a Schema instance.
*
* @param array $classes
* @return Schema
*/
public function getSchemaFromMetadata(array $classes)
{
$processedClasses = array(); // Reminder for processed classes, used for hierarchies
$sm = $this->_em->getConnection()->getSchemaManager();
$metadataSchemaConfig = $sm->createSchemaConfig();
$metadataSchemaConfig->setExplicitForeignKeyIndexes(false);
$schema = new Schema(array(), array(), $metadataSchemaConfig);
$evm = $this->_em->getEventManager();
foreach ($classes as $class) {
if ($this->processingNotRequired($class, $processedClasses)) {
continue;
}
$table = $schema->createTable($class->getQuotedTableName($this->_platform));
$columns = array(); // table columns
if ($class->isInheritanceTypeSingleTable()) {
$columns = $this->_gatherColumns($class, $table);
$this->_gatherRelationsSql($class, $table, $schema);
// Add the discriminator column
$this->addDiscriminatorColumnDefinition($class, $table);
// Aggregate all the information from all classes in the hierarchy
foreach ($class->parentClasses as $parentClassName) {
// Parent class information is already contained in this class
$processedClasses[$parentClassName] = true;
}
foreach ($class->subClasses as $subClassName) {
$subClass = $this->_em->getClassMetadata($subClassName);
$this->_gatherColumns($subClass, $table);
$this->_gatherRelationsSql($subClass, $table, $schema);
$processedClasses[$subClassName] = true;
}
} else if ($class->isInheritanceTypeJoined()) {
// Add all non-inherited fields as columns
$pkColumns = array();
foreach ($class->fieldMappings as $fieldName => $mapping) {
if ( ! isset($mapping['inherited'])) {
$columnName = $class->getQuotedColumnName($mapping['fieldName'], $this->_platform);
$this->_gatherColumn($class, $mapping, $table);
if ($class->isIdentifier($fieldName)) {
$pkColumns[] = $columnName;
}
}
}
$this->_gatherRelationsSql($class, $table, $schema);
// Add the discriminator column only to the root table
if ($class->name == $class->rootEntityName) {
$this->addDiscriminatorColumnDefinition($class, $table);
} else {
// Add an ID FK column to child tables
/* @var \Doctrine\ORM\Mapping\ClassMetadata $class */
$idMapping = $class->fieldMappings[$class->identifier[0]];
$this->_gatherColumn($class, $idMapping, $table);
$columnName = $class->getQuotedColumnName($class->identifier[0], $this->_platform);
// TODO: This seems rather hackish, can we optimize it?
$table->getColumn($columnName)->setAutoincrement(false);
$pkColumns[] = $columnName;
// Add a FK constraint on the ID column
$table->addUnnamedForeignKeyConstraint(
$this->_em->getClassMetadata($class->rootEntityName)->getQuotedTableName($this->_platform),
array($columnName), array($columnName), array('onDelete' => 'CASCADE')
);
}
$table->setPrimaryKey($pkColumns);
} else if ($class->isInheritanceTypeTablePerClass()) {
throw ORMException::notSupported();
} else {
$this->_gatherColumns($class, $table);
$this->_gatherRelationsSql($class, $table, $schema);
}
$pkColumns = array();
foreach ($class->identifier AS $identifierField) {
if (isset($class->fieldMappings[$identifierField])) {
$pkColumns[] = $class->getQuotedColumnName($identifierField, $this->_platform);
} else if (isset($class->associationMappings[$identifierField])) {
/* @var $assoc \Doctrine\ORM\Mapping\OneToOne */
$assoc = $class->associationMappings[$identifierField];
foreach ($assoc['joinColumns'] AS $joinColumn) {
$pkColumns[] = $joinColumn['name'];
}
}
}
if (!$table->hasIndex('primary')) {
$table->setPrimaryKey($pkColumns);
}
if (isset($class->table['indexes'])) {
foreach ($class->table['indexes'] AS $indexName => $indexData) {
$table->addIndex($indexData['columns'], is_numeric($indexName) ? null : $indexName);
}
}
if (isset($class->table['uniqueConstraints'])) {
foreach ($class->table['uniqueConstraints'] AS $indexName => $indexData) {
$table->addUniqueIndex($indexData['columns'], is_numeric($indexName) ? null : $indexName);
}
}
$processedClasses[$class->name] = true;
if ($class->isIdGeneratorSequence() && $class->name == $class->rootEntityName) {
$seqDef = $class->sequenceGeneratorDefinition;
if (!$schema->hasSequence($seqDef['sequenceName'])) {
$schema->createSequence(
$seqDef['sequenceName'],
$seqDef['allocationSize'],
$seqDef['initialValue']
);
}
}
if ($evm->hasListeners(ToolEvents::postGenerateSchemaTable)) {
$evm->dispatchEvent(ToolEvents::postGenerateSchemaTable, new GenerateSchemaTableEventArgs($class, $schema, $table));
}
}
if ( ! $this->_platform->supportsSchemas() && ! $this->_platform->canEmulateSchemas() ) {
$schema->visit(new RemoveNamespacedAssets());
}
if ($evm->hasListeners(ToolEvents::postGenerateSchema)) {
$evm->dispatchEvent(ToolEvents::postGenerateSchema, new GenerateSchemaEventArgs($this->_em, $schema));
}
return $schema;
}
/**
* Gets a portable column definition as required by the DBAL for the discriminator
* column of a class.
*
* @param ClassMetadata $class
* @return array The portable column definition of the discriminator column as required by
* the DBAL.
*/
private function addDiscriminatorColumnDefinition($class, $table)
{
$discrColumn = $class->discriminatorColumn;
if (!isset($discrColumn['type']) || (strtolower($discrColumn['type']) == 'string' && $discrColumn['length'] === null)) {
$discrColumn['type'] = 'string';
$discrColumn['length'] = 255;
}
$table->addColumn(
$discrColumn['name'],
$discrColumn['type'],
array('length' => $discrColumn['length'], 'notnull' => true)
);
}
/**
* Gathers the column definitions as required by the DBAL of all field mappings
* found in the given class.
*
* @param ClassMetadata $class
* @param Table $table
* @return array The list of portable column definitions as required by the DBAL.
*/
private function _gatherColumns($class, $table)
{
$columns = array();
$pkColumns = array();
foreach ($class->fieldMappings as $fieldName => $mapping) {
if ($class->isInheritanceTypeSingleTable() && isset($mapping['inherited'])) {
continue;
}
$column = $this->_gatherColumn($class, $mapping, $table);
if ($class->isIdentifier($mapping['fieldName'])) {
$pkColumns[] = $class->getQuotedColumnName($mapping['fieldName'], $this->_platform);
}
}
// For now, this is a hack required for single table inheritence, since this method is called
// twice by single table inheritence relations
if(!$table->hasIndex('primary')) {
//$table->setPrimaryKey($pkColumns);
}
return $columns;
}
/**
* Creates a column definition as required by the DBAL from an ORM field mapping definition.
*
* @param ClassMetadata $class The class that owns the field mapping.
* @param array $mapping The field mapping.
* @param Table $table
* @return array The portable column definition as required by the DBAL.
*/
private function _gatherColumn($class, array $mapping, $table)
{
$columnName = $class->getQuotedColumnName($mapping['fieldName'], $this->_platform);
$columnType = $mapping['type'];
$options = array();
$options['length'] = isset($mapping['length']) ? $mapping['length'] : null;
$options['notnull'] = isset($mapping['nullable']) ? ! $mapping['nullable'] : true;
if ($class->isInheritanceTypeSingleTable() && count($class->parentClasses) > 0) {
$options['notnull'] = false;
}
$options['platformOptions'] = array();
$options['platformOptions']['version'] = $class->isVersioned && $class->versionField == $mapping['fieldName'] ? true : false;
if(strtolower($columnType) == 'string' && $options['length'] === null) {
$options['length'] = 255;
}
if (isset($mapping['precision'])) {
$options['precision'] = $mapping['precision'];
}
if (isset($mapping['scale'])) {
$options['scale'] = $mapping['scale'];
}
if (isset($mapping['default'])) {
$options['default'] = $mapping['default'];
}
if (isset($mapping['columnDefinition'])) {
$options['columnDefinition'] = $mapping['columnDefinition'];
}
if (isset($mapping['options'])) {
$options['customSchemaOptions'] = $mapping['options'];
}
if ($class->isIdGeneratorIdentity() && $class->getIdentifierFieldNames() == array($mapping['fieldName'])) {
$options['autoincrement'] = true;
}
if ($class->isInheritanceTypeJoined() && $class->name != $class->rootEntityName) {
$options['autoincrement'] = false;
}
if ($table->hasColumn($columnName)) {
// required in some inheritance scenarios
$table->changeColumn($columnName, $options);
} else {
$table->addColumn($columnName, $columnType, $options);
}
$isUnique = isset($mapping['unique']) ? $mapping['unique'] : false;
if ($isUnique) {
$table->addUniqueIndex(array($columnName));
}
}
/**
* Gathers the SQL for properly setting up the relations of the given class.
* This includes the SQL for foreign key constraints and join tables.
*
* @param ClassMetadata $class
* @param \Doctrine\DBAL\Schema\Table $table
* @param \Doctrine\DBAL\Schema\Schema $schema
* @return void
*/
private function _gatherRelationsSql($class, $table, $schema)
{
foreach ($class->associationMappings as $fieldName => $mapping) {
if (isset($mapping['inherited'])) {
continue;
}
$foreignClass = $this->_em->getClassMetadata($mapping['targetEntity']);
if ($mapping['type'] & ClassMetadata::TO_ONE && $mapping['isOwningSide']) {
$primaryKeyColumns = $uniqueConstraints = array(); // PK is unnecessary for this relation-type
$this->_gatherRelationJoinColumns($mapping['joinColumns'], $table, $foreignClass, $mapping, $primaryKeyColumns, $uniqueConstraints);
foreach($uniqueConstraints AS $indexName => $unique) {
$table->addUniqueIndex($unique['columns'], is_numeric($indexName) ? null : $indexName);
}
} else if ($mapping['type'] == ClassMetadata::ONE_TO_MANY && $mapping['isOwningSide']) {
//... create join table, one-many through join table supported later
throw ORMException::notSupported();
} else if ($mapping['type'] == ClassMetadata::MANY_TO_MANY && $mapping['isOwningSide']) {
// create join table
$joinTable = $mapping['joinTable'];
$theJoinTable = $schema->createTable($foreignClass->getQuotedJoinTableName($mapping, $this->_platform));
$primaryKeyColumns = $uniqueConstraints = array();
// Build first FK constraint (relation table => source table)
$this->_gatherRelationJoinColumns($joinTable['joinColumns'], $theJoinTable, $class, $mapping, $primaryKeyColumns, $uniqueConstraints);
// Build second FK constraint (relation table => target table)
$this->_gatherRelationJoinColumns($joinTable['inverseJoinColumns'], $theJoinTable, $foreignClass, $mapping, $primaryKeyColumns, $uniqueConstraints);
$theJoinTable->setPrimaryKey($primaryKeyColumns);
foreach($uniqueConstraints AS $indexName => $unique) {
$theJoinTable->addUniqueIndex($unique['columns'], is_numeric($indexName) ? null : $indexName);
}
}
}
}
/**
* Get the class metadata that is responsible for the definition of the referenced column name.
*
* Previously this was a simple task, but with DDC-117 this problem is actually recursive. If its
* not a simple field, go through all identifier field names that are associations recursivly and
* find that referenced column name.
*
* TODO: Is there any way to make this code more pleasing?
*
* @param ClassMetadata $class
* @param string $referencedColumnName
* @return array(ClassMetadata, referencedFieldName)
*/
private function getDefiningClass($class, $referencedColumnName)
{
$referencedFieldName = $class->getFieldName($referencedColumnName);
if ($class->hasField($referencedFieldName)) {
return array($class, $referencedFieldName);
} else if (in_array($referencedColumnName, $class->getIdentifierColumnNames())) {
// it seems to be an entity as foreign key
foreach ($class->getIdentifierFieldNames() AS $fieldName) {
if ($class->hasAssociation($fieldName) && $class->getSingleAssociationJoinColumnName($fieldName) == $referencedColumnName) {
return $this->getDefiningClass(
$this->_em->getClassMetadata($class->associationMappings[$fieldName]['targetEntity']),
$class->getSingleAssociationReferencedJoinColumnName($fieldName)
);
}
}
}
return null;
}
/**
* Gather columns and fk constraints that are required for one part of relationship.
*
* @param array $joinColumns
* @param \Doctrine\DBAL\Schema\Table $theJoinTable
* @param ClassMetadata $class
* @param array $mapping
* @param array $primaryKeyColumns
* @param array $uniqueConstraints
*/
private function _gatherRelationJoinColumns($joinColumns, $theJoinTable, $class, $mapping, &$primaryKeyColumns, &$uniqueConstraints)
{
$localColumns = array();
$foreignColumns = array();
$fkOptions = array();
$foreignTableName = $class->getQuotedTableName($this->_platform);
foreach ($joinColumns as $joinColumn) {
$columnName = $joinColumn['name'];
list($definingClass, $referencedFieldName) = $this->getDefiningClass($class, $joinColumn['referencedColumnName']);
if (!$definingClass) {
throw new \Doctrine\ORM\ORMException(
"Column name `".$joinColumn['referencedColumnName']."` referenced for relation from ".
$mapping['sourceEntity'] . " towards ". $mapping['targetEntity'] . " does not exist."
);
}
$primaryKeyColumns[] = $columnName;
$localColumns[] = $columnName;
$foreignColumns[] = $joinColumn['referencedColumnName'];
if ( ! $theJoinTable->hasColumn($joinColumn['name'])) {
// Only add the column to the table if it does not exist already.
// It might exist already if the foreign key is mapped into a regular
// property as well.
$fieldMapping = $definingClass->getFieldMapping($referencedFieldName);
$columnDef = null;
if (isset($joinColumn['columnDefinition'])) {
$columnDef = $joinColumn['columnDefinition'];
} else if (isset($fieldMapping['columnDefinition'])) {
$columnDef = $fieldMapping['columnDefinition'];
}
$columnOptions = array('notnull' => false, 'columnDefinition' => $columnDef);
if (isset($joinColumn['nullable'])) {
$columnOptions['notnull'] = !$joinColumn['nullable'];
}
if ($fieldMapping['type'] == "string" && isset($fieldMapping['length'])) {
$columnOptions['length'] = $fieldMapping['length'];
} else if ($fieldMapping['type'] == "decimal") {
$columnOptions['scale'] = $fieldMapping['scale'];
$columnOptions['precision'] = $fieldMapping['precision'];
}
$theJoinTable->addColumn($columnName, $fieldMapping['type'], $columnOptions);
}
if (isset($joinColumn['unique']) && $joinColumn['unique'] == true) {
$uniqueConstraints[] = array('columns' => array($columnName));
}
if (isset($joinColumn['onDelete'])) {
$fkOptions['onDelete'] = $joinColumn['onDelete'];
}
}
$theJoinTable->addUnnamedForeignKeyConstraint(
$foreignTableName, $localColumns, $foreignColumns, $fkOptions
);
}
/**
* Drops the database schema for the given classes.
*
* In any way when an exception is thrown it is supressed since drop was
* issued for all classes of the schema and some probably just don't exist.
*
* @param array $classes
* @return void
*/
public function dropSchema(array $classes)
{
$dropSchemaSql = $this->getDropSchemaSQL($classes);
$conn = $this->_em->getConnection();
foreach ($dropSchemaSql as $sql) {
try {
$conn->executeQuery($sql);
} catch(\Exception $e) {
}
}
}
/**
* Drops all elements in the database of the current connection.
*
* @return void
*/
public function dropDatabase()
{
$dropSchemaSql = $this->getDropDatabaseSQL();
$conn = $this->_em->getConnection();
foreach ($dropSchemaSql as $sql) {
$conn->executeQuery($sql);
}
}
/**
* Gets the SQL needed to drop the database schema for the connections database.
*
* @return array
*/
public function getDropDatabaseSQL()
{
$sm = $this->_em->getConnection()->getSchemaManager();
$schema = $sm->createSchema();
$visitor = new \Doctrine\DBAL\Schema\Visitor\DropSchemaSqlCollector($this->_platform);
/* @var $schema \Doctrine\DBAL\Schema\Schema */
$schema->visit($visitor);
return $visitor->getQueries();
}
/**
* Get SQL to drop the tables defined by the passed classes.
*
* @param array $classes
* @return array
*/
public function getDropSchemaSQL(array $classes)
{
$visitor = new \Doctrine\DBAL\Schema\Visitor\DropSchemaSqlCollector($this->_platform);
$schema = $this->getSchemaFromMetadata($classes);
$sm = $this->_em->getConnection()->getSchemaManager();
$fullSchema = $sm->createSchema();
foreach ($fullSchema->getTables() AS $table) {
if (!$schema->hasTable($table->getName())) {
foreach ($table->getForeignKeys() AS $foreignKey) {
/* @var $foreignKey \Doctrine\DBAL\Schema\ForeignKeyConstraint */
if ($schema->hasTable($foreignKey->getForeignTableName())) {
$visitor->acceptForeignKey($table, $foreignKey);
}
}
} else {
$visitor->acceptTable($table);
foreach ($table->getForeignKeys() AS $foreignKey) {
$visitor->acceptForeignKey($table, $foreignKey);
}
}
}
if ($this->_platform->supportsSequences()) {
foreach ($schema->getSequences() AS $sequence) {
$visitor->acceptSequence($sequence);
}
foreach ($schema->getTables() AS $table) {
/* @var $sequence Table */
if ($table->hasPrimaryKey()) {
$columns = $table->getPrimaryKey()->getColumns();
if (count($columns) == 1) {
$checkSequence = $table->getName() . "_" . $columns[0] . "_seq";
if ($fullSchema->hasSequence($checkSequence)) {
$visitor->acceptSequence($fullSchema->getSequence($checkSequence));
}
}
}
}
}
return $visitor->getQueries();
}
/**
* Updates the database schema of the given classes by comparing the ClassMetadata
* instances to the current database schema that is inspected. If $saveMode is set
* to true the command is executed in the Database, else SQL is returned.
*
* @param array $classes
* @param boolean $saveMode
* @return void
*/
public function updateSchema(array $classes, $saveMode=false)
{
$updateSchemaSql = $this->getUpdateSchemaSql($classes, $saveMode);
$conn = $this->_em->getConnection();
foreach ($updateSchemaSql as $sql) {
$conn->executeQuery($sql);
}
}
/**
* Gets the sequence of SQL statements that need to be performed in order
* to bring the given class mappings in-synch with the relational schema.
* If $saveMode is set to true the command is executed in the Database,
* else SQL is returned.
*
* @param array $classes The classes to consider.
* @param boolean $saveMode True for writing to DB, false for SQL string
* @return array The sequence of SQL statements.
*/
public function getUpdateSchemaSql(array $classes, $saveMode=false)
{
$sm = $this->_em->getConnection()->getSchemaManager();
$fromSchema = $sm->createSchema();
$toSchema = $this->getSchemaFromMetadata($classes);
$comparator = new \Doctrine\DBAL\Schema\Comparator();
$schemaDiff = $comparator->compare($fromSchema, $toSchema);
if ($saveMode) {
return $schemaDiff->toSaveSql($this->_platform);
} else {
return $schemaDiff->toSql($this->_platform);
}
}
}

View File

@@ -0,0 +1,267 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\DBAL\Types\Type;
/**
* Performs strict validation of the mapping schema
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.com
* @since 1.0
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class SchemaValidator
{
/**
* @var EntityManager
*/
private $em;
/**
* @param EntityManager $em
*/
public function __construct(EntityManager $em)
{
$this->em = $em;
}
/**
* Checks the internal consistency of all mapping files.
*
* There are several checks that can't be done at runtime or are too expensive, which can be verified
* with this command. For example:
*
* 1. Check if a relation with "mappedBy" is actually connected to that specified field.
* 2. Check if "mappedBy" and "inversedBy" are consistent to each other.
* 3. Check if "referencedColumnName" attributes are really pointing to primary key columns.
* 4. Check if there are public properties that might cause problems with lazy loading.
*
* @return array
*/
public function validateMapping()
{
$errors = array();
$cmf = $this->em->getMetadataFactory();
$classes = $cmf->getAllMetadata();
foreach ($classes AS $class) {
if ($ce = $this->validateClass($class)) {
$errors[$class->name] = $ce;
}
}
return $errors;
}
/**
* Validate a single class of the current
*
* @param ClassMetadataInfo $class
* @return array
*/
public function validateClass(ClassMetadataInfo $class)
{
$ce = array();
$cmf = $this->em->getMetadataFactory();
foreach ($class->fieldMappings as $fieldName => $mapping) {
if (!Type::hasType($mapping['type'])) {
$ce[] = "The field '" . $class->name . "#" . $fieldName."' uses a non-existant type '" . $mapping['type'] . "'.";
}
}
foreach ($class->associationMappings AS $fieldName => $assoc) {
if (!class_exists($assoc['targetEntity']) || $cmf->isTransient($assoc['targetEntity'])) {
$ce[] = "The target entity '" . $assoc['targetEntity'] . "' specified on " . $class->name . '#' . $fieldName . ' is unknown or not an entity.';
return $ce;
}
if ($assoc['mappedBy'] && $assoc['inversedBy']) {
$ce[] = "The association " . $class . "#" . $fieldName . " cannot be defined as both inverse and owning.";
}
$targetMetadata = $cmf->getMetadataFor($assoc['targetEntity']);
if (isset($assoc['id']) && $targetMetadata->containsForeignIdentifier) {
$ce[] = "Cannot map association '" . $class->name. "#". $fieldName ." as identifier, because " .
"the target entity '". $targetMetadata->name . "' also maps an association as identifier.";
}
/* @var $assoc AssociationMapping */
if ($assoc['mappedBy']) {
if ($targetMetadata->hasField($assoc['mappedBy'])) {
$ce[] = "The association " . $class->name . "#" . $fieldName . " refers to the owning side ".
"field " . $assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " which is not defined as association.";
}
if (!$targetMetadata->hasAssociation($assoc['mappedBy'])) {
$ce[] = "The association " . $class->name . "#" . $fieldName . " refers to the owning side ".
"field " . $assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " which does not exist.";
} else if ($targetMetadata->associationMappings[$assoc['mappedBy']]['inversedBy'] == null) {
$ce[] = "The field " . $class->name . "#" . $fieldName . " is on the inverse side of a ".
"bi-directional relationship, but the specified mappedBy association on the target-entity ".
$assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " does not contain the required ".
"'inversedBy=".$fieldName."' attribute.";
} else if ($targetMetadata->associationMappings[$assoc['mappedBy']]['inversedBy'] != $fieldName) {
$ce[] = "The mappings " . $class->name . "#" . $fieldName . " and " .
$assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " are ".
"incosistent with each other.";
}
}
if ($assoc['inversedBy']) {
if ($targetMetadata->hasField($assoc['inversedBy'])) {
$ce[] = "The association " . $class->name . "#" . $fieldName . " refers to the inverse side ".
"field " . $assoc['targetEntity'] . "#" . $assoc['inversedBy'] . " which is not defined as association.";
}
if (!$targetMetadata->hasAssociation($assoc['inversedBy'])) {
$ce[] = "The association " . $class->name . "#" . $fieldName . " refers to the inverse side ".
"field " . $assoc['targetEntity'] . "#" . $assoc['inversedBy'] . " which does not exist.";
} else if ($targetMetadata->associationMappings[$assoc['inversedBy']]['mappedBy'] == null) {
$ce[] = "The field " . $class->name . "#" . $fieldName . " is on the owning side of a ".
"bi-directional relationship, but the specified mappedBy association on the target-entity ".
$assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " does not contain the required ".
"'inversedBy' attribute.";
} else if ($targetMetadata->associationMappings[$assoc['inversedBy']]['mappedBy'] != $fieldName) {
$ce[] = "The mappings " . $class->name . "#" . $fieldName . " and " .
$assoc['targetEntity'] . "#" . $assoc['inversedBy'] . " are ".
"incosistent with each other.";
}
// Verify inverse side/owning side match each other
if (array_key_exists($assoc['inversedBy'], $targetMetadata->associationMappings)) {
$targetAssoc = $targetMetadata->associationMappings[$assoc['inversedBy']];
if ($assoc['type'] == ClassMetadataInfo::ONE_TO_ONE && $targetAssoc['type'] !== ClassMetadataInfo::ONE_TO_ONE){
$ce[] = "If association " . $class->name . "#" . $fieldName . " is one-to-one, then the inversed " .
"side " . $targetMetadata->name . "#" . $assoc['inversedBy'] . " has to be one-to-one as well.";
} else if ($assoc['type'] == ClassMetadataInfo::MANY_TO_ONE && $targetAssoc['type'] !== ClassMetadataInfo::ONE_TO_MANY){
$ce[] = "If association " . $class->name . "#" . $fieldName . " is many-to-one, then the inversed " .
"side " . $targetMetadata->name . "#" . $assoc['inversedBy'] . " has to be one-to-many.";
} else if ($assoc['type'] == ClassMetadataInfo::MANY_TO_MANY && $targetAssoc['type'] !== ClassMetadataInfo::MANY_TO_MANY){
$ce[] = "If association " . $class->name . "#" . $fieldName . " is many-to-many, then the inversed " .
"side " . $targetMetadata->name . "#" . $assoc['inversedBy'] . " has to be many-to-many as well.";
}
}
}
if ($assoc['isOwningSide']) {
if ($assoc['type'] == ClassMetadataInfo::MANY_TO_MANY) {
$identifierColumns = $class->getIdentifierColumnNames();
foreach ($assoc['joinTable']['joinColumns'] AS $joinColumn) {
if (!in_array($joinColumn['referencedColumnName'], $identifierColumns)) {
$ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' " .
"has to be a primary key column on the target entity class '".$class->name."'.";
break;
}
}
$identifierColumns = $targetMetadata->getIdentifierColumnNames();
foreach ($assoc['joinTable']['inverseJoinColumns'] AS $inverseJoinColumn) {
if (!in_array($inverseJoinColumn['referencedColumnName'], $identifierColumns)) {
$ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' " .
"has to be a primary key column on the target entity class '".$targetMetadata->name."'.";
break;
}
}
if (count($targetMetadata->getIdentifierColumnNames()) != count($assoc['joinTable']['inverseJoinColumns'])) {
$ce[] = "The inverse join columns of the many-to-many table '" . $assoc['joinTable']['name'] . "' " .
"have to contain to ALL identifier columns of the target entity '". $targetMetadata->name . "', " .
"however '" . implode(", ", array_diff($targetMetadata->getIdentifierColumnNames(), array_values($assoc['relationToTargetKeyColumns']))) .
"' are missing.";
}
if (count($class->getIdentifierColumnNames()) != count($assoc['joinTable']['joinColumns'])) {
$ce[] = "The join columns of the many-to-many table '" . $assoc['joinTable']['name'] . "' " .
"have to contain to ALL identifier columns of the source entity '". $class->name . "', " .
"however '" . implode(", ", array_diff($class->getIdentifierColumnNames(), array_values($assoc['relationToSourceKeyColumns']))) .
"' are missing.";
}
} else if ($assoc['type'] & ClassMetadataInfo::TO_ONE) {
$identifierColumns = $targetMetadata->getIdentifierColumnNames();
foreach ($assoc['joinColumns'] AS $joinColumn) {
if (!in_array($joinColumn['referencedColumnName'], $identifierColumns)) {
$ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' " .
"has to be a primary key column on the target entity class '".$targetMetadata->name."'.";
}
}
if (count($identifierColumns) != count($assoc['joinColumns'])) {
$ids = array();
foreach ($assoc['joinColumns'] AS $joinColumn) {
$ids[] = $joinColumn['name'];
}
$ce[] = "The join columns of the association '" . $assoc['fieldName'] . "' " .
"have to match to ALL identifier columns of the target entity '". $class->name . "', " .
"however '" . implode(", ", array_diff($targetMetadata->getIdentifierColumnNames(), $ids)) .
"' are missing.";
}
}
}
if (isset($assoc['orderBy']) && $assoc['orderBy'] !== null) {
foreach ($assoc['orderBy'] AS $orderField => $orientation) {
if (!$targetMetadata->hasField($orderField)) {
$ce[] = "The association " . $class->name."#".$fieldName." is ordered by a foreign field " .
$orderField . " that is not a field on the target entity " . $targetMetadata->name;
}
}
}
}
foreach ($class->reflClass->getProperties(\ReflectionProperty::IS_PUBLIC) as $publicAttr) {
if ($publicAttr->isStatic()) {
continue;
}
$ce[] = "Field '".$publicAttr->getName()."' in class '".$class->name."' must be private ".
"or protected. Public fields may break lazy-loading.";
}
foreach ($class->subClasses AS $subClass) {
if (!in_array($class->name, class_parents($subClass))) {
$ce[] = "According to the discriminator map class '" . $subClass . "' has to be a child ".
"of '" . $class->name . "' but these entities are not related through inheritance.";
}
}
return $ce;
}
/**
* Check if the Database Schema is in sync with the current metadata state.
*
* @return bool
*/
public function schemaInSyncWithMetadata()
{
$schemaTool = new SchemaTool($this->em);
$allMetadata = $this->em->getMetadataFactory()->getAllMetadata();
return (count($schemaTool->getUpdateSchemaSql($allMetadata, true)) == 0);
}
}

View File

@@ -0,0 +1,194 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools;
use Doctrine\Common\ClassLoader;
use Doctrine\Common\Cache\Cache;
use Doctrine\Common\Cache\ArrayCache;
use Doctrine\ORM\Configuration;
use Doctrine\ORM\Mapping\Driver\XmlDriver;
use Doctrine\ORM\Mapping\Driver\YamlDriver;
/**
* Convenience class for setting up Doctrine from different installations and configurations.
*
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class Setup
{
/**
* Use this method to register all autoloaders for a setup where Doctrine is checked out from
* its github repository at {@link http://github.com/doctrine/doctrine2}
*
* @param string $gitCheckoutRootPath
* @return void
*/
static public function registerAutoloadGit($gitCheckoutRootPath)
{
if (!class_exists('Doctrine\Common\ClassLoader', false)) {
require_once $gitCheckoutRootPath . "/lib/vendor/doctrine-common/lib/Doctrine/Common/ClassLoader.php";
}
$loader = new ClassLoader("Doctrine\Common", $gitCheckoutRootPath . "/lib/vendor/doctrine-common/lib");
$loader->register();
$loader = new ClassLoader("Doctrine\DBAL", $gitCheckoutRootPath . "/lib/vendor/doctrine-dbal/lib");
$loader->register();
$loader = new ClassLoader("Doctrine\ORM", $gitCheckoutRootPath . "/lib");
$loader->register();
$loader = new ClassLoader("Symfony\Component", $gitCheckoutRootPath . "/lib/vendor");
$loader->register();
}
/**
* Use this method to register all autoloaders for a setup where Doctrine is installed
* though {@link http://pear.doctrine-project.org}.
*
* @return void
*/
static public function registerAutoloadPEAR()
{
if (!class_exists('Doctrine\Common\ClassLoader', false)) {
require_once "Doctrine/Common/ClassLoader.php";
}
$loader = new ClassLoader("Doctrine");
$loader->register();
$parts = explode(PATH_SEPARATOR, get_include_path());
foreach ($parts AS $includePath) {
if ($includePath != "." && file_exists($includePath . "/Doctrine")) {
$loader = new ClassLoader("Symfony\Component", $includePath . "/Doctrine");
$loader->register();
return;
}
}
}
/**
* Use this method to register all autoloads for a downloaded Doctrine library.
* Pick the directory the library was uncompressed into.
*
* @param string $directory
*/
static public function registerAutoloadDirectory($directory)
{
if (!class_exists('Doctrine\Common\ClassLoader', false)) {
require_once $directory . "/Doctrine/Common/ClassLoader.php";
}
$loader = new ClassLoader("Doctrine", $directory);
$loader->register();
$loader = new ClassLoader("Symfony\Component", $directory . "/Doctrine");
$loader->register();
}
/**
* Create a configuration with an annotation metadata driver.
*
* @param array $paths
* @param boolean $isDevMode
* @param string $proxyDir
* @param Cache $cache
* @return Configuration
*/
static public function createAnnotationMetadataConfiguration(array $paths, $isDevMode = false, $proxyDir = null, Cache $cache = null)
{
$config = self::createConfiguration($isDevMode, $proxyDir, $cache);
$config->setMetadataDriverImpl($config->newDefaultAnnotationDriver($paths));
return $config;
}
/**
* Create a configuration with a xml metadata driver.
*
* @param array $paths
* @param boolean $isDevMode
* @param string $proxyDir
* @param Cache $cache
* @return Configuration
*/
static public function createXMLMetadataConfiguration(array $paths, $isDevMode = false, $proxyDir = null, Cache $cache = null)
{
$config = self::createConfiguration($isDevMode, $proxyDir, $cache);
$config->setMetadataDriverImpl(new XmlDriver($paths));
return $config;
}
/**
* Create a configuration with a yaml metadata driver.
*
* @param array $paths
* @param boolean $isDevMode
* @param string $proxyDir
* @param Cache $cache
* @return Configuration
*/
static public function createYAMLMetadataConfiguration(array $paths, $isDevMode = false, $proxyDir = null, Cache $cache = null)
{
$config = self::createConfiguration($isDevMode, $proxyDir, $cache);
$config->setMetadataDriverImpl(new YamlDriver($paths));
return $config;
}
/**
* Create a configuration without a metadata driver.
*
* @param bool $isDevMode
* @param string $proxyDir
* @param Cache $cache
* @return Configuration
*/
static public function createConfiguration($isDevMode = false, $proxyDir = null, Cache $cache = null)
{
$proxyDir = $proxyDir ?: sys_get_temp_dir();
if ($isDevMode === false && $cache === null) {
if (extension_loaded('apc')) {
$cache = new \Doctrine\Common\Cache\ApcCache;
} else if (extension_loaded('xcache')) {
$cache = new \Doctrine\Common\Cache\XcacheCache;
} else if (extension_loaded('memcache')) {
$memcache = new \Memcache();
$memcache->connect('127.0.0.1');
$cache = new \Doctrine\Common\Cache\MemcacheCache();
$cache->setMemcache($memcache);
} else {
$cache = new ArrayCache;
}
} else if ($cache === null) {
$cache = new ArrayCache;
}
$cache->setNamespace("dc2_" . md5($proxyDir) . "_"); // to avoid collisions
$config = new Configuration();
$config->setMetadataCacheImpl($cache);
$config->setQueryCacheImpl($cache);
$config->setResultCacheImpl($cache);
$config->setProxyDir( $proxyDir );
$config->setProxyNamespace('DoctrineProxies');
$config->setAutoGenerateProxyClasses($isDevMode);
return $config;
}
}

View File

@@ -0,0 +1,44 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools;
class ToolEvents
{
/**
* The postGenerateSchemaTable event occurs in SchemaTool#getSchemaFromMetadata()
* whenever an entity class is transformed into its table representation. It recieves
* the current non-complete Schema instance, the Entity Metadata Class instance and
* the Schema Table instance of this entity.
*
* @var string
*/
const postGenerateSchemaTable = 'postGenerateSchemaTable';
/**
* The postGenerateSchema event is triggered in SchemaTool#getSchemaFromMetadata()
* after all entity classes have been transformed into the related Schema structure.
* The EventArgs contain the EntityManager and the created Schema instance.
*
* @var string
*/
const postGenerateSchema = 'postGenerateSchema';
}

View File

@@ -0,0 +1,40 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools;
use Doctrine\ORM\ORMException;
/**
* Tools related Exceptions
*
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class ToolsException extends ORMException
{
public static function schemaToolFailure($sql, \Exception $e)
{
return new self("Schema-Tool failed with Error '" . $e->getMessage() . "' while executing DDL: " . $sql, "0", $e);
}
public static function couldNotMapDoctrine1Type($type)
{
return new self("Could not map doctrine 1 type '$type'!");
}
}