diff --git a/lib/Doctrine/ORM/Configuration.php b/lib/Doctrine/ORM/Configuration.php index 840ae01b4..015dce5ca 100644 --- a/lib/Doctrine/ORM/Configuration.php +++ b/lib/Doctrine/ORM/Configuration.php @@ -629,4 +629,34 @@ class Configuration extends \Doctrine\DBAL\Configuration return $this->_attributes['namingStrategy']; } + + /** + * Set quote strategy class. + * + * @since 2.4 + * @param string $namingStrategy + */ + public function setQuoteStrategyClassName($className) + { + $quoteStrategy = 'Doctrine\Mapping\AbstractQuoteStrategy'; + + if ($className !== $quoteStrategy && ! is_subclass_of($className, $quoteStrategy)) { + throw new \InvalidArgumentException("Invalid quote strategy class"); + } + + $this->_attributes['quoteStrategyClassName'] = $namingStrategy; + } + + /** + * Get quote strategy class. + * + * @since 2.4 + * @return string + */ + public function getQuoteStrategyClassName() + { + return isset($this->_attributes['quoteStrategyClassName']) + ? $this->_attributes['quoteStrategyClassName'] + : 'Doctrine\Mapping\DefaultQuoteStrategy'; + } } diff --git a/lib/Doctrine/ORM/Mapping/DefaultQuoteStrategy.php b/lib/Doctrine/ORM/Mapping/DefaultQuoteStrategy.php new file mode 100644 index 000000000..8a8d2bbe9 --- /dev/null +++ b/lib/Doctrine/ORM/Mapping/DefaultQuoteStrategy.php @@ -0,0 +1,138 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + + +/** + * A set of rules for determining the physical column, alias and table quotes + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.4 + * @author Fabio B. Silva + */ +class DefaultQuoteStrategy extends QuoteStrategy +{ + + public function isQuotedIdentifier($identifier) + { + return strlen($identifier) > 0 && $identifier[0] === '`'; + } + + public function getUnquotedIdentifier($identifier) + { + return trim($identifier, '`'); + } + + /** + * @param string $fieldName + * @param ClassMetadata $class + * @return string + */ + public function getColumnName($fieldName, ClassMetadata $class) + { + return isset($class->fieldMappings[$fieldName]['quoted']) + ? $this->platform->quoteIdentifier($class->fieldMappings[$fieldName]['columnName']) + : $class->fieldMappings[$fieldName]['columnName']; + } + + /** + * Gets the (possibly quoted) primary table name of this class for safe use + * in an SQL statement. + * + * @param ClassMetadata $class + * @return string + */ + public function getTableName(ClassMetadata $class) + { + return isset($class->table['quoted']) + ? $this->platform->quoteIdentifier($class->table['name']) + : $class->table['name']; + } + + /** + * Gets the (possibly quoted) name of the join table. + * + * @param ClassMetadata $class + * @return string + */ + public function getJoinTableName($relation, ClassMetadata $class) + { + $assoc = $class->associationMappings[$relation]; + return isset($assoc['joinTable']['quoted']) + ? $this->platform->quoteIdentifier($assoc['joinTable']['name']) + : $assoc['joinTable']['name']; + } + + /** + * Gets the (possibly quoted) identifier column names for safe use in an SQL statement. + * + * @param ClassMetadata $class + * @return array + */ + public function getIdentifierColumnNames(ClassMetadata $class) + { + $quotedColumnNames = array(); + + foreach ($class->identifier as $fieldName) { + if (isset($this->fieldMappings[$fieldName])) { + $quotedColumnNames[] = $this->getColumnName($fieldName, $class); + + continue; + } + + // Association defined as Id field + $platform = $this->platform; + $joinColumns = $class->associationMappings[$fieldName]['joinColumns']; + $assocQuotedColumnNames = array_map( + function ($joinColumn) use ($platform) { + return isset($joinColumn['quoted']) + ? $platform->quoteIdentifier($joinColumn['name']) + : $joinColumn['name']; + }, + $joinColumns + ); + + $quotedColumnNames = array_merge($quotedColumnNames, $assocQuotedColumnNames); + } + + return $quotedColumnNames; + } + + /** + * @param string $columnName + * @param string $counter + * @param ClassMetadata $class + * @return string + */ + public function getColumnAlias($columnName, $counter, ClassMetadata $class = null) + { + // Trim the column alias to the maximum identifier length of the platform. + // If the alias is to long, characters are cut off from the beginning. + // And strip non alphanumeric characters + $columnName = $columnName . $counter; + $columnName = substr($columnName, -$this->platform->getMaxIdentifierLength()); + $columnName = preg_replace('/[^A-Za-z0-9_]/', '', $columnName); + + return $this->platform->getSQLResultCasing($columnName); + } + +} \ No newline at end of file diff --git a/lib/Doctrine/ORM/Mapping/QuoteStrategy.php b/lib/Doctrine/ORM/Mapping/QuoteStrategy.php new file mode 100644 index 000000000..366efb877 --- /dev/null +++ b/lib/Doctrine/ORM/Mapping/QuoteStrategy.php @@ -0,0 +1,49 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * A set of rules for determining the physical column, alias and table quotes + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.4 + * @author Fabio B. Silva + */ +abstract class QuoteStrategy +{ + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + protected $platform; + + /** + * @param AbstractPlatform $platform + */ + public function __construct(AbstractPlatform $platform) + { + $this->platform = $platform; + } + +} \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Mapping/QuoteStrategyTest.php b/tests/Doctrine/Tests/ORM/Mapping/QuoteStrategyTest.php new file mode 100644 index 000000000..38bc4de21 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Mapping/QuoteStrategyTest.php @@ -0,0 +1,106 @@ +_getTestEntityManager(); + $this->strategy = new DefaultQuoteStrategy($em->getConnection()->getDatabasePlatform()); + } + + + /** + * @param string $className + * @return \Doctrine\ORM\Mapping\ClassMetadata + */ + private function createClassMetadata($className) + { + $cm = new ClassMetadata($className); + $cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService); + + return $cm; + } + + + public function testIsQuotedIdentifier() + { + $this->assertTrue($this->strategy->isQuotedIdentifier('`table_name`')); + $this->assertFalse($this->strategy->isQuotedIdentifier('table_name')); + $this->assertFalse($this->strategy->isQuotedIdentifier(null)); + $this->assertFalse($this->strategy->isQuotedIdentifier('')); + } + + public function testGetUnquotedIdentifier() + { + $this->assertEquals('table_name' ,$this->strategy->getUnquotedIdentifier('`table_name`')); + $this->assertEquals('table_name' ,$this->strategy->getUnquotedIdentifier('table_name')); + } + + public function testGetColumnName() + { + $cm = $this->createClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'); + $cm->mapField(array('fieldName' => 'name', 'columnName' => '`name`')); + $cm->mapField(array('fieldName' => 'id', 'columnName' => 'id')); + + $this->assertEquals('id' ,$this->strategy->getColumnName('id', $cm)); + $this->assertEquals('"name"' ,$this->strategy->getColumnName('name', $cm)); + } + + public function testGetTableName() + { + $cm = $this->createClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'); + $cm->setPrimaryTable(array('name'=>'`cms_user`')); + $this->assertEquals('"cms_user"' ,$this->strategy->getTableName($cm)); + + $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'); + $cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService); + $cm->setPrimaryTable(array('name'=>'cms_user')); + $this->assertEquals('cms_user' ,$this->strategy->getTableName($cm)); + } + + public function testJoinTableName() + { + $cm = $this->createClassMetadata('Doctrine\Tests\Models\CMS\CmsAddress'); + $cm->mapManyToMany(array( + 'fieldName' => 'user', + 'targetEntity' => 'CmsUser', + 'inversedBy' => 'users', + 'joinTable' => array( + 'name' => '`cmsaddress_cmsuser`' + ) + ) + ); + $this->assertEquals('"cmsaddress_cmsuser"', $this->strategy->getJoinTableName('user', $cm)); + + $cm = $this->createClassMetadata('Doctrine\Tests\Models\CMS\CmsAddress'); + $cm->mapManyToMany(array( + 'fieldName' => 'user', + 'targetEntity' => 'CmsUser', + 'inversedBy' => 'users', + 'joinTable' => array( + 'name' => 'cmsaddress_cmsuser' + ) + ) + ); + $this->assertEquals('cmsaddress_cmsuser', $this->strategy->getJoinTableName('user', $cm)); + + } +} \ No newline at end of file