ClassMetadataTest.php 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. <?php
  2. namespace Doctrine\Tests\ORM\Mapping;
  3. use Doctrine\ORM\Mapping\ClassMetadata;
  4. use Doctrine\ORM\Events;
  5. require_once __DIR__ . '/../../TestInit.php';
  6. require_once __DIR__ . '/../../Models/Global/GlobalNamespaceModel.php';
  7. class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
  8. {
  9. public function testClassMetadataInstanceSerialization()
  10. {
  11. $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
  12. // Test initial state
  13. $this->assertTrue(count($cm->getReflectionProperties()) == 0);
  14. $this->assertTrue($cm->reflClass instanceof \ReflectionClass);
  15. $this->assertEquals('Doctrine\Tests\Models\CMS\CmsUser', $cm->name);
  16. $this->assertEquals('Doctrine\Tests\Models\CMS\CmsUser', $cm->rootEntityName);
  17. $this->assertEquals(array(), $cm->subClasses);
  18. $this->assertEquals(array(), $cm->parentClasses);
  19. $this->assertEquals(ClassMetadata::INHERITANCE_TYPE_NONE, $cm->inheritanceType);
  20. // Customize state
  21. $cm->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_SINGLE_TABLE);
  22. $cm->setSubclasses(array("One", "Two", "Three"));
  23. $cm->setParentClasses(array("UserParent"));
  24. $cm->setCustomRepositoryClass("UserRepository");
  25. $cm->setDiscriminatorColumn(array('name' => 'disc', 'type' => 'integer'));
  26. $cm->mapOneToOne(array('fieldName' => 'phonenumbers', 'targetEntity' => 'Bar', 'mappedBy' => 'foo'));
  27. $cm->markReadOnly();
  28. $cm->addNamedQuery(array('name' => 'dql', 'query' => 'foo'));
  29. $this->assertEquals(1, count($cm->associationMappings));
  30. $serialized = serialize($cm);
  31. $cm = unserialize($serialized);
  32. // Check state
  33. $this->assertTrue(count($cm->getReflectionProperties()) > 0);
  34. $this->assertEquals('Doctrine\Tests\Models\CMS', $cm->namespace);
  35. $this->assertTrue($cm->reflClass instanceof \ReflectionClass);
  36. $this->assertEquals('Doctrine\Tests\Models\CMS\CmsUser', $cm->name);
  37. $this->assertEquals('UserParent', $cm->rootEntityName);
  38. $this->assertEquals(array('Doctrine\Tests\Models\CMS\One', 'Doctrine\Tests\Models\CMS\Two', 'Doctrine\Tests\Models\CMS\Three'), $cm->subClasses);
  39. $this->assertEquals(array('UserParent'), $cm->parentClasses);
  40. $this->assertEquals('UserRepository', $cm->customRepositoryClassName);
  41. $this->assertEquals(array('name' => 'disc', 'type' => 'integer', 'fieldName' => 'disc'), $cm->discriminatorColumn);
  42. $this->assertTrue($cm->associationMappings['phonenumbers']['type'] == ClassMetadata::ONE_TO_ONE);
  43. $this->assertEquals(1, count($cm->associationMappings));
  44. $oneOneMapping = $cm->getAssociationMapping('phonenumbers');
  45. $this->assertTrue($oneOneMapping['fetch'] == ClassMetadata::FETCH_LAZY);
  46. $this->assertEquals('phonenumbers', $oneOneMapping['fieldName']);
  47. $this->assertEquals('Doctrine\Tests\Models\CMS\Bar', $oneOneMapping['targetEntity']);
  48. $this->assertTrue($cm->isReadOnly);
  49. $this->assertEquals(array('dql' => 'foo'), $cm->namedQueries);
  50. }
  51. public function testFieldIsNullable()
  52. {
  53. $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
  54. // Explicit Nullable
  55. $cm->mapField(array('fieldName' => 'status', 'nullable' => true, 'type' => 'string', 'length' => 50));
  56. $this->assertTrue($cm->isNullable('status'));
  57. // Explicit Not Nullable
  58. $cm->mapField(array('fieldName' => 'username', 'nullable' => false, 'type' => 'string', 'length' => 50));
  59. $this->assertFalse($cm->isNullable('username'));
  60. // Implicit Not Nullable
  61. $cm->mapField(array('fieldName' => 'name', 'type' => 'string', 'length' => 50));
  62. $this->assertFalse($cm->isNullable('name'), "By default a field should not be nullable.");
  63. }
  64. /**
  65. * @group DDC-115
  66. */
  67. public function testMapAssocationInGlobalNamespace()
  68. {
  69. require_once __DIR__."/../../Models/Global/GlobalNamespaceModel.php";
  70. $cm = new ClassMetadata('DoctrineGlobal_Article');
  71. $cm->mapManyToMany(array(
  72. 'fieldName' => 'author',
  73. 'targetEntity' => 'DoctrineGlobal_User',
  74. 'joinTable' => array(
  75. 'name' => 'bar',
  76. 'joinColumns' => array(array('name' => 'bar_id', 'referencedColumnName' => 'id')),
  77. 'inverseJoinColumns' => array(array('name' => 'baz_id', 'referencedColumnName' => 'id')),
  78. ),
  79. ));
  80. $this->assertEquals("DoctrineGlobal_User", $cm->associationMappings['author']['targetEntity']);
  81. }
  82. public function testMapManyToManyJoinTableDefaults()
  83. {
  84. $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
  85. $cm->mapManyToMany(
  86. array(
  87. 'fieldName' => 'groups',
  88. 'targetEntity' => 'CmsGroup'
  89. ));
  90. $assoc = $cm->associationMappings['groups'];
  91. //$this->assertTrue($assoc instanceof \Doctrine\ORM\Mapping\ManyToManyMapping);
  92. $this->assertEquals(array(
  93. 'name' => 'cmsuser_cmsgroup',
  94. 'joinColumns' => array(array('name' => 'cmsuser_id', 'referencedColumnName' => 'id', 'onDelete' => 'CASCADE')),
  95. 'inverseJoinColumns' => array(array('name' => 'cmsgroup_id', 'referencedColumnName' => 'id', 'onDelete' => 'CASCADE'))
  96. ), $assoc['joinTable']);
  97. $this->assertTrue($assoc['isOnDeleteCascade']);
  98. }
  99. public function testSerializeManyToManyJoinTableCascade()
  100. {
  101. $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
  102. $cm->mapManyToMany(
  103. array(
  104. 'fieldName' => 'groups',
  105. 'targetEntity' => 'CmsGroup'
  106. ));
  107. /* @var $assoc \Doctrine\ORM\Mapping\ManyToManyMapping */
  108. $assoc = $cm->associationMappings['groups'];
  109. $assoc = unserialize(serialize($assoc));
  110. $this->assertTrue($assoc['isOnDeleteCascade']);
  111. }
  112. /**
  113. * @group DDC-115
  114. */
  115. public function testSetDiscriminatorMapInGlobalNamespace()
  116. {
  117. require_once __DIR__."/../../Models/Global/GlobalNamespaceModel.php";
  118. $cm = new ClassMetadata('DoctrineGlobal_User');
  119. $cm->setDiscriminatorMap(array('descr' => 'DoctrineGlobal_Article', 'foo' => 'DoctrineGlobal_User'));
  120. $this->assertEquals("DoctrineGlobal_Article", $cm->discriminatorMap['descr']);
  121. $this->assertEquals("DoctrineGlobal_User", $cm->discriminatorMap['foo']);
  122. }
  123. /**
  124. * @group DDC-115
  125. */
  126. public function testSetSubClassesInGlobalNamespace()
  127. {
  128. require_once __DIR__."/../../Models/Global/GlobalNamespaceModel.php";
  129. $cm = new ClassMetadata('DoctrineGlobal_User');
  130. $cm->setSubclasses(array('DoctrineGlobal_Article'));
  131. $this->assertEquals("DoctrineGlobal_Article", $cm->subClasses[0]);
  132. }
  133. /**
  134. * @group DDC-268
  135. */
  136. public function testSetInvalidVersionMapping_ThrowsException()
  137. {
  138. $field = array();
  139. $field['fieldName'] = 'foo';
  140. $field['type'] = 'string';
  141. $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
  142. $this->setExpectedException('Doctrine\ORM\Mapping\MappingException');
  143. $cm->setVersionMapping($field);
  144. }
  145. public function testGetSingleIdentifierFieldName_MultipleIdentifierEntity_ThrowsException()
  146. {
  147. $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
  148. $cm->isIdentifierComposite = true;
  149. $this->setExpectedException('Doctrine\ORM\Mapping\MappingException');
  150. $cm->getSingleIdentifierFieldName();
  151. }
  152. public function testDuplicateAssociationMappingException()
  153. {
  154. $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
  155. $a1 = array('fieldName' => 'foo', 'sourceEntity' => 'stdClass', 'targetEntity' => 'stdClass', 'mappedBy' => 'foo');
  156. $a2 = array('fieldName' => 'foo', 'sourceEntity' => 'stdClass', 'targetEntity' => 'stdClass', 'mappedBy' => 'foo');
  157. $cm->addInheritedAssociationMapping($a1);
  158. $this->setExpectedException('Doctrine\ORM\Mapping\MappingException');
  159. $cm->addInheritedAssociationMapping($a2);
  160. }
  161. public function testDuplicateColumnName_ThrowsMappingException()
  162. {
  163. $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
  164. $cm->mapField(array('fieldName' => 'name', 'columnName' => 'name'));
  165. $this->setExpectedException('Doctrine\ORM\Mapping\MappingException');
  166. $cm->mapField(array('fieldName' => 'username', 'columnName' => 'name'));
  167. }
  168. public function testDuplicateColumnName_DiscriminatorColumn_ThrowsMappingException()
  169. {
  170. $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
  171. $cm->mapField(array('fieldName' => 'name', 'columnName' => 'name'));
  172. $this->setExpectedException('Doctrine\ORM\Mapping\MappingException');
  173. $cm->setDiscriminatorColumn(array('name' => 'name'));
  174. }
  175. public function testDuplicateColumnName_DiscriminatorColumn2_ThrowsMappingException()
  176. {
  177. $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
  178. $cm->setDiscriminatorColumn(array('name' => 'name'));
  179. $this->setExpectedException('Doctrine\ORM\Mapping\MappingException');
  180. $cm->mapField(array('fieldName' => 'name', 'columnName' => 'name'));
  181. }
  182. public function testDuplicateFieldAndAssocationMapping1_ThrowsException()
  183. {
  184. $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
  185. $cm->mapField(array('fieldName' => 'name', 'columnName' => 'name'));
  186. $this->setExpectedException('Doctrine\ORM\Mapping\MappingException');
  187. $cm->mapOneToOne(array('fieldName' => 'name', 'targetEntity' => 'CmsUser'));
  188. }
  189. public function testDuplicateFieldAndAssocationMapping2_ThrowsException()
  190. {
  191. $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
  192. $cm->mapOneToOne(array('fieldName' => 'name', 'targetEntity' => 'CmsUser'));
  193. $this->setExpectedException('Doctrine\ORM\Mapping\MappingException');
  194. $cm->mapField(array('fieldName' => 'name', 'columnName' => 'name'));
  195. }
  196. /**
  197. * @group DDC-1224
  198. */
  199. public function testGetTemporaryTableNameSchema()
  200. {
  201. $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
  202. $cm->setTableName('foo.bar');
  203. $this->assertEquals('foo_bar_id_tmp', $cm->getTemporaryIdTableName());
  204. }
  205. public function testDefaultTableName()
  206. {
  207. $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
  208. // When table's name is not given
  209. $primaryTable = array();
  210. $cm->setPrimaryTable($primaryTable);
  211. $this->assertEquals('CmsUser', $cm->getTableName());
  212. $this->assertEquals('CmsUser', $cm->table['name']);
  213. $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsAddress');
  214. // When joinTable's name is not given
  215. $cm->mapManyToMany(array(
  216. 'fieldName' => 'user',
  217. 'targetEntity' => 'CmsUser',
  218. 'inversedBy' => 'users',
  219. 'joinTable' => array('joinColumns' => array(array('referencedColumnName' => 'id')),
  220. 'inverseJoinColumns' => array(array('referencedColumnName' => 'id')))));
  221. $this->assertEquals('cmsaddress_cmsuser', $cm->associationMappings['user']['joinTable']['name']);
  222. }
  223. public function testDefaultJoinColumnName()
  224. {
  225. $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsAddress');
  226. // this is really dirty, but it's the simpliest way to test whether
  227. // joinColumn's name will be automatically set to user_id
  228. $cm->mapOneToOne(array(
  229. 'fieldName' => 'user',
  230. 'targetEntity' => 'CmsUser',
  231. 'joinColumns' => array(array('referencedColumnName' => 'id'))));
  232. $this->assertEquals('user_id', $cm->associationMappings['user']['joinColumns'][0]['name']);
  233. $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsAddress');
  234. $cm->mapManyToMany(array(
  235. 'fieldName' => 'user',
  236. 'targetEntity' => 'CmsUser',
  237. 'inversedBy' => 'users',
  238. 'joinTable' => array('name' => 'user_CmsUser',
  239. 'joinColumns' => array(array('referencedColumnName' => 'id')),
  240. 'inverseJoinColumns' => array(array('referencedColumnName' => 'id')))));
  241. $this->assertEquals('cmsaddress_id', $cm->associationMappings['user']['joinTable']['joinColumns'][0]['name']);
  242. $this->assertEquals('cmsuser_id', $cm->associationMappings['user']['joinTable']['inverseJoinColumns'][0]['name']);
  243. }
  244. /**
  245. * @group DDC-886
  246. */
  247. public function testSetMultipleIdentifierSetsComposite()
  248. {
  249. $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
  250. $cm->mapField(array('fieldName' => 'name'));
  251. $cm->mapField(array('fieldName' => 'username'));
  252. $cm->setIdentifier(array('name', 'username'));
  253. $this->assertTrue($cm->isIdentifierComposite);
  254. }
  255. /**
  256. * @group DDC-944
  257. */
  258. public function testMappingNotFound()
  259. {
  260. $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
  261. $this->setExpectedException('Doctrine\ORM\Mapping\MappingException', "No mapping found for field 'foo' on class 'Doctrine\Tests\Models\CMS\CmsUser'.");
  262. $cm->getFieldMapping('foo');
  263. }
  264. /**
  265. * @group DDC-961
  266. */
  267. public function testJoinTableMappingDefaults()
  268. {
  269. $cm = new ClassMetadata('DoctrineGlobal_Article');
  270. $cm->mapManyToMany(array('fieldName' => 'author', 'targetEntity' => 'Doctrine\Tests\Models\CMS\CmsUser'));
  271. $this->assertEquals('doctrineglobal_article_cmsuser', $cm->associationMappings['author']['joinTable']['name']);
  272. }
  273. /**
  274. * @group DDC-117
  275. */
  276. public function testMapIdentifierAssociation()
  277. {
  278. $cm = new ClassMetadata('Doctrine\Tests\Models\DDC117\DDC117ArticleDetails');
  279. $cm->mapOneToOne(array(
  280. 'fieldName' => 'article',
  281. 'id' => true,
  282. 'targetEntity' => 'Doctrine\Tests\Models\DDC117\DDC117Article',
  283. 'joinColumns' => array(),
  284. ));
  285. $this->assertTrue($cm->containsForeignIdentifier, "Identifier Association should set 'containsForeignIdentifier' boolean flag.");
  286. $this->assertEquals(array("article"), $cm->identifier);
  287. }
  288. /**
  289. * @group DDC-117
  290. */
  291. public function testOrphanRemovalIdentifierAssociation()
  292. {
  293. $cm = new ClassMetadata('Doctrine\Tests\Models\DDC117\DDC117ArticleDetails');
  294. $this->setExpectedException('Doctrine\ORM\Mapping\MappingException', 'The orphan removal option is not allowed on an association that');
  295. $cm->mapOneToOne(array(
  296. 'fieldName' => 'article',
  297. 'id' => true,
  298. 'targetEntity' => 'Doctrine\Tests\Models\DDC117\DDC117Article',
  299. 'orphanRemoval' => true,
  300. 'joinColumns' => array(),
  301. ));
  302. }
  303. /**
  304. * @group DDC-117
  305. */
  306. public function testInverseIdentifierAssocation()
  307. {
  308. $cm = new ClassMetadata('Doctrine\Tests\Models\DDC117\DDC117ArticleDetails');
  309. $this->setExpectedException('Doctrine\ORM\Mapping\MappingException', 'An inverse association is not allowed to be identifier in');
  310. $cm->mapOneToOne(array(
  311. 'fieldName' => 'article',
  312. 'id' => true,
  313. 'mappedBy' => 'details', // INVERSE!
  314. 'targetEntity' => 'Doctrine\Tests\Models\DDC117\DDC117Article',
  315. 'joinColumns' => array(),
  316. ));
  317. }
  318. /**
  319. * @group DDC-117
  320. */
  321. public function testIdentifierAssocationManyToMany()
  322. {
  323. $cm = new ClassMetadata('Doctrine\Tests\Models\DDC117\DDC117ArticleDetails');
  324. $this->setExpectedException('Doctrine\ORM\Mapping\MappingException', 'Many-to-many or one-to-many associations are not allowed to be identifier in');
  325. $cm->mapManyToMany(array(
  326. 'fieldName' => 'article',
  327. 'id' => true,
  328. 'targetEntity' => 'Doctrine\Tests\Models\DDC117\DDC117Article',
  329. 'joinColumns' => array(),
  330. ));
  331. }
  332. /**
  333. * @group DDC-996
  334. */
  335. public function testEmptyFieldNameThrowsException()
  336. {
  337. $this->setExpectedException('Doctrine\ORM\Mapping\MappingException',
  338. "The field or association mapping misses the 'fieldName' attribute in entity 'Doctrine\Tests\Models\CMS\CmsUser'.");
  339. $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
  340. $cm->mapField(array('fieldName' => ''));
  341. }
  342. public function testRetrievalOfNamedQueries()
  343. {
  344. $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
  345. $this->assertEquals(0, count($cm->getNamedQueries()));
  346. $cm->addNamedQuery(array(
  347. 'name' => 'userById',
  348. 'query' => 'SELECT u FROM __CLASS__ u WHERE u.id = ?1'
  349. ));
  350. $this->assertEquals(1, count($cm->getNamedQueries()));
  351. }
  352. public function testExistanceOfNamedQuery()
  353. {
  354. $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
  355. $cm->addNamedQuery(array(
  356. 'name' => 'all',
  357. 'query' => 'SELECT u FROM __CLASS__ u'
  358. ));
  359. $this->assertTrue($cm->hasNamedQuery('all'));
  360. $this->assertFalse($cm->hasNamedQuery('userById'));
  361. }
  362. public function testRetrieveOfNamedQuery()
  363. {
  364. $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
  365. $cm->addNamedQuery(array(
  366. 'name' => 'userById',
  367. 'query' => 'SELECT u FROM __CLASS__ u WHERE u.id = ?1'
  368. ));
  369. $this->assertEquals('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?1', $cm->getNamedQuery('userById'));
  370. }
  371. public function testNamingCollisionNamedQueryShouldThrowException()
  372. {
  373. $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
  374. $this->setExpectedException('Doctrine\ORM\Mapping\MappingException');
  375. $cm->addNamedQuery(array(
  376. 'name' => 'userById',
  377. 'query' => 'SELECT u FROM __CLASS__ u WHERE u.id = ?1'
  378. ));
  379. $cm->addNamedQuery(array(
  380. 'name' => 'userById',
  381. 'query' => 'SELECT u FROM __CLASS__ u WHERE u.id = ?1'
  382. ));
  383. }
  384. /**
  385. * @group DDC-1068
  386. */
  387. public function testClassCaseSensitivity()
  388. {
  389. $user = new \Doctrine\Tests\Models\CMS\CmsUser();
  390. $cm = new ClassMetadata('DOCTRINE\TESTS\MODELS\CMS\CMSUSER');
  391. $this->assertEquals('Doctrine\Tests\Models\CMS\CmsUser', $cm->name);
  392. }
  393. }