Advertisement
Guest User

Postgres datatables server-side

a guest
Aug 6th, 2014
1,098
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 10.66 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.  
  19. // REMOVE THIS BLOCK - used for DataTables test environment only!
  20. /*
  21. $file = $_SERVER['DOCUMENT_ROOT'].'/datatables/mysql.php';
  22. if ( is_file( $file ) ) {
  23.     include( $file );
  24. }
  25. */
  26.  
  27.  
  28. class SSP {
  29.     /**
  30.      * Create the data output array for the DataTables rows
  31.      *
  32.      *  @param  array $columns Column information array
  33.      *  @param  array $data    Data from the SQL get
  34.      *  @return array          Formatted data in a row based format
  35.      */
  36.     static function data_output ( $columns, $data )
  37.     {
  38.         $out = array();
  39.  
  40.         for ( $i=0, $ien=count($data) ; $i<$ien ; $i++ ) {
  41.             $row = array();
  42.  
  43.             for ( $j=0, $jen=count($columns) ; $j<$jen ; $j++ ) {
  44.                 $column = $columns[$j];
  45.  
  46.                 // Is there a formatter?
  47.                 if ( isset( $column['formatter'] ) ) {
  48.                     $row[ $column['dt'] ] = $column['formatter']( $data[$i][ $column['db'] ], $data[$i] );
  49.                 }
  50.                 else {
  51.                     $row[ $column['dt'] ] = $data[$i][ $columns[$j]['db'] ];
  52.                 }
  53.             }
  54.  
  55.             $out[] = $row;
  56.         }
  57.  
  58.         return $out;
  59.     }
  60.  
  61.  
  62.     /**
  63.      * Paging
  64.      *
  65.      * Construct the LIMIT clause for server-side processing SQL query
  66.      *
  67.      *  @param  array $request Data sent to server by DataTables
  68.      *  @param  array $columns Column information array
  69.      *  @return string SQL limit clause
  70.      */
  71.     static function limit ( $request, $columns )
  72.     {
  73.         $limit = '';
  74.  
  75.         if ( isset($request['start']) && $request['length'] != -1 ) {
  76.             $limit = "OFFSET ".intval($request['start'])." LIMIT ".intval($request['length']);
  77.         }
  78.  
  79.         return $limit;
  80.     }
  81.  
  82.  
  83.     /**
  84.      * Ordering
  85.      *
  86.      * Construct the ORDER BY clause for server-side processing SQL query
  87.      *
  88.      *  @param  array $request Data sent to server by DataTables
  89.      *  @param  array $columns Column information array
  90.      *  @return string SQL order by clause
  91.      */
  92.     static function order ( $request, $columns )
  93.     {
  94.         $order = '';
  95.  
  96.         if ( isset($request['order']) && count($request['order']) ) {
  97.             $orderBy = array();
  98.             $dtColumns = SSP::pluck( $columns, 'dt' );
  99.  
  100.             for ( $i=0, $ien=count($request['order']) ; $i<$ien ; $i++ ) {
  101.                 // Convert the column index into the column data property
  102.                 $columnIdx = intval($request['order'][$i]['column']);
  103.                 $requestColumn = $request['columns'][$columnIdx];
  104.  
  105.                 $columnIdx = array_search( $requestColumn['data'], $dtColumns );
  106.                 $column = $columns[ $columnIdx ];
  107.  
  108.                 if ( $requestColumn['orderable'] == 'true' ) {
  109.                     $dir = $request['order'][$i]['dir'] === 'asc' ?
  110.                         'ASC' :
  111.                         'DESC';
  112.  
  113.                     $orderBy[] = ''.$column['db'].' '.$dir;
  114.                 }
  115.             }
  116.  
  117.             $order = 'ORDER BY '.implode(', ', $orderBy);
  118.         }
  119.  
  120.         return $order;
  121.     }
  122.  
  123.  
  124.     /**
  125.      * Searching / Filtering
  126.      *
  127.      * Construct the WHERE clause for server-side processing SQL query.
  128.      *
  129.      * NOTE this does not match the built-in DataTables filtering which does it
  130.      * word by word on any field. It's possible to do here performance on large
  131.      * databases would be very poor
  132.      *
  133.      *  @param  array $request Data sent to server by DataTables
  134.      *  @param  array $columns Column information array
  135.      *  @param  array $bindings Array of values for PDO bindings, used in the
  136.      *    sql_exec() function
  137.      *  @return string SQL where clause
  138.      */
  139.     static function filter ( $request, $columns, &$bindings )
  140.     {
  141.         $globalSearch = array();
  142.         $columnSearch = array();
  143.         $dtColumns = SSP::pluck( $columns, 'dt' );
  144.  
  145.         if ( isset($request['search']) && $request['search']['value'] != '' ) {
  146.             $str = $request['search']['value'];
  147.  
  148.             for ( $i=1, $ien=count($request['columns']) ; $i<$ien ; $i++ ) {
  149.                 $requestColumn = $request['columns'][$i];
  150.                 $columnIdx = array_search( $requestColumn['data'], $dtColumns );
  151.                 $column = $columns[ $columnIdx ];
  152.  
  153.                 if ( $requestColumn['searchable'] == 'true' ) {
  154.                     //$binding = SSP::bind( $bindings, "'%".$str."'%", PDO::PARAM_STR );
  155.                     //$globalSearch[] = "".$column['db']." LIKE ".$binding;
  156.                     $globalSearch[] = "".$column['db']." ILIKE '%".$str."%' ";
  157.                 }
  158.             }
  159.         }
  160.  
  161.         // Individual column filtering
  162.         for ( $i=0, $ien=count($request['columns']) ; $i<$ien ; $i++ ) {
  163.             $requestColumn = $request['columns'][$i];
  164.             $columnIdx = array_search( $requestColumn['data'], $dtColumns );
  165.             $column = $columns[ $columnIdx ];
  166.  
  167.             $str = $requestColumn['search']['value'];
  168.  
  169.             if ( $requestColumn['searchable'] == 'true' &&
  170.              $str != '' ) {
  171.                 $binding = SSP::bind( $bindings, '%'.$str.'%', PDO::PARAM_STR );
  172.                 $columnSearch[] = "".$column['db']." ILIKE ".$binding;
  173.             }
  174.         }
  175.  
  176.         // Combine the filters into a single string
  177.         $where = '';
  178.  
  179.         if ( count( $globalSearch ) ) {
  180.             $where = '('.implode(' OR ', $globalSearch).')';
  181.         }
  182.  
  183.         if ( count( $columnSearch ) ) {
  184.             $where = $where === '' ?
  185.                 implode(' AND ', $columnSearch) :
  186.                 $where .' AND '. implode(' AND ', $columnSearch);
  187.         }
  188.  
  189.         if ( $where !== '' ) {
  190.             $where = 'WHERE '.$where;
  191.         }
  192.  
  193.         return $where;
  194.     }
  195.  
  196.  
  197.     /**
  198.      * Perform the SQL queries needed for an server-side processing requested,
  199.      * utilising the helper functions of this class, limit(), order() and
  200.      * filter() among others. The returned array is ready to be encoded as JSON
  201.      * in response to an SSP request, or can be modified if needed before
  202.      * sending back to the client.
  203.      *
  204.      *  @param  array $request Data sent to server by DataTables
  205.      *  @param  array $sql_details SQL connection details - see sql_connect()
  206.      *  @param  string $table SQL table to query
  207.      *  @param  string $primaryKey Primary key of the table
  208.      *  @param  array $columns Column information array
  209.      *  @return array          Server-side processing response array
  210.      */
  211.     static function simple ( $request, $sql_details, $table, $primaryKey, $columns )
  212.     {
  213.         $bindings = array();
  214.         $db = SSP::sql_connect( $sql_details );
  215.  
  216.         // Build the SQL query string from the request
  217.         $limit = SSP::limit( $request, $columns );
  218.         $order = SSP::order( $request, $columns );
  219.         $where = SSP::filter( $request, $columns, $bindings );
  220.  
  221.         // Main query to actually get the data
  222.         $psql = "SELECT ".implode(", ", SSP::pluck($columns, 'db')).", count(*) OVER() AS total_count
  223.              FROM $table
  224.              $where
  225.              $order
  226.              $limit";
  227.         $data = SSP::sql_exec( $db, $bindings,$psql);
  228.        
  229.         // Data set length after filtering
  230.         if ( !isset($data[0]["total_count"]) ) {
  231.             $recordsFiltered = 0;
  232.         }
  233.         else {
  234.             $recordsFiltered = $data[0]["total_count"];
  235.         }
  236.         // Total data set length
  237.         $resTotalLength = SSP::sql_exec( $db,
  238.             "SELECT COUNT({$primaryKey})
  239.              FROM   $table"
  240.         );
  241.         $recordsTotal = $resTotalLength[0][0];
  242.  
  243.  
  244.         /*
  245.          * Output
  246.          */
  247.         return array(
  248.             "draw"            => intval( $request['draw'] ),
  249.             "recordsTotal"    => intval( $recordsTotal ),
  250.             "recordsFiltered" => intval( $recordsFiltered ),
  251.             "data"            => SSP::data_output( $columns, $data ),
  252.             "sql"             => $psql
  253.         );
  254.     }
  255.  
  256.  
  257.     /**
  258.      * Connect to the database
  259.      *
  260.      * @param  array $sql_details SQL server connection details array, with the
  261.      *   properties:
  262.      *     * host - host name
  263.      *     * db   - database name
  264.      *     * user - user name
  265.      *     * pass - user password
  266.      * @return resource Database connection handle
  267.      */
  268.     static function sql_connect ( $sql_details )
  269.     {
  270.         try {
  271.             $db = @new PDO(
  272.                 "pgsql:host={$sql_details['host']};dbname={$sql_details['db']}",
  273.                 $sql_details['user'],
  274.                 $sql_details['pass'],
  275.                 array( PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION )
  276.             );
  277.         }
  278.         catch (PDOException $e) {
  279.             SSP::fatal(
  280.                 "An error occurred while connecting to the database. ".
  281.                 "The error reported by the server was: ".$e->getMessage()
  282.             );
  283.         }
  284.  
  285.         return $db;
  286.     }
  287.  
  288.  
  289.     /**
  290.      * Execute an SQL query on the database
  291.      *
  292.      * @param  resource $db  Database handler
  293.      * @param  array    $bindings Array of PDO binding values from bind() to be
  294.      *   used for safely escaping strings. Note that this can be given as the
  295.      *   SQL query string if no bindings are required.
  296.      * @param  string   $sql SQL query to execute.
  297.      * @return array         Result from the query (all rows)
  298.      */
  299.     static function sql_exec ( $db, $bindings, $sql=null )
  300.     {
  301.         // Argument shifting
  302.         if ( $sql === null ) {
  303.             $sql = $bindings;
  304.         }
  305.  
  306.         $stmt = $db->prepare( $sql );
  307.         //echo $sql;
  308.  
  309.         // Bind parameters
  310.         if ( is_array( $bindings ) ) {
  311.             for ( $i=0, $ien=count($bindings) ; $i<$ien ; $i++ ) {
  312.                 $binding = $bindings[$i];
  313.                 $stmt->bindValue( $binding['key'], $binding['val'], $binding['type'] );
  314.             }
  315.         }
  316.  
  317.         // Execute
  318.         try {
  319.             $stmt->execute();
  320.         }
  321.         catch (PDOException $e) {
  322.             SSP::fatal( "An SQL error occurred: ".$e->getMessage()." [with] '".$sql."'" );
  323.         }
  324.  
  325.         // Return all
  326.         return $stmt->fetchAll();
  327.     }
  328.  
  329.  
  330.     /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  331.      * Internal methods
  332.      */
  333.  
  334.     /**
  335.      * Throw a fatal error.
  336.      *
  337.      * This writes out an error message in a JSON string which DataTables will
  338.      * see and show to the user in the browser.
  339.      *
  340.      * @param  string $msg Message to send to the client
  341.      */
  342.     static function fatal ( $msg )
  343.     {
  344.         echo json_encode( array(
  345.             "error" => $msg
  346.         ) );
  347.  
  348.         exit(0);
  349.     }
  350.  
  351.     /**
  352.      * Create a PDO binding key which can be used for escaping variables safely
  353.      * when executing a query with sql_exec()
  354.      *
  355.      * @param  array &$a    Array of bindings
  356.      * @param  *      $val  Value to bind
  357.      * @param  int    $type PDO field type
  358.      * @return string       Bound key to be used in the SQL where this parameter
  359.      *   would be used.
  360.      */
  361.     static function bind ( &$a, $val, $type )
  362.     {
  363.         $key = ':binding_'.count( $a );
  364.  
  365.         $a[] = array(
  366.             'key' => $key,
  367.             'val' => $val,
  368.             'type' => $type
  369.         );
  370.  
  371.         return $key;
  372.     }
  373.  
  374.  
  375.     /**
  376.      * Pull a particular property from each assoc. array in a numeric array,
  377.      * returning and array of the property values from each item.
  378.      *
  379.      *  @param  array  $a    Array to get data from
  380.      *  @param  string $prop Property to read
  381.      *  @return array        Array of property values
  382.      */
  383.     static function pluck ( $a, $prop )
  384.     {
  385.         $out = array();
  386.  
  387.         for ( $i=0, $len=count($a) ; $i<$len ; $i++ ) {
  388.             $out[] = $a[$i][$prop];
  389.         }
  390.  
  391.         return $out;
  392.     }
  393. }
  394. ?>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement