diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php index 4e2a07519..32ee9401d 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -680,9 +680,11 @@ class ClassMetadataInfo } /** - * Validates & completes the mapping. Mapping defaults are applied here. + * Validates & completes the basic mapping information that is common to all + * association mappings (one-to-one, many-ot-one, one-to-many, many-to-many). * - * @param array $mapping + * @param array $mapping The mapping. + * @return array The updated mapping. * @throws MappingException If something is wrong with the mapping. */ protected function _validateAndCompleteAssociationMapping(array $mapping) @@ -693,31 +695,28 @@ class ClassMetadataInfo if ( ! isset($mapping['inversedBy'])) { $mapping['inversedBy'] = null; } - $mapping['isOwningSide'] = true; + $mapping['isOwningSide'] = true; // assume owning side until we hit mappedBy + // If targetEntity is unqualified, assume it is in the same namespace as + // the sourceEntity. $mapping['sourceEntity'] = $this->name; - if (isset($mapping['targetEntity']) && strpos($mapping['targetEntity'], '\\') === false && strlen($this->namespace) > 0) { + if (isset($mapping['targetEntity']) && strpos($mapping['targetEntity'], '\\') === false + && strlen($this->namespace) > 0) { $mapping['targetEntity'] = $this->namespace . '\\' . $mapping['targetEntity']; } - // Mandatory attributes for both sides + // Mandatory: fieldName, targetEntity if ( ! isset($mapping['fieldName'])) { throw MappingException::missingFieldName(); } - - if ( ! isset($mapping['sourceEntity'])) { - throw MappingException::missingSourceEntity($mapping['fieldName']); - } - if ( ! isset($mapping['targetEntity'])) { throw MappingException::missingTargetEntity($mapping['fieldName']); } // Mandatory and optional attributes for either side - if ( ! isset($mapping['mappedBy'])) { - // Optional + if ( ! $mapping['mappedBy']) { if (isset($mapping['joinTable']) && $mapping['joinTable']) { - if ($mapping['joinTable']['name'][0] == '`') { + if (isset($mapping['joinTable']['name']) && $mapping['joinTable']['name'][0] == '`') { $mapping['joinTable']['name'] = trim($mapping['joinTable']['name'], '`'); $mapping['joinTable']['quoted'] = true; } @@ -726,12 +725,13 @@ class ClassMetadataInfo $mapping['isOwningSide'] = false; } - // Optional attributes for both sides + // Fetch mode. Default fetch mode to LAZY, if not set. if ( ! isset($mapping['fetch'])) { $mapping['fetch'] = self::FETCH_LAZY; } + + // Cascades $cascades = isset($mapping['cascade']) ? $mapping['cascade'] : array(); - if (in_array('all', $cascades)) { $cascades = array( 'remove', @@ -752,10 +752,10 @@ class ClassMetadataInfo } /** - * {@inheritdoc} + * Validates & completes a one-to-one association mapping. * * @param array $mapping The mapping to validate & complete. - * @return array The validated & completed mapping. + * @return array The validated & completed mapping. * @override */ protected function _validateAndCompleteOneToOneMapping(array $mapping) @@ -774,11 +774,16 @@ class ClassMetadataInfo 'referencedColumnName' => 'id' )); } - foreach ($mapping['joinColumns'] AS $key => $joinColumn) { - if ($mapping['type'] == self::ONE_TO_ONE) { - $mapping['joinColumns'][$key]['unique'] = true; + foreach ($mapping['joinColumns'] as $key => &$joinColumn) { + if ($mapping['type'] === self::ONE_TO_ONE) { + $joinColumn['unique'] = true; + } + if (empty($joinColumn['name'])) { + $joinColumn['name'] = $mapping['fieldName'] . '_id'; + } + if (empty($joinColumn['referencedColumnName'])) { + $joinColumn['referencedColumnName'] = 'id'; } - $mapping['sourceToTargetKeyColumns'][$joinColumn['name']] = $joinColumn['referencedColumnName']; $mapping['joinColumnFieldNames'][$joinColumn['name']] = isset($joinColumn['fieldName']) ? $joinColumn['fieldName'] : $joinColumn['name']; @@ -826,58 +831,49 @@ class ClassMetadataInfo { $mapping = $this->_validateAndCompleteAssociationMapping($mapping); if ($mapping['isOwningSide']) { + $sourceShortName = strtolower(substr($mapping['sourceEntity'], strrpos($mapping['sourceEntity'], '\\') + 1)); + $targetShortName = strtolower(substr($mapping['targetEntity'], strrpos($mapping['targetEntity'], '\\') + 1)); // owning side MUST have a join table - if ( ! isset($mapping['joinTable']) || ! $mapping['joinTable']) { - // Apply default join table - $sourceShortName = substr($mapping['sourceEntity'], strrpos($mapping['sourceEntity'], '\\') + 1); - $targetShortName = substr($mapping['targetEntity'], strrpos($mapping['targetEntity'], '\\') + 1); - $mapping['joinTable'] = array( - 'name' => $sourceShortName .'_' . $targetShortName, - 'joinColumns' => array( - array( - 'name' => $sourceShortName . '_id', - 'referencedColumnName' => 'id', - 'onDelete' => 'CASCADE' - ) - ), - 'inverseJoinColumns' => array( - array( - 'name' => $targetShortName . '_id', - 'referencedColumnName' => 'id', - 'onDelete' => 'CASCADE' - ) - ) - ); + if ( ! isset($mapping['joinTable']['name'])) { + $mapping['joinTable']['name'] = $sourceShortName .'_' . $targetShortName; } - // owning side MUST specify joinColumns - else if ( ! isset($mapping['joinTable']['joinColumns'])) { - throw MappingException::missingRequiredOption( - $mapping['fieldName'], 'joinColumns', - 'Did you think of case sensitivity / plural s?' - ); + if ( ! isset($mapping['joinTable']['joinColumns'])) { + $mapping['joinTable']['joinColumns'] = array(array( + 'name' => $sourceShortName . '_id', + 'referencedColumnName' => 'id', + 'onDelete' => 'CASCADE')); } - // owning side MUST specify inverseJoinColumns - else if ( ! isset($mapping['joinTable']['inverseJoinColumns'])) { - throw MappingException::missingRequiredOption( - $mapping['fieldName'], 'inverseJoinColumns', - 'Did you think of case sensitivity / plural s?' - ); + if ( ! isset($mapping['joinTable']['inverseJoinColumns'])) { + $mapping['joinTable']['inverseJoinColumns'] = array(array( + 'name' => $targetShortName . '_id', + 'referencedColumnName' => 'id', + 'onDelete' => 'CASCADE')); } - - foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) { + + foreach ($mapping['joinTable']['joinColumns'] as &$joinColumn) { + if (empty($joinColumn['name'])) { + $joinColumn['name'] = $sourceShortName . '_id'; + } + if (empty($joinColumn['referencedColumnName'])) { + $joinColumn['referencedColumnName'] = 'id'; + } if (isset($joinColumn['onDelete']) && strtolower($joinColumn['onDelete']) == 'cascade') { $mapping['isOnDeleteCascade'] = true; } - $mapping['relationToSourceKeyColumns'][$joinColumn['name']] = $joinColumn['referencedColumnName']; $mapping['joinTableColumns'][] = $joinColumn['name']; } - - foreach ($mapping['joinTable']['inverseJoinColumns'] as $inverseJoinColumn) { + + foreach ($mapping['joinTable']['inverseJoinColumns'] as &$inverseJoinColumn) { + if (empty($inverseJoinColumn['name'])) { + $inverseJoinColumn['name'] = $targetShortName . '_id'; + } + if (empty($inverseJoinColumn['referencedColumnName'])) { + $inverseJoinColumn['referencedColumnName'] = 'id'; + } if (isset($inverseJoinColumn['onDelete']) && strtolower($inverseJoinColumn['onDelete']) == 'cascade') { $mapping['isOnDeleteCascade'] = true; } - $mapping['relationToTargetKeyColumns'][$inverseJoinColumn['name']] = $inverseJoinColumn['referencedColumnName']; $mapping['joinTableColumns'][] = $inverseJoinColumn['name']; } @@ -888,7 +884,7 @@ class ClassMetadataInfo throw new \InvalidArgumentException("'orderBy' is expected to be an array, not ".gettype($mapping['orderBy'])); } } - + return $mapping; } @@ -1211,22 +1207,33 @@ class ClassMetadataInfo * indexes => array of indexes (optional) * uniqueConstraints => array of constraints (optional) * - * @param array $table + * If a key is omitted, the current value is kept. + * + * @param array $table The table description. */ public function setPrimaryTable(array $table) { - if (isset($table['name']) && $table['name'][0] == '`') { - $table['name'] = trim($table['name'], '`'); - $table['quoted'] = true; + if (isset($table['name'])) { + if ($table['name'][0] == '`') { + $this->table['name'] = trim($table['name'], '`'); + $this->table['quoted'] = true; + } else { + $this->table['name'] = $table['name']; + } + } + if (isset($table['indexes'])) { + $this->table['indexes'] = $table['indexes']; + } + if (isset($table['uniqueConstraints'])) { + $this->table['uniqueConstraints'] = $table['uniqueConstraints']; } - $this->table = $table; } /** * Checks whether the given type identifies an inheritance type. * - * @param string $type - * @return boolean + * @param integer $type + * @return boolean TRUE if the given type identifies an inheritance type, FALSe otherwise. */ private function _isInheritanceType($type) { diff --git a/tests/Doctrine/Tests/Models/CMS/CmsAddress.php b/tests/Doctrine/Tests/Models/CMS/CmsAddress.php index 32af0fd85..9db2b6475 100644 --- a/tests/Doctrine/Tests/Models/CMS/CmsAddress.php +++ b/tests/Doctrine/Tests/Models/CMS/CmsAddress.php @@ -39,6 +39,7 @@ class CmsAddress /** * @OneToOne(targetEntity="CmsUser", inversedBy="address") + * @JoinColumn(referencedColumnName="id") */ public $user; diff --git a/tests/Doctrine/Tests/ORM/Functional/SchemaTool/PostgreSqlSchemaToolTest.php b/tests/Doctrine/Tests/ORM/Functional/SchemaTool/PostgreSqlSchemaToolTest.php index fcdfb0c2a..6a691a1d6 100644 --- a/tests/Doctrine/Tests/ORM/Functional/SchemaTool/PostgreSqlSchemaToolTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/SchemaTool/PostgreSqlSchemaToolTest.php @@ -19,7 +19,7 @@ class PostgreSqlSchemaToolTest extends \Doctrine\Tests\OrmFunctionalTestCase public function testPostgresMetadataSequenceIncrementedBy10() { $address = $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsAddress'); - $this->assertEquals(10, $address->sequenceGeneratorDefinition['allocationSize']); + $this->assertEquals(1, $address->sequenceGeneratorDefinition['allocationSize']); } public function testGetCreateSchemaSql() @@ -39,8 +39,8 @@ class PostgreSqlSchemaToolTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertEquals("CREATE UNIQUE INDEX cms_users_username_uniq ON cms_users (username)", $sql[3]); $this->assertEquals("CREATE TABLE cms_users_groups (user_id INT NOT NULL, group_id INT NOT NULL, PRIMARY KEY(user_id, group_id))", $sql[4]); $this->assertEquals("CREATE TABLE cms_phonenumbers (phonenumber VARCHAR(50) NOT NULL, user_id INT DEFAULT NULL, PRIMARY KEY(phonenumber))", $sql[5]); - $this->assertEquals("CREATE SEQUENCE cms_addresses_id_seq INCREMENT BY 10 MINVALUE 1 START 1", $sql[6]); - $this->assertEquals("CREATE SEQUENCE cms_users_id_seq INCREMENT BY 10 MINVALUE 1 START 1", $sql[7]); + $this->assertEquals("CREATE SEQUENCE cms_addresses_id_seq INCREMENT BY 1 MINVALUE 1 START 1", $sql[6]); + $this->assertEquals("CREATE SEQUENCE cms_users_id_seq INCREMENT BY 1 MINVALUE 1 START 1", $sql[7]); $this->assertEquals("ALTER TABLE cms_addresses ADD FOREIGN KEY (user_id) REFERENCES cms_users(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[8]); $this->assertEquals("ALTER TABLE cms_users_groups ADD FOREIGN KEY (user_id) REFERENCES cms_users(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[9]); $this->assertEquals("ALTER TABLE cms_users_groups ADD FOREIGN KEY (group_id) REFERENCES cms_groups(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[10]); @@ -61,7 +61,7 @@ class PostgreSqlSchemaToolTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertEquals(2, count($sql)); $this->assertEquals('CREATE TABLE decimal_model (id INT NOT NULL, "decimal" NUMERIC(5, 2) NOT NULL, "high_scale" NUMERIC(14, 4) NOT NULL, PRIMARY KEY(id))', $sql[0]); - $this->assertEquals("CREATE SEQUENCE decimal_model_id_seq INCREMENT BY 10 MINVALUE 1 START 1", $sql[1]); + $this->assertEquals("CREATE SEQUENCE decimal_model_id_seq INCREMENT BY 1 MINVALUE 1 START 1", $sql[1]); } public function testGetCreateSchemaSql3() @@ -75,6 +75,6 @@ class PostgreSqlSchemaToolTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertEquals(2, count($sql)); $this->assertEquals("CREATE TABLE boolean_model (id INT NOT NULL, booleanField BOOLEAN NOT NULL, PRIMARY KEY(id))", $sql[0]); - $this->assertEquals("CREATE SEQUENCE boolean_model_id_seq INCREMENT BY 10 MINVALUE 1 START 1", $sql[1]); + $this->assertEquals("CREATE SEQUENCE boolean_model_id_seq INCREMENT BY 1 MINVALUE 1 START 1", $sql[1]); } } diff --git a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php index b51d34831..34829fcae 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php @@ -102,9 +102,9 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase $assoc = $cm->associationMappings['groups']; //$this->assertTrue($assoc instanceof \Doctrine\ORM\Mapping\ManyToManyMapping); $this->assertEquals(array( - 'name' => 'CmsUser_CmsGroup', - 'joinColumns' => array(array('name' => 'CmsUser_id', 'referencedColumnName' => 'id', 'onDelete' => 'CASCADE')), - 'inverseJoinColumns' => array(array('name' => 'CmsGroup_id', 'referencedColumnName' => 'id', 'onDelete' => 'CASCADE')) + 'name' => 'cmsuser_cmsgroup', + 'joinColumns' => array(array('name' => 'cmsuser_id', 'referencedColumnName' => 'id', 'onDelete' => 'CASCADE')), + 'inverseJoinColumns' => array(array('name' => 'cmsgroup_id', 'referencedColumnName' => 'id', 'onDelete' => 'CASCADE')) ), $assoc['joinTable']); $this->assertTrue($assoc['isOnDeleteCascade']); } @@ -231,4 +231,49 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase $this->setExpectedException('Doctrine\ORM\Mapping\MappingException'); $cm->mapField(array('fieldName' => 'name', 'columnName' => 'name')); } + + public function testDefaultTableName() + { + $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'); + + // When table's name is not given + $primaryTable = array(); + $cm->setPrimaryTable($primaryTable); + + $this->assertEquals('CmsUser', $cm->getTableName()); + $this->assertEquals('CmsUser', $cm->table['name']); + + $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsAddress'); + // When joinTable's name is not given + $cm->mapManyToMany(array( + 'fieldName' => 'user', + 'targetEntity' => 'CmsUser', + 'inversedBy' => 'users', + 'joinTable' => array('joinColumns' => array(array('referencedColumnName' => 'id')), + 'inverseJoinColumns' => array(array('referencedColumnName' => 'id'))))); + $this->assertEquals('cmsaddress_cmsuser', $cm->associationMappings['user']['joinTable']['name']); + } + + public function testDefaultJoinColumnName() + { + $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsAddress'); + // this is really dirty, but it's the simpliest way to test whether + // joinColumn's name will be automatically set to user_id + $cm->mapOneToOne(array( + 'fieldName' => 'user', + 'targetEntity' => 'CmsUser', + 'joinColumns' => array(array('referencedColumnName' => 'id')))); + $this->assertEquals('user_id', $cm->associationMappings['user']['joinColumns'][0]['name']); + + $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsAddress'); + $cm->mapManyToMany(array( + 'fieldName' => 'user', + 'targetEntity' => 'CmsUser', + 'inversedBy' => 'users', + 'joinTable' => array('name' => 'user_CmsUser', + 'joinColumns' => array(array('referencedColumnName' => 'id')), + 'inverseJoinColumns' => array(array('referencedColumnName' => 'id'))))); + $this->assertEquals('cmsaddress_id', $cm->associationMappings['user']['joinTable']['joinColumns'][0]['name']); + $this->assertEquals('cmsuser_id', $cm->associationMappings['user']['joinTable']['inverseJoinColumns'][0]['name']); + } } \ No newline at end of file