api.nyanorm.php 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039
  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. // can't use ternary here 'cause we need to avoid the array element creation
  350. if (!empty($tmpStr)) {
  351. $this->order[] = $tmpStr;
  352. }
  353. } else {
  354. $this->flushOrder();
  355. }
  356. }
  357. /**
  358. * Setter of GROUP BY clause fields list which will be optionally used in getAll
  359. *
  360. * @param array/string $fieldSet $fieldSet fields names to be selectable from model in array or as comma separated string
  361. * @param bool $escapeFields determines if there's a need to escape fields with backticks or not
  362. *
  363. * @return void
  364. */
  365. public function groupBy($fieldSet = '', $escapeFields = false) {
  366. if (!empty($fieldSet)) {
  367. if (is_array($fieldSet)) {
  368. $this->groupby = $fieldSet;
  369. } else {
  370. if (is_string($fieldSet)) {
  371. $this->groupby = explode(',', $fieldSet);
  372. }
  373. }
  374. if ($escapeFields) {
  375. $tmpArr = array();
  376. foreach ($this->groupby as $eachField) {
  377. $tmpArr[] = $this->escapeField(trim($eachField));
  378. }
  379. $this->groupby = empty($tmpArr) ? $this->groupby : $tmpArr;
  380. }
  381. } else {
  382. $this->flushGroupBy();
  383. }
  384. }
  385. /**
  386. * Flushes order cumullative array
  387. *
  388. * @return void
  389. */
  390. protected function flushOrder() {
  391. $this->order = array();
  392. }
  393. /**
  394. * Flushes groupby cumullative array
  395. *
  396. * @return void
  397. */
  398. protected function flushGroupBy() {
  399. $this->groupby = array();
  400. }
  401. /**
  402. * Flushes selectable cumullative struct
  403. *
  404. * @return void
  405. */
  406. protected function flushSelectable() {
  407. $this->selectable = array();
  408. }
  409. /**
  410. * Flushed join cumullative struct
  411. *
  412. * @return void
  413. */
  414. protected function flushJoin() {
  415. $this->join = array();
  416. }
  417. /**
  418. * Process some debugging data if required
  419. *
  420. * @param string $data now it just string that will be displayed in debug output
  421. *
  422. * @return void
  423. */
  424. protected function debugLog($data) {
  425. if ($this->debug) {
  426. show_window(__('NyanORM Debug'), $data);
  427. $curDate = curdatetime();
  428. $logData = '';
  429. if ($this->deepDebug) {
  430. $logData = $curDate . ' Model name: "' . $this->tableName . '"' . "\n";
  431. $logData .= $curDate . ' Model state: ' . print_r($this, true) . "\n";
  432. }
  433. $logData .= $curDate . ' ' . $data . "\n";
  434. if ($this->deepDebug) {
  435. $logData .= str_repeat('=', 40) . "\n";
  436. }
  437. file_put_contents(self::LOG_PATH, $logData, FILE_APPEND);
  438. }
  439. }
  440. /**
  441. * Build join expression from protected join expressions array.
  442. *
  443. * @return string
  444. */
  445. protected function buildJoinString() {
  446. $result = '';
  447. if (!empty($this->join)) {
  448. if (is_array($this->join)) {
  449. $result .= implode(' ', $this->join);
  450. }
  451. }
  452. return ($result);
  453. }
  454. /**
  455. * Builds where string expression from protected where expressions array
  456. *
  457. * @return string
  458. */
  459. protected function buildWhereString() {
  460. $result = '';
  461. if (!empty($this->where)) {
  462. if (is_array($this->where)) {
  463. $result .= " WHERE ";
  464. $result .= implode(' AND ', $this->where);
  465. }
  466. }
  467. if (!empty($this->orWhere)) {
  468. if (is_array($this->orWhere)) {
  469. if (empty($result)) {
  470. //maybe only OR statements here
  471. $result .= " WHERE ";
  472. } else {
  473. $result .= " OR ";
  474. }
  475. $result .= implode(' OR ', $this->orWhere);
  476. }
  477. }
  478. return ($result);
  479. }
  480. /**
  481. * Retruns order expressions as string
  482. *
  483. * @return string
  484. */
  485. protected function buildOrderString() {
  486. $result = '';
  487. if (!empty($this->order)) {
  488. if (is_array($this->order)) {
  489. $result = implode(' , ', $this->order);
  490. }
  491. }
  492. // trying to avoid possible empty-string elements arrays(particularly - an array with one empty-string element)
  493. $result = empty($result) ? '' : " ORDER BY " . $result;
  494. return ($result);
  495. }
  496. /**
  497. * Retruns groupby expressions as string
  498. *
  499. * @return string
  500. */
  501. protected function buildGroupByString() {
  502. $result = '';
  503. if (!empty($this->groupby)) {
  504. if (is_array($this->groupby)) {
  505. $result .= implode(',', $this->groupby);
  506. }
  507. }
  508. // trying to avoid possible empty-string elements arrays(particularly - an array with one empty-string element)
  509. $result = empty($result) ? '' : " GROUP BY " . $result;
  510. return ($result);
  511. }
  512. /**
  513. * Sets query limits with optional offset
  514. *
  515. * @param int $limit results limit count
  516. * @param int $offset results limit offset
  517. *
  518. * @return void
  519. */
  520. public function limit($limit = '', $offset = '') {
  521. if (!empty($limit)) {
  522. $this->limit = $limit;
  523. if (!empty($offset)) {
  524. $this->offset = $offset;
  525. }
  526. } else {
  527. $this->flushLimit();
  528. }
  529. }
  530. /**
  531. * Flushes limits values for further queries. No limits anymore! Meow!
  532. *
  533. * @return void
  534. */
  535. protected function flushLimit() {
  536. $this->limit = 0;
  537. $this->offset = 0;
  538. }
  539. /**
  540. * Builds SQL formatted limits string
  541. *
  542. * @return string
  543. */
  544. protected function buildLimitString() {
  545. $result = '';
  546. if (!empty($this->limit)) {
  547. $result .= ' LIMIT ';
  548. if (!empty($this->offset)) {
  549. $result .= ' ' . $this->offset . ',' . $this->limit;
  550. } else {
  551. $result .= ' ' . $this->limit;
  552. }
  553. }
  554. return ($result);
  555. }
  556. /**
  557. * Constucts field names which will be optionally used for data getting
  558. *
  559. * @return string
  560. */
  561. protected function buildSelectableString() {
  562. $result = '';
  563. if (!empty($this->selectable)) {
  564. $result .= implode(',', $this->selectable);
  565. } else {
  566. $result .= '*';
  567. }
  568. return ($result);
  569. }
  570. /**
  571. * Returns all records of current database object instance
  572. *
  573. * @param string $assocByField field name to automatically make it as index key in results array
  574. * @param bool $flushParams flush all query parameters like where, order, limit and other after execution?
  575. * @param bool $distinctON add "DISTINCT" keyword for a "SELECT" clause
  576. *
  577. * @return array
  578. */
  579. public function getAll($assocByField = '', $flushParams = true, $distinctON = false) {
  580. $joinString = $this->buildJoinString();
  581. $whereString = $this->buildWhereString();
  582. $groupbyString = $this->buildGroupByString();
  583. $orderString = $this->buildOrderString();
  584. $limitString = $this->buildLimitString();
  585. $selectableString = $this->buildSelectableString();
  586. $distinct = ($distinctON ? ' DISTINCT ' : '');
  587. //building some dummy query
  588. $query = "SELECT " . $distinct . $selectableString . " from `" . $this->tableName . "` "; //base query
  589. $query .= $joinString . $whereString . $groupbyString . $orderString . $limitString; //optional parameters
  590. $this->debugLog($query);
  591. $result = simple_queryall($query);
  592. //automatic data preprocessing
  593. if (!empty($assocByField)) {
  594. $resultTmp = array();
  595. if (!empty($result)) {
  596. foreach ($result as $io => $each) {
  597. if (isset($each[$assocByField])) {
  598. $resultTmp[$each[$assocByField]] = $each;
  599. }
  600. }
  601. }
  602. $result = $resultTmp;
  603. $resultTmp = array(); //cleanup?
  604. }
  605. if ($flushParams) {
  606. //flush instance parameters for further queries
  607. $this->destroyAllStructs();
  608. }
  609. return ($result);
  610. }
  611. /**
  612. * Deletes record from database. Where must be not empty!
  613. *
  614. * @param bool $flushParams flush all query parameters like where, order, limit and other after execution?
  615. *
  616. * @return void
  617. */
  618. public function delete($flushParams = true) {
  619. if (!empty($this->where) or ! empty($this->orWhere)) {
  620. $whereString = $this->buildWhereString();
  621. $limitString = $this->buildLimitString();
  622. if (!empty($whereString)) {
  623. //double check yeah!
  624. $query = "DELETE from `" . $this->tableName . "`"; //base deletion query
  625. $query .= $whereString . $limitString; //optional parameters
  626. $this->debugLog($query);
  627. nr_query($query);
  628. } else {
  629. throw new Exception('MEOW_WHERE_STRUCT_EMPTY');
  630. }
  631. } else {
  632. throw new Exception('MEOW_WHERE_STRUCT_EMPTY');
  633. }
  634. if ($flushParams) {
  635. //flush instance parameters for further queries
  636. $this->destroyAllStructs();
  637. }
  638. }
  639. /**
  640. * Puts some data into protected data property for furrrrther save()/create() operations.
  641. *
  642. * @param string $field record field name to push data
  643. * @param string $value field content to push
  644. *
  645. * @return void
  646. */
  647. public function data($field = '', $value = '') {
  648. if (!empty($field)) {
  649. $this->data[$field] = $value;
  650. } else {
  651. $this->flushData();
  652. }
  653. }
  654. /**
  655. * Same as data() method, but works not with separate $field and $value data but with array of $fields => $values
  656. * Useful if some "third-party" code returns an already prepared array of $fields => $values
  657. *
  658. * @param array $field_value array with $field => $value structure
  659. *
  660. * @return void
  661. */
  662. public function dataArr($field_value = array()) {
  663. $dataInsCnt = 0;
  664. if (!empty($field_value)) {
  665. foreach ($field_value as $field => $value) {
  666. if (!empty($field)) {
  667. $this->data[$field] = $value;
  668. $dataInsCnt++;
  669. }
  670. }
  671. }
  672. if ($dataInsCnt == 0) {
  673. $this->flushData();
  674. }
  675. }
  676. /**
  677. * Flushes current instance data set
  678. *
  679. * @return void
  680. */
  681. protected function flushData() {
  682. $this->data = array();
  683. }
  684. /**
  685. * Saves current model data fields changes to database.
  686. *
  687. * @param bool $flushParams flush all query parameters like where, order, limit and other after execution?
  688. * @param bool $fieldsBatch gather all the fields together in a single query from $this->data structure
  689. * before actually running the query to reduce the amount of subsequential DB queries for every table field
  690. *
  691. * @return void
  692. */
  693. public function save($flushParams = true, $fieldsBatch = false) {
  694. if (!empty($this->data)) {
  695. if (!empty($this->where)) {
  696. $whereString = $this->buildWhereString();
  697. if (!empty($whereString)) {
  698. //double check, yeah.
  699. if ($fieldsBatch) {
  700. $query = "UPDATE `" . $this->tableName . "` SET ";
  701. foreach ($this->data as $field => $value) {
  702. $query .= $this->escapeField($field) . "='" . $value . "', ";
  703. }
  704. $query = rtrim($query, ', ');
  705. $query .= $whereString;
  706. $this->debugLog($query);
  707. nr_query($query);
  708. } else {
  709. foreach ($this->data as $field => $value) {
  710. $query = "UPDATE `" . $this->tableName . "` SET " . $this->escapeField($field) . "='" . $value . "'" . $whereString;
  711. $this->debugLog($query);
  712. nr_query($query);
  713. }
  714. }
  715. } else {
  716. throw new Exception('MEOW_WHERE_STRUCT_EMPTY');
  717. }
  718. } else {
  719. throw new Exception('MEOW_WHERE_STRUCT_EMPTY');
  720. }
  721. } else {
  722. throw new Exception('MEOW_DATA_STRUCT_EMPTY');
  723. }
  724. if ($flushParams) {
  725. //flush instance parameters for further queries
  726. $this->destroyAllStructs();
  727. }
  728. }
  729. /**
  730. * Creates new database record for current model instance.
  731. *
  732. * @param bool $autoAiId append default NULL autoincrementing primary key?
  733. * @param bool $flushParams flush all query parameters like where, order, limit and other after execution?
  734. *
  735. * @return void
  736. */
  737. public function create($autoAiId = true, $flushParams = true) {
  738. if (!empty($this->data)) {
  739. $dataStruct = '';
  740. $dataValues = '';
  741. if ($autoAiId) {
  742. $dataStruct .= '`' . $this->defaultPk . '`,';
  743. $dataValues .= 'NULL,';
  744. }
  745. foreach ($this->data as $field => $value) {
  746. $dataStruct .= $this->escapeField($field) . ',';
  747. $dataValues .= "'" . $value . "',";
  748. }
  749. $dataStruct = substr($dataStruct, 0, -1);
  750. $dataValues = substr($dataValues, 0, -1);
  751. $query = "INSERT INTO `" . $this->tableName . "` (" . $dataStruct . ') VALUES (' . $dataValues . ')';
  752. $this->debugLog($query);
  753. nr_query($query); //RUN THAT MEOW!!!!
  754. } else {
  755. throw new Exception('MEOW_DATA_STRUCT_EMPTY');
  756. }
  757. if ($flushParams) {
  758. //flush instance parameters for further queries
  759. $this->destroyAllStructs();
  760. }
  761. }
  762. /**
  763. * Returns last ID key in table
  764. *
  765. * @return int
  766. */
  767. public function getLastId() {
  768. $query = "SELECT " . $this->escapeField($this->defaultPk) . " from `" . $this->tableName . "` ORDER BY " . $this->escapeField($this->defaultPk) . " DESC LIMIT 1";
  769. $result = simple_query($query);
  770. return ($result[$this->defaultPk]);
  771. }
  772. /**
  773. * Returns fields count in datatabase instance
  774. *
  775. * @param string $fieldsToCount field name to count results
  776. * @param bool $flushParams flush all query parameters like where, order, limit and other after execution?
  777. *
  778. * @return int
  779. */
  780. public function getFieldsCount($fieldsToCount = 'id', $flushParams = true) {
  781. $whereString = $this->buildWhereString();
  782. $raw = simple_query("SELECT COUNT(" . $this->escapeField($fieldsToCount) . ") AS `result` from `" . $this->tableName . "`" . $whereString);
  783. if ($flushParams) {
  784. //flush instance parameters for further queries
  785. $this->destroyAllStructs();
  786. }
  787. return ($raw['result']);
  788. }
  789. /**
  790. * Returns fields sum in datatabase instance
  791. *
  792. * @param string $fieldsToSum field name to retrive its sum
  793. * @param bool $flushParams flush all query parameters like where, order, limit and other after execution?
  794. *
  795. * @return int
  796. */
  797. public function getFieldsSum($fieldsToSum, $flushParams = true) {
  798. if (!empty($fieldsToSum)) {
  799. $whereString = $this->buildWhereString();
  800. $raw = simple_query("SELECT SUM(" . $this->escapeField($fieldsToSum) . ") AS `result` from `" . $this->tableName . "`" . $whereString);
  801. if ($flushParams) {
  802. //flush instance parameters for further queries
  803. $this->destroyAllStructs();
  804. }
  805. return ($raw['result']);
  806. } else {
  807. throw new Exception('MEOW_NO_FIELD_NAME');
  808. }
  809. }
  810. /**
  811. * Enables or disables debug flag
  812. *
  813. * @param bool $state object instance debug state
  814. * @param bool $deep deep debugging mode with full model dumps
  815. *
  816. * @return void
  817. */
  818. public function setDebug($state, $deep = false) {
  819. $this->debug = $state;
  820. if ($deep) {
  821. $this->deepDebug = true;
  822. }
  823. }
  824. /**
  825. * Sets default primary key for model instance
  826. *
  827. * @param string $fieldName
  828. *
  829. * @return void
  830. */
  831. public function setDefaultPk($fieldName = 'id') {
  832. $this->defaultPk = $fieldName;
  833. }
  834. /**
  835. * Trying to correctly escape fields when using table_name.field_name.
  836. *
  837. * @param string $field
  838. *
  839. * @return string
  840. */
  841. protected function escapeField($field) {
  842. $field = str_ireplace('`', '', $field);
  843. if (strpos($field, '.') !== false) {
  844. $parts = explode(".", $field);
  845. $field = "`" . $parts[0] . "`.`" . $parts[1] . "`";
  846. } else {
  847. if (trim($field != "*")) {
  848. $field = "`" . $field . "`";
  849. }
  850. }
  851. return ($field);
  852. }
  853. /**
  854. * Returns true if record with such fields values already exists in current table
  855. *
  856. * @param array $dbFilterArr represents structure, like:
  857. * array($fieldname => array('operator' => $opStr,
  858. * 'fieldval' => $fldVal,
  859. * 'cmprlogic' => 'OR'
  860. * ))
  861. * where:
  862. * $opStr - is one of the permitted in MYSQL WHERE clause, like:
  863. * >, =, <, IS NOT, LIKE, IN, etc
  864. * $fldVal - represents the field value to compare against
  865. * 'cmprlogic' - is optional and can contain 'OR' keyword to point
  866. * the need to use NyanORM's orWhere(). No need to use it for 'AND' logical clause
  867. * as it is used by DEFAULT and will be ignored
  868. * @param int $excludeRecID record ID to make exclusion on (e.g. for finding possible duplicates). For record editing purposes generally.
  869. * @param bool $flushSelectable
  870. * @param string $primaryKey name of the table's primary key field
  871. *
  872. * @return mixed|string returns the primary key value(ID usually) if rec found or empty string
  873. */
  874. public function checkRecExists($dbFilterArr, $excludeRecID = 0, $flushSelectable = true, $primaryKey = '') {
  875. $result = '';
  876. $primaryKey = (empty($primaryKey)) ? $this->defaultPk : $primaryKey;
  877. $this->selectable($primaryKey);
  878. foreach ($dbFilterArr as $dbFieldName => $fieldData) {
  879. if (!empty($fieldData['operator'])) {
  880. if (!empty($fieldData['cmprlogic']) and strtoupper($fieldData['cmprlogic']) == 'OR') {
  881. $this->orWhere($dbFieldName, $fieldData['operator'], $fieldData['fieldval']);
  882. } else {
  883. $this->where($dbFieldName, $fieldData['operator'], $fieldData['fieldval']);
  884. }
  885. }
  886. }
  887. if (!empty($excludeRecID)) {
  888. $this->where($primaryKey, '!=', $excludeRecID);
  889. }
  890. $result = $this->getAll();
  891. $result = empty($result) ? '' : $result[0][$primaryKey];
  892. if ($flushSelectable) {
  893. $this->flushSelectable();
  894. }
  895. return ($result);
  896. }
  897. /**
  898. * Simple getter for tableName property
  899. *
  900. * @param $trimQuotes
  901. *
  902. * @return string
  903. */
  904. public function getTableName($trimQuotes = false) {
  905. if ($trimQuotes) {
  906. return (trim($this->tableName, '"`\''));
  907. } else {
  908. return ($this->tableName);
  909. }
  910. }
  911. /**
  912. * Returns model's base table structure
  913. *
  914. * @param bool $fieldNamesOnly
  915. * @param bool $excludeIDField
  916. * @param bool $addLeadingTabName
  917. * @param bool $makeFieldAliases
  918. * @param string $fieldAliasSeparator
  919. *
  920. * @return array
  921. */
  922. public function getTableStructure(
  923. $fieldNamesOnly = false,
  924. $excludeIDField = false,
  925. $addLeadingTabName = false,
  926. $makeFieldAliases = false,
  927. $fieldAliasSeparator = ''
  928. ) {
  929. $result = array();
  930. $query = 'DESCRIBE ' . $this->tableName;
  931. $tableStructure = simple_queryall($query);
  932. if (!empty($tableStructure)) {
  933. if ($fieldNamesOnly) {
  934. foreach ($tableStructure as $io => $eachField) {
  935. if ($excludeIDField and $eachField['Field'] == $this->defaultPk) {
  936. continue;
  937. }
  938. // create field alias using combination of $this->tableName + $fieldAliasSeparator + $eachField['Field']?
  939. $fieldName = ($makeFieldAliases) ? $eachField['Field'] . ' AS ' . $this->tableName . $fieldAliasSeparator . $eachField['Field']
  940. : $eachField['Field'];
  941. // append leading table name with dot to the field name to explicitly distinguish fields in a query?
  942. $result[] = ($addLeadingTabName) ? $this->tableName . '.' . $fieldName : $fieldName;
  943. }
  944. } else {
  945. $result = $tableStructure;
  946. }
  947. }
  948. return ($result);
  949. }
  950. }