TableRelationController.class.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /**
  4. * Holds the PMA\TableRelationController
  5. *
  6. * @package PMA
  7. */
  8. namespace PMA\Controllers\Table;
  9. require_once 'libraries/DatabaseInterface.class.php';
  10. require_once 'libraries/controllers/TableController.class.php';
  11. require_once 'libraries/index.lib.php';
  12. require_once 'libraries/Template.class.php';
  13. require_once 'libraries/Table.class.php';
  14. require_once 'libraries/Index.class.php';
  15. require_once 'libraries/Util.class.php';
  16. use PMA_DatabaseInterface;
  17. use PMA_Table;
  18. use PMA_Index;
  19. use PMA_Util;
  20. use PMA\Controllers\TableController;
  21. use PMA\Template;
  22. /**
  23. * Handles table relation logic
  24. *
  25. * @package PhpMyAdmin
  26. */
  27. class TableRelationController extends TableController
  28. {
  29. /**
  30. * @var array $options_array
  31. */
  32. protected $options_array;
  33. /**
  34. * @var array $cfgRelation
  35. */
  36. protected $cfgRelation;
  37. /**
  38. * @var array $existrel
  39. */
  40. protected $existrel;
  41. /**
  42. * @var string $disp
  43. */
  44. protected $disp;
  45. /**
  46. * @var string $tbl_storage_engine
  47. */
  48. protected $tbl_storage_engine;
  49. /**
  50. * @var array $existrel_foreign
  51. */
  52. protected $existrel_foreign;
  53. /**
  54. * @var PMA_Table $udp_query
  55. */
  56. protected $upd_query;
  57. /**
  58. * Constructor
  59. *
  60. * @param array $options_array Options
  61. * @param array $cfgRelation Config relation
  62. * @param string $tbl_storage_engine Table storage engine
  63. * @param array $existrel Relations
  64. * @param array $existrel_foreign External relations
  65. * @param string $disp Display
  66. * @param string $upd_query Update query
  67. */
  68. public function __construct($options_array, $cfgRelation, $tbl_storage_engine,
  69. $existrel, $existrel_foreign, $disp, $upd_query
  70. ) {
  71. parent::__construct();
  72. $this->options_array = $options_array;
  73. $this->cfgRelation = $cfgRelation;
  74. $this->tbl_storage_engine = $tbl_storage_engine;
  75. $this->existrel = $existrel;
  76. $this->existrel_foreign = $existrel_foreign;
  77. $this->disp = $disp;
  78. $this->upd_query = $upd_query;
  79. }
  80. /**
  81. * Index
  82. *
  83. * @return void
  84. */
  85. public function indexAction()
  86. {
  87. // Send table of column names to populate corresponding dropdowns depending
  88. // on the current selection
  89. if (isset($_REQUEST['getDropdownValues'])
  90. && $_REQUEST['getDropdownValues'] === 'true'
  91. ) {
  92. // if both db and table are selected
  93. if (isset($_REQUEST['foreignTable'])) {
  94. $this->getDropdownValueForTableAction();
  95. } else { // if only the db is selected
  96. $this->getDropdownValueForDbAction();
  97. }
  98. return;
  99. }
  100. $this->response->getHeader()->getScripts()->addFiles(
  101. array(
  102. 'tbl_relation.js',
  103. 'indexes.js'
  104. )
  105. );
  106. // Gets tables information
  107. include_once 'libraries/tbl_info.inc.php';
  108. // updates for Internal relations
  109. if (isset($_POST['destination_db']) && $this->cfgRelation['relwork']) {
  110. $this->updateForInternalRelationAction();
  111. }
  112. // updates for foreign keys
  113. if (isset($_POST['destination_foreign_db'])) {
  114. $this->updateForForeignKeysAction();
  115. }
  116. // Updates for display field
  117. if ($this->cfgRelation['displaywork'] && isset($_POST['display_field'])) {
  118. $this->updateForDisplayField();
  119. }
  120. // If we did an update, refresh our data
  121. if (isset($_POST['destination_db']) && $this->cfgRelation['relwork']) {
  122. $this->existrel = PMA_getForeigners(
  123. $this->db, $this->table, '', 'internal'
  124. );
  125. }
  126. if (isset($_POST['destination_foreign_db'])
  127. && PMA_Util::isForeignKeySupported($this->tbl_storage_engine)
  128. ) {
  129. $this->existrel_foreign = PMA_getForeigners(
  130. $this->db, $this->table, '', 'foreign'
  131. );
  132. }
  133. if ($this->cfgRelation['displaywork']) {
  134. $this->disp = PMA_getDisplayField($this->db, $this->table);
  135. }
  136. // display secondary level tabs if necessary
  137. $engine = $this->dbi->getTable($this->db, $this->table)
  138. ->getStatusInfo('ENGINE');
  139. $this->response->addHTML(
  140. Template::get('table/secondary_tabs')->render(
  141. array(
  142. 'url_params' => array(
  143. 'db' => $GLOBALS['db'],
  144. 'table' => $GLOBALS['table']
  145. ),
  146. 'engine' => $engine
  147. )
  148. )
  149. );
  150. $this->response->addHTML('<div id="structure_content">');
  151. /**
  152. * Dialog
  153. */
  154. // Now find out the columns of our $table
  155. // need to use PMA_DatabaseInterface::QUERY_STORE with $this->dbi->numRows()
  156. // in mysqli
  157. $columns = $this->dbi->getColumns($this->db, $this->table);
  158. // common form
  159. $this->response->addHTML(
  160. Template::get('table/relation/common_form')->render(
  161. array(
  162. 'db' => $this->db,
  163. 'table' => $this->table,
  164. 'columns' => $columns,
  165. 'cfgRelation' => $this->cfgRelation,
  166. 'tbl_storage_engine' => $this->tbl_storage_engine,
  167. 'existrel' => isset($this->existrel) ? $this->existrel : array(),
  168. 'existrel_foreign' => isset($this->existrel_foreign)
  169. ? $this->existrel_foreign['foreign_keys_data'] : array(),
  170. 'options_array' => $this->options_array
  171. )
  172. )
  173. );
  174. if (PMA_Util::isForeignKeySupported($this->tbl_storage_engine)) {
  175. $this->response->addHTML(PMA_getHtmlForDisplayIndexes());
  176. }
  177. $this->response->addHTML('</div>');
  178. }
  179. /**
  180. * Update for display field
  181. *
  182. * @return void
  183. */
  184. public function updateForDisplayField()
  185. {
  186. if ($this->upd_query->updateDisplayField(
  187. $this->disp, $_POST['display_field'], $this->cfgRelation
  188. )
  189. ) {
  190. $this->response->addHTML(
  191. PMA_Util::getMessage(
  192. __('Display column was successfully updated.'),
  193. '', 'success'
  194. )
  195. );
  196. }
  197. }
  198. /**
  199. * Update for FK
  200. *
  201. * @return void
  202. */
  203. public function updateForForeignKeysAction()
  204. {
  205. $multi_edit_columns_name = isset($_REQUEST['foreign_key_fields_name'])
  206. ? $_REQUEST['foreign_key_fields_name']
  207. : null;
  208. // (for now, one index name only; we keep the definitions if the
  209. // foreign db is not the same)
  210. list($html, $preview_sql_data, $display_query, $seen_error)
  211. = $this->upd_query->updateForeignKeys(
  212. $_POST['destination_foreign_db'],
  213. $multi_edit_columns_name, $_POST['destination_foreign_table'],
  214. $_POST['destination_foreign_column'], $this->options_array,
  215. $this->table,
  216. isset($this->existrel_foreign)
  217. ? $this->existrel_foreign['foreign_keys_data']
  218. : null
  219. );
  220. $this->response->addHTML($html);
  221. // If there is a request for SQL previewing.
  222. if (isset($_REQUEST['preview_sql'])) {
  223. PMA_previewSQL($preview_sql_data);
  224. }
  225. if (!empty($display_query) && !$seen_error) {
  226. $GLOBALS['display_query'] = $display_query;
  227. $this->response->addHTML(
  228. PMA_Util::getMessage(
  229. __('Your SQL query has been executed successfully.'),
  230. null, 'success'
  231. )
  232. );
  233. }
  234. }
  235. /**
  236. * Update for internal relation
  237. *
  238. * @return void
  239. */
  240. public function updateForInternalRelationAction()
  241. {
  242. $multi_edit_columns_name = isset($_REQUEST['fields_name'])
  243. ? $_REQUEST['fields_name']
  244. : null;
  245. if ($this->upd_query->updateInternalRelations(
  246. $multi_edit_columns_name,
  247. $_POST['destination_db'],
  248. $_POST['destination_table'],
  249. $_POST['destination_column'],
  250. $this->cfgRelation,
  251. isset($this->existrel) ? $this->existrel : null
  252. )
  253. ) {
  254. $this->response->addHTML(
  255. PMA_Util::getMessage(
  256. __('Internal relations were successfully updated.'),
  257. '', 'success'
  258. )
  259. );
  260. }
  261. }
  262. /**
  263. * Send table columns for foreign table dropdown
  264. *
  265. * @return void
  266. *
  267. */
  268. public function getDropdownValueForTableAction()
  269. {
  270. $foreignTable = $_REQUEST['foreignTable'];
  271. $table_obj = $this->dbi->getTable($_REQUEST['foreignDb'], $foreignTable);
  272. // Since views do not have keys defined on them provide the full list of
  273. // columns
  274. if ($table_obj->isView()) {
  275. $columnList = $table_obj->getColumns(false, false);
  276. } else {
  277. $columnList = $table_obj->getIndexedColumns(false, false);
  278. }
  279. $columns = array();
  280. foreach ($columnList as $column) {
  281. $columns[] = htmlspecialchars($column);
  282. }
  283. $this->response->addJSON('columns', $columns);
  284. // @todo should be: $server->db($db)->table($table)->primary()
  285. $primary = PMA_Index::getPrimary($foreignTable, $_REQUEST['foreignDb']);
  286. if (false === $primary) {
  287. return;
  288. }
  289. $this->response->addJSON('primary', array_keys($primary->getColumns()));
  290. }
  291. /**
  292. * Send database selection values for dropdown
  293. *
  294. * @return void
  295. *
  296. */
  297. public function getDropdownValueForDbAction()
  298. {
  299. $tables = array();
  300. $foreign = isset($_REQUEST['foreign']) && $_REQUEST['foreign'] === 'true';
  301. // In Drizzle, 'SHOW TABLE STATUS' will show status only for the tables
  302. // which are currently in the table cache. Hence we have to use 'SHOW TABLES'
  303. // and manually retrieve table engine values.
  304. if ($foreign && !PMA_DRIZZLE) {
  305. $query = 'SHOW TABLE STATUS FROM '
  306. . PMA_Util::backquote($_REQUEST['foreignDb']);
  307. $tables_rs = $this->dbi->query(
  308. $query,
  309. null,
  310. PMA_DatabaseInterface::QUERY_STORE
  311. );
  312. while ($row = $this->dbi->fetchArray($tables_rs)) {
  313. if (isset($row['Engine'])
  314. && /*overload*/ mb_strtoupper($row['Engine']) == $this->tbl_storage_engine
  315. ) {
  316. $tables[] = htmlspecialchars($row['Name']);
  317. }
  318. }
  319. } else {
  320. $query = 'SHOW TABLES FROM '
  321. . PMA_Util::backquote($_REQUEST['foreignDb']);
  322. $tables_rs = $this->dbi->query(
  323. $query,
  324. null,
  325. PMA_DatabaseInterface::QUERY_STORE
  326. );
  327. while ($row = $this->dbi->fetchArray($tables_rs)) {
  328. if ($foreign && PMA_DRIZZLE) {
  329. $engine = /*overload*/
  330. mb_strtoupper(
  331. $GLOBALS['dbi']->getTable(
  332. $_REQUEST['foreignDb'],
  333. $row[0]
  334. )->getStatusInfo('Engine')
  335. );
  336. if (isset($engine) && $engine == $this->tbl_storage_engine) {
  337. $tables[] = htmlspecialchars($row[0]);
  338. }
  339. } else {
  340. $tables[] = htmlspecialchars($row[0]);
  341. }
  342. }
  343. }
  344. $this->response->addJSON('tables', $tables);
  345. }
  346. }