entity_generator.class.php 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170
  1. <?php
  2. /**
  3. * Modifications of the entity generator class from Doctrine (Doctrine\ORM\Tools\EntityGenerator)
  4. * to account for chamilo.
  5. *
  6. * Most functions being private it is not possible to inherit from it.
  7. *
  8. * Original work, credits, licence see Docrine
  9. *
  10. *
  11. */
  12. namespace Tools;
  13. use Doctrine\ORM\Mapping\ClassMetadataInfo,
  14. Doctrine\ORM\Mapping\AssociationMapping,
  15. Doctrine\Common\Util\Inflector;
  16. /**
  17. * Generic class used to generate PHP5 entity classes from ClassMetadataInfo instances
  18. *
  19. * [php]
  20. * $classes = $em->getClassMetadataFactory()->getAllMetadata();
  21. *
  22. * $generator = new \Doctrine\ORM\Tools\EntityGenerator();
  23. * $generator->setGenerateAnnotations(true);
  24. * $generator->setGenerateStubMethods(true);
  25. * $generator->setRegenerateEntityIfExists(false);
  26. * $generator->setUpdateEntityIfExists(true);
  27. * $generator->generate($classes, '/path/to/generate/entities');
  28. *
  29. * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
  30. * @link www.doctrine-project.org
  31. * @since 2.0
  32. * @version $Revision$
  33. * @author Benjamin Eberlei <kontakt@beberlei.de>
  34. * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
  35. * @author Jonathan Wage <jonwage@gmail.com>
  36. * @author Roman Borschel <roman@code-factory.org>
  37. */
  38. class EntityGenerator
  39. {
  40. /**
  41. * @var bool
  42. */
  43. private $_backupExisting = true;
  44. /** The extension to use for written php files */
  45. private $_extension = '.php';
  46. /** Whether or not the current ClassMetadataInfo instance is new or old */
  47. private $_isNew = true;
  48. private $_staticReflection = array();
  49. /** Number of spaces to use for indention in generated code */
  50. private $_numSpaces = 4;
  51. /** The actual spaces to use for indention */
  52. private $_spaces = ' ';
  53. /** The class all generated entities should extend */
  54. private $_classToExtend;
  55. /** Whether or not to generation annotations */
  56. private $_generateAnnotations = false;
  57. /**
  58. * @var string
  59. */
  60. private $_annotationsPrefix = '';
  61. /** Whether or not to generated sub methods */
  62. private $_generateEntityStubMethods = false;
  63. /** Whether or not to update the entity class if it exists already */
  64. private $_updateEntityIfExists = false;
  65. /** Whether or not to re-generate entity class if it exists already */
  66. private $_regenerateEntityIfExists = false;
  67. private static $_classTemplate =
  68. '<?php
  69. namespace Entity;
  70. use Doctrine\ORM\Mapping as ORM;
  71. /**
  72. *
  73. * @license see /license.txt
  74. * @author autogenerated
  75. */
  76. <entityClassName>
  77. {
  78. <spaces>/**
  79. <spaces> * @return \Entity\Repository\<entityName>Repository
  80. <spaces> */
  81. <spaces> public static function repository(){
  82. <spaces><spaces>return \Entity\Repository\<entityName>Repository::instance();
  83. <spaces>}
  84. <spaces>/**
  85. <spaces> * @return \Entity\<entityName>
  86. <spaces> */
  87. <spaces> public static function create(){
  88. <spaces><spaces>return new self();
  89. <spaces>}
  90. <entityBody>
  91. }';
  92. private static $_getMethodTemplate =
  93. '/**
  94. * <description>
  95. *
  96. * @return <variableType>
  97. */
  98. public function <methodName>()
  99. {
  100. <spaces>return $this-><fieldName>;
  101. }';
  102. private static $_setMethodTemplate =
  103. '/**
  104. * <description>
  105. *
  106. * @param <variableType>$<variableName>
  107. * @return <entity>
  108. */
  109. public function <methodName>(<methodTypeHint>$<variableName><variableDefault>)
  110. {
  111. <spaces>$this-><fieldName> = $<variableName>;
  112. <spaces>return $this;
  113. }';
  114. private static $_addMethodTemplate =
  115. '/**
  116. * <description>
  117. *
  118. * @param <variableType>$<variableName>
  119. * @return <entity>
  120. */
  121. public function <methodName>(<methodTypeHint>$<variableName>)
  122. {
  123. <spaces>$this-><fieldName>[] = $<variableName>;
  124. <spaces>return $this;
  125. }';
  126. private static $_lifecycleCallbackMethodTemplate =
  127. '/**
  128. * @<name>
  129. */
  130. public function <methodName>()
  131. {
  132. <spaces>// Add your code here
  133. }';
  134. private static $_constructorMethodTemplate =
  135. 'public function __construct()
  136. {
  137. <spaces><collections>
  138. }
  139. ';
  140. public function __construct()
  141. {
  142. if (version_compare(\Doctrine\Common\Version::VERSION, '2.2.0-DEV', '>=')) {
  143. $this->_annotationsPrefix = 'ORM\\';
  144. }
  145. }
  146. /**
  147. * Generate and write entity classes for the given array of ClassMetadataInfo instances
  148. *
  149. * @param array $metadatas
  150. * @param string $outputDirectory
  151. * @return void
  152. */
  153. public function generate(array $metadatas, $outputDirectory)
  154. {
  155. foreach ($metadatas as $metadata) {
  156. $this->writeEntityClass($metadata, $outputDirectory);
  157. }
  158. }
  159. /**
  160. * Generated and write entity class to disk for the given ClassMetadataInfo instance
  161. *
  162. * @param ClassMetadataInfo $metadata
  163. * @param string $outputDirectory
  164. * @return void
  165. */
  166. public function writeEntityClass(ClassMetadataInfo $metadata, $outputDirectory)
  167. {
  168. //change
  169. $name = $metadata->name;
  170. $name = explode('\\', $name);
  171. $name = end($name);
  172. $name = Inflector::tableize($name);
  173. $is_course_table = (strpos($name, 'c_') === 0);
  174. if ($is_course_table) {
  175. $name = substr($name, 2, strlen($name) - 2);
  176. }
  177. $name = Inflector::tableize($name);
  178. //
  179. $path = $outputDirectory . '/' . str_replace('\\', DIRECTORY_SEPARATOR, $name) . $this->_extension;
  180. $dir = dirname($path);
  181. if (!is_dir($dir)) {
  182. mkdir($dir, 0777, true);
  183. }
  184. $this->_isNew = !file_exists($path) || (file_exists($path) && $this->_regenerateEntityIfExists);
  185. if (!$this->_isNew) {
  186. $this->_parseTokensInEntityFile(file_get_contents($path));
  187. } else {
  188. $this->_staticReflection[$metadata->name] = array('properties' => array(), 'methods' => array());
  189. }
  190. if ($this->_backupExisting && file_exists($path)) {
  191. $backupPath = dirname($path) . DIRECTORY_SEPARATOR . basename($path) . "~";
  192. if (!copy($path, $backupPath)) {
  193. throw new \RuntimeException("Attempt to backup overwritten entity file but copy operation failed.");
  194. }
  195. }
  196. // If entity doesn't exist or we're re-generating the entities entirely
  197. if ($this->_isNew) {
  198. file_put_contents($path, $this->generateEntityClass($metadata));
  199. // If entity exists and we're allowed to update the entity class
  200. } else if (!$this->_isNew && $this->_updateEntityIfExists) {
  201. file_put_contents($path, $this->generateUpdatedEntityClass($metadata, $path));
  202. }
  203. }
  204. /**
  205. * Generate a PHP5 Doctrine 2 entity class from the given ClassMetadataInfo instance
  206. *
  207. * @param ClassMetadataInfo $metadata
  208. * @return string $code
  209. */
  210. public function generateEntityClass(ClassMetadataInfo $metadata)
  211. {
  212. $placeHolders = array(
  213. '<namespace>',
  214. '<entityAnnotation>',
  215. '<entityClassName>',
  216. '<entityBody>'
  217. );
  218. $replacements = array(
  219. $this->_generateEntityNamespace($metadata),
  220. $this->_generateEntityDocBlock($metadata),
  221. $this->_generateEntityClassName($metadata),
  222. $this->_generateEntityBody($metadata)
  223. );
  224. $code = str_replace($placeHolders, $replacements, self::$_classTemplate);
  225. $result = str_replace('<spaces>', $this->_spaces, $code);
  226. $result = str_replace('<entityName>', $this->_getClassName($metadata), $result);
  227. return $result;
  228. }
  229. /**
  230. * Generate the updated code for the given ClassMetadataInfo and entity at path
  231. *
  232. * @param ClassMetadataInfo $metadata
  233. * @param string $path
  234. * @return string $code;
  235. */
  236. public function generateUpdatedEntityClass(ClassMetadataInfo $metadata, $path)
  237. {
  238. $currentCode = file_get_contents($path);
  239. $body = $this->_generateEntityBody($metadata);
  240. $body = str_replace('<spaces>', $this->_spaces, $body);
  241. $last = strrpos($currentCode, '}');
  242. return substr($currentCode, 0, $last) . $body . (strlen($body) > 0 ? "\n" : '') . "}";
  243. }
  244. /**
  245. * Set the number of spaces the exported class should have
  246. *
  247. * @param integer $numSpaces
  248. * @return void
  249. */
  250. public function setNumSpaces($numSpaces)
  251. {
  252. $this->_spaces = str_repeat(' ', $numSpaces);
  253. $this->_numSpaces = $numSpaces;
  254. }
  255. /**
  256. * Set the extension to use when writing php files to disk
  257. *
  258. * @param string $extension
  259. * @return void
  260. */
  261. public function setExtension($extension)
  262. {
  263. $this->_extension = $extension;
  264. }
  265. /**
  266. * Set the name of the class the generated classes should extend from
  267. *
  268. * @return void
  269. */
  270. public function setClassToExtend($classToExtend)
  271. {
  272. $this->_classToExtend = $classToExtend;
  273. }
  274. /**
  275. * Set whether or not to generate annotations for the entity
  276. *
  277. * @param bool $bool
  278. * @return void
  279. */
  280. public function setGenerateAnnotations($bool)
  281. {
  282. $this->_generateAnnotations = $bool;
  283. }
  284. /**
  285. * Set an annotation prefix.
  286. *
  287. * @param string $prefix
  288. */
  289. public function setAnnotationPrefix($prefix)
  290. {
  291. $this->_annotationsPrefix = $prefix;
  292. }
  293. /**
  294. * Set whether or not to try and update the entity if it already exists
  295. *
  296. * @param bool $bool
  297. * @return void
  298. */
  299. public function setUpdateEntityIfExists($bool)
  300. {
  301. $this->_updateEntityIfExists = $bool;
  302. }
  303. /**
  304. * Set whether or not to regenerate the entity if it exists
  305. *
  306. * @param bool $bool
  307. * @return void
  308. */
  309. public function setRegenerateEntityIfExists($bool)
  310. {
  311. $this->_regenerateEntityIfExists = $bool;
  312. }
  313. /**
  314. * Set whether or not to generate stub methods for the entity
  315. *
  316. * @param bool $bool
  317. * @return void
  318. */
  319. public function setGenerateStubMethods($bool)
  320. {
  321. $this->_generateEntityStubMethods = $bool;
  322. }
  323. /**
  324. * Should an existing entity be backed up if it already exists?
  325. */
  326. public function setBackupExisting($bool)
  327. {
  328. $this->_backupExisting = $bool;
  329. }
  330. private function _generateEntityNamespace(ClassMetadataInfo $metadata)
  331. {
  332. if ($this->_hasNamespace($metadata)) {
  333. return 'namespace ' . $this->_getNamespace($metadata) . ';';
  334. }
  335. }
  336. private function _generateEntityClassName(ClassMetadataInfo $metadata)
  337. {
  338. return 'class ' . $this->_getClassName($metadata) .
  339. ($this->_extendsClass() ? ' extends ' . $this->_getClassToExtendName($metadata) : null);
  340. }
  341. private function _generateEntityBody(ClassMetadataInfo $metadata)
  342. {
  343. $fieldMappingProperties = $this->_generateEntityFieldMappingProperties($metadata);
  344. $associationMappingProperties = $this->_generateEntityAssociationMappingProperties($metadata);
  345. $stubMethods = $this->_generateEntityStubMethods ? $this->_generateEntityStubMethods($metadata) : null;
  346. $lifecycleCallbackMethods = $this->_generateEntityLifecycleCallbackMethods($metadata);
  347. $code = array();
  348. if ($fieldMappingProperties) {
  349. $code[] = $fieldMappingProperties;
  350. }
  351. if ($associationMappingProperties) {
  352. $code[] = $associationMappingProperties;
  353. }
  354. $code[] = $this->_generateEntityConstructor($metadata);
  355. if ($stubMethods) {
  356. $code[] = $stubMethods;
  357. }
  358. if ($lifecycleCallbackMethods) {
  359. $code[] = $lifecycleCallbackMethods;
  360. }
  361. return implode("\n", $code);
  362. }
  363. private function _generateEntityConstructor(ClassMetadataInfo $metadata)
  364. {
  365. if ($this->_hasMethod('__construct', $metadata)) {
  366. return '';
  367. }
  368. $collections = array();
  369. foreach ($metadata->associationMappings AS $mapping) {
  370. if ($mapping['type'] & ClassMetadataInfo::TO_MANY) {
  371. $collections[] = '$this->' . $mapping['fieldName'] . ' = new \Doctrine\Common\Collections\ArrayCollection();';
  372. }
  373. }
  374. if ($collections) {
  375. return $this->_prefixCodeWithSpaces(str_replace("<collections>", implode("\n" . $this->_spaces, $collections), self::$_constructorMethodTemplate));
  376. }
  377. return '';
  378. }
  379. /**
  380. * @todo this won't work if there is a namespace in brackets and a class outside of it.
  381. * @param string $src
  382. */
  383. private function _parseTokensInEntityFile($src)
  384. {
  385. $tokens = token_get_all($src);
  386. $lastSeenNamespace = "";
  387. $lastSeenClass = false;
  388. $inNamespace = false;
  389. $inClass = false;
  390. for ($i = 0; $i < count($tokens); $i++) {
  391. $token = $tokens[$i];
  392. if (in_array($token[0], array(T_WHITESPACE, T_COMMENT, T_DOC_COMMENT))) {
  393. continue;
  394. }
  395. if ($inNamespace) {
  396. if ($token[0] == T_NS_SEPARATOR || $token[0] == T_STRING) {
  397. $lastSeenNamespace .= $token[1];
  398. } else if (is_string($token) && in_array($token, array(';', '{'))) {
  399. $inNamespace = false;
  400. }
  401. }
  402. if ($inClass) {
  403. $inClass = false;
  404. $lastSeenClass = $lastSeenNamespace . ($lastSeenNamespace ? '\\' : '') . $token[1];
  405. $this->_staticReflection[$lastSeenClass]['properties'] = array();
  406. $this->_staticReflection[$lastSeenClass]['methods'] = array();
  407. }
  408. if ($token[0] == T_NAMESPACE) {
  409. $lastSeenNamespace = "";
  410. $inNamespace = true;
  411. } else if ($token[0] == T_CLASS) {
  412. $inClass = true;
  413. } else if ($token[0] == T_FUNCTION) {
  414. if ($tokens[$i + 2][0] == T_STRING) {
  415. $this->_staticReflection[$lastSeenClass]['methods'][] = $tokens[$i + 2][1];
  416. } else if ($tokens[$i + 2] == "&" && $tokens[$i + 3][0] == T_STRING) {
  417. $this->_staticReflection[$lastSeenClass]['methods'][] = $tokens[$i + 3][1];
  418. }
  419. } else if (in_array($token[0], array(T_VAR, T_PUBLIC, T_PRIVATE, T_PROTECTED)) && $tokens[$i + 2][0] != T_FUNCTION) {
  420. $this->_staticReflection[$lastSeenClass]['properties'][] = substr($tokens[$i + 2][1], 1);
  421. }
  422. }
  423. }
  424. private function _hasProperty($property, ClassMetadataInfo $metadata)
  425. {
  426. if ($this->_extendsClass()) {
  427. // don't generate property if its already on the base class.
  428. $reflClass = new \ReflectionClass($this->_getClassToExtend($metadata));
  429. if ($reflClass->hasProperty($property)) {
  430. return true;
  431. }
  432. }
  433. return (
  434. isset($this->_staticReflection[$metadata->name]) &&
  435. in_array($property, $this->_staticReflection[$metadata->name]['properties'])
  436. );
  437. }
  438. private function _hasMethod($method, ClassMetadataInfo $metadata)
  439. {
  440. if ($this->_extendsClass()) {
  441. // don't generate method if its already on the base class.
  442. $reflClass = new \ReflectionClass($this->_getClassToExtend($metadata));
  443. if ($reflClass->hasMethod($method)) {
  444. return true;
  445. }
  446. }
  447. return (
  448. isset($this->_staticReflection[$metadata->name]) &&
  449. in_array($method, $this->_staticReflection[$metadata->name]['methods'])
  450. );
  451. }
  452. private function _hasNamespace(ClassMetadataInfo $metadata)
  453. {
  454. return strpos($metadata->name, '\\') ? true : false;
  455. }
  456. private function _extendsClass()
  457. {
  458. return $this->_classToExtend ? true : false;
  459. }
  460. private function _getClassToExtend($metadata)
  461. {
  462. return isset($metadata->class_to_extend) ? $metadata->class_to_extend : $this->_classToExtend;
  463. }
  464. private function _getClassToExtendName($metadata)
  465. {
  466. $refl = new \ReflectionClass($this->_getClassToExtend($metadata));
  467. return '\\' . $refl->getName();
  468. }
  469. private function _getClassName(ClassMetadataInfo $metadata)
  470. {
  471. //changed
  472. $name = $metadata->name;
  473. $name = explode('\\', $name);
  474. $name = end($name);
  475. $name = Inflector::tableize($name);
  476. $is_course_table = (strpos($name, 'c_') === 0);
  477. if ($is_course_table) {
  478. $name = substr($name, 2, strlen($name) - 2);
  479. }
  480. $name = Inflector::classify($name);
  481. $result = ($pos = strrpos($name, '\\')) ? substr($name, $pos + 1, strlen($name)) : $name;
  482. return $result;
  483. }
  484. private function _getNamespace(ClassMetadataInfo $metadata)
  485. {
  486. return substr($metadata->name, 0, strrpos($metadata->name, '\\'));
  487. }
  488. private function _generateEntityDocBlock(ClassMetadataInfo $metadata)
  489. {
  490. $lines = array();
  491. $lines[] = '/**';
  492. $lines[] = ' * ' . $metadata->name;
  493. if ($this->_generateAnnotations) {
  494. $lines[] = ' *';
  495. $methods = array(
  496. '_generateTableAnnotation',
  497. '_generateInheritanceAnnotation',
  498. '_generateDiscriminatorColumnAnnotation',
  499. '_generateDiscriminatorMapAnnotation'
  500. );
  501. foreach ($methods as $method) {
  502. if ($code = $this->$method($metadata)) {
  503. $lines[] = ' * ' . $code;
  504. }
  505. }
  506. if ($metadata->isMappedSuperclass) {
  507. $lines[] = ' * @' . $this->_annotationsPrefix . 'MappedSuperClass';
  508. } else {
  509. $lines[] = ' * @' . $this->_annotationsPrefix . 'Entity';
  510. }
  511. if ($metadata->customRepositoryClassName) {
  512. $lines[count($lines) - 1] .= '(repositoryClass="' . $metadata->customRepositoryClassName . '")';
  513. }
  514. if (isset($metadata->lifecycleCallbacks) && $metadata->lifecycleCallbacks) {
  515. $lines[] = ' * @' . $this->_annotationsPrefix . 'HasLifecycleCallbacks';
  516. }
  517. }
  518. $lines[] = ' */';
  519. return implode("\n", $lines);
  520. }
  521. private function _generateTableAnnotation($metadata)
  522. {
  523. $table = array();
  524. if (isset($metadata->table['schema'])) {
  525. $table[] = 'schema="' . $metadata->table['schema'] . '"';
  526. }
  527. if (isset($metadata->table['name'])) {
  528. $table[] = 'name="' . $metadata->table['name'] . '"';
  529. }
  530. if (isset($metadata->table['uniqueConstraints']) && $metadata->table['uniqueConstraints']) {
  531. $constraints = $this->_generateTableConstraints('UniqueConstraint', $metadata->table['uniqueConstraints']);
  532. $table[] = 'uniqueConstraints={' . $constraints . '}';
  533. }
  534. if (isset($metadata->table['indexes']) && $metadata->table['indexes']) {
  535. $constraints = $this->_generateTableConstraints('Index', $metadata->table['indexes']);
  536. $table[] = 'indexes={' . $constraints . '}';
  537. }
  538. return '@' . $this->_annotationsPrefix . 'Table(' . implode(', ', $table) . ')';
  539. }
  540. private function _generateTableConstraints($constraintName, $constraints)
  541. {
  542. $annotations = array();
  543. foreach ($constraints as $name => $constraint) {
  544. $columns = array();
  545. foreach ($constraint['columns'] as $column) {
  546. $columns[] = '"' . $column . '"';
  547. }
  548. $annotations[] = '@' . $this->_annotationsPrefix . $constraintName . '(name="' . $name . '", columns={' . implode(', ', $columns) . '})';
  549. }
  550. return implode(', ', $annotations);
  551. }
  552. private function _generateInheritanceAnnotation($metadata)
  553. {
  554. if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) {
  555. return '@' . $this->_annotationsPrefix . 'InheritanceType("' . $this->_getInheritanceTypeString($metadata->inheritanceType) . '")';
  556. }
  557. }
  558. private function _generateDiscriminatorColumnAnnotation($metadata)
  559. {
  560. if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) {
  561. $discrColumn = $metadata->discriminatorValue;
  562. $columnDefinition = 'name="' . $discrColumn['name']
  563. . '", type="' . $discrColumn['type']
  564. . '", length=' . $discrColumn['length'];
  565. return '@' . $this->_annotationsPrefix . 'DiscriminatorColumn(' . $columnDefinition . ')';
  566. }
  567. }
  568. private function _generateDiscriminatorMapAnnotation($metadata)
  569. {
  570. if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) {
  571. $inheritanceClassMap = array();
  572. foreach ($metadata->discriminatorMap as $type => $class) {
  573. $inheritanceClassMap[] .= '"' . $type . '" = "' . $class . '"';
  574. }
  575. return '@' . $this->_annotationsPrefix . 'DiscriminatorMap({' . implode(', ', $inheritanceClassMap) . '})';
  576. }
  577. }
  578. private function _generateEntityStubMethods(ClassMetadataInfo $metadata)
  579. {
  580. $methods = array();
  581. foreach ($metadata->fieldMappings as $fieldMapping) {
  582. if (!isset($fieldMapping['id']) || !$fieldMapping['id'] || $metadata->generatorType == ClassMetadataInfo::GENERATOR_TYPE_NONE) {
  583. if ($code = $this->_generateEntityStubMethod($metadata, 'set', $fieldMapping['fieldName'], $fieldMapping['type'])) {
  584. $methods[] = $code;
  585. }
  586. }
  587. if ($code = $this->_generateEntityStubMethod($metadata, 'get', $fieldMapping['fieldName'], $fieldMapping['type'])) {
  588. $methods[] = $code;
  589. }
  590. }
  591. foreach ($metadata->associationMappings as $associationMapping) {
  592. if ($associationMapping['type'] & ClassMetadataInfo::TO_ONE) {
  593. $nullable = $this->_isAssociationIsNullable($associationMapping) ? 'null' : null;
  594. if ($code = $this->_generateEntityStubMethod($metadata, 'set', $associationMapping['fieldName'], $associationMapping['targetEntity'], $nullable)) {
  595. $methods[] = $code;
  596. }
  597. if ($code = $this->_generateEntityStubMethod($metadata, 'get', $associationMapping['fieldName'], $associationMapping['targetEntity'])) {
  598. $methods[] = $code;
  599. }
  600. } else if ($associationMapping['type'] & ClassMetadataInfo::TO_MANY) {
  601. if ($code = $this->_generateEntityStubMethod($metadata, 'add', $associationMapping['fieldName'], $associationMapping['targetEntity'])) {
  602. $methods[] = $code;
  603. }
  604. if ($code = $this->_generateEntityStubMethod($metadata, 'get', $associationMapping['fieldName'], 'Doctrine\Common\Collections\Collection')) {
  605. $methods[] = $code;
  606. }
  607. }
  608. }
  609. return implode("\n\n", $methods);
  610. }
  611. private function _isAssociationIsNullable($associationMapping)
  612. {
  613. if (isset($associationMapping['id']) && $associationMapping['id']) {
  614. return false;
  615. }
  616. if (isset($associationMapping['joinColumns'])) {
  617. $joinColumns = $associationMapping['joinColumns'];
  618. } else {
  619. //@todo thereis no way to retreive targetEntity metadata
  620. $joinColumns = array();
  621. }
  622. foreach ($joinColumns as $joinColumn) {
  623. if (isset($joinColumn['nullable']) && !$joinColumn['nullable']) {
  624. return false;
  625. }
  626. }
  627. return true;
  628. }
  629. private function _generateEntityLifecycleCallbackMethods(ClassMetadataInfo $metadata)
  630. {
  631. if (isset($metadata->lifecycleCallbacks) && $metadata->lifecycleCallbacks) {
  632. $methods = array();
  633. foreach ($metadata->lifecycleCallbacks as $name => $callbacks) {
  634. foreach ($callbacks as $callback) {
  635. if ($code = $this->_generateLifecycleCallbackMethod($name, $callback, $metadata)) {
  636. $methods[] = $code;
  637. }
  638. }
  639. }
  640. return implode("\n\n", $methods);
  641. }
  642. return "";
  643. }
  644. private function _generateEntityAssociationMappingProperties(ClassMetadataInfo $metadata)
  645. {
  646. $lines = array();
  647. foreach ($metadata->associationMappings as $associationMapping) {
  648. if ($this->_hasProperty($associationMapping['fieldName'], $metadata)) {
  649. continue;
  650. }
  651. $lines[] = $this->_generateAssociationMappingPropertyDocBlock($associationMapping, $metadata);
  652. $lines[] = $this->_spaces . 'protected $' . $associationMapping['fieldName']
  653. . ($associationMapping['type'] == 'manyToMany' ? ' = array()' : null) . ";\n";
  654. }
  655. return implode("\n", $lines);
  656. }
  657. private function _generateEntityFieldMappingProperties(ClassMetadataInfo $metadata)
  658. {
  659. $lines = array();
  660. foreach ($metadata->fieldMappings as $fieldMapping) {
  661. if ($this->_hasProperty($fieldMapping['fieldName'], $metadata) ||
  662. $metadata->isInheritedField($fieldMapping['fieldName'])) {
  663. continue;
  664. }
  665. $fieldMapping['fieldName'] = Inflector::tableize($fieldMapping['fieldName']);
  666. $lines[] = $this->_generateFieldMappingPropertyDocBlock($fieldMapping, $metadata);
  667. $lines[] = $this->_spaces . 'protected $' . $fieldMapping['fieldName']
  668. . (isset($fieldMapping['default']) ? ' = ' . var_export($fieldMapping['default'], true) : null) . ";\n";
  669. }
  670. return implode("\n", $lines);
  671. }
  672. private function _generateEntityStubMethod(ClassMetadataInfo $metadata, $type, $fieldName, $typeHint = null, $defaultValue = null)
  673. {
  674. $fieldName = Inflector::tableize($fieldName);
  675. if ($type == "add") {
  676. $addMethod = explode("\\", $typeHint);
  677. $addMethod = end($addMethod);
  678. $methodName = $type . $addMethod;
  679. } else {
  680. //change
  681. $methodName = $type . '_' . Inflector::tableize($fieldName);
  682. //
  683. }
  684. if ($this->_hasMethod($methodName, $metadata)) {
  685. return;
  686. }
  687. $this->_staticReflection[$metadata->name]['methods'][] = $methodName;
  688. $var = sprintf('_%sMethodTemplate', $type);
  689. $template = self::$$var;
  690. $variableType = $typeHint ? $typeHint . ' ' : null;
  691. $types = \Doctrine\DBAL\Types\Type::getTypesMap();
  692. $methodTypeHint = $typeHint && !isset($types[$typeHint]) ? '\\' . $typeHint . ' ' : null;
  693. $replacements = array(
  694. '<description>' => ucfirst($type) . ' ' . $fieldName,
  695. '<methodTypeHint>' => $methodTypeHint,
  696. '<variableType>' => $variableType,
  697. '<variableName>' => 'value',
  698. '<methodName>' => $methodName,
  699. '<fieldName>' => $fieldName,
  700. '<variableDefault>' => ($defaultValue !== null ) ? (' = ' . $defaultValue) : '',
  701. '<entity>' => $this->_getClassName($metadata)
  702. );
  703. $method = str_replace(
  704. array_keys($replacements), array_values($replacements), $template
  705. );
  706. return $this->_prefixCodeWithSpaces($method);
  707. }
  708. private function _generateLifecycleCallbackMethod($name, $methodName, $metadata)
  709. {
  710. if ($this->_hasMethod($methodName, $metadata)) {
  711. return;
  712. }
  713. $this->_staticReflection[$metadata->name]['methods'][] = $methodName;
  714. $replacements = array(
  715. '<name>' => $this->_annotationsPrefix . $name,
  716. '<methodName>' => $methodName,
  717. );
  718. $method = str_replace(
  719. array_keys($replacements), array_values($replacements), self::$_lifecycleCallbackMethodTemplate
  720. );
  721. return $this->_prefixCodeWithSpaces($method);
  722. }
  723. private function _generateJoinColumnAnnotation(array $joinColumn)
  724. {
  725. $joinColumnAnnot = array();
  726. if (isset($joinColumn['name'])) {
  727. $joinColumnAnnot[] = 'name="' . $joinColumn['name'] . '"';
  728. }
  729. if (isset($joinColumn['referencedColumnName'])) {
  730. $joinColumnAnnot[] = 'referencedColumnName="' . $joinColumn['referencedColumnName'] . '"';
  731. }
  732. if (isset($joinColumn['unique']) && $joinColumn['unique']) {
  733. $joinColumnAnnot[] = 'unique=' . ($joinColumn['unique'] ? 'true' : 'false');
  734. }
  735. if (isset($joinColumn['nullable'])) {
  736. $joinColumnAnnot[] = 'nullable=' . ($joinColumn['nullable'] ? 'true' : 'false');
  737. }
  738. if (isset($joinColumn['onDelete'])) {
  739. $joinColumnAnnot[] = 'onDelete="' . ($joinColumn['onDelete'] . '"');
  740. }
  741. if (isset($joinColumn['columnDefinition'])) {
  742. $joinColumnAnnot[] = 'columnDefinition="' . $joinColumn['columnDefinition'] . '"';
  743. }
  744. return '@' . $this->_annotationsPrefix . 'JoinColumn(' . implode(', ', $joinColumnAnnot) . ')';
  745. }
  746. private function _generateAssociationMappingPropertyDocBlock(array $associationMapping, ClassMetadataInfo $metadata)
  747. {
  748. $lines = array();
  749. $lines[] = $this->_spaces . '/**';
  750. if ($associationMapping['type'] & ClassMetadataInfo::TO_MANY) {
  751. $lines[] = $this->_spaces . ' * @var \Doctrine\Common\Collections\ArrayCollection';
  752. } else {
  753. $lines[] = $this->_spaces . ' * @var ' . $associationMapping['targetEntity'];
  754. }
  755. if ($this->_generateAnnotations) {
  756. $lines[] = $this->_spaces . ' *';
  757. if (isset($associationMapping['id']) && $associationMapping['id']) {
  758. $lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'Id';
  759. if ($generatorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) {
  760. $lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'GeneratedValue(strategy="' . $generatorType . '")';
  761. }
  762. }
  763. $type = null;
  764. switch ($associationMapping['type']) {
  765. case ClassMetadataInfo::ONE_TO_ONE:
  766. $type = 'OneToOne';
  767. break;
  768. case ClassMetadataInfo::MANY_TO_ONE:
  769. $type = 'ManyToOne';
  770. break;
  771. case ClassMetadataInfo::ONE_TO_MANY:
  772. $type = 'OneToMany';
  773. break;
  774. case ClassMetadataInfo::MANY_TO_MANY:
  775. $type = 'ManyToMany';
  776. break;
  777. }
  778. $typeOptions = array();
  779. if (isset($associationMapping['targetEntity'])) {
  780. $typeOptions[] = 'targetEntity="' . $associationMapping['targetEntity'] . '"';
  781. }
  782. if (isset($associationMapping['inversedBy'])) {
  783. $typeOptions[] = 'inversedBy="' . $associationMapping['inversedBy'] . '"';
  784. }
  785. if (isset($associationMapping['mappedBy'])) {
  786. $typeOptions[] = 'mappedBy="' . $associationMapping['mappedBy'] . '"';
  787. }
  788. if ($associationMapping['cascade']) {
  789. $cascades = array();
  790. if ($associationMapping['isCascadePersist'])
  791. $cascades[] = '"persist"';
  792. if ($associationMapping['isCascadeRemove'])
  793. $cascades[] = '"remove"';
  794. if ($associationMapping['isCascadeDetach'])
  795. $cascades[] = '"detach"';
  796. if ($associationMapping['isCascadeMerge'])
  797. $cascades[] = '"merge"';
  798. if ($associationMapping['isCascadeRefresh'])
  799. $cascades[] = '"refresh"';
  800. $typeOptions[] = 'cascade={' . implode(',', $cascades) . '}';
  801. }
  802. if (isset($associationMapping['orphanRemoval']) && $associationMapping['orphanRemoval']) {
  803. $typeOptions[] = 'orphanRemoval=' . ($associationMapping['orphanRemoval'] ? 'true' : 'false');
  804. }
  805. $lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . '' . $type . '(' . implode(', ', $typeOptions) . ')';
  806. if (isset($associationMapping['joinColumns']) && $associationMapping['joinColumns']) {
  807. $lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'JoinColumns({';
  808. $joinColumnsLines = array();
  809. foreach ($associationMapping['joinColumns'] as $joinColumn) {
  810. if ($joinColumnAnnot = $this->_generateJoinColumnAnnotation($joinColumn)) {
  811. $joinColumnsLines[] = $this->_spaces . ' * ' . $joinColumnAnnot;
  812. }
  813. }
  814. $lines[] = implode(",\n", $joinColumnsLines);
  815. $lines[] = $this->_spaces . ' * })';
  816. }
  817. if (isset($associationMapping['joinTable']) && $associationMapping['joinTable']) {
  818. $joinTable = array();
  819. $joinTable[] = 'name="' . $associationMapping['joinTable']['name'] . '"';
  820. if (isset($associationMapping['joinTable']['schema'])) {
  821. $joinTable[] = 'schema="' . $associationMapping['joinTable']['schema'] . '"';
  822. }
  823. $lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'JoinTable(' . implode(', ', $joinTable) . ',';
  824. $lines[] = $this->_spaces . ' * joinColumns={';
  825. foreach ($associationMapping['joinTable']['joinColumns'] as $joinColumn) {
  826. $lines[] = $this->_spaces . ' * ' . $this->_generateJoinColumnAnnotation($joinColumn);
  827. }
  828. $lines[] = $this->_spaces . ' * },';
  829. $lines[] = $this->_spaces . ' * inverseJoinColumns={';
  830. foreach ($associationMapping['joinTable']['inverseJoinColumns'] as $joinColumn) {
  831. $lines[] = $this->_spaces . ' * ' . $this->_generateJoinColumnAnnotation($joinColumn);
  832. }
  833. $lines[] = $this->_spaces . ' * }';
  834. $lines[] = $this->_spaces . ' * )';
  835. }
  836. if (isset($associationMapping['orderBy'])) {
  837. $lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'OrderBy({';
  838. foreach ($associationMapping['orderBy'] as $name => $direction) {
  839. $lines[] = $this->_spaces . ' * "' . $name . '"="' . $direction . '",';
  840. }
  841. $lines[count($lines) - 1] = substr($lines[count($lines) - 1], 0, strlen($lines[count($lines) - 1]) - 1);
  842. $lines[] = $this->_spaces . ' * })';
  843. }
  844. }
  845. $lines[] = $this->_spaces . ' */';
  846. return implode("\n", $lines);
  847. }
  848. private function _generateFieldMappingPropertyDocBlock(array $fieldMapping, ClassMetadataInfo $metadata)
  849. {
  850. $lines = array();
  851. $lines[] = $this->_spaces . '/**';
  852. $lines[] = $this->_spaces . ' * @var ' . $fieldMapping['type'] . ' $' . $fieldMapping['fieldName'];
  853. if ($this->_generateAnnotations) {
  854. $lines[] = $this->_spaces . ' *';
  855. $column = array();
  856. if (isset($fieldMapping['columnName'])) {
  857. $column[] = 'name="' . $fieldMapping['columnName'] . '"';
  858. }
  859. if (isset($fieldMapping['type'])) {
  860. $column[] = 'type="' . $fieldMapping['type'] . '"';
  861. }
  862. if (isset($fieldMapping['length'])) {
  863. $column[] = 'length=' . $fieldMapping['length'];
  864. }
  865. if (isset($fieldMapping['precision'])) {
  866. $column[] = 'precision=' . $fieldMapping['precision'];
  867. }
  868. if (isset($fieldMapping['scale'])) {
  869. $column[] = 'scale=' . $fieldMapping['scale'];
  870. }
  871. if (isset($fieldMapping['nullable'])) {
  872. $column[] = 'nullable=' . var_export($fieldMapping['nullable'], true);
  873. }
  874. if (isset($fieldMapping['columnDefinition'])) {
  875. $column[] = 'columnDefinition="' . $fieldMapping['columnDefinition'] . '"';
  876. }
  877. if (isset($fieldMapping['unique'])) {
  878. $column[] = 'unique=' . var_export($fieldMapping['unique'], true);
  879. }
  880. $lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'Column(' . implode(', ', $column) . ')';
  881. if (isset($fieldMapping['id']) && $fieldMapping['id']) {
  882. $lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'Id';
  883. if ($generatorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) {
  884. $lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'GeneratedValue(strategy="' . $generatorType . '")';
  885. }
  886. if ($metadata->sequenceGeneratorDefinition) {
  887. $sequenceGenerator = array();
  888. if (isset($metadata->sequenceGeneratorDefinition['sequenceName'])) {
  889. $sequenceGenerator[] = 'sequenceName="' . $metadata->sequenceGeneratorDefinition['sequenceName'] . '"';
  890. }
  891. if (isset($metadata->sequenceGeneratorDefinition['allocationSize'])) {
  892. $sequenceGenerator[] = 'allocationSize="' . $metadata->sequenceGeneratorDefinition['allocationSize'] . '"';
  893. }
  894. if (isset($metadata->sequenceGeneratorDefinition['initialValue'])) {
  895. $sequenceGenerator[] = 'initialValue="' . $metadata->sequenceGeneratorDefinition['initialValue'] . '"';
  896. }
  897. $lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'SequenceGenerator(' . implode(', ', $sequenceGenerator) . ')';
  898. }
  899. }
  900. if (isset($fieldMapping['version']) && $fieldMapping['version']) {
  901. $lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'Version';
  902. }
  903. }
  904. $lines[] = $this->_spaces . ' */';
  905. return implode("\n", $lines);
  906. }
  907. private function _prefixCodeWithSpaces($code, $num = 1)
  908. {
  909. $lines = explode("\n", $code);
  910. foreach ($lines as $key => $value) {
  911. $lines[$key] = str_repeat($this->_spaces, $num) . $lines[$key];
  912. }
  913. return implode("\n", $lines);
  914. }
  915. private function _getInheritanceTypeString($type)
  916. {
  917. switch ($type) {
  918. case ClassMetadataInfo::INHERITANCE_TYPE_NONE:
  919. return 'NONE';
  920. case ClassMetadataInfo::INHERITANCE_TYPE_JOINED:
  921. return 'JOINED';
  922. case ClassMetadataInfo::INHERITANCE_TYPE_SINGLE_TABLE:
  923. return 'SINGLE_TABLE';
  924. case ClassMetadataInfo::INHERITANCE_TYPE_TABLE_PER_CLASS:
  925. return 'PER_CLASS';
  926. default:
  927. throw new \InvalidArgumentException('Invalid provided InheritanceType: ' . $type);
  928. }
  929. }
  930. private function _getChangeTrackingPolicyString($policy)
  931. {
  932. switch ($policy) {
  933. case ClassMetadataInfo::CHANGETRACKING_DEFERRED_IMPLICIT:
  934. return 'DEFERRED_IMPLICIT';
  935. case ClassMetadataInfo::CHANGETRACKING_DEFERRED_EXPLICIT:
  936. return 'DEFERRED_EXPLICIT';
  937. case ClassMetadataInfo::CHANGETRACKING_NOTIFY:
  938. return 'NOTIFY';
  939. default:
  940. throw new \InvalidArgumentException('Invalid provided ChangeTrackingPolicy: ' . $policy);
  941. }
  942. }
  943. private function _getIdGeneratorTypeString($type)
  944. {
  945. switch ($type) {
  946. case ClassMetadataInfo::GENERATOR_TYPE_AUTO:
  947. return 'AUTO';
  948. case ClassMetadataInfo::GENERATOR_TYPE_SEQUENCE:
  949. return 'SEQUENCE';
  950. case ClassMetadataInfo::GENERATOR_TYPE_TABLE:
  951. return 'TABLE';
  952. case ClassMetadataInfo::GENERATOR_TYPE_IDENTITY:
  953. return 'IDENTITY';
  954. case ClassMetadataInfo::GENERATOR_TYPE_NONE:
  955. return 'NONE';
  956. default:
  957. throw new \InvalidArgumentException('Invalid provided IdGeneratorType: ' . $type);
  958. }
  959. }
  960. }