OwlManAtt / activephp

A suite of tools (read as: ActiveTable) for easily making data objects to represent your model data.

This URL has Read+Write access

activephp / csvio / csvio.class.php
100644 434 lines (370 sloc) 13.108 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
<?php
/**
* An ActiveTable-based library for loading/writing CSVs to/from database tables.
*
* @package ActivePHP
* @author OwlManAtt <owlmanatt@gmail.com>
* @copyright 2007, Yasashii Syndicate
* @version 2.4.0
*/
 
/**
* The base class for CSVIO-enabled ActiveTable classes.
*
* This class should never be instantiated; extend the object
* and set the required attributes to get it working.
*
* @package ActivePHP
* @author OwlManAtt <owlmanatt@gmail.com>
* @copyright 2007, Yasashii Syndicate
* @version Release: @package_version@
**/
class CSVIO extends ActiveTable
{
    /**
* Empty the table before loading anything in.
*
* @var boolean
**/
    protected $truncate = false;
    
    /**
* The field mapping for a CSV's row.
*
* This represents the field mapping for a CSV row.
* The mapping should be an indexed array, starting from 0,
* where each value is the table's column name that it maps to.
* The mappings should in *in the order they appear* in the CSV
* file.
*
* Also note that if you do not want a field imported, leave it's column
* name null.
*
* <code>
* protected $FIELDS = array(
* 0 => 'acct',
* 1 => 'foo',
* 2 => null,
* 3 => 'other_db_column',
* );
* </code>
*
* CSVIO also allows for 'formatters' to be applied to data. You can instruct
* CSVIO to format an input before adding it to the database by doing the following:
*
* <code>
* protected $FIELDS = array(
* 0 => 'acct',
* 1 => 'foo',
* 2 => array(
* 'column' => 'db_column',
* 'format' => 'date|datetime',
* ),
* 3 => 'other_db_column',
* );
* </code>
*
* At this time, only 'date' and 'datetime' are supported. These will take any date that
* can be read by strtotime() and turn it into the appropriate date/datetime format for the
* RDBMS you are using.
*
* Custom formatters using a callback function will be supported in a future release.
*
* @var array
**/
    protected $FIELDS = array();
 
    /**
* A list of records to be outputted in the header.
*
* If this is left empty, no header row will be written.
*
* @var array
**/
    protected $HEADERS = array();
 
    /**
* The normalized form of $FIELDS. Do not define this; it is auto-generated.
*
* @var array
* @internal
**/
    protected $FIELDS_NORMALIZED = array();
 
    /**
* The field seperator.
*
* @var string
**/
    protected $seperator = ',';
 
    /**
* The 'quote' character. This may be null OR one single character.
*
* @var string
**/
    protected $quote = '"';
 
    /**
* This indicates whether or not a header row is present in the CSV.
*
* Row 0 of the CSV will be ignored if this is true.
*
* @var bool
**/
    protected $header_row = true;
 
    /**
* Set up the CSVIO instance.
*
* @param object See ActiveTable documentation.
* @return void
**/
    public function __construct($db)
    {
        parent::__construct($db);
 
        if(strlen($this->quote) > 1)
        {
            throw new ArgumentError("Invalid quote character ({$this->quote}). The quote character may only be one character long or null.");
        }
 
        if(is_array($this->FIELDS) == false)
        {
            throw new ArgumentError('The field mapping list must be an array.');
        }
        elseif(sizeof($this->FIELDS) <= 0)
        {
            throw new ArgumentError('The field mapping list cannot be empty.');
        }
        else
        {
            // Validate & normalize field list.
            $this->FIELDS_NORMALIZED = $this->FIELDS;
 
            // For our purposes, the field list need only be 0 => foo; the 0 => array(...) needs to be normalized.
            foreach($this->FIELDS_NORMALIZED as $index => $field)
            {
                if(is_array($field) == true)
                {
                    if(array_key_exists('column',$field) == false)
                    {
                        throw new ArgumentError("Error in field $index - array given as value with no column defined.");
                    }
                    else
                    {
                        $this->FIELDS_NORMALIZED[$index] = $field['column'];
                    }
 
                    if(array_key_exists('format',$field) == true)
                    {
                        $FORMATS = array('date','datetime');
                        
                        if(in_array($field['format'],$FORMATS) == false)
                        {
                            throw new ArgumentError("Invalid format '{$field['format']}' specified for column $index ('{$field['column']}').");
                        }
                    } // end format is set
                } // end if field is array
            } // end field normalizer
        } // end field size looks OK; check in detail
    } // end __construct
 
    /**
* Load a CSV file's contents into CSVIO.
*
* @param string The path to the file.
**/
    public function loadCSVFile($path)
    {
        if($this->truncate == true)
        {
            $this->db->query("DELETE FROM `{$this->table_name}`");
        }
        
        $handle = fopen($path,'r');
        
        $imported = 0;
        $i = 0;
        while($data = fgetcsv($handle,0,$this->seperator,$this->quote))
        {
            $i++;
            if($i == 1 && $this->header_row == true)
            {
                continue;
            }
            
            $CREATE = $this->map_fields($data);
            $CREATE = $this->handle_formatters($CREATE);
            eval('$tmp = new '.get_class($this).'($this->db);');
 
            if($this->debug == true)
            {
                $tmp->debug = true;
            }
 
            $tmp->create($CREATE);
            $imported++;
        } // end line loop
 
        return $imported;
    } // end loadFile
    
