PageRenderTime 78ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/htdocs/solar/1.1.1/source/solar/Solar/Cli/MakeModel.php

http://github.com/pmjones/php-framework-benchmarks
PHP | 599 lines | 294 code | 67 blank | 238 comment | 27 complexity | 9f52817f94d44e54502232e9d060b2d7 MD5 | raw file
Possible License(s): LGPL-3.0, Apache-2.0, BSD-3-Clause, ISC, AGPL-3.0, LGPL-2.1
  1. <?php
  2. /**
  3. *
  4. * Solar command to make a model class from an SQL table.
  5. *
  6. * @category Solar
  7. *
  8. * @package Solar_Cli
  9. *
  10. * @author Paul M. Jones <pmjones@solarphp.com>
  11. *
  12. * @license http://opensource.org/licenses/bsd-license.php BSD
  13. *
  14. * @version $Id: MakeModel.php 4597 2010-06-15 21:11:48Z pmjones $
  15. *
  16. */
  17. class Solar_Cli_MakeModel extends Solar_Controller_Command
  18. {
  19. /**
  20. *
  21. * Default configuration values.
  22. *
  23. * @config string extends The default model class to extend.
  24. *
  25. * @var array
  26. *
  27. */
  28. protected $_Solar_Cli_MakeModel = array(
  29. 'extends' => null,
  30. );
  31. /**
  32. *
  33. * The base directory where we will write the class file to, typically
  34. * the local PEAR directory.
  35. *
  36. * @var string
  37. *
  38. */
  39. protected $_target = null;
  40. /**
  41. *
  42. * The table name we're making the model from.
  43. *
  44. * @var string
  45. *
  46. */
  47. protected $_table_name = null;
  48. /**
  49. *
  50. * The columns from the table.
  51. *
  52. * @var array
  53. *
  54. */
  55. protected $_table_cols = array();
  56. /**
  57. *
  58. * The indexes from the table.
  59. *
  60. * @var string
  61. *
  62. */
  63. protected $_index_info = array();
  64. /**
  65. *
  66. * The class name this model extends from.
  67. *
  68. * @var string
  69. *
  70. */
  71. protected $_extends = null;
  72. /**
  73. *
  74. * Array of model-class templates (skeletons).
  75. *
  76. * @var array
  77. *
  78. */
  79. protected $_tpl = array();
  80. /**
  81. *
  82. * Is the model class inherited or not?
  83. *
  84. * @var bool
  85. *
  86. */
  87. protected $_inherit = false;
  88. /**
  89. *
  90. * Write out a series of model, record, and collection classes for a model.
  91. *
  92. * @param string $class The target class name for the model.
  93. *
  94. * @return void
  95. *
  96. */
  97. protected function _exec($class = null)
  98. {
  99. // we need a class name, at least
  100. if (! $class) {
  101. throw $this->_exception('ERR_NO_CLASS');
  102. }
  103. // are we making multiple classes?
  104. if (substr($class, -2) == '_*') {
  105. $prefix = substr($class, 0, -2);
  106. return $this->_execMulti($prefix);
  107. }
  108. $this->_outln("Making model '$class'.");
  109. // load the templates
  110. $this->_loadTemplates();
  111. // we need a target directory
  112. $this->_setTarget();
  113. // we need a table name
  114. $this->_setTable($class);
  115. // extend this class
  116. $this->_setExtends($class);
  117. // using inheritance?
  118. $this->_setInherit();
  119. // write the model/record/collection files
  120. $this->_writeModel($class);
  121. $this->_writeRecord($class);
  122. $this->_writeCollection($class);
  123. // write the metadata file
  124. if ($this->_inherit) {
  125. $this->_outln('Using inheritance, so skipping metadata.');
  126. } else {
  127. $this->_loadMetadata();
  128. $this->_writeMetadata($class);
  129. }
  130. // write out locale info
  131. $this->_createLocaleDir($class);
  132. $this->_writeLocaleFile($class);
  133. // done!
  134. $this->_outln('Done.');
  135. }
  136. /**
  137. *
  138. * Makes one model/record/collection class for each table in the database
  139. * using a class-name prefix.
  140. *
  141. * @param string $prefix The prefix for each model class name.
  142. *
  143. * @return void
  144. *
  145. */
  146. protected function _execMulti($prefix)
  147. {
  148. $this->_outln("Making one '$prefix' class for each table in the database.");
  149. // get the list of tables
  150. $this->_out('Connecting to database for table list ... ');
  151. $sql = Solar::factory('Solar_Sql', $this->_getSqlConfig());
  152. $this->_outln('connected.');
  153. $list = $sql->fetchTableList();
  154. $this->_outln('Found ' . count($list) . ' tables.');
  155. // process each table in turn
  156. $inflect = Solar_Registry::get('inflect');
  157. foreach ($list as $table) {
  158. $name = $inflect->underToStudly($table);
  159. $class = "{$prefix}_$name";
  160. $this->_outln("Using table '$table' to make class '$class'.");
  161. $this->_exec($class);
  162. }
  163. }
  164. /**
  165. *
  166. * Loads the template array from skeleton files.
  167. *
  168. * @return void
  169. *
  170. */
  171. protected function _loadTemplates()
  172. {
  173. $this->_tpl = array();
  174. $dir = Solar_Class::dir($this, 'Data');
  175. $list = glob($dir . '*.php');
  176. foreach ($list as $file) {
  177. // strip .php off the end of the file name
  178. $key = substr(basename($file), 0, -4);
  179. // we need to add the php-open tag ourselves, instead of
  180. // having it in the template file, becuase the PEAR packager
  181. // complains about parsing the skeleton code.
  182. $this->_tpl[$key] = "<?php\n" . file_get_contents($file);
  183. }
  184. }
  185. /**
  186. *
  187. * Sets the base directory target.
  188. *
  189. * @return void
  190. *
  191. */
  192. protected function _setTarget()
  193. {
  194. // use the solar system 'include' directory.
  195. // that should automatically point to the right vendor for us.
  196. $target = Solar::$system . '/include';
  197. $this->_target = Solar_Dir::fix($target);
  198. $this->_outln("Will write to '{$this->_target}'.");
  199. }
  200. /**
  201. *
  202. * Sets the table name; determines from the class name if no table name is
  203. * given.
  204. *
  205. * @param string $class The class name for the model.
  206. *
  207. * @return void
  208. *
  209. */
  210. protected function _setTable($class)
  211. {
  212. $table = $this->_options['table'];
  213. if (! $table) {
  214. // try to determine from the class name
  215. $pos = strpos($class, 'Model_');
  216. if (! $pos) {
  217. throw $this->_exception('ERR_CANNOT_DETERMINE_TABLE', array(
  218. 'class' => $class,
  219. ));
  220. }
  221. // convert Solar_Model_TableName to table_name
  222. $table = substr($class, $pos + 6);
  223. $table = preg_replace('/([a-z])([A-Z])/', '$1_$2', $table);
  224. $table = strtolower($table);
  225. }
  226. $this->_table_name = $table;
  227. $this->_outln("Using table '{$this->_table_name}'.");
  228. }
  229. /**
  230. *
  231. * Sets the $_inherit property based on the $_extends value.
  232. *
  233. * @return void
  234. *
  235. */
  236. protected function _setInherit()
  237. {
  238. if (substr($this->_extends, -6) == '_Model') {
  239. $this->_inherit = false;
  240. $this->_outln('Not using inheritance.');
  241. } else {
  242. $this->_inherit = true;
  243. $this->_outln('Using inheritance.');
  244. }
  245. }
  246. /**
  247. *
  248. * Sets the class this model will extend from.
  249. *
  250. * @param string $class The model class name.
  251. *
  252. * @return void
  253. *
  254. */
  255. protected function _setExtends($class)
  256. {
  257. // explicit as cli option?
  258. $extends = $this->_options['extends'];
  259. if ($extends) {
  260. $this->_extends = $extends;
  261. return;
  262. }
  263. // explicit as config value?
  264. $extends = $this->_config['extends'];
  265. if ($extends) {
  266. $this->_extends = $this->_config['extends'];
  267. return;
  268. }
  269. // look at the class name and find a Vendor_Sql_Model class
  270. $vendor = Solar_Class::vendor($class);
  271. $file = $this->_target . "$vendor/Sql/Model.php";
  272. if (file_exists($file)) {
  273. $this->_extends = "{$vendor}_Sql_Model";
  274. return;
  275. }
  276. // final fallback: Solar_Sql_Model
  277. $this->_extends = 'Solar_Sql_Model';
  278. return;
  279. }
  280. /**
  281. *
  282. * Gets the SQL connection parameters from the command line options.
  283. *
  284. * @return array An array of SQL connection parameters suitable for
  285. * passing as a Solar_Sql_Adapter class config.
  286. *
  287. */
  288. protected function _getSqlConfig()
  289. {
  290. $config = array();
  291. $list = array('adapter', 'host', 'port', 'user', 'pass', 'name');
  292. foreach ($list as $key) {
  293. $val = $this->_options[$key];
  294. if ($val) {
  295. $config[$key] = $val;
  296. }
  297. }
  298. return $config;
  299. }
  300. /**
  301. *
  302. * Writes the model class file.
  303. *
  304. * @param string $class The model class name.
  305. *
  306. * @return void
  307. *
  308. */
  309. protected function _writeModel($class)
  310. {
  311. // the target file
  312. $file = $this->_target
  313. . str_replace('_', DIRECTORY_SEPARATOR, $class)
  314. . '.php';
  315. // does the class file already exist?
  316. if (file_exists($file)) {
  317. $this->_outln('Model class exists.');
  318. return;
  319. }
  320. // create the class dir before attempting to write the model class
  321. $dir = Solar_Dir::fix(
  322. $this->_target . str_replace('_', '/', $class)
  323. );
  324. if (! file_exists($dir)) {
  325. $this->_out('Making class directory ... ');
  326. mkdir($dir, 0755, true);
  327. $this->_outln('done.');
  328. }
  329. // get the class model template
  330. $tpl_key = $this->_inherit ? 'model-inherit' : 'model';
  331. $text = str_replace(
  332. array('{:class}', '{:extends}'),
  333. array($class, $this->_extends),
  334. $this->_tpl[$tpl_key]
  335. );
  336. $this->_out('Writing model class ... ');
  337. file_put_contents($file, $text);
  338. $this->_outln('done.');
  339. }
  340. /**
  341. *
  342. * Writes the record class file.
  343. *
  344. * @param string $class The model class name.
  345. *
  346. * @return void
  347. *
  348. */
  349. protected function _writeRecord($class)
  350. {
  351. $file = $this->_target
  352. . str_replace('_', DIRECTORY_SEPARATOR, $class)
  353. . DIRECTORY_SEPARATOR
  354. . 'Record.php';
  355. if (file_exists($file)) {
  356. $this->_outln('Record class exists.');
  357. return;
  358. }
  359. $text = str_replace(
  360. array('{:class}', '{:extends}'),
  361. array($class, $this->_extends),
  362. $this->_tpl['record']
  363. );
  364. $this->_out('Writing record class ... ');
  365. file_put_contents($file, $text);
  366. $this->_outln('done.');
  367. }
  368. /**
  369. *
  370. * Writes the collection class file.
  371. *
  372. * @param string $class The model class name.
  373. *
  374. * @return void
  375. *
  376. */
  377. protected function _writeCollection($class)
  378. {
  379. $file = $this->_target
  380. . str_replace('_', DIRECTORY_SEPARATOR, $class)
  381. . DIRECTORY_SEPARATOR
  382. . 'Collection.php';
  383. if (file_exists($file)) {
  384. $this->_outln('Collection class exists.');
  385. return;
  386. }
  387. $text = str_replace(
  388. array('{:class}', '{:extends}'),
  389. array($class, $this->_extends),
  390. $this->_tpl['collection']
  391. );
  392. $this->_out('Writing collection class ... ');
  393. file_put_contents($file, $text);
  394. $this->_outln('done.');
  395. }
  396. /**
  397. *
  398. * Reads and retains the table metadata from the database.
  399. *
  400. * @return void
  401. *
  402. */
  403. protected function _loadMetadata()
  404. {
  405. if (! $this->_options['connect']) {
  406. $this->_outln('Will not connect to database for metadata.');
  407. return;
  408. }
  409. $this->_out('Connecting to database for metadata ... ');
  410. $sql = Solar::factory('Solar_Sql', $this->_getSqlConfig());
  411. $this->_outln('connected.');
  412. // fetch table cols
  413. $this->_out('Fetching table cols ... ');
  414. $this->_table_cols = $sql->fetchTableCols($this->_table_name);
  415. if (! $this->_table_cols) {
  416. throw $this->_exception('ERR_NO_COLS', array(
  417. 'table' => $this->_table_name
  418. ));
  419. }
  420. $this->_outln('done.');
  421. // fetch index info
  422. $this->_out('Fetching index info ... ');
  423. $this->_index_info = $sql->fetchIndexInfo($this->_table_name);
  424. if (! $this->_index_info) {
  425. $this->_outln('no indexes found.');
  426. return;
  427. } else {
  428. $this->_outln('done.');
  429. }
  430. }
  431. /**
  432. *
  433. * Writes the metadata class file.
  434. *
  435. * @param string $class The model class name.
  436. *
  437. * @return void
  438. *
  439. */
  440. protected function _writeMetadata($class)
  441. {
  442. $file = $this->_target
  443. . str_replace('_', DIRECTORY_SEPARATOR, $class)
  444. . DIRECTORY_SEPARATOR
  445. . 'Metadata.php';
  446. $table_name = var_export($this->_table_name, true);
  447. $preg = "/\=\> \n(\s+)array/m";
  448. $replace = "=> array";
  449. $table_cols = preg_replace($preg, $replace, var_export($this->_table_cols, true));
  450. $index_info = preg_replace($preg, $replace, var_export($this->_index_info, true));
  451. $str_replace = array(
  452. '{:class}' => $class,
  453. '{:extends}' => $this->_extends,
  454. '{:table_name}' => $table_name,
  455. '{:table_cols}' => trim(preg_replace('/^/m', ' ', $table_cols)),
  456. '{:index_info}' => trim(preg_replace('/^/m', ' ', $index_info)),
  457. );
  458. $text = str_replace(
  459. array_keys($str_replace),
  460. array_values($str_replace),
  461. $this->_tpl['metadata']
  462. );
  463. $this->_out('Writing metadata class ... ');
  464. file_put_contents($file, $text);
  465. $this->_outln('done.');
  466. }
  467. /**
  468. *
  469. * Creates the model "Locale/" directory.
  470. *
  471. * @param string $class The model class name.
  472. *
  473. * @return void
  474. *
  475. */
  476. protected function _createLocaleDir($class)
  477. {
  478. // get the right dir
  479. $dir = Solar_Dir::fix(
  480. $this->_target . str_replace('_', '/', $class) . '/Locale'
  481. );
  482. if (! file_exists($dir)) {
  483. $this->_out('Creating locale directory ... ');
  484. mkdir($dir, 0755, true);
  485. $this->_outln('done.');
  486. } else {
  487. $this->_outln('Locale directory exists.');
  488. }
  489. }
  490. /**
  491. *
  492. * Writes the model "Locale/en_US.php" file.
  493. *
  494. * @param string $class The model class name.
  495. *
  496. * @return void
  497. *
  498. */
  499. protected function _writeLocaleFile($class)
  500. {
  501. $dir = Solar_Dir::fix(
  502. $this->_target . str_replace('_', '/', $class) . '/Locale'
  503. );
  504. // does the locale file already exist?
  505. $file = $dir . DIRECTORY_SEPARATOR . 'en_US.php';
  506. if (file_exists($file)) {
  507. $this->_outln('Locale file for en_US already exists.');
  508. return;
  509. }
  510. // does it exist?
  511. if (! $this->_table_cols) {
  512. $this->_outln('Not creating locale file; no table_cols available.');
  513. return;
  514. }
  515. // create a label value & descr placeholder for each column
  516. $list = array_keys($this->_table_cols);
  517. $label = array();
  518. $descr = array();
  519. foreach ($list as $col) {
  520. $key = strtoupper("LABEL_{$col}");
  521. $label[$key] = ucwords(str_replace('_', ' ', $col));
  522. $key = strtoupper("DESCR_{$col}");
  523. $descr[$key] = '';
  524. }
  525. // write the en_US file
  526. $this->_out('Saving locale file for en_US ... ');
  527. $data = array_merge($label, $descr);
  528. $text = var_export($data, true);
  529. file_put_contents($file, "<?php return $text;");
  530. $this->_outln('done.');
  531. }
  532. }