PageRenderTime 25ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/src/libraries/kunena/tables/kunena.php

https://github.com/xillibit/Kunena-forum
PHP | 486 lines | 285 code | 79 blank | 122 comment | 35 complexity | 4d9876587fb95554b5754de69ce5da8d MD5 | raw file
  1. <?php
  2. /**
  3. * Kunena Component
  4. *
  5. * @package Kunena.Framework
  6. * @subpackage Tables
  7. *
  8. * @copyright Copyright (C) 2008 - 2020 Kunena Team. All rights reserved.
  9. * @license https://www.gnu.org/copyleft/gpl.html GNU/GPL
  10. * @link https://www.kunena.org
  11. **/
  12. defined('_JEXEC') or die();
  13. /**
  14. * Class KunenaTable
  15. *
  16. * @since Kunena
  17. */
  18. abstract class KunenaTable extends Joomla\CMS\Table\Table
  19. {
  20. /**
  21. * @var boolean
  22. * @since Kunena
  23. */
  24. protected $_exists = false;
  25. /**
  26. * @param null $keys keys
  27. * @param bool $reset reset
  28. *
  29. * @return boolean
  30. * @since Kunena
  31. * @throws Exception
  32. */
  33. public function load($keys = null, $reset = true)
  34. {
  35. // Implement JObservableInterface: Pre-processing by observers
  36. // TODO: remove if when we're only supporting J!3.5+.
  37. if (isset($this->_observers))
  38. {
  39. $this->_observers->update('onBeforeLoad', array($keys, $reset));
  40. }
  41. // Workaround Joomla 3.2 change.
  42. // TODO: remove check when we're only supporting J!3.5+.
  43. $tbl_keys = isset($this->_tbl_keys) ? $this->_tbl_keys : (array) $this->_tbl_key;
  44. if (empty($keys))
  45. {
  46. $empty = true;
  47. $keys = array();
  48. // If empty, use the value of the current key
  49. foreach ($tbl_keys as $key)
  50. {
  51. $empty = $empty && empty($this->$key);
  52. $keys[$key] = $this->$key;
  53. }
  54. // If empty primary key there's is no need to load anything
  55. if ($empty)
  56. {
  57. return false;
  58. }
  59. }
  60. elseif (!is_array($keys))
  61. {
  62. // Load by primary key.
  63. $keyCount = count($tbl_keys);
  64. if (!$keyCount)
  65. {
  66. throw new RuntimeException('No table keys defined.');
  67. }
  68. elseif ($keyCount > 1)
  69. {
  70. throw new InvalidArgumentException('Table has multiple primary keys specified, only one primary key value provided.');
  71. }
  72. $keys = array($this->getKeyName() => $keys);
  73. }
  74. if ($reset)
  75. {
  76. $this->reset();
  77. }
  78. try
  79. {
  80. $this->_db->transactionStart();
  81. // Initialise the query.
  82. $query = $this->_db->getQuery(true)
  83. ->select('*')
  84. ->from($this->_db->quoteName($this->_tbl));
  85. $fields = array_keys($this->getProperties());
  86. foreach ($keys as $field => $value)
  87. {
  88. // Check that $field is in the table.
  89. if (!in_array($field, $fields))
  90. {
  91. throw new UnexpectedValueException(sprintf('Missing field in database: %s &#160; %s.', get_class($this), $field));
  92. }
  93. // Add the search tuple to the query.
  94. $query->Where($this->_db->quoteName($field) . ' = ' . $this->_db->quote($value));
  95. }
  96. $this->_db->setQuery($query);
  97. $row = $this->_db->loadAssoc();
  98. $this->_db->transactionCommit();
  99. }
  100. catch (Exception $e)
  101. {
  102. // Catch any database errors.
  103. $this->_db->transactionRollback();
  104. KunenaError::displayDatabaseError($e);
  105. return false;
  106. }
  107. if (empty($row))
  108. {
  109. // Check that we have a result.
  110. $result = false;
  111. }
  112. else
  113. {
  114. // Bind the object with the row and return.
  115. $result = $this->_exists = $this->bind($row);
  116. }
  117. // Implement JObservableInterface: Post-processing by observers
  118. // TODO: remove if when we're only supporting J!3.5+.
  119. if (isset($this->_observers))
  120. {
  121. $this->_observers->update('onAfterLoad', array(&$result, $row));
  122. }
  123. // Bind the object with the row and return.
  124. return $result;
  125. }
  126. /**
  127. * @param bool $updateNulls update
  128. *
  129. * @return boolean
  130. * @since Kunena
  131. * @throws Exception
  132. */
  133. public function store($updateNulls = false)
  134. {
  135. // Workaround Joomla 3.2 change.
  136. // TODO: remove check when we're only supporting J!3.5+.
  137. $k = isset($this->_tbl_keys) ? $this->_tbl_keys : (array) $this->_tbl_key;
  138. // Implement JObservableInterface: Pre-processing by observers
  139. // TODO: remove if when we're only supporting J!3.5+.
  140. if (isset($this->_observers))
  141. {
  142. $this->_observers->update('onBeforeStore', array($updateNulls, $k));
  143. }
  144. if ($this->exists())
  145. {
  146. try
  147. {
  148. $result = $this->updateObject($updateNulls);
  149. }
  150. catch (Exception $e)
  151. {
  152. throw new Exception($e->getMessage());
  153. }
  154. }
  155. else
  156. {
  157. try
  158. {
  159. $result = $this->insertObject();
  160. }
  161. catch (Exception $e)
  162. {
  163. throw new Exception($e->getMessage());
  164. }
  165. }
  166. if (!$result)
  167. {
  168. return false;
  169. }
  170. $this->_exists = true;
  171. // Implement JObservableInterface: Post-processing by observers
  172. // TODO: remove if when we're only supporting J!3.5+.
  173. if (isset($this->_observers))
  174. {
  175. $this->_observers->update('onAfterStore', array(&$result));
  176. }
  177. return true;
  178. }
  179. /**
  180. * @param null $exists exists
  181. *
  182. * @return boolean
  183. * @since Kunena
  184. */
  185. public function exists($exists = null)
  186. {
  187. $return = $this->_exists;
  188. if ($exists !== null)
  189. {
  190. $this->_exists = $exists;
  191. }
  192. return $return;
  193. }
  194. /**
  195. * Updates a row in a table based on an object's properties.
  196. *
  197. * @param boolean $nulls True to update null fields or false to ignore them.
  198. *
  199. * @return boolean True on success.
  200. *
  201. * @since Kunena
  202. * @throws Exception
  203. * @throws RuntimeException
  204. */
  205. public function updateObject($nulls = false)
  206. {
  207. $fields = array();
  208. $where = array();
  209. // Workaround Joomla 3.2 change.
  210. // TODO: remove check when we're only supporting J!3.5+.
  211. $tbl_keys = isset($this->_tbl_keys) ? $this->_tbl_keys : (array) $this->_tbl_key;
  212. // Create the base update statement.
  213. $statement = 'UPDATE ' . $this->_db->quoteName($this->_tbl) . ' SET %s WHERE %s';
  214. // Iterate over the object variables to build the query fields/value pairs.
  215. foreach (get_object_vars($this) as $k => $v)
  216. {
  217. // Only process scalars that are not internal fields.
  218. if (is_array($v) || is_object($v) || $k[0] == '_')
  219. {
  220. continue;
  221. }
  222. // Set the primary key to the WHERE clause instead of a field to update.
  223. if (in_array($k, $tbl_keys))
  224. {
  225. $where[] = $this->_db->quoteName($k) . '=' . $this->_db->quote($v);
  226. continue;
  227. }
  228. // Prepare and sanitize the fields and values for the database query.
  229. if ($v === null)
  230. {
  231. // If the value is null and we want to update nulls then set it.
  232. if ($nulls)
  233. {
  234. $val = 'NULL';
  235. }
  236. // If the value is null and we do not want to update nulls then ignore this field.
  237. else
  238. {
  239. continue;
  240. }
  241. }
  242. // The field is not null so we prep it for update.
  243. else
  244. {
  245. $val = $this->_db->quote($v);
  246. }
  247. // Add the field to be updated.
  248. $fields[] = $this->_db->quoteName($k) . '=' . $val;
  249. }
  250. // We don't have any fields to update.
  251. if (empty($fields))
  252. {
  253. return true;
  254. }
  255. try
  256. {
  257. $this->_db->transactionStart();
  258. // Set the query and execute the update.
  259. $this->_db->setQuery(sprintf($statement, implode(",", $fields), implode(' AND ', $where)));
  260. $this->_db->execute();
  261. $this->_db->transactionCommit();
  262. }
  263. catch (Exception $e)
  264. {
  265. // Catch any database errors.
  266. $this->_db->transactionRollback();
  267. throw new Exception($e->getMessage());
  268. return false;
  269. }
  270. return true;
  271. }
  272. /**
  273. * Inserts a row into a table based on an object's properties.
  274. *
  275. * @return boolean True on success.
  276. *
  277. * @since Kunena
  278. * @throws Exception
  279. * @throws RuntimeException
  280. */
  281. protected function insertObject()
  282. {
  283. $fields = array();
  284. $values = array();
  285. // Workaround Joomla 3.2 change.
  286. // TODO: remove check when we're only supporting J!3.5+.
  287. $tbl_keys = isset($this->_tbl_keys) ? $this->_tbl_keys : (array) $this->_tbl_key;
  288. // Iterate over the object variables to build the query fields and values.
  289. foreach (get_object_vars($this) as $k => $v)
  290. {
  291. // Only process non-null scalars.
  292. if (is_array($v) || is_object($v) || $v === null)
  293. {
  294. continue;
  295. }
  296. // Ignore any internal fields.
  297. if ($k[0] == '_')
  298. {
  299. continue;
  300. }
  301. // Prepare and sanitize the fields and values for the database query.
  302. $fields[] = $this->_db->quoteName($k);
  303. $values[] = $this->_db->quote($v);
  304. }
  305. try
  306. {
  307. $this->_db->transactionStart();
  308. // Create the base insert statement.
  309. $query = $this->_db->getQuery(true)
  310. ->insert($this->_db->quoteName($this->_tbl))
  311. ->columns($fields)
  312. ->values(implode(',', $values));
  313. // Set the query and execute the insert.
  314. $this->_db->setQuery($query);
  315. if (!$this->_db->execute())
  316. {
  317. return false;
  318. }
  319. // Update the primary key if it exists.
  320. $id = $this->_db->insertid();
  321. $this->_db->transactionCommit();
  322. }
  323. catch (Exception $e)
  324. {
  325. // Catch any database errors.
  326. $this->_db->transactionRollback();
  327. throw new Exception($e->getMessage());
  328. return false;
  329. }
  330. if (count($tbl_keys) == 1 && $id)
  331. {
  332. $key = reset($tbl_keys);
  333. $this->$key = $id;
  334. }
  335. return true;
  336. }
  337. /**
  338. * @param null $pk pk
  339. *
  340. * @return boolean
  341. * @since Kunena
  342. * @throws Exception
  343. */
  344. public function delete($pk = null)
  345. {
  346. // Workaround Joomla 3.2 change.
  347. // TODO: remove check when we're only supporting J!3.5+.
  348. $tbl_keys = isset($this->_tbl_keys) ? $this->_tbl_keys : (array) $this->_tbl_key;
  349. if (is_null($pk))
  350. {
  351. $pk = array();
  352. foreach ($tbl_keys as $key)
  353. {
  354. $pk[$key] = $this->$key;
  355. }
  356. }
  357. elseif (!is_array($pk))
  358. {
  359. $key = reset($tbl_keys);
  360. $pk = array($key => $pk);
  361. }
  362. foreach ($tbl_keys as $key)
  363. {
  364. $pk[$key] = is_null($pk[$key]) ? $this->$key : $pk[$key];
  365. if ($pk[$key] === null)
  366. {
  367. throw new UnexpectedValueException('Null primary key not allowed.');
  368. }
  369. $this->$key = $pk[$key];
  370. }
  371. // Implement JObservableInterface: Pre-processing by observers
  372. // TODO: remove if when we're only supporting J!3.5+.
  373. if (isset($this->_observers))
  374. {
  375. $this->_observers->update('onBeforeDelete', array($pk));
  376. }
  377. // Check for a database error.
  378. try
  379. {
  380. $this->_db->transactionStart();
  381. // Delete the row by primary key.
  382. $query = $this->_db->getQuery(true)
  383. ->delete($this->_db->quoteName($this->_tbl));
  384. foreach ($pk as $key => $value)
  385. {
  386. $query->where("{$this->_db->quoteName($key)} = {$this->_db->quote($value)}");
  387. }
  388. $this->_db->setQuery($query);
  389. $this->_db->execute();
  390. $this->_db->transactionCommit();
  391. }
  392. catch (Exception $e)
  393. {
  394. // Catch any database errors.
  395. $this->_db->transactionRollback();
  396. throw new Exception($e->getMessage());
  397. return false;
  398. }
  399. // Implement JObservableInterface: Post-processing by observers
  400. // TODO: remove if when we're only supporting J!3.5+.
  401. if (isset($this->_observers))
  402. {
  403. $this->_observers->update('onAfterDelete', array($pk));
  404. }
  405. return true;
  406. }
  407. }