1. sfModelGenerator.class.php
  2. /** * Model generator. * * @package symfony * @subpackage generator * @author Fabien Potencier * @version SVN: $Id: sfModelGenerator.class.php 25459 2009-12-16 13:08:43Z fabien $ */
  3. abstract class sfModelGenerator extends sfGenerator
  4. {
  5. protected
  6. $configuration = null,
  7. $primaryKey = array(),
  8. $modelClass = '',
  9. $params = array(),
  10. $config = array(),
  11. $formObject = null;
  12. /**
  13. * Generates classes and templates in cache.
  14. *
  15. * @param array $params The parameters
  16. *
  17. * @return string The data to put in configuration cache
  18. */
  19. public function generate($params = array())
  20. {
  21. $this->validateParameters($params);
  22. $this->modelClass = $this->params['model_class'];
  23. // generated module name
  24. $this->setModuleName($this->params['moduleName']);
  25. $this->setGeneratedModuleName('auto'.ucfirst($this->params['moduleName']));
  26. // theme exists?
  27. $theme = isset($this->params['theme']) ? $this->params['theme'] : 'default';
  28. $this->setTheme($theme);
  29. $themeDir = $this->generatorManager->getConfiguration()->getGeneratorTemplate($this->getGeneratorClass(), $theme, '');
  30. if (!is_dir($themeDir))
  31. {
  32. throw new sfConfigurationException(sprintf('The theme "%s" does not exist.', $theme));
  33. }
  34. // configure the model
  35. $this->configure();
  36. $this->configuration = $this->loadConfiguration();
  37. // generate files
  38. $this->generatePhpFiles($this->generatedModuleName, sfFinder::type('file')->relative()->in($themeDir));
  39. // move helper file
  40. if (file_exists($file = $this->generatorManager->getBasePath().'/'.$this->getGeneratedModuleName().'/lib/helper.php'))
  41. {
  42. @rename($file, $this->generatorManager->getBasePath().'/'.$this->getGeneratedModuleName().'/lib/Base'.ucfirst($this->moduleName).'GeneratorHelper.class.php');
  43. }
  44. return "require_once(sfConfig::get('sf_module_cache_dir').'/".$this->generatedModuleName."/actions/actions.class.php');";
  45. }
  46. /**
  47. * Gets the actions base class for the generated module.
  48. *
  49. * @return string The actions base class
  50. */
  51. public function getActionsBaseClass()
  52. {
  53. return isset($this->params['actions_base_class']) ? $this->params['actions_base_class'] : 'sfActions';
  54. }
  55. /**
  56. * Gets the class name for current model.
  57. *
  58. * @return string
  59. */
  60. public function getModelClass()
  61. {
  62. return $this->modelClass;
  63. }
  64. /**
  65. * Gets the primary key name.
  66. *
  67. * @param Boolean $firstOne Whether to return the first PK or not
  68. *
  69. * @return array An array of primary keys
  70. */
  71. public function getPrimaryKeys($firstOne = false)
  72. {
  73. return $firstOne ? $this->primaryKey[0] : $this->primaryKey;
  74. }
  75. /**
  76. * Gets the singular name for current model.
  77. *
  78. * @return string
  79. */
  80. public function getSingularName()
  81. {
  82. return isset($this->params['singular']) ? $this->params['singular'] : sfInflector::underscore($this->getModelClass());
  83. }
  84. /**
  85. * Gets the plural name for current model.
  86. *
  87. * @return string
  88. */
  89. public function getPluralName()
  90. {
  91. return isset($this->params['plural']) ? $this->params['plural'] : $this->getSingularName().'_list';
  92. }
  93. /**
  94. * Gets the i18n catalogue to use for user strings.
  95. *
  96. * @return string The i18n catalogue
  97. */
  98. public function getI18nCatalogue()
  99. {
  100. return isset($this->params['i18n_catalogue']) ? $this->params['i18n_catalogue'] : 'messages';
  101. }
  102. /**
  103. * Returns PHP code for primary keys parameters.
  104. *
  105. * @param integer $indent The indentation value
  106. * @param string $callee The function to call
  107. *
  108. * @return string The PHP code
  109. */
  110. public function getRetrieveByPkParamsForAction($indent)
  111. {
  112. $params = array();
  113. foreach ($this->getPrimaryKeys() as $pk)
  114. {
  115. $params[] = sprintf("\$request->getParameter('%s')", sfInflector::underscore($pk));
  116. }
  117. return implode(",\n".str_repeat(' ', max(0, $indent - strlen($this->getSingularName().$this->modelClass))), $params);
  118. }
  119. /**
  120. * Returns PHP code to add to a URL for primary keys.
  121. *
  122. * @param string $prefix The prefix value
  123. *
  124. * @return string PHP code
  125. */
  126. public function getPrimaryKeyUrlParams($prefix = '', $full = false)
  127. {
  128. $params = array();
  129. foreach ($this->getPrimaryKeys() as $pk)
  130. {
  131. $fieldName = sfInflector::underscore($pk);
  132. if ($full)
  133. {
  134. $params[] = sprintf("%s='.%s->%s()", $fieldName, $prefix, $this->getColumnGetter($fieldName, false));
  135. }
  136. else
  137. {
  138. $params[] = sprintf("%s='.%s", $fieldName, $this->getColumnGetter($fieldName, true, $prefix));
  139. }
  140. }
  141. return implode(".'&", $params);
  142. }
  143. /**
  144. * Configures this generator.
  145. */
  146. abstract protected function configure();
  147. abstract public function getType($column);
  148. abstract public function getAllFieldNames();
  149. /**
  150. * Returns the getter either non-developped: 'getFoo' or developped: '$class->getFoo()'.
  151. *
  152. * This method is ORM dependant.
  153. *
  154. * @param string $column The column name
  155. * @param boolean $developed true if you want developped method names, false otherwise
  156. * @param string $prefix The prefix value
  157. *
  158. * @return string PHP code
  159. */
  160. abstract public function getColumnGetter($column, $developed = false , $prefix = '');
  161. abstract public function getManyToManyTables();
  162. /**
  163. * Returns HTML code for an action link.
  164. *
  165. * @param string $actionName The action name
  166. * @param array $params The parameters
  167. * @param boolean $pk_link Whether to add a primary key link or not
  168. *
  169. * @return string HTML code
  170. */
  171. public function getLinkToAction($actionName, $params, $pk_link = false)
  172. {
  173. $action = isset($params['action']) ? $params['action'] : 'List'.sfInflector::camelize($actionName);
  174. $url_params = $pk_link ? '?'.$this->getPrimaryKeyUrlParams() : '\'';
  175. return '[?php echo link_to(__(\''.$params['label'].'\', array(), \''.$this->getI18nCatalogue().'\'), \''.$this->getModuleName().'/'.$action.$url_params.', '.$this->asPhp($params['params']).') ?]';
  176. }
  177. /**
  178. * Wraps content with a credential condition.
  179. *
  180. * @param string $content The content
  181. * @param array $params The parameters
  182. *
  183. * @return string HTML code
  184. */
  185. public function addCredentialCondition($content, $params = array())
  186. {
  187. if (isset($params['credentials']))
  188. {
  189. $credentials = $this->asPhp($params['credentials']);
  190. return <<<EOF
  191. [?php if (\$sf_user->hasCredential($credentials)): ?]
  192. $content
  193. [?php endif; ?]
  194. EOF;
  195. }
  196. else
  197. {
  198. return $content;
  199. }
  200. }
  201. /**
  202. * Returns HTML code for a field.
  203. *
  204. * @param sfModelGeneratorConfigurationField $field The field
  205. *
  206. * @return string HTML code
  207. */
  208. public function renderField($field)
  209. {
  210. $html = $this->getColumnGetter($field->getName(), true);
  211. if ($renderer = $field->getRenderer())
  212. {
  213. $html = sprintf("$html ? call_user_func_array(%s, array_merge(array(%s), %s)) : '&nbsp;'", $this->asPhp($renderer), $html, $this->asPhp($field->getRendererArguments()));
  214. }
  215. else if ($field->isComponent())
  216. {
  217. return sprintf("get_component('%s', '%s', array('type' => 'list', '%s' => \$%s))", $this->getModuleName(), $field->getName(), $this->getSingularName(), $this->getSingularName());
  218. }
  219. else if ($field->isPartial())
  220. {
  221. return sprintf("get_partial('%s/%s', array('type' => 'list', '%s' => \$%s))", $this->getModuleName(), $field->getName(), $this->getSingularName(), $this->getSingularName());
  222. }
  223. else if ('Date' == $field->getType())
  224. {
  225. $html = sprintf("false !== strtotime($html) ? format_date(%s, \"%s\") : '&nbsp;'", $html, $field->getConfig('date_format', 'f'));
  226. }
  227. else if ('Boolean' == $field->getType())
  228. {
  229. $html = sprintf("get_partial('%s/list_field_boolean', array('value' => %s))", $this->getModuleName(), $html);
  230. }
  231. if ($field->isLink())
  232. {
  233. $html = sprintf("link_to(%s, '%s', \$%s)", $html, $this->getUrlForAction('edit'), $this->getSingularName());
  234. }
  235. return $html;
  236. }
  237. /**
  238. * Wraps a content for I18N.
  239. *
  240. * @param string $key The configuration key name
  241. *
  242. * @return string HTML code
  243. */
  244. public function getI18NString($key)
  245. {
  246. $value = $this->configuration->getValue($key, '', true);
  247. $parts = explode('.', $key);
  248. $context = $parts[0];
  249. // find %%xx%% strings
  250. preg_match_all('/%%([^%]+)%%/', $value, $matches, PREG_PATTERN_ORDER);
  251. $fields = array();
  252. foreach ($matches[1] as $name)
  253. {
  254. $fields[] = $name;
  255. }
  256. $vars = array();
  257. foreach ($this->configuration->getContextConfiguration($context, $fields) as $field)
  258. {
  259. $vars[] = '\'%%'.$field->getName().'%%\' => '.$this->renderField($field);
  260. }
  261. return sprintf("__('%s', array(%s), '%s')", $value, implode(', ', $vars), $this->getI18nCatalogue());
  262. }
  263. /**
  264. * Gets the form object
  265. *
  266. * @return sfForm
  267. */
  268. public function getFormObject()
  269. {
  270. if (null === $this->formObject)
  271. {
  272. $class = null === $this->configuration ? $this->getModelClass().'Form' : $this->configuration->getFormClass();
  273. $this->formObject = new $class();
  274. }
  275. return $this->formObject;
  276. }
  277. /**
  278. * Gets the HTML to add to the form tag if the form is multipart.
  279. *
  280. * @return string
  281. */
  282. public function getFormMultipartHtml()
  283. {
  284. if (isset($this->params['non_verbose_templates']) && $this->params['non_verbose_templates'])
  285. {
  286. return '[?php $form->isMultipart() and print \' enctype="multipart/form-data"\' ?]';
  287. }
  288. else
  289. {
  290. return $this->getFormObject()->isMultipart() ? ' enctype="multipart/form-data"' : '';
  291. }
  292. }
  293. /**
  294. * Validates the basic structure of the parameters.
  295. *
  296. * @param array $params An array of parameters
  297. */
  298. protected function validateParameters($params)
  299. {
  300. foreach (array('model_class', 'moduleName') as $key)
  301. {
  302. if (!isset($params[$key]))
  303. {
  304. throw new sfParseException(sprintf('sfModelGenerator must have a "%s" parameter.', $key));
  305. }
  306. }
  307. if (!class_exists($params['model_class']))
  308. {
  309. throw new sfInitializationException(sprintf('Unable to generate a module for non-existent model "%s".', $params['model_class']));
  310. }
  311. $this->config = isset($params['config']) ? $params['config'] : array();
  312. unset($params['config']);
  313. $this->params = $params;
  314. }
  315. /**
  316. * Loads the configuration for this generated module.
  317. */
  318. protected function loadConfiguration()
  319. {
  320. try
  321. {
  322. $this->generatorManager->getConfiguration()->getGeneratorTemplate($this->getGeneratorClass(), $this->getTheme(), '../parts/configuration.php');
  323. }
  324. catch (sfException $e)
  325. {
  326. return null;
  327. }
  328. $config = $this->getGeneratorManager()->getConfiguration();
  329. if (!$config instanceof sfApplicationConfiguration)
  330. {
  331. throw new LogicException('The sfModelGenerator can only operates with an application configuration.');
  332. }
  333. $basePath = $this->getGeneratedModuleName().'/lib/Base'.ucfirst($this->getModuleName()).'GeneratorConfiguration.class.php';
  334. $this->getGeneratorManager()->save($basePath, $this->evalTemplate('../parts/configuration.php'));
  335. require_once $this->getGeneratorManager()->getBasePath().'/'.$basePath;
  336. $class = 'Base'.ucfirst($this->getModuleName()).'GeneratorConfiguration';
  337. foreach ($config->getLibDirs($this->getModuleName()) as $dir)
  338. {
  339. if (!is_file($configuration = $dir.'/'.$this->getModuleName().'GeneratorConfiguration.class.php'))
  340. {
  341. continue;
  342. }
  343. require_once $configuration;
  344. $class = $this->getModuleName().'GeneratorConfiguration';
  345. break;
  346. }
  347. // validate configuration
  348. foreach ($this->config as $context => $value)
  349. {
  350. if (!$value)
  351. {
  352. continue;
  353. }
  354. throw new InvalidArgumentException(sprintf('Your generator configuration contains some errors for the "%s" context. The following configuration cannot be parsed: %s.', $context, $this->asPhp($value)));
  355. }
  356. return new $class();
  357. }
  358. /**
  359. * Returns the URL for a given action.
  360. *
  361. * @return string The URL related to a given action
  362. */
  363. public function getUrlForAction($action)
  364. {
  365. if (isset($this->params['route_prefix']))
  366. {
  367. return 'list' == $action ? $this->params['route_prefix'] : $this->params['route_prefix'].'_'.$action;
  368. }
  369. else
  370. {
  371. return $this->getModuleName().'/'.$action;
  372. }
  373. }
  374. public function asPhp($variable)
  375. {
  376. return str_replace(array("\n", 'array ('), array('', 'array('), var_export($variable, true));
  377. }
  378. public function escapeString($string)
  379. {
  380. return str_replace("'", "\\'", $string);
  381. }
  382. }

Debug toolbar