PageRenderTime 40ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/generator/lib/builder/om/OMBuilder.php

https://github.com/1989gaurav/Propel
PHP | 542 lines | 326 code | 43 blank | 173 comment | 49 complexity | 48fc797e6b710dae01ae867c0e0346ee MD5 | raw file
  1. <?php
  2. /**
  3. * This file is part of the Propel package.
  4. * For the full copyright and license information, please view the LICENSE
  5. * file that was distributed with this source code.
  6. *
  7. * @license MIT License
  8. */
  9. require_once dirname(__FILE__) . '/../DataModelBuilder.php';
  10. /**
  11. * Baseclass for OM-building classes.
  12. *
  13. * OM-building classes are those that build a PHP (or other) class to service
  14. * a single table. This includes Peer classes, Entity classes, Map classes,
  15. * Node classes, Nested Set classes, etc.
  16. *
  17. * @author Hans Lellelid <hans@xmpl.org>
  18. * @package propel.generator.builder.om
  19. */
  20. abstract class OMBuilder extends DataModelBuilder
  21. {
  22. /**
  23. * Declared fully qualified classnames, to build the 'namespace' statements
  24. * according to this table's namespace.
  25. * @var array
  26. */
  27. protected $declaredClasses = array();
  28. /**
  29. * Builds the PHP source for current class and returns it as a string.
  30. *
  31. * This is the main entry point and defines a basic structure that classes should follow.
  32. * In most cases this method will not need to be overridden by subclasses. This method
  33. * does assume that the output language is PHP code, so it will need to be overridden if
  34. * this is not the case.
  35. *
  36. * @return string The resulting PHP sourcecode.
  37. */
  38. public function build()
  39. {
  40. $this->validateModel();
  41. $script = '';
  42. if ($this->isAddIncludes()) {
  43. $this->addIncludes($script);
  44. }
  45. $this->addClassOpen($script);
  46. $this->addClassBody($script);
  47. $this->addClassClose($script);
  48. if($useStatements = $this->getUseStatements($ignoredNamespace = $this->getNamespace())) {
  49. $script = $useStatements . $script;
  50. }
  51. if($namespaceStatement = $this->getNamespaceStatement()) {
  52. $script = $namespaceStatement . $script;
  53. }
  54. //if($this->getTable()->getName() == 'book_club_list') die($ignoredNamespace);
  55. return "<" . "?php
  56. " . $script;
  57. }
  58. /**
  59. * Validates the current table to make sure that it won't
  60. * result in generated code that will not parse.
  61. *
  62. * This method may emit warnings for code which may cause problems
  63. * and will throw exceptions for errors that will definitely cause
  64. * problems.
  65. */
  66. protected function validateModel()
  67. {
  68. // Validation is currently only implemented in the subclasses.
  69. }
  70. /**
  71. * Creates a $obj = new Book(); code snippet. Can be used by frameworks, for instance, to
  72. * extend this behavior, e.g. initialize the object after creating the instance or so.
  73. *
  74. * @return string Some code
  75. */
  76. public function buildObjectInstanceCreationCode($objName, $clsName)
  77. {
  78. return "$objName = new $clsName();";
  79. }
  80. /**
  81. * Returns the qualified (prefixed) classname that is being built by the current class.
  82. * This method must be implemented by child classes.
  83. * @return string
  84. */
  85. abstract public function getUnprefixedClassname();
  86. /**
  87. * Returns the prefixed classname that is being built by the current class.
  88. * @return string
  89. * @see DataModelBuilder#prefixClassname()
  90. */
  91. public function getClassname()
  92. {
  93. return $this->prefixClassname($this->getUnprefixedClassname());
  94. }
  95. /**
  96. * Returns the namespaced classname if there is a namespace, and the raw classname otherwise
  97. * @return string
  98. */
  99. public function getFullyQualifiedClassname()
  100. {
  101. if ($namespace = $this->getNamespace()) {
  102. return $namespace . '\\' . $this->getClassname();
  103. } else {
  104. return $this->getClassname();
  105. }
  106. }
  107. /**
  108. * Gets the dot-path representation of current class being built.
  109. * @return string
  110. */
  111. public function getClasspath()
  112. {
  113. if ($this->getPackage()) {
  114. $path = $this->getPackage() . '.' . $this->getClassname();
  115. } else {
  116. $path = $this->getClassname();
  117. }
  118. return $path;
  119. }
  120. /**
  121. * Gets the full path to the file for the current class.
  122. * @return string
  123. */
  124. public function getClassFilePath()
  125. {
  126. return ClassTools::createFilePath($this->getPackagePath(), $this->getClassname());
  127. }
  128. /**
  129. * Gets package name for this table.
  130. * This is overridden by child classes that have different packages.
  131. * @return string
  132. */
  133. public function getPackage()
  134. {
  135. $pkg = ($this->getTable()->getPackage() ? $this->getTable()->getPackage() : $this->getDatabase()->getPackage());
  136. if (!$pkg) {
  137. $pkg = $this->getBuildProperty('targetPackage');
  138. }
  139. return $pkg;
  140. }
  141. /**
  142. * Returns filesystem path for current package.
  143. * @return string
  144. */
  145. public function getPackagePath()
  146. {
  147. $pkg = $this->getPackage();
  148. if (strpos($pkg, '/') !== false) {
  149. return preg_replace('#\.(map|om)$#', '/\1', $pkg);
  150. }
  151. return strtr($pkg, '.', '/');
  152. }
  153. /**
  154. * Return the user-defined namespace for this table,
  155. * or the database namespace otherwise.
  156. *
  157. * @return string
  158. */
  159. public function getNamespace()
  160. {
  161. return $this->getTable()->getNamespace();
  162. }
  163. public function declareClassNamespace($class, $namespace = '')
  164. {
  165. if (isset($this->declaredClasses[$namespace])
  166. && in_array($class, $this->declaredClasses[$namespace])) {
  167. return;
  168. }
  169. $this->declaredClasses[$namespace][] = $class;
  170. }
  171. public function declareClass($fullyQualifiedClassName)
  172. {
  173. $fullyQualifiedClassName = trim($fullyQualifiedClassName, '\\');
  174. if (($pos = strrpos($fullyQualifiedClassName, '\\')) !== false) {
  175. $this->declareClassNamespace(substr($fullyQualifiedClassName, $pos + 1), substr($fullyQualifiedClassName, 0, $pos));
  176. } else {
  177. // root namespace
  178. $this->declareClassNamespace($fullyQualifiedClassName);
  179. }
  180. }
  181. public function declareClassFromBuilder($builder)
  182. {
  183. $this->declareClassNamespace($builder->getClassname(), $builder->getNamespace());
  184. }
  185. public function declareClasses()
  186. {
  187. $args = func_get_args();
  188. foreach ($args as $class) {
  189. $this->declareClass($class);
  190. }
  191. }
  192. public function getDeclaredClasses($namespace = null)
  193. {
  194. if (null !== $namespace && isset($this->declaredClasses[$namespace])) {
  195. return $this->declaredClasses[$namespace];
  196. } else {
  197. return $this->declaredClasses;
  198. }
  199. }
  200. public function getNamespaceStatement()
  201. {
  202. $namespace = $this->getNamespace();
  203. if ($namespace != '') {
  204. return sprintf("namespace %s;
  205. ", $namespace);
  206. }
  207. }
  208. public function getUseStatements($ignoredNamespace = null)
  209. {
  210. $script = '';
  211. $declaredClasses = $this->declaredClasses;
  212. unset($declaredClasses[$ignoredNamespace]);
  213. ksort($declaredClasses);
  214. foreach ($declaredClasses as $namespace => $classes) {
  215. sort($classes);
  216. foreach ($classes as $class) {
  217. $script .= sprintf("use %s\\%s;
  218. ", $namespace, $class);
  219. }
  220. }
  221. return $script;
  222. }
  223. /**
  224. * Shortcut method to return the [stub] peer classname for current table.
  225. * This is the classname that is used whenever object or peer classes want
  226. * to invoke methods of the peer classes.
  227. * @return string (e.g. 'MyPeer')
  228. * @see StubPeerBuilder::getClassname()
  229. */
  230. public function getPeerClassname() {
  231. return $this->getStubPeerBuilder()->getClassname();
  232. }
  233. /**
  234. * Shortcut method to return the [stub] query classname for current table.
  235. * This is the classname that is used whenever object or peer classes want
  236. * to invoke methods of the query classes.
  237. * @return string (e.g. 'Myquery')
  238. * @see StubQueryBuilder::getClassname()
  239. */
  240. public function getQueryClassname() {
  241. return $this->getStubQueryBuilder()->getClassname();
  242. }
  243. /**
  244. * Returns the object classname for current table.
  245. * This is the classname that is used whenever object or peer classes want
  246. * to invoke methods of the object classes.
  247. * @return string (e.g. 'My')
  248. * @see StubPeerBuilder::getClassname()
  249. */
  250. public function getObjectClassname() {
  251. return $this->getStubObjectBuilder()->getClassname();
  252. }
  253. /**
  254. * Get the column constant name (e.g. PeerName::COLUMN_NAME).
  255. *
  256. * @param Column $col The column we need a name for.
  257. * @param string $classname The Peer classname to use.
  258. *
  259. * @return string If $classname is provided, then will return $classname::COLUMN_NAME; if not, then the peername is looked up for current table to yield $currTablePeer::COLUMN_NAME.
  260. */
  261. public function getColumnConstant($col, $classname = null)
  262. {
  263. if ($col === null) {
  264. $e = new Exception("No col specified.");
  265. print $e;
  266. throw $e;
  267. }
  268. if ($classname === null) {
  269. return $this->getBuildProperty('classPrefix') . $col->getConstantName();
  270. }
  271. // was it overridden in schema.xml ?
  272. if ($col->getPeerName()) {
  273. $const = strtoupper($col->getPeerName());
  274. } else {
  275. $const = strtoupper($col->getName());
  276. }
  277. return $classname.'::'.$const;
  278. }
  279. /**
  280. * Gets the basePeer path if specified for table/db.
  281. * If not, will return 'propel.util.BasePeer'
  282. * @return string
  283. */
  284. public function getBasePeer(Table $table) {
  285. $class = $table->getBasePeer();
  286. if ($class === null) {
  287. $class = "propel.util.BasePeer";
  288. }
  289. return $class;
  290. }
  291. /**
  292. * Convenience method to get the foreign Table object for an fkey.
  293. * @deprecated use ForeignKey::getForeignTable() instead
  294. * @return Table
  295. */
  296. protected function getForeignTable(ForeignKey $fk)
  297. {
  298. return $this->getTable()->getDatabase()->getTable($fk->getForeignTableName());
  299. }
  300. /**
  301. * Convenience method to get the default Join Type for a relation.
  302. * If the key is required, an INNER JOIN will be returned, else a LEFT JOIN will be suggested,
  303. * unless the schema is provided with the DefaultJoin attribute, which overrules the default Join Type
  304. *
  305. * @param ForeignKey $fk
  306. * @return string
  307. */
  308. protected function getJoinType(ForeignKey $fk)
  309. {
  310. if ($defaultJoin = $fk->getDefaultJoin()) {
  311. return "'" . $defaultJoin . "'";
  312. }
  313. if ($fk->isLocalColumnsRequired()) {
  314. return 'Criteria::INNER_JOIN';
  315. }
  316. return 'Criteria::LEFT_JOIN';
  317. }
  318. /**
  319. * Gets the PHP method name affix to be used for fkeys for the current table (not referrers to this table).
  320. *
  321. * The difference between this method and the getRefFKPhpNameAffix() method is that in this method the
  322. * classname in the affix is the foreign table classname.
  323. *
  324. * @param ForeignKey $fk The local FK that we need a name for.
  325. * @param boolean $plural Whether the php name should be plural (e.g. initRelatedObjs() vs. addRelatedObj()
  326. * @return string
  327. */
  328. public function getFKPhpNameAffix(ForeignKey $fk, $plural = false)
  329. {
  330. if ($fk->getPhpName()) {
  331. if ($plural) {
  332. return $this->getPluralizer()->getPluralForm($fk->getPhpName());
  333. } else {
  334. return $fk->getPhpName();
  335. }
  336. } else {
  337. $className = $fk->getForeignTable()->getPhpName();
  338. if ($plural) {
  339. $className = $this->getPluralizer()->getPluralForm($className);
  340. }
  341. return $className . $this->getRelatedBySuffix($fk);
  342. }
  343. }
  344. /**
  345. * Gets the "RelatedBy*" suffix (if needed) that is attached to method and variable names.
  346. *
  347. * The related by suffix is based on the local columns of the foreign key. If there is more than
  348. * one column in a table that points to the same foreign table, then a 'RelatedByLocalColName' suffix
  349. * will be appended.
  350. *
  351. * @return string
  352. */
  353. protected static function getRelatedBySuffix(ForeignKey $fk)
  354. {
  355. $relCol = '';
  356. foreach ($fk->getLocalForeignMapping() as $localColumnName => $foreignColumnName) {
  357. $localTable = $fk->getTable();
  358. $localColumn = $localTable->getColumn($localColumnName);
  359. if (!$localColumn) {
  360. throw new Exception("Could not fetch column: $columnName in table " . $localTable->getName());
  361. }
  362. if (count($localTable->getForeignKeysReferencingTable($fk->getForeignTableName())) > 1
  363. || count($fk->getForeignTable()->getForeignKeysReferencingTable($fk->getTableName())) > 0
  364. || $fk->getForeignTableName() == $fk->getTableName()) {
  365. // self referential foreign key, or several foreign keys to the same table, or cross-reference fkey
  366. $relCol .= $localColumn->getPhpName();
  367. }
  368. }
  369. if ($relCol != '') {
  370. $relCol = 'RelatedBy' . $relCol;
  371. }
  372. return $relCol;
  373. }
  374. /**
  375. * Gets the PHP method name affix to be used for referencing foreign key methods and variable names (e.g. set????(), $coll???).
  376. *
  377. * The difference between this method and the getFKPhpNameAffix() method is that in this method the
  378. * classname in the affix is the classname of the local fkey table.
  379. *
  380. * @param ForeignKey $fk The referrer FK that we need a name for.
  381. * @param boolean $plural Whether the php name should be plural (e.g. initRelatedObjs() vs. addRelatedObj()
  382. * @return string
  383. */
  384. public function getRefFKPhpNameAffix(ForeignKey $fk, $plural = false)
  385. {
  386. if ($fk->getRefPhpName()) {
  387. if ($plural) {
  388. return $this->getPluralizer()->getPluralForm($fk->getRefPhpName());
  389. } else {
  390. return $fk->getRefPhpName();
  391. }
  392. } else {
  393. $className = $fk->getTable()->getPhpName();
  394. if ($plural) {
  395. $className = $this->getPluralizer()->getPluralForm($className);
  396. }
  397. return $className . $this->getRefRelatedBySuffix($fk);
  398. }
  399. }
  400. protected static function getRefRelatedBySuffix(ForeignKey $fk)
  401. {
  402. $relCol = '';
  403. foreach ($fk->getLocalForeignMapping() as $localColumnName => $foreignColumnName) {
  404. $localTable = $fk->getTable();
  405. $localColumn = $localTable->getColumn($localColumnName);
  406. if (!$localColumn) {
  407. throw new Exception("Could not fetch column: $columnName in table " . $localTable->getName());
  408. }
  409. $foreignKeysToForeignTable = $localTable->getForeignKeysReferencingTable($fk->getForeignTableName());
  410. if ($fk->getForeignTableName() == $fk->getTableName()) {
  411. // self referential foreign key
  412. $relCol .= $fk->getForeignTable()->getColumn($foreignColumnName)->getPhpName();
  413. if (count($foreignKeysToForeignTable) > 1) {
  414. // several self-referential foreign keys
  415. $relCol .= array_search($fk, $foreignKeysToForeignTable);
  416. }
  417. } elseif (count($foreignKeysToForeignTable) > 1 || count($fk->getForeignTable()->getForeignKeysReferencingTable($fk->getTableName())) > 0) {
  418. // several foreign keys to the same table, or symmetrical foreign key in foreign table
  419. $relCol .= $localColumn->getPhpName();
  420. }
  421. }
  422. if ($relCol != '') {
  423. $relCol = 'RelatedBy' . $relCol;
  424. }
  425. return $relCol;
  426. }
  427. /**
  428. * Whether to add the include statements.
  429. * This is based on the build property propel.addIncludes
  430. */
  431. protected function isAddIncludes()
  432. {
  433. return $this->getBuildProperty('addIncludes');
  434. }
  435. /**
  436. * Checks whether any registered behavior on that table has a modifier for a hook
  437. * @param string $hookName The name of the hook as called from one of this class methods, e.g. "preSave"
  438. * @param string $modifier The name of the modifier object providing the method in the behavior
  439. * @return boolean
  440. */
  441. public function hasBehaviorModifier($hookName, $modifier)
  442. {
  443. $modifierGetter = 'get' . $modifier;
  444. foreach ($this->getTable()->getBehaviors() as $behavior) {
  445. if(method_exists($behavior->$modifierGetter(), $hookName)) {
  446. return true;
  447. }
  448. }
  449. return false;
  450. }
  451. /**
  452. * Checks whether any registered behavior on that table has a modifier for a hook
  453. * @param string $hookName The name of the hook as called from one of this class methods, e.g. "preSave"
  454. * @param string $modifier The name of the modifier object providing the method in the behavior
  455. * @param string &$script The script will be modified in this method.
  456. */
  457. public function applyBehaviorModifierBase($hookName, $modifier, &$script, $tab = " ")
  458. {
  459. $modifierGetter = 'get' . $modifier;
  460. foreach ($this->getTable()->getBehaviors() as $behavior) {
  461. $modifier = $behavior->$modifierGetter();
  462. if(method_exists($modifier, $hookName)) {
  463. if (strpos($hookName, 'Filter') !== false) {
  464. // filter hook: the script string will be modified by the behavior
  465. $modifier->$hookName($script, $this);
  466. } else {
  467. // regular hook: the behavior returns a string to append to the script string
  468. if (!$addedScript = $modifier->$hookName($this)) {
  469. continue;
  470. }
  471. $script .= "
  472. " . $tab . '// ' . $behavior->getName() . " behavior
  473. ";
  474. $script .= preg_replace('/^/m', $tab, $addedScript);
  475. }
  476. }
  477. }
  478. }
  479. /**
  480. * Checks whether any registered behavior content creator on that table exists a contentName
  481. * @param string $contentName The name of the content as called from one of this class methods, e.g. "parentClassname"
  482. * @param string $modifier The name of the modifier object providing the method in the behavior
  483. */
  484. public function getBehaviorContentBase($contentName, $modifier)
  485. {
  486. $modifierGetter = 'get' . $modifier;
  487. foreach ($this->getTable()->getBehaviors() as $behavior) {
  488. $modifier = $behavior->$modifierGetter();
  489. if(method_exists($modifier, $contentName)) {
  490. return $modifier->$contentName($this);
  491. }
  492. }
  493. }
  494. }