Subversion Repositories php_library

Rev

Rev 40 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
38 rodolico 1
<?php
2
 
40 rodolico 3
   require_once "DBDatabase.class.php";
4
   require_once "Logging.class.php"; // For logging
5
 
6
   define( HTML_QUOTE_CHAR, '"' );
7
   define( CONSTANT_NO_VALUE_DROPDOWN, '--------' );
8
 
38 rodolico 9
   class DBQuery {
10
      public $query;          // the query to be processed
11
      public $rowsAffected;   // the number of rows 1) affectd by an insert, update or delete or 2) returned by a select
12
      public $lastInsertKey;  // the value of the last row inserted (mysql only)
13
      public $returnData;     // an array that contains the data retrieved by a query (select only)
14
      public $columnMetaData; // an array that contains the meta data from the query
15
      public $useAssociativeArray;   // if true, $returnData is an array of associative array, with the column names being the key fields
16
      public $showErrors;     // if true, will return errors to STDOUT
17
      public $error;          // set to last error returned
18
      public $logFile;        // if set to a fully qualified file, will write queries to that file
19
      public $auditTable;     // if set to a table name, all queries are written to it
20
 
21
      public function __construct( $query ) {
22
         $this->query = $query;
23
         $this->rowsAffected = 0;
24
         $this->lastInsertKey = '';
25
         $this->returnData = array();
26
         $this->columnMetaData = array();
27
         $this->useAssociativeArray = true;
28
         $this->showErrors = true;
29
         $this->error = '';
30
      }
31
 
32
 
33
      /* 
34
         function actually executes the query, populating the members with the results
35
         returns true if no errors, false if errors (see $this->error for code)
36
      */
40 rodolico 37
      function run () {
38 rodolico 38
         // print "<pre>In Query database\n---------------------\n$query\n---------------------\n</pre>";
39
         if ($logFile) printLog( $query );
40
         if ($auditTable) audit( $query );
41
         $result = @mysql_query($this->query);
42
         if( mysql_errno() ) {
43
            $this->error = 'MYSQL ERROR #'.mysql_errno().' : <small>' . mysql_error(). "</small><br><VAR>$query</VAR>";
44
            if ( $this->showErrors ) echo($this->error);
45
            return false;
46
         } // if
47
         if( preg_match ( '/^\s*select/i', $this->query ) ) { // this is a select statement
48
            $this->rowsAffected = @mysql_num_rows($result);
49
            $this->columnMetaData = array();
40 rodolico 50
            for ($i = 0; $i < mysql_num_fields( $result ); $i++) {
38 rodolico 51
               $meta = mysql_fetch_field ( $result );
52
               $this->columnMetaData[] = array('name' => $meta->name, 'length' => $meta->max_length, 'numeric' => $meta->numeric, 'type' => $meta->type );
53
            }
54
            if( ! $this->rowsAffected ) return;
40 rodolico 55
            $this->returnData = array();
56
            for( $i = 0; $i < $this->rowsAffected; $i++ ) {
57
               $this->returnData[] = $this->useAssociativeArray ? mysql_fetch_assoc($result) : mysql_fetch_row($result);
38 rodolico 58
            }
59
            mysql_free_result($result);
60
         } else { // not a select statement
61
            $this->rowsAffected = mysql_affected_rows();
62
            $this->lastInsertKey = mysql_insert_id();
63
         }
64
         return true;
65
      } // function run
66
 
67
      // function returns the first column of the first row of data returned from query
68
      // or null no value returned
69
      function getOneDBValue( $sql ) {
70
         $save = $this->useAssociativeArray;
71
         $useAssociativeArray = false;
72
         $this->run();
73
         $useAssociativeArray = $save;
74
         return $this->rowsAffected ? $this->returnData[0][0] : null;
75
      }
76
 
40 rodolico 77
      function countNumberOfRows ( $sql ) {
78
            $save = $this->useAssociativeArray;
79
            $useAssociativeArray = false;
80
            $this->run();
81
            $useAssociativeArray = $save;
82
            return $this->rowsAffected;
83
      }
38 rodolico 84
 
85
      function makeWhereClause ($conditions, $joinedBy = 'and') {
86
         $joinedBy = " $joinedBy "; // make sure there are spaces around it
87
         $whereClause = ' where ' . implode ( $joinedBy, $conditions );
88
         return $whereClause;
89
      }
90
 
91
      /* 
92
         values is an associative array of name/value pairs
93
         function will replace all items of the form <$name> with its values (the less than and greater than symbols around the key)
94
      */
95
      function insertValuesIntoQuery( $values ) {
96
         foreach ( $values as $name => $value ) {
97
            $this->query = search_replace_string($this->query, "<$name>", $value );
98
         }
99
      }
40 rodolico 100
 
101
   } // class DBQuery
102
 
38 rodolico 103
 
40 rodolico 104
   /* 
105
      This class simply extends DBQuery to allow output to be converted to HTML
106
   */
107
 
108
   class DBQueryHTML extends DBQuery {
109
 
38 rodolico 110
      /*
111
         Function takes an SQL statement and converts it to an HTML table.
112
         Return Value: HTML table representation of the query
113
         Parameters:
114
            $sql    A valid SQL query to run
115
            $format An optional array of format strings (suitable for printf) for each column (empty strings ignored)
116
            $makeTableDef  If True, the resulting HTML has the <table></table> tags; otherwise starts with header
117
            $append   Arbitrary string that is appended as a single column to each row
40 rodolico 118
 
38 rodolico 119
            NOTE ON $append
120
               $append may optionally contain variables of the form %colname%, which will be replaced
121
               with values from the current row. $colname is taken from the META data from the query, thus
122
               the query select foo from bar would have a meta of header of foo for column 0.
123
               $append is searched for strings of form %foo% in that case, and the current value of column
124
               foo replaces the tag %foo%.
125
               %foo% is ignored if foo is not a valid column name in a query.
126
               thus, a query such as select zip,city,state from zip_codes and a string of Hello %city% I'm glad
127
               you are in %State% will not replace the second as State is not a column of this query (it is case
128
               sensitive). Also, beware of items like select zip,concat(state,', ', city) from zip_codes. It is
129
               much better to rewrite that as select zip,concat(state,', ', city) mytown from zip_codes.
130
 
131
               If you don't know what all that means, get a book on SQL
132
      */
133
      function queryToTable ( $format = '', $makeTableDef = true, $append='' ) {
134
         //print "\n\n$this->query\n\n";
135
         $tdTextDefinition = '<td valign=' . HTML_QUOTE_CHAR . 'top' . HTML_QUOTE_CHAR . '>';
136
         $tdNumberDefinition = '<td valign=' . HTML_QUOTE_CHAR .'top' . HTML_QUOTE_CHAR . 'align=' . HTML_QUOTE_CHAR . 'right' . HTML_QUOTE_CHAR . '>';
137
         $html = '';
138
         $save = $this->useAssociativeArray;
139
         $useAssociativeArray = false; // temporarily turn off associative arrays
140
         $this->run();
141
         if ( $this->rowsAffected ) { // we returned at least one row
142
            // Build the searchFor array for $append
143
            $searchFor = array();
40 rodolico 144
            foreach ( $this->columnMetaData as $field ) {
38 rodolico 145
               $searchFor[] = '%' . $field['name'] . '%';
146
            }
40 rodolico 147
            $this->returnData = $result['data'];
38 rodolico 148
            unset ($result);
40 rodolico 149
            /* special condition where only one row is returned. In that case, $this->returnData is not an array
38 rodolico 150
            of associations, but a simple association. In this case, we need to convert it
151
            */
40 rodolico 152
            if (count($this->returnData[0]) == 1) { // convert from association to single row array of associations
38 rodolico 153
               $temp = array();
40 rodolico 154
               foreach ($this->returnData as $column => $value) {
38 rodolico 155
                  $temp[0][$column] = $value;
156
               } // foreach
40 rodolico 157
               $this->returnData = $temp;
38 rodolico 158
            }
159
            if (count($format) > 0 ) { // we have some formats, so let's do it the hard, slow way
40 rodolico 160
               for ( $row = 0; $row < count($this->returnData); $row++) {
38 rodolico 161
                  $rows[$row] = '';
40 rodolico 162
                  for ( $column = 0; $column < count($this->returnData[$row]); $column++  ) {
163
                     $rows[$row] .= strlen($format[$column]) && isset($this->returnData[$row][$column])> 0 ?
164
                                    ( $tdNumberDefinition . sprintf($format[$column],$this->returnData[$row][$column]) . '</td>')
165
                                    : ($tdTextDefinition . $this->returnData[$row][$column] . '</td>');
38 rodolico 166
                  } // for $column
167
                  /*
168
                     let's append some stuff to the row if it exists.
169
                     We will take the array of SearchFor containing the column names
170
                     and the array of values from this row, then look for matches and replace them
171
                     with the correct values. Note, the column names in $append should have percent
172
                     signs appended and prepended, thus a column name of joe would be %joe% in %append
173
                  */
174
                  if (strlen($append) > 0) { // let's append some stuff to the row
40 rodolico 175
                     $rows[$row] .= $tdTextDefinition . str_replace ( $searchFor, $this->returnData[$row], $append ) . '</td>';
38 rodolico 176
                  }
177
               } // for $row
178
            } else { // no formatting, so we just slam the stuff together
40 rodolico 179
               for ( $row = 0; $row < count($this->returnData); $row++) {
180
                  $currentValue = $tdTextDefinition . implode( '</td>' . $tdTextDefinition, $this->returnData[$row]) . '</td>';
38 rodolico 181
                  if (strlen($append) > 0) { // see explaination in if part of this structure
40 rodolico 182
                     $currentValue  .= $tdTextDefinition . str_replace ( $searchFor, $this->returnData[$row], $append ) . '</td>';
38 rodolico 183
                  }
184
                  $rows[] = $currentValue;
185
               }
186
            }
187
            // ok, let's get the field headers from the table
188
            $html .= '<tr>';
40 rodolico 189
            foreach ( $this->columnMetaData as $field ) {
38 rodolico 190
               $html .= '<th>' . $field['name'] . '</th>';
191
            }
192
            $html .= '</tr>';
193
            // we now have all the info, so let's make it into a table
194
            $html .= '<tr>' . implode('</tr><tr>', $rows ) . '</tr>';
195
 
196
            if ( $makeTableDef ) { // they want the <table> def, so let's give it to them
197
               $html = '<table border=' . HTML_QUOTE_CHAR . '1' . HTML_QUOTE_CHAR . '>' . $html . '</table>';
198
            } // if ( $makeTableDef
199
         } // if ( $info = queryDatabase($this->query,0) )
200
         // print '<pre>';  print_r ($info); print '</pre>';
201
         $useAssociativeArray = $save;
202
         return $html;
203
      }
204
 
205
      /*
206
         function executes a query, then returns an array containing only the values of the first
207
         field in the return as the keys to an associative array. NOTE: any duplicates will be
208
         discarded. This is useful when you simply want to know if a value exists, ie if you
209
         are building an html select from a query, and a separate query needs to hold the
210
         values that are currently selected
211
      */
212
 
213
      function valuesToKeys () {
214
         $returnValue = array();
215
         $save = $this->useAssociativeArray;
216
         $useAssociativeArray = false; // temporarily turn off associative arrays
217
         $this->run();
218
         if ( $this->rowsAffected ) { // we returned at least one row
219
            foreach ( $info as $key => $value ) { // only thing I know to do is iterate
220
               $returnValue[$this->returnData[0]] = true;
221
            }
222
         } // if
223
         $useAssociativeArray = $save;
224
         return $returnValue;
225
      }
226
 
227
      /* this function will take the result of an SQL query that returns at least two columns.
228
         The first column becomes the keys in an associative array, and the second column
229
         becomes the value.
230
 
231
         Note, because it is an associative array, duplicates of the first column will only contain
232
         the last value.
233
      */
234
 
235
      function ColumnColumnArray () {
236
         $returnValue = array();
237
         $save = $this->useAssociativeArray;
238
         $useAssociativeArray = false; // temporarily turn off associative arrays
239
         $this->run();
240
         if ( $this->rowsAffected ) { // we returned at least one row
241
            foreach ( $info as $key => $value ) { // only thing I know to do is iterate
242
               $returnValue[$this->returnData[0]] = $this->returnData[1];
243
            }
244
         } // if
245
         $useAssociativeArray = $save;
246
         return $returnValue;
247
      }
248
 
249
 
250
      /* the first returned column becomes the value, the second becomes the display element
251
         if $selectedFieldsQuery is begins with 'select', items matching that query will be selected
252
         if  $selectedFieldsQuery is any other value, it is assumed to be the value of the first column
253
 
254
      */
255
      function htmlSelect ( $selectedFieldsQuery = '' ) {
256
         $html = '';
257
         $selectedFields = array();
40 rodolico 258
         if (strlen( $selectedFieldsQuery )) {
38 rodolico 259
            if (  preg_match ( '/^\s*select/i', $selectedFieldsQuery ) ) { // They passed in a query
260
               if (strlen($selectedFieldsQuery) ) {
261
                  $subSet = new DBQuery($selectedFieldsQuery);
262
                  $subSet->run();
263
                  $selectedFields = $subSet->sqlValuesToKeys();
264
               }
265
            } else { // assume the passed in a value
266
               $selectedFields[$selectedFieldsQuery] = 1;
267
            }
268
         }
269
         $save = $this->useAssociativeArray;
270
         $useAssociativeArray = false; // temporarily turn off associative arrays
271
         $this->run();
272
         if ( $this->rowsAffected ) { // we returned at least one row
40 rodolico 273
            foreach ($this->returnData as $rowNumber => $value ) {
38 rodolico 274
               $html .= '<option value="' . $value[0] . '"';
275
               if ( $selectedFields[$value[0]] ) {
276
                  $html .= ' selected';
277
               }
278
               $html .= '>' . $value[1] . '</option>';
279
            }
280
         }
281
         $useAssociativeArray = $save;
282
         return $html;
283
      }
284
 
285
      /* function will take a query and turn it into a series of check boxes. It must contain
286
         two columns, with the first becoming the the name of the checkbox
287
         and the second becoming the displayed value. an optional third column will be used
288
         to match if the $checkedValuesQuery is used.
289
         if $checkedValuesQuery is not empty, it will be run to see what boxes need to be
290
         checked by being compared to the third column of the $sql query.
291
         $htmlBefore will be placed before each check box, and $htmlAfter will be placed after
292
         each checkbox.
293
 
294
         if $tableColumns is set to a number, the checkboxes will be embedded in a group of
295
         <tr></tr>, each containing table columns of $tableColumns width. In this case, $htmlBefore
296
         will have <td> prepended and $htmlAfter will have </td> appended, meaning any passed
297
         values will be INSIDE of the td. NOTE: the <table></table> tags are NOT put in.
298
 
299
         NOTE: currently, using the table stuff will leave a dangling row with 0 elements if
300
         the number of elements equal the number of columns.
301
      */
302
 
303
      function htmlCheckBoxes ( $checkedValuesQuery = '', $htmlBefore = '', $htmlAfter = '', $table_columns='' ) {
304
         $html = '';
305
         if ($table_columns) {
306
            $htmlBefore = '<td>' . $htmlBefore;
307
            $htmlAfter .= '</td>';
308
            $html .= '<tr>';
309
         }
310
         $numColumns = 0;
311
         $checkBoxes = queryDatabaseExtended( $sql,0 );
312
         $selectedFields = array();
313
         if (strlen($checkedValuesQuery) ) {
314
            $subSet = new DBQuery($selectedFieldsQuery);
315
            $subSet->run();
316
            $selectedFields = $subSet->sqlValuesToKeys();
317
         }
318
         $save = $this->useAssociativeArray;
319
         $useAssociativeArray = false; // temporarily turn off associative arrays
320
         foreach ($this->returnData as $row => $values) {
321
            if ($table_columns && ++$numColumns == $table_columns) {
322
               $html .= '</tr><tr>';
323
               $numColumns = 0;
324
            }
325
            //objectDebugScreen($row);
326
            $html .= $htmlBefore . '<input type="checkbox" name="' . $values[0] . '"';
327
            if ( $selectedFields[$values[2]] ) {
328
               $html .= ' checked';
329
            }
330
            $html .= '>' . $values[1] . $htmlAfter;
331
            //<INPUT type="checkbox" checked name="temp">
332
         } // foreach
333
         $html .= '</tr>';
334
         $useAssociativeArray = $save;
335
         return $html;
336
      }
337
 
338
   /*   
339
      function CSV ( $sql ) {
340
         $rows = array();
341
         $CSV = '';
342
         $info = array();
343
         if ( $result = queryDatabaseExtended($sql,0) ) {
344
            $meta = $result['meta'];
345
            //objectDebugScreen($result);
346
            $info = $result['data'];
347
            unset ($result);
348
            $headers = array();
349
            foreach ( $meta as $field ) {
350
               $headers[] = $field['name'];
351
            } // foreach
352
            $rows[] = implode("\t", $headers);
353
            // special condition where only one row is returned. In that case, $info is not an array
354
            //  of associations, but a simple association. In this case, we need to convert it
355
 
356
            if (count($info[0]) == 1) { // convert from association to single row array of associations
357
               $temp = array();
358
               foreach ($info as $column => $value) {
359
                  $temp[0][$column] = $value;
360
               } // foreach
361
               $info = $temp;
362
            }
363
            for ( $row = 0; $row < count($info); $row++) {
364
               $rows[] = implode("\t", $info[$row]);
365
            }
366
            // we now have all the info, so let's make it into a table
367
            $CSV .= implode("\n", $rows );
368
         } // if ( $info = queryDatabase($sql,0) )
369
         // print '<pre>';  print_r ($info); print '</pre>';
370
         return $CSV;
371
      }
372
 
373
   */
374
 
40 rodolico 375
   } // class DBQueryHTML
38 rodolico 376
 
377
?>