PageRenderTime 26ms CodeModel.GetById 12ms RepoModel.GetById 1ms app.codeStats 0ms

/public/assets/global/plugins/datatables/examples/server_side/scripts/ssp.class.php

https://gitlab.com/ealexis.t/trends
PHP | 390 lines | 190 code | 65 blank | 135 comment | 31 complexity | 8de415dfbc45f4c8eb439c4c3482b9df MD5 | raw file
  1. <?php
  2. /*
  3. * Helper functions for building a DataTables server-side processing SQL query
  4. *
  5. * The static functions in this class are just helper functions to help build
  6. * the SQL used in the DataTables demo server-side processing scripts. These
  7. * functions obviously do not represent all that can be done with server-side
  8. * processing, they are intentionally simple to show how it works. More complex
  9. * server-side processing operations will likely require a custom script.
  10. *
  11. * See http://datatables.net/usage/server-side for full details on the server-
  12. * side processing requirements of DataTables.
  13. *
  14. * @license MIT - http://datatables.net/license_mit
  15. */
  16. // REMOVE THIS BLOCK - used for DataTables test environment only!
  17. $file = $_SERVER['DOCUMENT_ROOT'].'/datatables/mysql.php';
  18. if ( is_file( $file ) ) {
  19. include( $file );
  20. }
  21. class SSP {
  22. /**
  23. * Create the data output array for the DataTables rows
  24. *
  25. * @param array $columns Column information array
  26. * @param array $data Data from the SQL get
  27. * @return array Formatted data in a row based format
  28. */
  29. static function data_output ( $columns, $data )
  30. {
  31. $out = array();
  32. for ( $i=0, $ien=count($data) ; $i<$ien ; $i++ ) {
  33. $row = array();
  34. for ( $j=0, $jen=count($columns) ; $j<$jen ; $j++ ) {
  35. $column = $columns[$j];
  36. // Is there a formatter?
  37. if ( isset( $column['formatter'] ) ) {
  38. $row[ $column['dt'] ] = $column['formatter']( $data[$i][ $column['db'] ], $data[$i] );
  39. }
  40. else {
  41. $row[ $column['dt'] ] = $data[$i][ $columns[$j]['db'] ];
  42. }
  43. }
  44. $out[] = $row;
  45. }
  46. return $out;
  47. }
  48. /**
  49. * Paging
  50. *
  51. * Construct the LIMIT clause for server-side processing SQL query
  52. *
  53. * @param array $request Data sent to server by DataTables
  54. * @param array $columns Column information array
  55. * @return string SQL limit clause
  56. */
  57. static function limit ( $request, $columns )
  58. {
  59. $limit = '';
  60. if ( isset($request['start']) && $request['length'] != -1 ) {
  61. $limit = "LIMIT ".intval($request['start']).", ".intval($request['length']);
  62. }
  63. return $limit;
  64. }
  65. /**
  66. * Ordering
  67. *
  68. * Construct the ORDER BY clause for server-side processing SQL query
  69. *
  70. * @param array $request Data sent to server by DataTables
  71. * @param array $columns Column information array
  72. * @return string SQL order by clause
  73. */
  74. static function order ( $request, $columns )
  75. {
  76. $order = '';
  77. if ( isset($request['order']) && count($request['order']) ) {
  78. $orderBy = array();
  79. $dtColumns = self::pluck( $columns, 'dt' );
  80. for ( $i=0, $ien=count($request['order']) ; $i<$ien ; $i++ ) {
  81. // Convert the column index into the column data property
  82. $columnIdx = intval($request['order'][$i]['column']);
  83. $requestColumn = $request['columns'][$columnIdx];
  84. $columnIdx = array_search( $requestColumn['data'], $dtColumns );
  85. $column = $columns[ $columnIdx ];
  86. if ( $requestColumn['orderable'] == 'true' ) {
  87. $dir = $request['order'][$i]['dir'] === 'asc' ?
  88. 'ASC' :
  89. 'DESC';
  90. $orderBy[] = '`'.$column['db'].'` '.$dir;
  91. }
  92. }
  93. $order = 'ORDER BY '.implode(', ', $orderBy);
  94. }
  95. return $order;
  96. }
  97. /**
  98. * Searching / Filtering
  99. *
  100. * Construct the WHERE clause for server-side processing SQL query.
  101. *
  102. * NOTE this does not match the built-in DataTables filtering which does it
  103. * word by word on any field. It's possible to do here performance on large
  104. * databases would be very poor
  105. *
  106. * @param array $request Data sent to server by DataTables
  107. * @param array $columns Column information array
  108. * @param array $bindings Array of values for PDO bindings, used in the
  109. * sql_exec() function
  110. * @return string SQL where clause
  111. */
  112. static function filter ( $request, $columns, &$bindings )
  113. {
  114. $globalSearch = array();
  115. $columnSearch = array();
  116. $dtColumns = self::pluck( $columns, 'dt' );
  117. if ( isset($request['search']) && $request['search']['value'] != '' ) {
  118. $str = $request['search']['value'];
  119. for ( $i=0, $ien=count($request['columns']) ; $i<$ien ; $i++ ) {
  120. $requestColumn = $request['columns'][$i];
  121. $columnIdx = array_search( $requestColumn['data'], $dtColumns );
  122. $column = $columns[ $columnIdx ];
  123. if ( $requestColumn['searchable'] == 'true' ) {
  124. $binding = self::bind( $bindings, '%'.$str.'%', PDO::PARAM_STR );
  125. $globalSearch[] = "`".$column['db']."` LIKE ".$binding;
  126. }
  127. }
  128. }
  129. // Individual column filtering
  130. for ( $i=0, $ien=count($request['columns']) ; $i<$ien ; $i++ ) {
  131. $requestColumn = $request['columns'][$i];
  132. $columnIdx = array_search( $requestColumn['data'], $dtColumns );
  133. $column = $columns[ $columnIdx ];
  134. $str = $requestColumn['search']['value'];
  135. if ( $requestColumn['searchable'] == 'true' &&
  136. $str != '' ) {
  137. $binding = self::bind( $bindings, '%'.$str.'%', PDO::PARAM_STR );
  138. $columnSearch[] = "`".$column['db']."` LIKE ".$binding;
  139. }
  140. }
  141. // Combine the filters into a single string
  142. $where = '';
  143. if ( count( $globalSearch ) ) {
  144. $where = '('.implode(' OR ', $globalSearch).')';
  145. }
  146. if ( count( $columnSearch ) ) {
  147. $where = $where === '' ?
  148. implode(' AND ', $columnSearch) :
  149. $where .' AND '. implode(' AND ', $columnSearch);
  150. }
  151. if ( $where !== '' ) {
  152. $where = 'WHERE '.$where;
  153. }
  154. return $where;
  155. }
  156. /**
  157. * Perform the SQL queries needed for an server-side processing requested,
  158. * utilising the helper functions of this class, limit(), order() and
  159. * filter() among others. The returned array is ready to be encoded as JSON
  160. * in response to an SSP request, or can be modified if needed before
  161. * sending back to the client.
  162. *
  163. * @param array $request Data sent to server by DataTables
  164. * @param array $sql_details SQL connection details - see sql_connect()
  165. * @param string $table SQL table to query
  166. * @param string $primaryKey Primary key of the table
  167. * @param array $columns Column information array
  168. * @return array Server-side processing response array
  169. */
  170. static function simple ( $request, $sql_details, $table, $primaryKey, $columns )
  171. {
  172. $bindings = array();
  173. $db = self::sql_connect( $sql_details );
  174. // Build the SQL query string from the request
  175. $limit = self::limit( $request, $columns );
  176. $order = self::order( $request, $columns );
  177. $where = self::filter( $request, $columns, $bindings );
  178. // Main query to actually get the data
  179. $data = self::sql_exec( $db, $bindings,
  180. "SELECT SQL_CALC_FOUND_ROWS `".implode("`, `", self::pluck($columns, 'db'))."`
  181. FROM `$table`
  182. $where
  183. $order
  184. $limit"
  185. );
  186. // Data set length after filtering
  187. $resFilterLength = self::sql_exec( $db,
  188. "SELECT FOUND_ROWS()"
  189. );
  190. $recordsFiltered = $resFilterLength[0][0];
  191. // Total data set length
  192. $resTotalLength = self::sql_exec( $db,
  193. "SELECT COUNT(`{$primaryKey}`)
  194. FROM `$table`"
  195. );
  196. $recordsTotal = $resTotalLength[0][0];
  197. /*
  198. * Output
  199. */
  200. return array(
  201. "draw" => intval( $request['draw'] ),
  202. "recordsTotal" => intval( $recordsTotal ),
  203. "recordsFiltered" => intval( $recordsFiltered ),
  204. "data" => self::data_output( $columns, $data )
  205. );
  206. }
  207. /**
  208. * Connect to the database
  209. *
  210. * @param array $sql_details SQL server connection details array, with the
  211. * properties:
  212. * * host - host name
  213. * * db - database name
  214. * * user - user name
  215. * * pass - user password
  216. * @return resource Database connection handle
  217. */
  218. static function sql_connect ( $sql_details )
  219. {
  220. try {
  221. $db = @new PDO(
  222. "mysql:host={$sql_details['host']};dbname={$sql_details['db']}",
  223. $sql_details['user'],
  224. $sql_details['pass'],
  225. array( PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION )
  226. );
  227. }
  228. catch (PDOException $e) {
  229. self::fatal(
  230. "An error occurred while connecting to the database. ".
  231. "The error reported by the server was: ".$e->getMessage()
  232. );
  233. }
  234. return $db;
  235. }
  236. /**
  237. * Execute an SQL query on the database
  238. *
  239. * @param resource $db Database handler
  240. * @param array $bindings Array of PDO binding values from bind() to be
  241. * used for safely escaping strings. Note that this can be given as the
  242. * SQL query string if no bindings are required.
  243. * @param string $sql SQL query to execute.
  244. * @return array Result from the query (all rows)
  245. */
  246. static function sql_exec ( $db, $bindings, $sql=null )
  247. {
  248. // Argument shifting
  249. if ( $sql === null ) {
  250. $sql = $bindings;
  251. }
  252. $stmt = $db->prepare( $sql );
  253. //echo $sql;
  254. // Bind parameters
  255. if ( is_array( $bindings ) ) {
  256. for ( $i=0, $ien=count($bindings) ; $i<$ien ; $i++ ) {
  257. $binding = $bindings[$i];
  258. $stmt->bindValue( $binding['key'], $binding['val'], $binding['type'] );
  259. }
  260. }
  261. // Execute
  262. try {
  263. $stmt->execute();
  264. }
  265. catch (PDOException $e) {
  266. self::fatal( "An SQL error occurred: ".$e->getMessage() );
  267. }
  268. // Return all
  269. return $stmt->fetchAll();
  270. }
  271. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  272. * Internal methods
  273. */
  274. /**
  275. * Throw a fatal error.
  276. *
  277. * This writes out an error message in a JSON string which DataTables will
  278. * see and show to the user in the browser.
  279. *
  280. * @param string $msg Message to send to the client
  281. */
  282. static function fatal ( $msg )
  283. {
  284. echo json_encode( array(
  285. "error" => $msg
  286. ) );
  287. exit(0);
  288. }
  289. /**
  290. * Create a PDO binding key which can be used for escaping variables safely
  291. * when executing a query with sql_exec()
  292. *
  293. * @param array &$a Array of bindings
  294. * @param * $val Value to bind
  295. * @param int $type PDO field type
  296. * @return string Bound key to be used in the SQL where this parameter
  297. * would be used.
  298. */
  299. static function bind ( &$a, $val, $type )
  300. {
  301. $key = ':binding_'.count( $a );
  302. $a[] = array(
  303. 'key' => $key,
  304. 'val' => $val,
  305. 'type' => $type
  306. );
  307. return $key;
  308. }
  309. /**
  310. * Pull a particular property from each assoc. array in a numeric array,
  311. * returning and array of the property values from each item.
  312. *
  313. * @param array $a Array to get data from
  314. * @param string $prop Property to read
  315. * @return array Array of property values
  316. */
  317. static function pluck ( $a, $prop )
  318. {
  319. $out = array();
  320. for ( $i=0, $len=count($a) ; $i<$len ; $i++ ) {
  321. $out[] = $a[$i][$prop];
  322. }
  323. return $out;
  324. }
  325. }