PageRenderTime 75ms CodeModel.GetById 38ms RepoModel.GetById 0ms app.codeStats 0ms

/classes/bors/storage/mysql.php

https://bitbucket.org/Balancer/bors-core
PHP | 1019 lines | 841 code | 131 blank | 47 comment | 83 complexity | fcb9ddc40990aba7a413586ab61df9af MD5 | raw file
Possible License(s): LGPL-2.1, GPL-3.0
  1. <?php
  2. class bors_storage_mysql extends bors_storage implements Iterator
  3. {
  4. function __construct($object = NULL)
  5. {
  6. if($object)
  7. {
  8. $this->__object = $object;
  9. $this->__db_name = $object->get('db_name');
  10. $this->__table_name = $object->get('table_name');
  11. }
  12. }
  13. function db()
  14. {
  15. if($this->__dbh)
  16. return $this->__dbh;
  17. return $this->__dbh = new driver_mysql($this->__db_name);
  18. }
  19. static private function __query_data_prepare($object, $where)
  20. {
  21. $select = array();
  22. $post_functions = array();
  23. $table = $object->table_name();
  24. $fields = popval($where, '*fields');
  25. foreach(bors_lib_orm::main_fields($object) as $f)
  26. {
  27. $field_name = $f['name'];
  28. if(!empty($fields) && !in_array($field_name, $fields))
  29. continue;
  30. if(!empty($f['sql_function']))
  31. $x = $f['sql_function']."(`{$table}`.`{$field_name}`)";
  32. elseif(preg_match('/^(\w+)\((\w+)\)$/', $field_name, $m))
  33. $x = $m[1].'('.$table.'.'.$m[2].')';
  34. elseif(preg_match('/^\w+\(.+\)$/', $field_name)) // id => CONCAT(keyword,":",keyword_id)
  35. $x = $field_name;
  36. elseif(preg_match('/^[\w`]+$/', $field_name))
  37. $x = $table.'.'.$field_name;
  38. else
  39. $x = $field_name;
  40. if($field_name != $f['property'] || !empty($f['sql_function']))
  41. {
  42. $x .= " AS `{$f['property']}`";
  43. if(array_key_exists($f['property'], $where))
  44. {
  45. $where[$f['sql_name']] = $where[$f['property']];
  46. unset($where[$f['property']]);
  47. }
  48. }
  49. $select[] = $x;
  50. if(!empty($f['post_function']))
  51. $post_functions[$f['property']] = $f['post_function'];
  52. }
  53. $dummy = array();
  54. self::__join('inner', $object, $select, $where, $post_functions, $dummy);
  55. self::__join('left', $object, $select, $where, $post_functions, $dummy);
  56. return array($select, $where, $post_functions);
  57. }
  58. static private function __update_data_prepare($object, $where)
  59. {
  60. $_back_functions = array(
  61. 'html_entity_decode' => 'htmlspecialchars',
  62. 'bors_entity_decode' => 'htmlspecialchars',
  63. 'UNIX_TIMESTAMP' => 'FROM_UNIXTIME',
  64. 'FROM_UNIXTIME' => 'UNIX_TIMESTAMP',
  65. 'aviaport_old_denormalize' => 'aviaport_old_normalize',
  66. 'stripslashes' => 'addslashes',
  67. );
  68. $update = array();
  69. $db_name = $object->get('db_name');
  70. $table_name = $object->get('table_name');
  71. $skip_joins = false;
  72. foreach(bors_lib_orm::main_fields($object) as $f)
  73. {
  74. $field_name = $f['name'];
  75. // Сюда сунуть обратное преобразование
  76. // if(!empty($f['post_function']))
  77. // $post_functions[$f['property']] = $f['post_function'];
  78. if(!empty($f['sql_function']))
  79. $sql = $_back_functions[$f['sql_function']];
  80. elseif(preg_match('/^(\w+)\(([\w`]+)\)$/', $field_name, $m))
  81. {
  82. $field_name = $m[2];
  83. $sql = @$_back_functions[$m[1]];
  84. }
  85. elseif(preg_match('/^\w+\(.+\)$/', $field_name)) // id => CONCAT(keyword,":",keyword_id)
  86. {
  87. $skip_joins = true;
  88. continue;
  89. }
  90. else
  91. $sql = false;
  92. if(!empty($object->changed_fields) && array_key_exists($f['property'], $object->changed_fields))
  93. {
  94. // Читаем не по get(), а прямо $data, т.к. формат данных в бэкенде
  95. // и используемых методах может быть разным. Например, в БД хранится comma-separated список,
  96. // а методы get/set оперируют массивом.
  97. $data = $object->data;
  98. $value = @$data[$f['property']];
  99. if($sql)
  100. $update[$db_name][$table_name]["raw $field_name"] = $sql.'("'.addslashes($value).'")';
  101. elseif(@$f['type'] == 'float')
  102. $update[$db_name][$table_name]["float $field_name"] = str_replace(',', '.', floatval($value));
  103. else
  104. $update[$db_name][$table_name][$field_name] = $value;
  105. }
  106. }
  107. $select = array(); // dummy
  108. if(!$skip_joins)
  109. {
  110. // self::__join('inner', $object, $select, $where, $post_functions, $update);
  111. // self::__join('left', $object, $select, $where, $post_functions, $update);
  112. // if(($idf = $object->id_field()) && empty($update[$db_name][$table_name][$idf]))
  113. // $update[$db_name][$table_name][$idf] = $object->id();
  114. if($lefts = $object->get('left_join_fields'))
  115. {
  116. foreach($lefts as $db_name => $tables)
  117. {
  118. foreach($tables as $tab => $fields)
  119. {
  120. if(!preg_match('/^(\w+)\((\w+)\)$/', $tab, $m))
  121. bors_throw("Unknown left join field with tab ".$tab);
  122. $table_name = $m[1];
  123. $id_field = $m[2];
  124. $update[$db_name][$table_name][$id_field] = $object->id();
  125. $update[$db_name][$table_name]['*id_field'] = $id_field;
  126. foreach($fields as $key => $desc)
  127. {
  128. $f = bors_lib_orm::field($key, $desc);
  129. $field_name = $f['name'];
  130. if(!empty($f['sql_function']))
  131. $sql = $_back_functions[$f['sql_function']];
  132. elseif(preg_match('/^(\w+)\(([\w`]+)\)$/', $field_name, $m))
  133. {
  134. $field_name = $m[2];
  135. $sql = @$_back_functions[$m[1]];
  136. }
  137. else
  138. $sql = false;
  139. if(!empty($object->changed_fields) && array_key_exists($f['property'], $object->changed_fields))
  140. {
  141. if($sql)
  142. $update[$db_name][$table_name]["raw $field_name"] = $sql.'("'.addslashes($object->get($f['property'])).'")';
  143. elseif(@$f['type'] == 'float')
  144. $update[$db_name][$table_name]["float $field_name"] = $object->get($f['property']);
  145. else
  146. $update[$db_name][$table_name][$field_name] = $object->get($f['property']);
  147. }
  148. }
  149. }
  150. }
  151. }
  152. }
  153. // echo "====== UPDATE ========\n";
  154. // print_r($object->changed_fields);
  155. // print_r($update);
  156. // $dbh = new driver_mysql($object->db_name());
  157. return array($update, $where);
  158. }
  159. static private function __join($type, $object, &$select, &$where, &$post_functions, &$update)
  160. {
  161. $where['*class_name'] = $object->class_name();
  162. $main_db = $object->db_name();
  163. $main_table = $object->table_name();
  164. $main_id_field = $object->id_field();
  165. $join = $object->get("{$type}_join_fields");
  166. if($join)
  167. {
  168. foreach($join as $db_name => $tables)
  169. {
  170. foreach($tables as $table_name => $fields)
  171. {
  172. if(!preg_match('/^(\w+)\((\w+)\)$/', $table_name, $m))
  173. throw new Exception('Unknown field format '.$table_name.' for class '.$object->class_name());
  174. $id_field = $m[2];
  175. $table_name = $m[1];
  176. $t = '';
  177. if($db_name != $main_db)
  178. $t = "`$db_name`.";
  179. $t .= "`$table_name`";
  180. $j = "$t ON `$main_table`.`$main_id_field` = $t.`$id_field`";
  181. $where[$type.'_join'][] = $j;
  182. $update[$db_name][$table_name]['*id_field'] = $id_field;
  183. foreach($fields as $property => $field)
  184. {
  185. $field = bors_lib_orm::field($property, $field);
  186. $x = "$t.`{$field['name']}`";
  187. if(!empty($field['sql_function']))
  188. // Если у нас это SQL-функция 'modify_time' => 'UNIX_TIMESTAMP(`modify_time`)'
  189. $x = $field['sql_function']."({$t}.`{$field['name']}`) AS `{$field['property']}`";
  190. elseif($field['name'] != $field['property'])
  191. $x .= " AS `{$field['property']}`";
  192. $select[] = $x;
  193. if(!empty($field['post_function']))
  194. $post_functions[$field['property']] = $field['post_function'];
  195. // echo "{$field['property']} => {$field['name']}: ".$object->get($field['property'])."<br/>\n";
  196. if(!empty($object->changed_fields) && array_key_exists($field['property'], $object->changed_fields))
  197. $update[$db_name][$table_name][$field['name']] = $object->get($field['property']);
  198. }
  199. }
  200. }
  201. }
  202. }
  203. static function post_functions_do(&$object, $post_functions)
  204. {
  205. foreach($post_functions as $property => $function)
  206. $object->set_attr($property, call_user_func($function, $object->data[$property]));
  207. }
  208. /**********************************************************
  209. Загрузка одиночного объекта
  210. ***********************************************************/
  211. function load($object)
  212. {
  213. $set = popval($where, '*set');
  214. $must_be_configured = $object->get('must_be_configured');
  215. $select = array();
  216. $post_functions = array();
  217. $table_prefix = '`'.$object->table_name().'`.';
  218. // if(config('is_developer')) { bors_use('debug/print_dd'); echo "<b>Load: {$object->class_name()}</b><br/>"; print_dd($where); print_dd(bors_lib_orm::main_fields($object)); }
  219. foreach(bors_lib_orm::main_fields($object) as $f)
  220. {
  221. if(preg_match('/^\w+$/', $sql_name = $f['sql_name']))
  222. $x = $table_prefix.'`'.$sql_name.'`'; // убирать апострофы нельзя, иначе тупо не работабт поля с некорректными именами
  223. elseif(preg_match('/^(\w+)\+(\w+)$/', $f['sql_name'], $m)) // id => forum_id+group_id
  224. // http://forums.airbase.ru/2008/06/t62054,12--poslednij-pokhod-unikalnogo-korablya.html
  225. $x = "CONCAT({$m[1]},':',{$m[2]})";
  226. elseif(preg_match('/^`\w+`$/', $sql_name))
  227. $x = $table_prefix.$sql_name;
  228. else
  229. $x = $sql_name;
  230. if($f['sql_name'] != ($property = $f['property']))
  231. $x .= " AS `{$property}`";
  232. $select[] = $x;
  233. if(!empty($f['post_function']))
  234. $post_functions[$f['property']] = $f['post_function'];
  235. }
  236. $where = array('`'.$object->table_name().'`.`'.$object->id_field().'`=' => $object->id());
  237. $dummy = array();
  238. self::__join('inner', $object, $select, $where, $post_functions, $dummy);
  239. self::__join('left', $object, $select, $where, $post_functions, $dummy);
  240. // формат: array(..., '*set' => 'MAX(create_time) AS max_create_time, ...')
  241. if($set)
  242. foreach(preg_split('/,\s*/', $set) as $s)
  243. $select[] = $s;
  244. $dbh = new driver_mysql($object->db_name());
  245. $data = $dbh->select($object->table_name(), join(',', $select), $where);
  246. if(!$data)
  247. return $object->set_is_loaded(false);
  248. $object->data = $data;
  249. if($must_be_configured)
  250. $object->b2_configure();
  251. if(!empty($post_functions))
  252. self::post_functions_do($object, $post_functions);
  253. $object->set_is_loaded(true);
  254. save_cached_object($object);
  255. return true;
  256. }
  257. /**********************************************************
  258. Загрузка массива объектов
  259. ***********************************************************/
  260. function load_array($object, $where)
  261. {
  262. if(!$by_id = popval($where, 'by_id'))
  263. $by_id = popval($where, '*by_id');
  264. if(!($select = popval($where, '*select')))
  265. $select = popval($where, 'select');
  266. $target_info = popval($where, '*join_object');
  267. $set = popval($where, '*set');
  268. $must_be_configured = $object->get('must_be_configured');
  269. if(is_null($object))
  270. {
  271. $db_name = $where['*db'];
  272. $table_name = $where['*table'];
  273. unset($where['*db'], $where['*table']);
  274. $select = array('*');
  275. $class_name = 'base_object_db';
  276. $object = new base_object_db(NULL);
  277. $post_functions = array();
  278. }
  279. else
  280. {
  281. $db_name = $object->db_name();
  282. $table_name = $object->table_name();
  283. $class_name = $object->class_name();
  284. $class_file = $object->class_file();
  285. list($select, $where, $post_functions) = self::__query_data_prepare($object, $where);
  286. }
  287. if(empty($db_name))
  288. throw new Exception("Not defined database to load $class_name");
  289. $dbh = new driver_mysql($db_name);
  290. // формат: array(..., '*set' => 'MAX(create_time) AS max_create_time, ...')
  291. if($set)
  292. foreach(preg_split('/\s*,\s*/', $set) as $s)
  293. $select[] = $s;
  294. $datas = $dbh->select_array($table_name, join(',', $select), $where, $class_name);
  295. $objects = array();
  296. $num_objects = count($datas);
  297. if($num_objects > 200 && config('debug.profiling'))
  298. bors_debug::syslog('profiling', "Load {$num_objects} of $class_name");
  299. foreach($datas as $data)
  300. {
  301. $object->set_id(@$data['id']);
  302. $object->data = $data;
  303. if($target_info)
  304. {
  305. foreach($target_info as $target_class_name => $info)
  306. {
  307. $target = new $target_class_name(NULL);
  308. foreach($info['target_properties'] as $p)
  309. {
  310. $value = $data[$p];
  311. $p = preg_replace('/^\w+\.(.+)$/', '$1', $p);
  312. if(preg_match('/^(\w+)\|(\w+)$/', $p, $m))
  313. {
  314. $p = $m[1];
  315. $value = $m[2]($value);
  316. }
  317. $target_data[$p] = $value;
  318. unset($data[$p]);
  319. }
  320. $target->set_id(@$target_data['id']);
  321. $target->data = $target_data;
  322. $object->set_attr($info['property_for_target'], $target);
  323. }
  324. }
  325. if($must_be_configured)
  326. $object->b2_configure();
  327. $object->set_is_loaded(true);
  328. if(!empty($post_functions))
  329. self::post_functions_do($object, $post_functions);
  330. if($by_id === true)
  331. $objects[$object->id()] = $object;
  332. elseif($by_id)
  333. $objects[$object->$by_id()] = $object;
  334. else
  335. $objects[] = $object;
  336. if($num_objects <= 100)
  337. save_cached_object($object);
  338. $object = new $class_name(NULL);
  339. $object->set_class_file($class_file);
  340. }
  341. return $objects;
  342. }
  343. /**********************************************************
  344. Загрузка массива смешанных объектов
  345. ***********************************************************/
  346. static function load_multi_array($class_names, $fields, $where)
  347. {
  348. $union = array();
  349. $post = array();
  350. foreach($class_names as $class_name)
  351. {
  352. $foo = new $class_name(NULL);
  353. $db_name = $foo->db_name();
  354. $dbh = new driver_mysql($db_name);
  355. $table_name = $foo->table_name();
  356. $class_file = $foo->class_file();
  357. $where['*fields'] = $fields;
  358. list($select, $where, $post_functions) = self::__query_data_prepare($foo, $where);
  359. if(count($select) != count($fields))
  360. bors_throw(ec('У класса ').$class_name
  361. .ec(' не хватает нужных полей. В наличии только ').join(', ', $select)
  362. .ec(' при необходимых ').join(', ', $fields));
  363. $select[] = "'$class_name' AS `class_name`";
  364. $post[$class_name] = $post_functions;
  365. $union[] = array($table_name, join(',', $select), $where, $class_name);
  366. }
  367. $datas = $dbh->union_select_array($union);
  368. $objects = array();
  369. foreach($datas as $data)
  370. {
  371. $class_name = $data['class_name'];
  372. $object = new $class_name(NULL);
  373. // $object->set_class_file($class_file);
  374. $object->set_id(@$data['id']);
  375. $object->data = $data;
  376. $object->set_is_loaded(true);
  377. if(!empty($post[$class_name]))
  378. self::post_functions_do($object, $post[$class_name]);
  379. $objects[] = $object;
  380. save_cached_object($object);
  381. }
  382. return $objects;
  383. }
  384. function count($object, $where)
  385. {
  386. $set = popval($where, '*set'); // Не используется
  387. if(is_null($object))
  388. {
  389. $db_name = $where['*db'];
  390. $table_name = $where['*table'];
  391. unset($where['*db'], $where['*table']);
  392. $select = array('*');
  393. $class_name = 'base_object_db';
  394. $object = new base_object_db(NULL);
  395. }
  396. else
  397. {
  398. $db_name = $object->db_name();
  399. $table_name = $object->table_name();
  400. $class_name = $object->class_name();
  401. list($select, $where) = self::__query_data_prepare($object, $where);
  402. }
  403. $dbh = new driver_mysql($db_name);
  404. if(empty($where['group']))
  405. $count = $dbh->select($table_name, 'COUNT(*)', $where, $class_name);
  406. else
  407. {
  408. // var_dump($where['group']); exit();
  409. $select = array('COUNT(*)');
  410. $grouped = false;
  411. // 'group' => '*BYMONTHS(create_time)*',
  412. if(preg_match('/^\*BY([A-Z]+)\((\w+)\)\*$/', @$where['group'], $m))
  413. {
  414. // var_dump($m, $class_name); exit();
  415. // тестировать на http://dev.forexpf.ru/news_arch/2010/10/01/
  416. $property = $m[2];
  417. $field = bors_lib_orm::property_to_field($class_name, $property);
  418. switch($m[1])
  419. {
  420. case 'DAYS':
  421. $where['group'] = "YEAR(FROM_UNIXTIME({$field})),MONTH(FROM_UNIXTIME({$field})),DAY(FROM_UNIXTIME({$field}))";
  422. $select[] = "DATE(FROM_UNIXTIME({$field})) AS group_date";
  423. $where['*select_index_field*'] = 'group_date';
  424. $grouped = true;
  425. break;
  426. case 'MONTHS':
  427. $where['group'] = "YEAR(FROM_UNIXTIME({$field})),MONTH(FROM_UNIXTIME({$field}))";
  428. $select[] = "CONCAT(YEAR(FROM_UNIXTIME({$field})),'-',LPAD(MONTH(FROM_UNIXTIME({$field})),2,'0')) AS group_date";
  429. $where['*select_index_field*'] = 'group_date';
  430. $grouped = true;
  431. break;
  432. }
  433. }
  434. elseif(preg_match('/^\*BY([A-Z]+)\(UNIX_TIMESTAMP\((`\w+`)\)\)\*$/', @$where['group'], $m))
  435. {
  436. $property = $m[2];
  437. $field = bors_lib_orm::property_to_field($class_name, $property);
  438. switch($m[1])
  439. {
  440. case 'DAYS':
  441. $where['group'] = "YEAR({$field}),MONTH({$field}),DAY({$field})";
  442. $select[] = "DATE({$field}) AS group_date";
  443. $where['*select_index_field*'] = 'group_date';
  444. $grouped = true;
  445. break;
  446. case 'MONTHS':
  447. $where['group'] = "YEAR({$field}),MONTH({$field})";
  448. $select[] = "CONCAT(YEAR({$field}),'-',LPAD(MONTH({$field}),2,'0')) AS group_date";
  449. $where['*select_index_field*'] = 'group_date';
  450. $grouped = true;
  451. break;
  452. }
  453. }
  454. if($grouped)
  455. return $dbh->select_array($table_name, join(',',$select), $where, $class_name);
  456. $where['*fake_select'] = true;
  457. $dbh->select_array($table_name, join(',',$select), $where, $class_name);
  458. $count = intval($dbh->get('SELECT FOUND_ROWS()'));
  459. }
  460. return $count;
  461. }
  462. function save($object)
  463. {
  464. $idf = $object->id_field();
  465. if(preg_match('/\)$/', $idf))
  466. $where = array($idf.'=' => $object->id());
  467. else
  468. $where = array($idf => $object->id());
  469. // var_dump($where);
  470. list($update, $where) = self::__update_data_prepare($object, $where);
  471. // var_dump($update);
  472. $main_table = $object->table_name();
  473. foreach($update as $db_name => $tables)
  474. {
  475. $dbh = new driver_mysql($db_name);
  476. foreach($tables as $table_name => $fields)
  477. {
  478. if($table_name == $main_table)
  479. $dbh->update($table_name, $where, $fields);
  480. else
  481. {
  482. $id_field = $fields['*id_field'];
  483. unset($fields['*id_field']);
  484. $where = array($id_field => $object->id());
  485. $dbh->insert_ignore($table_name, $where);
  486. $dbh->update($table_name, $where, $fields);
  487. }
  488. }
  489. }
  490. }
  491. private $data;
  492. private $dbi;
  493. private $object;
  494. private $__class_name;
  495. static function each($class_name, $where)
  496. {
  497. $set = popval($where, '*set');
  498. $object = new $class_name(NULL);
  499. list($select, $where) = self::__query_data_prepare($object, $where);
  500. $db_name = $object->db_name();
  501. $table_name = $object->table_name();
  502. // формат: array(..., '*set' => 'MAX(create_time) AS max_create_time, ...')
  503. if($set)
  504. foreach(preg_split('/,\s*/', $set) as $s)
  505. $select[] = $s;
  506. $iterator = new bors_storage_mysql();
  507. $iterator->object = $object;
  508. $iterator->__class_name = $class_name;
  509. $iterator->dbi = driver_mysql::factory($db_name)->each($table_name, join(',', $select), $where);
  510. return $iterator;
  511. }
  512. public function key() { } // Not implemented
  513. public function current() { return $this->object; }
  514. public function next()
  515. {
  516. $this->data = $this->dbi->next();
  517. return $this->__init_object();
  518. }
  519. public function rewind()
  520. {
  521. if(!$this->dbi)
  522. {
  523. bors_debug::syslog("db-error", "empty dbi");
  524. bors_throw('empty dbi');
  525. }
  526. $this->data = $this->dbi->rewind();
  527. return $this->__init_object();
  528. }
  529. public function valid() { return $this->data != false; }
  530. private function __init_object()
  531. {
  532. $data = $this->data;
  533. $class_name = $this->__class_name;
  534. $object = new $class_name($data['id']);
  535. // $object->set_id($data['id']);
  536. $object->data = $data;
  537. $object->set_is_loaded(true);
  538. return $this->object = $object;
  539. }
  540. function create($object)
  541. {
  542. $where = array();
  543. list($data, $where) = self::__update_data_prepare($object, $where);
  544. if(!$data)
  545. return;
  546. $main_table = true;
  547. $new_id = NULL;
  548. foreach($data as $db_name => $tables)
  549. {
  550. $dbh = new driver_mysql($db_name);
  551. foreach($tables as $table_name => $fields)
  552. {
  553. if(!$main_table)
  554. {
  555. $id_field = $fields['*id_field'];
  556. unset($fields['*id_field']);
  557. $fields[$id_field] = $new_id;
  558. }
  559. // bors_debug::syslog("inserts", "insert $table_name, ".print_r($fields, true));
  560. $object->storage()->storage_create();
  561. $insert_type = popval($fields, '*insert_type');
  562. if(!$insert_type && ($object->replace_on_new_instance() || $object->attr('__replace_on_new_instance')))
  563. $insert_type = 'REPLACE';
  564. if(!$insert_type && $object->ignore_on_new_instance())
  565. $insert_type = 'IGNORE';
  566. switch($insert_type)
  567. {
  568. case 'REPLACE':
  569. $dbh->replace($table_name, $fields);
  570. break;
  571. case 'IGNORE':
  572. $dbh->insert_ignore($table_name, $fields);
  573. break;
  574. default:
  575. if($object->get('insert_delayed_on_new_instance'))
  576. $fields['*DELAYED'] = true;
  577. $dbh->insert($table_name, $fields);
  578. break;
  579. }
  580. // Закомментировано, так как не позволяет аплоадить изображения с ignore.
  581. if($main_table
  582. && !$object->get('insert_delayed_on_new_instance')
  583. // && !$object->ignore_on_new_instance()
  584. )
  585. {
  586. $main_table = false;
  587. $new_id = $dbh->last_id();
  588. if(!$new_id)
  589. $new_id = $object->id();
  590. if(!$new_id && ($idf = $object->id_field()) && preg_match('/^\w+$/', $idf))
  591. $new_id = $object->get($idf);
  592. if(!$new_id && !$object->ignore_on_new_instance() && preg_match('/^\w+$/', $idf))
  593. bors_debug::syslog('_orm_error', "Can't get new id on new instance for ".$object->debug_title()."; data=".print_r($object->data, true));
  594. }
  595. }
  596. }
  597. $object->set_id($new_id);
  598. // echo "New id=$new_id, {$object->id()}<br/>";
  599. // exit();
  600. }
  601. function delete($object)
  602. {
  603. if(method_exists($object, 'on_delete'))
  604. $object->on_delete();
  605. $update = array();
  606. $where = array();
  607. $select = array();
  608. $post_functions = array();
  609. self::__join('inner', $object, $select, $where, $post_functions, $update);
  610. self::__join('left', $object, $select, $where, $post_functions, $update);
  611. foreach($update as $db_name => $tables)
  612. {
  613. $dbh = new driver_mysql($db_name);
  614. foreach($tables as $table_name => $fields)
  615. $dbh->delete($table_name, array($fields['*id_field'] => $object->id()));
  616. }
  617. $dbh = new driver_mysql($object->db_name());
  618. $dbh->delete($object->table_name(), array($object->id_field() => $object->id()));
  619. }
  620. function create_table($class_name = NULL)
  621. {
  622. $map = array(
  623. 'string' => 'VARCHAR(255)',
  624. 'text' => 'TEXT',
  625. 'timestamp' => 'TIMESTAMP NULL',
  626. 'int' => 'INT',
  627. 'uint' => 'INT UNSIGNED',
  628. 'bool' => 'TINYINT(1) UNSIGNED',
  629. 'float' => 'FLOAT',
  630. 'enum' => 'ENUM(%)',
  631. );
  632. if($class_name)
  633. {
  634. $object = new $class_name(NULL);
  635. $db_name = $object->db_name();
  636. $table_name = $object->table_name();
  637. $db = new driver_mysql($db_name);
  638. }
  639. else
  640. {
  641. $object = $this->__object;
  642. $db_name = $this->__db_name;
  643. $table_name = $this->__table_name;
  644. $db = $this->db();
  645. }
  646. $db_fields = array();
  647. $primary = false;
  648. $fields = bors_lib_orm::main_fields($object);
  649. foreach($fields as $field)
  650. {
  651. $db_field = '`'.$field['name'].'` '.$map[$field['type']];
  652. if($field['property'] == 'id')
  653. {
  654. $db_field .= ' AUTO_INCREMENT';
  655. $primary = $field['name'];
  656. }
  657. if(@$field['index'])
  658. $db_fields[] = "KEY (`{$field['name']}`)";
  659. $db_fields[$db_field] = $db_field;
  660. }
  661. if(empty($primary))
  662. return bors_throw(ec("Не найден первичный индекс для ").print_r($fields, true));
  663. $db_fields[] = "PRIMARY KEY (`$primary`)";
  664. $query = "CREATE TABLE IF NOT EXISTS `$table_name` (".join(', ', array_values($db_fields)).");";
  665. $db->query($query);
  666. if($lefts = $object->get('left_join_fields'))
  667. {
  668. foreach($lefts as $db_name => $tables)
  669. {
  670. foreach($tables as $tab => $fields)
  671. {
  672. if(!preg_match('/^(\w+)\((\w+)\)$/', $tab, $m))
  673. bors_throw("Unknown left join field with tab ".$tab);
  674. $db_fields = array();
  675. $table_name = $m[1];
  676. $id_field = $m[2];
  677. array_unshift($fields, $id_field);
  678. foreach($fields as $prop => $desc)
  679. {
  680. $f = bors_lib_orm::field($prop, $desc);
  681. $db_field = '`'.$f['name'].'` '.$map[$f['type']];
  682. if(@$f['index'])
  683. $db_fields[] = "KEY (`{$f['name']}`)";
  684. $db_fields[$db_field] = $db_field;
  685. }
  686. $db_fields[] = "PRIMARY KEY (`$id_field`)";
  687. $query = "CREATE TABLE IF NOT EXISTS `$table_name` (".join(', ', array_values($db_fields)).");";
  688. $db = new driver_mysql($db_name);
  689. $db->query($query);
  690. }
  691. }
  692. }
  693. // $db->close();
  694. }
  695. static function drop_table($class_name)
  696. {
  697. if(!config('can-drop-tables'))
  698. return bors_throw(ec('Удаление таблиц запрещено'));
  699. $class = bors_foo($class_name);
  700. foreach($class->fields() as $db_name => $tables)
  701. {
  702. $db = new driver_mysql($db_name);
  703. foreach($tables as $table_name => $fields)
  704. {
  705. if(preg_match('/^(\w+)\((\w+)\)$/', $table_name, $m))
  706. $table_name = $m[1];
  707. $db->query("DROP TABLE IF EXISTS $table_name");
  708. // $db->close();
  709. }
  710. }
  711. if($lefts = $class->get('left_join_fields'))
  712. {
  713. foreach($lefts as $db_name => $tables)
  714. {
  715. $db_fields = array();
  716. foreach($tables as $table_name => $fields)
  717. {
  718. if(preg_match('/^(\w+)\((\w+)\)$/', $table_name, $m))
  719. $table_name = $m[1];
  720. $db->query("DROP TABLE IF EXISTS $table_name");
  721. }
  722. }
  723. }
  724. }
  725. function storage_exists()
  726. {
  727. static $exists = array();
  728. $table = $this->__table_name;
  729. if(array_key_exists($table, $exists))
  730. return $exists[$table];
  731. $db = $this->db();
  732. return $exists[$table] = count($db->get_array("SHOW TABLES LIKE '".addslashes($table)."'")) > 0;
  733. }
  734. function storage_create()
  735. {
  736. if(config('mysql_tables_autocreate') && !$this->storage_exists())
  737. $this->create_table();
  738. }
  739. static function add_field($class_name, $field_name, $type = NULL)
  740. {
  741. $class = new $class_name(NULL);
  742. $db = new driver_mysql($class->db_name());
  743. if(!$type)
  744. $type = bors_lib_orm::property_type_autodetect($field_name);
  745. $q = "ALTER TABLE `{$class->table_name()}` ADD `$field_name` ".self::bors_type_to_sql($type)." NULL";
  746. echo $q.PHP_EOL;
  747. $db->query($q);
  748. bors_function_include('cache/clear_global_key');
  749. clear_global_key('bors_lib_orm_class_fields-0', $class_name);
  750. clear_global_key('bors_lib_orm_class_fields-1', $class_name);
  751. $class->clear_table_fields_cache();
  752. }
  753. static function del_field($class_name, $field_name, $confirm = false)
  754. {
  755. $class = new $class_name(NULL);
  756. $db = new driver_mysql($class->db_name());
  757. $q = "ALTER TABLE `{$class->table_name()}` DROP `$field_name`";
  758. echo $q.PHP_EOL;
  759. $db->query($q);
  760. bors_function_include('cache/clear_global_key');
  761. clear_global_key('bors_lib_orm_class_fields-0', $class_name);
  762. clear_global_key('bors_lib_orm_class_fields-1', $class_name);
  763. $class->clear_table_fields_cache();
  764. }
  765. static function check_properties($class_name, $properties) // ucrm_stat_counts
  766. {
  767. foreach($properties as $field_name)
  768. if(!self::field_exists($class_name, $field_name))
  769. self::add_field($class_name, $field_name);
  770. }
  771. function field_exists($class_name, $field_name)
  772. {
  773. $class = new $class_name(NULL);
  774. $db = new driver_mysql($class->db_name());
  775. // $table_fields = mysql_list_fields($class->db_name(), $class->table_name());
  776. $fields = $db->get_array("SHOW COLUMNS FROM `{$class->table_name()}`");
  777. /*
  778. array(2) {
  779. [0]=> array(6) {
  780. ["Field"]=> string(4) "date"
  781. ["Type"]=> string(4) "date"
  782. ["Null"]=> string(2) "NO"
  783. ["Key"]=> string(3) "PRI"
  784. ["Default"]=> NULL
  785. ["Extra"]=> string(0) ""
  786. }
  787. [1]=> array(6) { ... }
  788. }
  789. */
  790. foreach($fields as $x)
  791. if($x['Field'] == $field_name)
  792. return $x;
  793. return false;
  794. }
  795. static function bors_type_to_sql($type)
  796. {
  797. static $map = array(
  798. 'string' => 'VARCHAR(255)',
  799. 'str2' => 'VARCHAR(2)',
  800. 'str3' => 'VARCHAR(3)',
  801. 'text' => 'TEXT',
  802. 'timestamp' => 'TIMESTAMP',
  803. 'int' => 'INT',
  804. 'uint' => 'INT UNSIGNED',
  805. 'bool' => 'TINYINT(1) UNSIGNED',
  806. 'float' => 'FLOAT',
  807. 'enum' => 'ENUM(%)',
  808. );
  809. return $map[$type];
  810. }
  811. static function condition_optimize($condition)
  812. {
  813. require_once(BORS_CORE.'/inc/functions/time/date_format_mysqltime.php');
  814. static $_php_back_functions = array(
  815. 'UNIX_TIMESTAMP' => 'date_format_mysqltime',
  816. );
  817. if(preg_match("/^(UNIX_TIMESTAMP)\((.+?)\) BETWEEN '?(\d+)'? AND '?(\d+)'?$/i", trim($condition), $m))
  818. {
  819. if($bf = @$_php_back_functions[bors_upper($m[1])])
  820. $condition = "{$m[2]} BETWEEN ".$bf($m[3])." AND ".$bf($m[4]);
  821. }
  822. if(preg_match("/^(UNIX_TIMESTAMP)\((.+?)\)\s*(>|>=|<|<=|=)\s*'?(\d+)'?$/i", trim($condition), $m))
  823. {
  824. if($bf = @$_php_back_functions[bors_upper($m[1])])
  825. $condition = "{$m[2]} {$m[3]} ".$bf($m[4]);
  826. }
  827. // if(config('is_developer') && preg_match('/Date/', $condition)) { var_dump($condition); exit(); }
  828. return $condition;
  829. }
  830. }