PageRenderTime 67ms CodeModel.GetById 32ms RepoModel.GetById 0ms app.codeStats 0ms

/hylax/lib/SQL/Attributes.php

https://github.com/imr/horde
PHP | 303 lines | 167 code | 31 blank | 105 comment | 27 complexity | 8e8f3d2b6190d0aa2c7312fda33d4e1e MD5 | raw file
  1. <?php
  2. /**
  3. * This class provides attributes methods for any existing SQL class.
  4. *
  5. * Copyright 1999-2014 Horde LLC (http://www.horde.org/)
  6. *
  7. * See the enclosed file COPYING for license information (LGPL). If you
  8. * did not receive this file, see http://www.horde.org/licenses/lgpl21.
  9. *
  10. * @author Chuck Hagenbuch <chuck@horde.org>
  11. * @package Hylax
  12. */
  13. class Hylax_SQL_Attributes {
  14. /**
  15. * The PEAR::DB object to run queries with.
  16. *
  17. * @var DB
  18. */
  19. var $_db;
  20. /**
  21. * Parameters to use when generating queries:
  22. * id_column - The primary id column to use in joins.
  23. * primary_table - The main table name.
  24. * attribute_table - The table that the attributes are stored in.
  25. *
  26. * @var array
  27. */
  28. var $_params = array();
  29. /**
  30. * The number of copies of the attributes table that we need to join on in
  31. * the current query.
  32. *
  33. * @var integer
  34. */
  35. var $_table_count = 1;
  36. /**
  37. * Constructor.
  38. *
  39. * @param DB $dbh A PEAR::DB object.
  40. * @param array $params The id column, table names, etc.
  41. */
  42. function Hylax_SQL_Attributes($dbh, $params)
  43. {
  44. $this->_db = $dbh;
  45. $this->_params = $params;
  46. }
  47. /**
  48. * Returns all attributes for a given id or multiple ids.
  49. *
  50. * @param integer | array $id The id to fetch or an array of ids.
  51. *
  52. * @return array A hash of attributes, or a multi-level hash
  53. * of ids => their attributes.
  54. */
  55. function getAttributes($id)
  56. {
  57. if (is_array($id)) {
  58. $query = sprintf('SELECT %1$s, attribute_name as name, attribute_key as "key", attribute_value as value FROM %2$s WHERE %1$s IN (%3$s)',
  59. $this->_params['id_column'],
  60. $this->_params['attribute_table'],
  61. implode(', ', $id));
  62. Horde::log('SQL Query by Hylax_SQL_Attributes::getAttributes(): ' . $query, 'DEBUG');
  63. $rows = $this->_db->getAll($query, DB_FETCHMODE_ASSOC);
  64. if (is_a($rows, 'PEAR_Error')) {
  65. return $rows;
  66. }
  67. $id_column = $this->_params['id_column'];
  68. $data = array();
  69. foreach ($rows as $row) {
  70. if (empty($data[$row[$id_column]])) {
  71. $data[$row[$id_column]] = array();
  72. }
  73. $data[$row[$id_column]][] = array('name' => $row['name'],
  74. 'key' => $row['key'],
  75. 'value' => $row['value']);
  76. }
  77. return $data;
  78. } else {
  79. $query = sprintf('SELECT %1$s, attribute_name as name, attribute_key as "key", attribute_value as value FROM %2$s WHERE %1$s = %3$s',
  80. $this->_params['id_column'],
  81. $this->_params['attribute_table'],
  82. (int)$id);
  83. Horde::log('SQL Query by Hylax_SQL_Attributes::getAttributes(): ' . $query, 'DEBUG');
  84. return $this->_db->getAll($query, DB_FETCHMODE_ASSOC);
  85. }
  86. }
  87. /**
  88. * Return a set of ids based on a set of attribute criteria.
  89. *
  90. * @param array $criteria The array of criteria. Example:
  91. * $criteria['OR'] = array(
  92. * array('field' => 'name',
  93. * 'op' => '=',
  94. * 'test' => 'foo'),
  95. * array('field' => 'name',
  96. * 'op' => '=',
  97. * 'test' => 'bar'));
  98. * This would return all ids for which the field
  99. * attribute_name is either 'foo' or 'bar'.
  100. */
  101. function getByAttributes($criteria)
  102. {
  103. if (!count($criteria)) {
  104. return array();
  105. }
  106. /* Build the query. */
  107. $this->_table_count = 1;
  108. $query = '';
  109. foreach ($criteria as $key => $vals) {
  110. if ($key == 'OR' || $key == 'AND') {
  111. if (!empty($query)) {
  112. $query .= ' ' . $key . ' ';
  113. }
  114. $query .= '(' . $this->_buildAttributeQuery($key, $vals) . ')';
  115. }
  116. }
  117. /* Build the FROM/JOIN clauses. */
  118. $joins = array();
  119. $pairs = array();
  120. for ($i = 1; $i <= $this->_table_count; $i++) {
  121. $joins[] = sprintf('LEFT JOIN %1$s a%2$s ON a%2$s.%3$s = m.%3$s',
  122. $this->_params['attribute_table'],
  123. $i,
  124. $this->_params['id_column']);
  125. $pairs[] = 'AND a1.attribute_name = a' . $i . '.attribute_name';
  126. }
  127. $joins = implode(' ', $joins);
  128. $pairs = implode(' ', $pairs);
  129. $query = sprintf('SELECT DISTINCT a1.%s FROM %s m %s WHERE %s %s',
  130. $this->_params['id_column'],
  131. $this->_params['primary_table'],
  132. $joins,
  133. $query,
  134. $pairs);
  135. Horde::log('SQL Query by Hylax_SQL_Attributes::getByAttributes(): ' . $query, 'DEBUG');
  136. return $this->_db->getCol($query);
  137. }
  138. /**
  139. * Given a new attribute set and an id, insert each into the DB. If
  140. * anything fails in here, rollback the transaction, return the relevant
  141. * error and bail out.
  142. *
  143. * @param integer $id The id of the record for which attributes are
  144. * being inserted.
  145. * @param array $attributes An hash containing the attributes.
  146. */
  147. function insertAttributes($id, $attributes)
  148. {
  149. foreach ($attributes as $attr) {
  150. $query = 'INSERT INTO ' . $this->_params['attribute_table'] .
  151. ' (' . $this->_params['id_column'] . ', attribute_name,' .
  152. ' attribute_key, attribute_value) VALUES (?, ?, ?, ?)';
  153. $values = array((int)$id,
  154. $attr['name'],
  155. $attr['key'],
  156. $attr['value']);
  157. Horde::log('SQL Query by Hylax_SQL_Attributes::insertAttributes(): ' . $query, 'DEBUG');
  158. $result = $this->_db->query($query, $values);
  159. if (is_a($result, 'PEAR_Error')) {
  160. $this->_db->rollback();
  161. $this->_db->autoCommit(true);
  162. return $result;
  163. }
  164. }
  165. /* Commit the transaction, and turn autocommit back on. */
  166. $result = $this->_db->commit();
  167. $this->_db->autoCommit(true);
  168. }
  169. /**
  170. * Given an id, delete all attributes for that id from the
  171. * attributes table.
  172. *
  173. * @param integer $id The id of the record for which attributes are being
  174. * deleted.
  175. */
  176. function deleteAttributes($id)
  177. {
  178. /* Delete attributes. */
  179. $query = sprintf('DELETE FROM %s WHERE %s = %s',
  180. $this->_params['attribute_table'],
  181. $this->_params['id_column'],
  182. (int)$id);
  183. Horde::log('SQL Query by Hylax_SQL_Attributes::deleteAttributes(): ' . $query, 'DEBUG');
  184. $result = $this->_db->query($query);
  185. if (is_a($result, 'PEAR_Error')) {
  186. return $result;
  187. }
  188. return true;
  189. }
  190. /**
  191. * Given an id, update all attributes for that id in the attributes table
  192. * with the new attributes.
  193. *
  194. * @param integer $id The id of the record for which attributes are
  195. * being deleted.
  196. * @param array $attributes An hash containing the attributes.
  197. */
  198. function updateAttributes($id, $attributes)
  199. {
  200. /* Delete the old attributes. */
  201. $result = $this->deleteAttributes($id);
  202. if (is_a($result, 'PEAR_Error')) {
  203. return $result;
  204. }
  205. /* Insert the new attribute set. */
  206. $result = $this->insertAttributes($id, $attributes);
  207. return $result;
  208. }
  209. /**
  210. * Build a piece of an attribute query.
  211. *
  212. * @param string $glue The glue to join the criteria (OR/AND).
  213. * @param array $criteria The array of criteria.
  214. * @param boolean $join Should we join on a clean attributes table?
  215. *
  216. * @return string An SQL fragment.
  217. */
  218. function _buildAttributeQuery($glue, $criteria, $join = false)
  219. {
  220. /* Initialize the clause that we're building. */
  221. $clause = '';
  222. /* Get the table alias to use for this set of criteria. */
  223. if ($join) {
  224. $alias = $this->_getAlias(true);
  225. } else {
  226. $alias = $this->_getAlias();
  227. }
  228. foreach ($criteria as $key => $vals) {
  229. if (!empty($vals['OR']) || !empty($vals['AND'])) {
  230. if (!empty($clause)) {
  231. $clause .= ' ' . $glue . ' ';
  232. }
  233. $clause .= '(' . $this->_buildAttributeQuery($glue, $vals) . ')';
  234. } elseif (!empty($vals['JOIN'])) {
  235. if (!empty($clause)) {
  236. $clause .= ' ' . $glue . ' ';
  237. }
  238. $clause .= $this->_buildAttributeQuery($glue, $vals['JOIN'], true);
  239. } else {
  240. if (isset($vals['field'])) {
  241. if (!empty($clause)) {
  242. $clause .= ' ' . $glue . ' ';
  243. }
  244. $clause .= Horde_Sql::buildClause($this->_db, $alias . '.attribute_' . $vals['field'], $vals['op'], $vals['test']);
  245. } else {
  246. foreach ($vals as $test) {
  247. if (!empty($clause)) {
  248. $clause .= ' ' . $key . ' ';
  249. }
  250. $clause .= Horde_Sql::buildClause($this->_db, $alias . '.attribute_' . $test['field'], $test['op'], $test['test']);
  251. }
  252. }
  253. }
  254. }
  255. return $clause;
  256. }
  257. /**
  258. * Get an alias to an attributes table, incrementing it if
  259. * necessary.
  260. *
  261. * @param boolean $increment Increment the alias count? Defaults to false.
  262. */
  263. function _getAlias($increment = false)
  264. {
  265. static $seen = array();
  266. if ($increment && !empty($seen[$this->_table_count])) {
  267. $this->_table_count++;
  268. }
  269. $seen[$this->_table_count] = true;
  270. return 'a' . $this->_table_count;
  271. }
  272. }