    public function writeSearchResults($args,$order,$output_file)
    {
        $start = microtime(true);
        $SEARCH_VALUES = array();
 
        if(is_array($args) == false)
        {
            throw new ArgumentError('Args must be an array.',950);
        }
 
        $sql_generator = $this->newSqlGenerator();
        $columns = $this->make_columns($sql_generator);
        $sql_generator = $columns['sql_generator'];
 
        if($sql_generator->getMagicPkName() != null)
        {
            $sql_generator->addMagicPkToKeys($this->table_name);
        }
 
        $sql_generator->addFrom($this->table_name,$this->database);
        $foo = $this->make_join($this->LOOKUPS,$sql_generator);
        $sql_generator = $foo['sql_generator'];
        
        if(is_array($foo['search_values']) == true)
        {
            $SEARCH_VALUES = array_merge($SEARCH_VALUES,$foo['search_values']);
        }
        elseif($foo['search_values'] != null)
        {
            $SEARCH_VALUES[] = $foo['search_values'];
        }
 
        foreach($args as $column => $value)
        {
            $where = $this->make_wheres($column,$value,$sql_generator);
            $sql_generator = $where['sql_generator'];
            $bar = $where['search_values'];
 
            if($bar !== null)
            {
                if(is_array($bar) == true)
                {
                    $SEARCH_VALUES = array_merge($SEARCH_VALUES,$bar);
                }
                else
                {
                    $SEARCH_VALUES[] = $bar;
                }
            }
        } // end loop
 
        // TODO - This is taken exactly as-is and put into the query. Probably a *bad* idea...
        if($order_by != null)
        {
            $sql_generator->addOrder($order_by);
        }
        
        $sql = $sql_generator->getQuery('select');
        $handle = $this->db->prepare($sql);
        $resource = $this->execute($handle,$SEARCH_VALUES);
        $this->debug($this->db->last_query,'sql');
 
        $this->db->freePrepared($handle);
 
        if(PEAR::isError($resource))
        {
            throw new SQLError($resource->getDebugInfo(),$resource->userinfo,909);
        }
        
        // Try to open a file pointer.
        $fp = fopen($output_file,'w');
 
        if($fp == false)
        {
            throw new FileHandleError('The specified file could not be opened and written to.');
        }
 
        // Header row.
        if(sizeof($this->HEADERS) != 0)
        {
            fputcsv($fp,$this->HEADERS,$this->seperator,$this->quote);
        }
        
        $class_name = get_class($this);
        while($resource->fetchInto($row))
        {
            $RESULT_DATA = $this->parse_columns($row,$columns['lookup_mapping'],$columns['virtual_mapping']);
            
            // If things were passed to this instance's constructor (additonal db connectors?),
            // then pass them on to what we're finding.
            $arg_fragment = '';
            if(sizeof($this->CONSTRUCTOR_ARGS) > 0)
            {
                $keys = array_keys($this->CONSTRUCTOR_ARGS);
                $arg_fragment = array();
 
                foreach($keys as $key)
                {
                    $arg_fragment[] = '$this->CONSTRUCTOR_ARGS['.$key.']';
                } // end key loop
 
                $arg_fragment = ','.implode(',',$arg_fragment);
            } // end additional constructor arg handler
 
            // The eval() is slow, so only do it if we need the additional args fragment.
            if($arg_fragment != null)
            {
                eval('$tmp = new '.$class_name.'($this->db'.$arg_fragment.');');
            }
            else
            {
                $tmp = new $class_name($this->db);
            }
            
            $tmp->setUp($RESULT_DATA['primary_table'],$RESULT_DATA['lookup_tables'],$RESULT_DATA['virtual_fields']);
           
            $RECORD = array();
            foreach($this->FIELDS as $field)
            {
                $RECORD[$field] = $tmp->get($field);
            }
            
            fputcsv($fp,$RECORD,$this->seperator,$this->quote);
        } // end while
 
        fclose($fp);
    
        return true;
    } // end writeSearchResults
    
    /**
* Load a CSV from a string.
*
* @param string The CSV contents.
**/
    public function loadCSV($csv)
    {
        if($csv === null)
        {
            throw new FileEmptyError('The CSV file supplied contains no data.');
        } // end csv is not null
     
        throw new ArgumentError('Method not implemented at this time.');
    } // end loadCSV
 
    /**
* Process formatters on a column.
*
* @param array An imported row from the CSV.
* @return array The array with formatted values.
**/
    protected function handle_formatters($data)
    {
        $i = 0;
        foreach($data as $key_index => $value)
        {
            $field = $this->FIELDS[$i];
            
            if(is_array($field) == true)
            {
                if(array_key_exists('format',$field) == true)
                {
                    // Note - this has been validated before we got to this point. Do not worry
                    // about a failure case.
                    switch($field['format'])
                    {
                        case 'date':
                        {
                            $date = $this->newSqlGenerator()->getFormattedDate($value);
                            $date = explode(' ',$date);
                            
                            $value = $date[0];
 
                            break;
                        } // end date
                        
                        case 'datetime':
                        {
                            $value = $this->newSqlGenerator()->getFormattedDate($value);
 
                            break;
                        } // end datetime
 
                    } // end format switch
 
                    $data[$key_index] = $value;
                } // end format
            } // end is array
 
            $i++;
        } // end data loop
 
        return $data;
    } // end handle_formatters
 
    protected function map_fields($data)
    {
        if(sizeof($this->FIELDS_NORMALIZED) > sizeof($data))
        {
            // Pad the data w/ difference
            $data = array_pad($data,sizeof($this->FIELDS_NORMALIZED),null);
        }
        elseif(sizeof($this->FIELDS_NORMALIZED) < sizeof($data))
        {
            $original = sizeof($data);
            
            while($original > sizeof($this->FIELDS_NORMALIZED))
            {
                array_pop($data);
                $original--;
            } // end pop loop
        } // end data too large
 
        $RETURN = array_combine($this->FIELDS_NORMALIZED,$data);
 
        return $RETURN;
    } // end map_fields
 
    
} // end CSVIO
?>