Advertisement
Guest User

Untitled

a guest
Oct 28th, 2021
127
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 17.42 KB | None | 0 0
  1. <?php
  2.  
  3. /*
  4. * Helper functions for building a DataTables server-side processing SQL query
  5. *
  6. * The static functions in this class are just helper functions to help build
  7. * the SQL used in the DataTables demo server-side processing scripts. These
  8. * functions obviously do not represent all that can be done with server-side
  9. * processing, they are intentionally simple to show how it works. More complex
  10. * server-side processing operations will likely require a custom script.
  11. *
  12. * See http://datatables.net/usage/server-side for full details on the server-
  13. * side processing requirements of DataTables.
  14. *
  15. * @license MIT - http://datatables.net/license_mit
  16. */
  17.  
  18. class SSP
  19. {
  20. /**
  21. * Create the data output array for the DataTables rows
  22. *
  23. * @param array $columns Column information array
  24. * @param array $data Data from the SQL get
  25. * @return array Formatted data in a row based format
  26. */
  27. static function data_output($columns, $data)
  28. {
  29. $out = array();
  30.  
  31. for ($i = 0, $ien = count($data); $i < $ien; $i++) {
  32. $row = array();
  33.  
  34. for ($j = 0, $jen = count($columns); $j < $jen; $j++) {
  35. $column = $columns[$j];
  36.  
  37. // Is there a formatter?
  38. if (isset($column['formatter'])) {
  39. $row[$column['dt']] = $column['formatter']($data[$i][$column['db']], $data[$i]);
  40. } else {
  41. $row[$column['dt']] = $data[$i][$columns[$j]['db']];
  42. }
  43. }
  44.  
  45. $out[] = $row;
  46. }
  47.  
  48. return $out;
  49. }
  50.  
  51.  
  52. /**
  53. * Database connection
  54. *
  55. * Obtain an PHP PDO connection from a connection details array
  56. *
  57. * @param array $conn SQL connection details. The array should have
  58. * the following properties
  59. * * host - host name
  60. * * db - database name
  61. * * user - user name
  62. * * pass - user password
  63. * @return resource PDO connection
  64. */
  65. static function db($conn)
  66. {
  67. if (is_array($conn)) {
  68. return self::sql_connect($conn);
  69. }
  70.  
  71. return $conn;
  72. }
  73.  
  74.  
  75. /**
  76. * Paging
  77. *
  78. * Construct the LIMIT clause for server-side processing SQL query
  79. *
  80. * @param array $request Data sent to server by DataTables
  81. * @param array $columns Column information array
  82. * @return string SQL limit clause
  83. */
  84. static function limit($request, $columns)
  85. {
  86. $limit = '';
  87.  
  88. if (isset($request['start']) && $request['length'] != -1) {
  89. // MYSQL
  90. // $limit = "LIMIT ".intval($request['start']).", ".intval($request['length']);
  91. // SQLSRV EDITS
  92. $limit = "OFFSET " . intval($request['start']) . " ROWS FETCH NEXT " . intval($request['length']) . " ROWS ONLY";
  93. }
  94.  
  95. return $limit;
  96. }
  97.  
  98.  
  99. /**
  100. * Ordering
  101. *
  102. * Construct the ORDER BY clause for server-side processing SQL query
  103. *
  104. * @param array $request Data sent to server by DataTables
  105. * @param array $columns Column information array
  106. * @return string SQL order by clause
  107. */
  108. static function order($request, $columns)
  109. {
  110. $order = '';
  111.  
  112. if (isset($request['order']) && count($request['order'])) {
  113. $orderBy = array();
  114. $dtColumns = self::pluck($columns, 'dt');
  115.  
  116. for ($i = 0, $ien = count($request['order']); $i < $ien; $i++) {
  117. // Convert the column index into the column data property
  118. $columnIdx = intval($request['order'][$i]['column']);
  119. $requestColumn = $request['columns'][$columnIdx];
  120.  
  121. $columnIdx = array_search($requestColumn['data'], $dtColumns);
  122. $column = $columns[$columnIdx];
  123.  
  124. if ($requestColumn['orderable'] == 'true') {
  125. $dir = $request['order'][$i]['dir'] === 'asc' ?
  126. 'ASC' :
  127. 'DESC';
  128.  
  129. $orderBy[] = '' . $column['db'] . ' ' . $dir;
  130. }
  131. }
  132.  
  133. if (count($orderBy)) {
  134. $order = 'ORDER BY ' . implode(', ', $orderBy);
  135. }
  136. }
  137.  
  138. return $order;
  139. }
  140.  
  141.  
  142.  
  143. /**
  144. * Searching / Filtering
  145. *
  146. * Construct the WHERE clause for server-side processing SQL query.
  147. *
  148. * NOTE this does not match the built-in DataTables filtering which does it
  149. * word by word on any field. It's possible to do here performance on large
  150. * databases would be very poor
  151. *
  152. * @param array $request Data sent to server by DataTables
  153. * @param array $columns Column information array
  154. * @param array $bindings Array of values for PDO bindings, used in the
  155. * sql_exec() function
  156. * @return string SQL where clause
  157. */
  158. static function filter ( $request, $columns, &$bindings )
  159. {
  160. $globalSearch = array();
  161. $columnSearch = array();
  162. $dtColumns = self::pluck( $columns, 'dt' );
  163.  
  164. if ( isset($request['search']) && $request['search']['value'] != '' ) {
  165. $str = $request['search']['value'];
  166.  
  167. for ( $i=0, $ien=count($request['columns']) ; $i<$ien ; $i++ ) {
  168. $requestColumn = $request['columns'][$i];
  169. $columnIdx = array_search( $requestColumn['data'], $dtColumns );
  170. $column = $columns[ $columnIdx ];
  171.  
  172. if ( $requestColumn['searchable'] == 'true' ) {
  173. $binding = self::bind( $bindings, '%'.$str.'%', PDO::PARAM_STR );
  174. $globalSearch[] = "".$column['db']." LIKE ".$binding;
  175. }
  176. }
  177. }
  178.  
  179. // Individual column filtering
  180. if ( isset( $request['columns'] ) ) {
  181. for ( $i=0, $ien=count($request['columns']) ; $i<$ien ; $i++ ) {
  182. $requestColumn = $request['columns'][$i];
  183. $columnIdx = array_search( $requestColumn['data'], $dtColumns );
  184. $column = $columns[ $columnIdx ];
  185.  
  186. $str = $requestColumn['search']['value'];
  187.  
  188. if ( $requestColumn['searchable'] == 'true' &&
  189. $str != '' ) {
  190. $binding = self::bind( $bindings, '%'.$str.'%', PDO::PARAM_STR );
  191. $columnSearch[] = "".$column['db']." LIKE ".$binding;
  192. }
  193. }
  194. }
  195.  
  196. // Combine the filters into a single string
  197. $where = '';
  198.  
  199. if ( count( $globalSearch ) ) {
  200. $where = '('.implode(' OR ', $globalSearch).')';
  201. }
  202.  
  203. if ( count( $columnSearch ) ) {
  204. $where = $where === '' ?
  205. implode(' AND ', $columnSearch) :
  206. $where .' AND '. implode(' AND ', $columnSearch);
  207. }
  208.  
  209. if ( $where !== '' ) {
  210. $where = 'WHERE '.$where;
  211. }
  212.  
  213.  
  214.  
  215. return $where;
  216. }
  217.  
  218.  
  219. /**
  220. * Perform the SQL queries needed for an server-side processing requested,
  221. * utilising the helper functions of this class, limit(), order() and
  222. * filter() among others. The returned array is ready to be encoded as JSON
  223. * in response to an SSP request, or can be modified if needed before
  224. * sending back to the client.
  225. *
  226. * @param array $request Data sent to server by DataTables
  227. * @param array|PDO $conn PDO connection resource or connection parameters array
  228. * @param string $table SQL table to query
  229. * @param string $primaryKey Primary key of the table
  230. * @param array $columns Column information array
  231. * @return array Server-side processing response array
  232. */
  233. static function simple($request, $conn, $table, $primaryKey, $columns)
  234. {
  235. $bindings = array();
  236. $db = self::db($conn);
  237.  
  238. // Build the SQL query string from the request
  239. $limit = self::limit($request, $columns);
  240. $order = self::order($request, $columns);
  241. $where = self::filter($request, $columns, $bindings);
  242.  
  243. // Main query to actually get the data
  244. $data = self::sql_exec(
  245. $db,
  246. $bindings,
  247. "SELECT " . implode(", ", self::pluck($columns, 'db')) . "
  248. FROM $table
  249. $where
  250. $order
  251. $limit"
  252. );
  253.  
  254. // Data set length after filtering
  255. $resFilterLength = self::sql_exec(
  256. $db,
  257. $bindings,
  258. "SELECT COUNT({$primaryKey})
  259. FROM $table
  260. $where"
  261. );
  262. $recordsFiltered = $resFilterLength[0][0];
  263.  
  264. // Total data set length
  265. $resTotalLength = self::sql_exec(
  266. $db,
  267. "SELECT COUNT({$primaryKey})
  268. FROM $table"
  269. );
  270. $recordsTotal = $resTotalLength[0][0];
  271.  
  272. /*
  273. * Output
  274. */
  275. return array(
  276. "draw" => isset($request['draw']) ?
  277. intval($request['draw']) :
  278. 0,
  279. "recordsTotal" => intval($recordsTotal),
  280. "recordsFiltered" => intval($recordsFiltered),
  281. "data" => self::data_output($columns, $data)
  282. );
  283. }
  284.  
  285.  
  286. /**
  287. * The difference between this method and the simple one, is that you can
  288. * apply additional where conditions to the SQL queries. These can be in
  289. * one of two forms:
  290. *
  291. * * 'Result condition' - This is applied to the result set, but not the
  292. * overall paging information query - i.e. it will not effect the number
  293. * of records that a user sees they can have access to. This should be
  294. * used when you want apply a filtering condition that the user has sent.
  295. * * 'All condition' - This is applied to all queries that are made and
  296. * reduces the number of records that the user can access. This should be
  297. * used in conditions where you don't want the user to ever have access to
  298. * particular records (for example, restricting by a login id).
  299. *
  300. * @param array $request Data sent to server by DataTables
  301. * @param array|PDO $conn PDO connection resource or connection parameters array
  302. * @param string $table SQL table to query
  303. * @param string $primaryKey Primary key of the table
  304. * @param array $columns Column information array
  305. * @param string $whereResult WHERE condition to apply to the result set
  306. * @param string $whereAll WHERE condition to apply to all queries
  307. * @return array Server-side processing response array
  308. */
  309. static function complex($request, $conn, $table, $primaryKey, $columns, $whereResult = null, $whereAll = null)
  310. {
  311. $bindings = array();
  312. $db = self::db($conn);
  313. $localWhereResult = array();
  314. $localWhereAll = array();
  315. $whereAllSql = '';
  316.  
  317. // Build the SQL query string from the request
  318. $limit = self::limit($request, $columns);
  319. $order = self::order($request, $columns);
  320. $where = self::filter($request, $columns, $bindings);
  321.  
  322. $whereResult = self::_flatten($whereResult);
  323. $whereAll = self::_flatten($whereAll);
  324. echo $whereResult;
  325.  
  326. if ($whereResult) {
  327. $where = $where ?
  328. $where . ' AND ' . $whereResult :
  329. 'WHERE ' . $whereResult;
  330. }
  331.  
  332. if ($whereAll) {
  333. $where = $where ?
  334. $where . ' AND ' . $whereAll :
  335. 'WHERE ' . $whereAll;
  336.  
  337. $whereAllSql = 'WHERE ' . $whereAll;
  338. }
  339.  
  340. // Main query to actually get the data
  341. $data = self::sql_exec(
  342. $db,
  343. $bindings,
  344. "SELECT " . implode(", ", self::pluck($columns, 'db')) . "
  345. FROM $table
  346. $where
  347. $order
  348. $limit"
  349. );
  350.  
  351. // Data set length after filtering
  352. $resFilterLength = self::sql_exec(
  353. $db,
  354. $bindings,
  355. "SELECT COUNT({$primaryKey})
  356. FROM $table
  357. $where"
  358. );
  359. $recordsFiltered = $resFilterLength[0][0];
  360.  
  361. // Total data set length
  362. $resTotalLength = self::sql_exec(
  363. $db,
  364. $bindings,
  365. "SELECT COUNT({$primaryKey})
  366. FROM $table " .
  367. $whereAllSql
  368. );
  369. $recordsTotal = $resTotalLength[0][0];
  370.  
  371. /*
  372. * Output
  373. */
  374. return array(
  375. "draw" => isset($request['draw']) ?
  376. intval($request['draw']) :
  377. 0,
  378. "recordsTotal" => intval($recordsTotal),
  379. "recordsFiltered" => intval($recordsFiltered),
  380. "data" => self::data_output($columns, $data)
  381. );
  382. }
  383.  
  384.  
  385.  
  386. /**
  387. * Connect to the database
  388. *
  389. * @param array $sql_details SQL server connection details array, with the
  390. * properties:
  391. * * host - host name
  392. * * db - database name
  393. * * user - user name
  394. * * pass - user password
  395. * @return resource Database connection handle
  396. */
  397. static function sql_connect($sql_details)
  398. {
  399.  
  400. // MYSQL
  401. /*try {
  402. $db = @new PDO(
  403. "mysql:host={$sql_details['host']};dbname={$sql_details['db']}",
  404. $sql_details['user'],
  405. $sql_details['pass'],
  406. array( PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION )
  407. );
  408. }*/
  409.  
  410. // SQLSRV EDITS
  411. try {
  412. $db = @new PDO("sqlsrv:Server={$sql_details['host']};Database={$sql_details['db']}", "{$sql_details['user']}", "{$sql_details['pass']}");
  413.  
  414.  
  415. $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  416. } catch (PDOException $e) {
  417. self::fatal(
  418. "An error occurred while connecting to the database. " .
  419. "The error reported by the server was: " . $e->getMessage()
  420. );
  421. }
  422.  
  423. return $db;
  424. }
  425.  
  426.  
  427. /**
  428. * Execute an SQL query on the database
  429. *
  430. * @param resource $db Database handler
  431. * @param array $bindings Array of PDO binding values from bind() to be
  432. * used for safely escaping strings. Note that this can be given as the
  433. * SQL query string if no bindings are required.
  434. * @param string $sql SQL query to execute.
  435. * @return array Result from the query (all rows)
  436. */
  437. static function sql_exec($db, $bindings, $sql = null)
  438. {
  439. // Argument shifting
  440. if ($sql === null) {
  441. $sql = $bindings;
  442. }
  443.  
  444. $stmt = $db->prepare($sql);
  445. //echo $sql;
  446.  
  447. // Bind parameters
  448. if (is_array($bindings)) {
  449. for ($i = 0, $ien = count($bindings); $i < $ien; $i++) {
  450. $binding = $bindings[$i];
  451. $stmt->bindValue($binding['key'], $binding['val'], $binding['type']);
  452. }
  453. }
  454.  
  455. // Execute
  456. try {
  457. $stmt->execute();
  458. } catch (PDOException $e) {
  459. self::fatal("An SQL error occurred: " . $e->getMessage());
  460. }
  461.  
  462. // Return all
  463. return $stmt->fetchAll(PDO::FETCH_BOTH);
  464. }
  465.  
  466.  
  467. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  468. * Internal methods
  469. */
  470.  
  471. /**
  472. * Throw a fatal error.
  473. *
  474. * This writes out an error message in a JSON string which DataTables will
  475. * see and show to the user in the browser.
  476. *
  477. * @param string $msg Message to send to the client
  478. */
  479. static function fatal($msg)
  480. {
  481. echo json_encode(array(
  482. "error" => $msg
  483. ));
  484.  
  485. exit(0);
  486. }
  487.  
  488. /**
  489. * Create a PDO binding key which can be used for escaping variables safely
  490. * when executing a query with sql_exec()
  491. *
  492. * @param array &$a Array of bindings
  493. * @param * $val Value to bind
  494. * @param int $type PDO field type
  495. * @return string Bound key to be used in the SQL where this parameter
  496. * would be used.
  497. */
  498. static function bind(&$a, $val, $type)
  499. {
  500. $key = ':binding_' . count($a);
  501.  
  502. $a[] = array(
  503. 'key' => $key,
  504. 'val' => $val,
  505. 'type' => $type
  506. );
  507.  
  508. return $key;
  509. }
  510.  
  511.  
  512. /**
  513. * Pull a particular property from each assoc. array in a numeric array,
  514. * returning and array of the property values from each item.
  515. *
  516. * @param array $a Array to get data from
  517. * @param string $prop Property to read
  518. * @return array Array of property values
  519. */
  520. static function pluck($a, $prop)
  521. {
  522. $out = array();
  523.  
  524. for ($i = 0, $len = count($a); $i < $len; $i++) {
  525. $out[] = $a[$i][$prop];
  526. }
  527.  
  528. return $out;
  529. }
  530.  
  531.  
  532. /**
  533. * Return a string from an array or a string
  534. *
  535. * @param array|string $a Array to join
  536. * @param string $join Glue for the concatenation
  537. * @return string Joined string
  538. */
  539. static function _flatten($a, $join = ' AND ')
  540. {
  541. if (!$a) {
  542. return '';
  543. } else if ($a && is_array($a)) {
  544. return implode($join, $a);
  545. }
  546. return $a;
  547. }
  548. }
  549.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement