api.nyanorm.php 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030
  1. <?php
  2. /**
  3. * Basic Ubilling database abstraction prototype
  4. */
  5. class NyanORM {
  6. /**
  7. ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  8. ░░░░░░░░░░▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄░░░░░░░░░
  9. ░░░░░░░░▄▀░░░░░░░░░░░░▄░░░░░░░▀▄░░░░░░░
  10. ░░░░░░░░█░░▄░░░░▄░░░░░░░░░░░░░░█░░░░░░░
  11. ░░░░░░░░█░░░░░░░░░░░░▄█▄▄░░▄░░░█░▄▄▄░░░
  12. ░▄▄▄▄▄░░█░░░░░░▀░░░░▀█░░▀▄░░░░░█▀▀░██░░
  13. ░██▄▀██▄█░░░▄░░░░░░░██░░░░▀▀▀▀▀░░░░██░░
  14. ░░▀██▄▀██░░░░░░░░▀░██▀░░░░░░░░░░░░░▀██░
  15. ░░░░▀████░▀░░░░▄░░░██░░░▄█░░░░▄░▄█░░██░
  16. ░░░░░░░▀█░░░░▄░░░░░██░░░░▄░░░▄░░▄░░░██░
  17. ░░░░░░░▄█▄░░░░░░░░░░░▀▄░░▀▀▀▀▀▀▀▀░░▄▀░░
  18. ░░░░░░█▀▀█████████▀▀▀▀████████████▀░░░░
  19. ░░░░░░████▀░░███▀░░░░░░▀███░░▀██▀░░░░░░
  20. ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  21. */
  22. /**
  23. * Contains table name for all instance operations
  24. *
  25. * @var string
  26. */
  27. protected $tableName = '';
  28. /**
  29. * Contains default primary key field name for current instance model
  30. *
  31. * @var string
  32. */
  33. protected $defaultPk = 'id';
  34. /**
  35. * Contains selectable fields list
  36. *
  37. * @var array
  38. */
  39. protected $selectable = array();
  40. /**
  41. * Contains key=>value data sets array for INSERT/UPDATE operations
  42. *
  43. * @var array
  44. */
  45. protected $data = array();
  46. /**
  47. * Cumulative where expressions array. This will used as AND glue.
  48. *
  49. * @var array
  50. */
  51. protected $where = array();
  52. /**
  53. * Cumulative where expressions array. This will used as OR glue.
  54. *
  55. * @var array
  56. */
  57. protected $orWhere = array();
  58. /**
  59. * Contains ORDER BY expressions for some queries
  60. *
  61. * @var array
  62. */
  63. protected $order = array();
  64. /**
  65. * Contains GROUP BY expressions for some queries
  66. *
  67. * @var array
  68. */
  69. protected $groupby = array();
  70. /**
  71. * Contains JOIN expression.
  72. *
  73. * @var array
  74. */
  75. protected $join = array();
  76. /**
  77. * Contains default query results limit
  78. *
  79. * @var int
  80. */
  81. protected $limit = 0;
  82. /**
  83. * Contains default query limit offset
  84. *
  85. * @var int
  86. */
  87. protected $offset = 0;
  88. /**
  89. * Object wide debug flag
  90. *
  91. * @var bool
  92. */
  93. protected $debug = false;
  94. /**
  95. * Yet another debug flag, for full model dumping
  96. *
  97. * @var bool
  98. */
  99. protected $deepDebug = false;
  100. /**
  101. * Default log path
  102. */
  103. const LOG_PATH = 'exports/nyanorm.log';
  104. /**
  105. * Creates new model instance
  106. *
  107. * @param string $name table name
  108. */
  109. public function __construct($name = '') {
  110. $this->setTableName($name);
  111. }
  112. /**
  113. * Table name automatic setter
  114. *
  115. * @param string $name table name to set
  116. *
  117. * @return void
  118. */
  119. protected function setTableName($name) {
  120. if (!empty($name)) {
  121. $this->tableName = $name;
  122. } else {
  123. $this->tableName = strtolower(get_class($this));
  124. }
  125. }
  126. /**
  127. * Setter of fields list which will be optionally used in getAll
  128. *
  129. * @param array/string $fieldSet $fieldSet fields names to be selectable from model in array or as comma separated string
  130. * @param bool $escapeFields determines if there's a need to escape fields with backticks or not
  131. *
  132. * @return void
  133. */
  134. public function selectable($fieldSet = '', $escapeFields = false) {
  135. if (!empty($fieldSet)) {
  136. if (is_array($fieldSet)) {
  137. $this->selectable = $fieldSet;
  138. } else {
  139. if (is_string($fieldSet)) {
  140. $this->selectable = explode(',', $fieldSet);
  141. }
  142. }
  143. if ($escapeFields) {
  144. $tmpArr = array();
  145. foreach ($this->selectable as $eachField) {
  146. $tmpArr[] = $this->escapeField(trim($eachField));
  147. }
  148. $this->selectable = empty($tmpArr) ? $this->selectable : $tmpArr;
  149. }
  150. } else {
  151. $this->flushSelectable();
  152. }
  153. }
  154. /**
  155. * Setter for join (with USING) list which used in getAll.
  156. *
  157. * @param string $joinExpression LEFT or RIGHT or whatever you need type of JOIN
  158. * @param string $tableName table name (for example switches)
  159. * @param string $using field to use for USING expression
  160. * @param bool $noTabNameEnclosure do not enclose table name with ``
  161. *
  162. * @throws MEOW_JOIN_WRONG_TYPE
  163. *
  164. * @return void
  165. */
  166. public function join($joinExpression = '', $tableName = '', $using = '', $noTabNameEnclosure = false) {
  167. if (!empty($joinExpression) and ! empty($tableName) and ! empty($using)) {
  168. $joinExpression = trim($joinExpression);
  169. switch ($joinExpression) {
  170. case 'INNER':
  171. break;
  172. case 'LEFT':
  173. break;
  174. case 'RIGHT':
  175. break;
  176. default :
  177. throw new Exception('MEOW_JOIN_WRONG_TYPE');
  178. }
  179. if (is_string($joinExpression) and is_string($tableName) and is_string($using)) {
  180. if ($noTabNameEnclosure) {
  181. $this->join[] = $joinExpression . " JOIN " . $tableName . " USING (" . $using . ")";
  182. } else {
  183. $this->join[] = $joinExpression . " JOIN `" . $tableName . "` USING (" . $using . ")";
  184. }
  185. }
  186. } else {
  187. $this->flushJoin();
  188. }
  189. }
  190. /**
  191. * Setter for join (with ON) list which used in getAll.
  192. *
  193. * @param string $joinExpression
  194. * @param string $tableName
  195. * @param string $on
  196. * @param bool $noTabNameEnclosure
  197. *
  198. * @throws MEOW_JOIN_WRONG_TYPE
  199. *
  200. * @return void
  201. */
  202. public function joinOn($joinExpression = '', $tableName = '', $on = '', $noTabNameEnclosure = false) {
  203. if (!empty($joinExpression) and ! empty($tableName) and ! empty($on)) {
  204. $joinExpression = trim($joinExpression);
  205. switch ($joinExpression) {
  206. case 'INNER':
  207. break;
  208. case 'LEFT':
  209. break;
  210. case 'RIGHT':
  211. break;
  212. default :
  213. throw new Exception('MEOW_JOIN_WRONG_TYPE');
  214. }
  215. if (is_string($joinExpression) and is_string($tableName) and is_string($on)) {
  216. if ($noTabNameEnclosure) {
  217. $this->join[] = $joinExpression . " JOIN " . $tableName . " ON (" . $on . ")";
  218. } else {
  219. $this->join[] = $joinExpression . " JOIN `" . $tableName . "` ON (" . $on . ")";
  220. }
  221. }
  222. } else {
  223. $this->flushJoin();
  224. }
  225. }
  226. /**
  227. * Appends some where expression to protected prop for further database queries. Cleans it if all params empty.
  228. *
  229. * @param string $field field name to apply expression
  230. * @param string $expression SQL expression. For example > = <, IS NOT, LIKE etc...
  231. * @param string $value expression parameter
  232. *
  233. * @return void
  234. */
  235. public function where($field = '', $expression = '', $value = '') {
  236. if (!empty($field) AND ! empty($expression)) {
  237. $value = ($value == 'NULL' OR $value == 'null') ? $value : "'" . $value . "'";
  238. $this->where[] = $this->escapeField($field) . " " . $expression . " " . $value;
  239. } else {
  240. $this->flushWhere();
  241. }
  242. }
  243. /**
  244. * Appends some raw where expression into cumullative where array. Or cleanup all if empty. Yeah.
  245. *
  246. * @param string $expression raw SQL expression
  247. *
  248. * @return void
  249. */
  250. public function whereRaw($expression = '') {
  251. if (!empty($expression)) {
  252. $this->where[] = $expression;
  253. } else {
  254. $this->where = array();
  255. }
  256. }
  257. /**
  258. * Flushes all available cumulative structures in safety reasons.
  259. *
  260. * @return void
  261. */
  262. protected function destroyAllStructs() {
  263. $this->flushData();
  264. $this->flushWhere();
  265. $this->flushGroupBy();
  266. $this->flushOrder();
  267. $this->flushLimit();
  268. $this->flushJoin();
  269. }
  270. /**
  271. * Appends some OR where expression to protected prop for further database queries. Cleans it if all params empty.
  272. *
  273. * @param string $field field name to apply expression
  274. * @param string $expression SQL expression. For example > = <, IS NOT, LIKE etc...
  275. * @param string $value expression parameter
  276. *
  277. * @return void
  278. */
  279. public function orWhere($field = '', $expression = '', $value = '') {
  280. if (!empty($field) AND ! empty($expression)) {
  281. $value = ($value == 'NULL' OR $value == 'null') ? $value : "'" . $value . "'";
  282. $this->orWhere[] = $this->escapeField($field) . " " . $expression . " " . $value;
  283. } else {
  284. $this->flushWhere();
  285. }
  286. }
  287. /**
  288. * Appends some raw OR where expression into cumullative where array. Or cleanup all if empty.
  289. *
  290. * @param string $expression raw SQL expression
  291. *
  292. * @return void
  293. */
  294. public function orWhereRaw($expression = '') {
  295. if (!empty($expression)) {
  296. $this->orWhere[] = $expression;
  297. } else {
  298. $this->flushWhere();
  299. }
  300. }
  301. /**
  302. * Flushes both where cumullative arrays
  303. *
  304. * @return void
  305. */
  306. protected function flushWhere() {
  307. $this->where = array();
  308. $this->orWhere = array();
  309. }
  310. /**
  311. * Appends some order by expression to protected prop
  312. * Can be either a one-field name string, a string of fields separated by coma or an array of field names
  313. *
  314. * @param string/array $fieldSet fields for ordering
  315. * @param string $order SQL order direction like ASC/DESC
  316. * @param bool $escapeFields determines if there's a need to escape fields with backticks or not
  317. * @param bool $orderWithinFields allows to put individual sort direction(ASC/DESC) for each field specified.
  318. * $fieldSet must be a RAW COMA-DELIMITED STRING value if using this parameter, as it's not processed in any way
  319. * Keep in mind that this option ignores $order and $escapeFields params and fields should be escaped manually, if needed
  320. *
  321. * @return void
  322. */
  323. public function orderBy($fieldSet = '', $order = '', $escapeFields = true, $orderWithinFields = false) {
  324. if (!empty($fieldSet)) {
  325. $tmpArr = array();
  326. $tmpStr = '';
  327. if ($orderWithinFields) {
  328. $tmpStr = $fieldSet;
  329. } else {
  330. if (!empty($order)) {
  331. if (is_array($fieldSet)) {
  332. $tmpArr = $fieldSet;
  333. } else {
  334. if (is_string($fieldSet)) {
  335. $tmpArr = explode(',', $fieldSet);
  336. }
  337. }
  338. foreach ($tmpArr as $eachField) {
  339. if ($escapeFields) {
  340. $tmpStr.= $this->escapeField(trim($eachField)) . ", ";
  341. } else {
  342. $tmpStr.= $eachField . ", ";
  343. }
  344. }
  345. $tmpStr = trim(trim($tmpStr, ", "));
  346. $tmpStr.= " " . $order;
  347. }
  348. }
  349. $this->order[] = $tmpStr;
  350. } else {
  351. $this->flushOrder();
  352. }
  353. }
  354. /**
  355. * Setter of GROUP BY clause fields list which will be optionally used in getAll
  356. *
  357. * @param array/string $fieldSet $fieldSet fields names to be selectable from model in array or as comma separated string
  358. * @param bool $escapeFields determines if there's a need to escape fields with backticks or not
  359. *
  360. * @return void
  361. */
  362. public function groupBy($fieldSet = '', $escapeFields = false) {
  363. if (!empty($fieldSet)) {
  364. if (is_array($fieldSet)) {
  365. $this->groupby = $fieldSet;
  366. } else {
  367. if (is_string($fieldSet)) {
  368. $this->groupby = explode(',', $fieldSet);
  369. }
  370. }
  371. if ($escapeFields) {
  372. $tmpArr = array();
  373. foreach ($this->groupby as $eachField) {
  374. $tmpArr[] = $this->escapeField(trim($eachField));
  375. }
  376. $this->groupby = empty($tmpArr) ? $this->groupby : $tmpArr;
  377. }
  378. } else {
  379. $this->flushGroupBy();
  380. }
  381. }
  382. /**
  383. * Flushes order cumullative array
  384. *
  385. * @return void
  386. */
  387. protected function flushOrder() {
  388. $this->order = array();
  389. }
  390. /**
  391. * Flushes groupby cumullative array
  392. *
  393. * @return void
  394. */
  395. protected function flushGroupBy() {
  396. $this->groupby = array();
  397. }
  398. /**
  399. * Flushes selectable cumullative struct
  400. *
  401. * @return void
  402. */
  403. protected function flushSelectable() {
  404. $this->selectable = array();
  405. }
  406. /**
  407. * Flushed join cumullative struct
  408. *
  409. * @return void
  410. */
  411. protected function flushJoin() {
  412. $this->join = array();
  413. }
  414. /**
  415. * Process some debugging data if required
  416. *
  417. * @param string $data now it just string that will be displayed in debug output
  418. *
  419. * @return void
  420. */
  421. protected function debugLog($data) {
  422. if ($this->debug) {
  423. show_window(__('NyanORM Debug'), $data);
  424. $curDate = curdatetime();
  425. $logData = '';
  426. if ($this->deepDebug) {
  427. $logData = $curDate . ' Model name: "' . $this->tableName . '"' . "\n";
  428. $logData .= $curDate . ' Model state: ' . print_r($this, true) . "\n";
  429. }
  430. $logData .= $curDate . ' ' . $data . "\n";
  431. if ($this->deepDebug) {
  432. $logData .= str_repeat('=', 40) . "\n";
  433. }
  434. file_put_contents(self::LOG_PATH, $logData, FILE_APPEND);
  435. }
  436. }
  437. /**
  438. * Build join expression from protected join expressions array.
  439. *
  440. * @return string
  441. */
  442. protected function buildJoinString() {
  443. $result = '';
  444. if (!empty($this->join)) {
  445. if (is_array($this->join)) {
  446. $result .= implode(' ', $this->join);
  447. }
  448. }
  449. return ($result);
  450. }
  451. /**
  452. * Builds where string expression from protected where expressions array
  453. *
  454. * @return string
  455. */
  456. protected function buildWhereString() {
  457. $result = '';
  458. if (!empty($this->where)) {
  459. if (is_array($this->where)) {
  460. $result .= " WHERE ";
  461. $result .= implode(' AND ', $this->where);
  462. }
  463. }
  464. if (!empty($this->orWhere)) {
  465. if (is_array($this->orWhere)) {
  466. if (empty($result)) {
  467. //maybe only OR statements here
  468. $result .= " WHERE ";
  469. } else {
  470. $result .= " OR ";
  471. }
  472. $result .= implode(' OR ', $this->orWhere);
  473. }
  474. }
  475. return($result);
  476. }
  477. /**
  478. * Retruns order expressions as string
  479. *
  480. * @return string
  481. */
  482. protected function buildOrderString() {
  483. $result = '';
  484. if (!empty($this->order)) {
  485. if (is_array($this->order)) {
  486. $result .= " ORDER BY ";
  487. $result .= implode(' , ', $this->order);
  488. }
  489. }
  490. return($result);
  491. }
  492. /**
  493. * Retruns groupby expressions as string
  494. *
  495. * @return string
  496. */
  497. protected function buildGroupByString() {
  498. $result = '';
  499. if (!empty($this->groupby)) {
  500. if (is_array($this->groupby)) {
  501. $result .= " GROUP BY ";
  502. $result .= implode(',', $this->groupby);
  503. }
  504. }
  505. return($result);
  506. }
  507. /**
  508. * Sets query limits with optional offset
  509. *
  510. * @param int $limit results limit count
  511. * @param int $offset results limit offset
  512. *
  513. * @return void
  514. */
  515. public function limit($limit = '', $offset = '') {
  516. if (!empty($limit)) {
  517. $this->limit = $limit;
  518. if (!empty($offset)) {
  519. $this->offset = $offset;
  520. }
  521. } else {
  522. $this->flushLimit();
  523. }
  524. }
  525. /**
  526. * Flushes limits values for further queries. No limits anymore! Meow!
  527. *
  528. * @return void
  529. */
  530. protected function flushLimit() {
  531. $this->limit = 0;
  532. $this->offset = 0;
  533. }
  534. /**
  535. * Builds SQL formatted limits string
  536. *
  537. * @return string
  538. */
  539. protected function buildLimitString() {
  540. $result = '';
  541. if (!empty($this->limit)) {
  542. $result .= ' LIMIT ';
  543. if (!empty($this->offset)) {
  544. $result .= ' ' . $this->offset . ',' . $this->limit;
  545. } else {
  546. $result .= ' ' . $this->limit;
  547. }
  548. }
  549. return($result);
  550. }
  551. /**
  552. * Constucts field names which will be optionally used for data getting
  553. *
  554. * @return string
  555. */
  556. protected function buildSelectableString() {
  557. $result = '';
  558. if (!empty($this->selectable)) {
  559. $result.= implode(',', $this->selectable);
  560. } else {
  561. $result.= '*';
  562. }
  563. return($result);
  564. }
  565. /**
  566. * Returns all records of current database object instance
  567. *
  568. * @param string $assocByField field name to automatically make it as index key in results array
  569. * @param bool $flushParams flush all query parameters like where, order, limit and other after execution?
  570. * @param bool $distinctON add "DISTINCT" keyword for a "SELECT" clause
  571. *
  572. * @return array
  573. */
  574. public function getAll($assocByField = '', $flushParams = true, $distinctON = false) {
  575. $joinString = $this->buildJoinString();
  576. $whereString = $this->buildWhereString();
  577. $groupbyString = $this->buildGroupByString();
  578. $orderString = $this->buildOrderString();
  579. $limitString = $this->buildLimitString();
  580. $selectableString = $this->buildSelectableString();
  581. $distinct = ($distinctON ? ' DISTINCT ' : '');
  582. //building some dummy query
  583. $query = "SELECT " . $distinct . $selectableString . " from `" . $this->tableName . "` "; //base query
  584. $query .= $joinString . $whereString . $groupbyString . $orderString . $limitString; //optional parameters
  585. $this->debugLog($query);
  586. $result = simple_queryall($query);
  587. //automatic data preprocessing
  588. if (!empty($assocByField)) {
  589. $resultTmp = array();
  590. if (!empty($result)) {
  591. foreach ($result as $io => $each) {
  592. if (isset($each[$assocByField])) {
  593. $resultTmp[$each[$assocByField]] = $each;
  594. }
  595. }
  596. }
  597. $result = $resultTmp;
  598. $resultTmp = array(); //cleanup?
  599. }
  600. if ($flushParams) {
  601. //flush instance parameters for further queries
  602. $this->destroyAllStructs();
  603. }
  604. return($result);
  605. }
  606. /**
  607. * Deletes record from database. Where must be not empty!
  608. *
  609. * @param bool $flushParams flush all query parameters like where, order, limit and other after execution?
  610. *
  611. * @return void
  612. */
  613. public function delete($flushParams = true) {
  614. if (!empty($this->where) OR ! empty($this->orWhere)) {
  615. $whereString = $this->buildWhereString();
  616. $limitString = $this->buildLimitString();
  617. if (!empty($whereString)) {
  618. //double check yeah!
  619. $query = "DELETE from `" . $this->tableName . "`"; //base deletion query
  620. $query .= $whereString . $limitString; //optional parameters
  621. $this->debugLog($query);
  622. nr_query($query);
  623. } else {
  624. throw new Exception('MEOW_WHERE_STRUCT_EMPTY');
  625. }
  626. } else {
  627. throw new Exception('MEOW_WHERE_STRUCT_EMPTY');
  628. }
  629. if ($flushParams) {
  630. //flush instance parameters for further queries
  631. $this->destroyAllStructs();
  632. }
  633. }
  634. /**
  635. * Puts some data into protected data property for furrrrther save()/create() operations.
  636. *
  637. * @param string $field record field name to push data
  638. * @param string $value field content to push
  639. *
  640. * @return void
  641. */
  642. public function data($field = '', $value = '') {
  643. if (!empty($field)) {
  644. $this->data[$field] = $value;
  645. } else {
  646. $this->flushData();
  647. }
  648. }
  649. /**
  650. * Same as data() method, but works not with separate $field and $value data but with array of $fields => $values
  651. * Useful if some "third-party" code returns an already prepared array of $fields => $values
  652. *
  653. * @param array $field_value array with $field => $value structure
  654. *
  655. * @return void
  656. */
  657. public function dataArr($field_value = array()) {
  658. $dataInsCnt = 0;
  659. if (!empty($field_value)) {
  660. foreach ($field_value as $field => $value) {
  661. if (!empty($field)) {
  662. $this->data[$field] = $value;
  663. $dataInsCnt++;
  664. }
  665. }
  666. }
  667. if ($dataInsCnt == 0) {
  668. $this->flushData();
  669. }
  670. }
  671. /**
  672. * Flushes current instance data set
  673. *
  674. * @return void
  675. */
  676. protected function flushData() {
  677. $this->data = array();
  678. }
  679. /**
  680. * Saves current model data fields changes to database.
  681. *
  682. * @param bool $flushParams flush all query parameters like where, order, limit and other after execution?
  683. * @param bool $fieldsBatch gather all the fields together in a single query from $this->data structure
  684. * before actually running the query to reduce the amount of subsequential DB queries for every table field
  685. *
  686. * @return void
  687. */
  688. public function save($flushParams = true, $fieldsBatch = false) {
  689. if (!empty($this->data)) {
  690. if (!empty($this->where)) {
  691. $whereString = $this->buildWhereString();
  692. if (!empty($whereString)) {
  693. //double check, yeah.
  694. if ($fieldsBatch) {
  695. $query = "UPDATE `" . $this->tableName . "` SET ";
  696. foreach ($this->data as $field => $value) {
  697. $query .= $this->escapeField($field) . "='" . $value . "', ";
  698. }
  699. $query = rtrim($query, ', ');
  700. $query .= $whereString;
  701. $this->debugLog($query);
  702. nr_query($query);
  703. } else {
  704. foreach ($this->data as $field => $value) {
  705. $query = "UPDATE `" . $this->tableName . "` SET " . $this->escapeField($field) . "='" . $value . "'" . $whereString;
  706. $this->debugLog($query);
  707. nr_query($query);
  708. }
  709. }
  710. } else {
  711. throw new Exception('MEOW_WHERE_STRUCT_EMPTY');
  712. }
  713. } else {
  714. throw new Exception('MEOW_WHERE_STRUCT_EMPTY');
  715. }
  716. } else {
  717. throw new Exception('MEOW_DATA_STRUCT_EMPTY');
  718. }
  719. if ($flushParams) {
  720. //flush instance parameters for further queries
  721. $this->destroyAllStructs();
  722. }
  723. }
  724. /**
  725. * Creates new database record for current model instance.
  726. *
  727. * @param bool $autoAiId append default NULL autoincrementing primary key?
  728. * @param bool $flushParams flush all query parameters like where, order, limit and other after execution?
  729. *
  730. * @return void
  731. */
  732. public function create($autoAiId = true, $flushParams = true) {
  733. if (!empty($this->data)) {
  734. $dataStruct = '';
  735. $dataValues = '';
  736. if ($autoAiId) {
  737. $dataStruct .= '`' . $this->defaultPk . '`,';
  738. $dataValues .= 'NULL,';
  739. }
  740. foreach ($this->data as $field => $value) {
  741. $dataStruct .= $this->escapeField($field) . ',';
  742. $dataValues .= "'" . $value . "',";
  743. }
  744. $dataStruct = substr($dataStruct, 0, -1);
  745. $dataValues = substr($dataValues, 0, -1);
  746. $query = "INSERT INTO `" . $this->tableName . "` (" . $dataStruct . ') VALUES (' . $dataValues . ')';
  747. $this->debugLog($query);
  748. nr_query($query); //RUN THAT MEOW!!!!
  749. } else {
  750. throw new Exception('MEOW_DATA_STRUCT_EMPTY');
  751. }
  752. if ($flushParams) {
  753. //flush instance parameters for further queries
  754. $this->destroyAllStructs();
  755. }
  756. }
  757. /**
  758. * Returns last ID key in table
  759. *
  760. * @return int
  761. */
  762. public function getLastId() {
  763. $query = "SELECT " . $this->escapeField($this->defaultPk) . " from `" . $this->tableName . "` ORDER BY " . $this->escapeField($this->defaultPk) . " DESC LIMIT 1";
  764. $result = simple_query($query);
  765. return ($result[$this->defaultPk]);
  766. }
  767. /**
  768. * Returns fields count in datatabase instance
  769. *
  770. * @param string $fieldsToCount field name to count results
  771. * @param bool $flushParams flush all query parameters like where, order, limit and other after execution?
  772. *
  773. * @return int
  774. */
  775. public function getFieldsCount($fieldsToCount = 'id', $flushParams = true) {
  776. $whereString = $this->buildWhereString();
  777. $raw = simple_query("SELECT COUNT(" . $this->escapeField($fieldsToCount) . ") AS `result` from `" . $this->tableName . "`" . $whereString);
  778. if ($flushParams) {
  779. //flush instance parameters for further queries
  780. $this->destroyAllStructs();
  781. }
  782. return($raw['result']);
  783. }
  784. /**
  785. * Returns fields sum in datatabase instance
  786. *
  787. * @param string $fieldsToSum field name to retrive its sum
  788. * @param bool $flushParams flush all query parameters like where, order, limit and other after execution?
  789. *
  790. * @return int
  791. */
  792. public function getFieldsSum($fieldsToSum, $flushParams = true) {
  793. if (!empty($fieldsToSum)) {
  794. $whereString = $this->buildWhereString();
  795. $raw = simple_query("SELECT SUM(" . $this->escapeField($fieldsToSum) . ") AS `result` from `" . $this->tableName . "`" . $whereString);
  796. if ($flushParams) {
  797. //flush instance parameters for further queries
  798. $this->destroyAllStructs();
  799. }
  800. return($raw['result']);
  801. } else {
  802. throw new Exception('MEOW_NO_FIELD_NAME');
  803. }
  804. }
  805. /**
  806. * Enables or disables debug flag
  807. *
  808. * @param bool $state object instance debug state
  809. * @param bool $deep deep debugging mode with full model dumps
  810. *
  811. * @return void
  812. */
  813. public function setDebug($state, $deep = false) {
  814. $this->debug = $state;
  815. if ($deep) {
  816. $this->deepDebug = true;
  817. }
  818. }
  819. /**
  820. * Sets default primary key for model instance
  821. *
  822. * @param string $fieldName
  823. *
  824. * @return void
  825. */
  826. public function setDefaultPk($fieldName = 'id') {
  827. $this->defaultPk = $fieldName;
  828. }
  829. /**
  830. * Trying to correctly escape fields when using table_name.field_name.
  831. *
  832. * @param string $field
  833. *
  834. * @return string
  835. */
  836. protected function escapeField($field) {
  837. $field = str_ireplace('`', '', $field);
  838. if (strpos($field, '.') !== false) {
  839. $parts = explode(".", $field);
  840. $field = "`" . $parts[0] . "`.`" . $parts[1] . "`";
  841. } else {
  842. if (trim($field != "*")) {
  843. $field = "`" . $field . "`";
  844. }
  845. }
  846. return ($field);
  847. }
  848. /**
  849. * Returns true if record with such fields values already exists in current table
  850. *
  851. * @param array $dbFilterArr represents structure, like:
  852. * array($fieldname => array('operator' => $opStr,
  853. * 'fieldval' => $fldVal,
  854. * 'cmprlogic' => 'OR'
  855. * ))
  856. * where:
  857. * $opStr - is one of the permitted in MYSQL WHERE clause, like:
  858. * >, =, <, IS NOT, LIKE, IN, etc
  859. * $fldVal - represents the field value to compare against
  860. * 'cmprlogic' - is optional and can contain 'OR' keyword to point
  861. * the need to use NyanORM's orWhere(). No need to use it for 'AND' logical clause
  862. * as it is used by DEFAULT and will be ignored
  863. * @param int $excludeRecID record ID to make exclusion on (e.g. for finding possible duplicates). For record editing purposes generally.
  864. * @param bool $flushSelectable
  865. * @param string $primaryKey name of the table's primary key field
  866. *
  867. * @return mixed|string returns the primary key value(ID usually) if rec found or empty string
  868. */
  869. public function checkRecExists($dbFilterArr, $excludeRecID = 0, $flushSelectable = true, $primaryKey = '') {
  870. $result = '';
  871. $primaryKey = (empty($primaryKey)) ? $this->defaultPk : $primaryKey;
  872. $this->selectable($primaryKey);
  873. foreach ($dbFilterArr as $dbFieldName => $fieldData) {
  874. if (!empty($fieldData['operator'])) {
  875. if (!empty($fieldData['cmprlogic']) and strtoupper($fieldData['cmprlogic']) == 'OR') {
  876. $this->orWhere($dbFieldName, $fieldData['operator'], $fieldData['fieldval']);
  877. } else {
  878. $this->where($dbFieldName, $fieldData['operator'], $fieldData['fieldval']);
  879. }
  880. }
  881. }
  882. if (!empty($excludeRecID)) {
  883. $this->where($primaryKey, '!=', $excludeRecID);
  884. }
  885. $result = $this->getAll();
  886. $result = empty($result) ? '' : $result[0][$primaryKey];
  887. if ($flushSelectable) {
  888. $this->flushSelectable();
  889. }
  890. return ($result);
  891. }
  892. /**
  893. * Simple getter for tableName property
  894. *
  895. * @param $trimQuotes
  896. *
  897. * @return string
  898. */
  899. public function getTableName($trimQuotes = false) {
  900. if ($trimQuotes) {
  901. return (trim($this->tableName, '"`\''));
  902. } else {
  903. return ($this->tableName);
  904. }
  905. }
  906. /**
  907. * Returns model's base table structure
  908. *
  909. * @param bool $fieldNamesOnly
  910. * @param bool $excludeIDField
  911. * @param bool $addLeadingTabName
  912. * @param bool $makeFieldAliases
  913. * @param string $fieldAliasSeparator
  914. *
  915. * @return array
  916. */
  917. public function getTableStructure($fieldNamesOnly = false, $excludeIDField = false, $addLeadingTabName = false,
  918. $makeFieldAliases = false, $fieldAliasSeparator = '') {
  919. $result = array();
  920. $query = 'DESCRIBE ' . $this->tableName;
  921. $tableStructure = simple_queryall($query);
  922. if (!empty($tableStructure)) {
  923. if ($fieldNamesOnly) {
  924. foreach ($tableStructure as $io => $eachField) {
  925. if ($excludeIDField and $eachField['Field'] == $this->defaultPk) {
  926. continue;
  927. }
  928. // create field alias using combination of $this->tableName + $fieldAliasSeparator + $eachField['Field']?
  929. $fieldName = ($makeFieldAliases) ? $eachField['Field'] . ' AS ' . $this->tableName . $fieldAliasSeparator . $eachField['Field']
  930. : $eachField['Field'];
  931. // append leading table name with dot to the field name to explicitly distinguish fields in a query?
  932. $result[] = ($addLeadingTabName) ? $this->tableName . '.' . $fieldName : $fieldName;
  933. }
  934. } else {
  935. $result = $tableStructure;
  936. }
  937. }
  938. return ($result);
  939. }
  940. }