~wouterbulten/mai-lib/main

« back to all changes in this revision

Viewing changes to library/Zend/Db/Table/Abstract.php

  • Committer: Wouter Bulten
  • Date: 2009-09-09 18:52:29 UTC
  • Revision ID: wouterbulten@fabricgames.com-20090909185229-98kwknzlx7jl1epd
First commit for new Mai Lib project.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?php
 
2
/**
 
3
 * Zend Framework
 
4
 *
 
5
 * LICENSE
 
6
 *
 
7
 * This source file is subject to the new BSD license that is bundled
 
8
 * with this package in the file LICENSE.txt.
 
9
 * It is also available through the world-wide-web at this URL:
 
10
 * http://framework.zend.com/license/new-bsd
 
11
 * If you did not receive a copy of the license and are unable to
 
12
 * obtain it through the world-wide-web, please send an email
 
13
 * to license@zend.com so we can send you a copy immediately.
 
14
 *
 
15
 * @category   Zend
 
16
 * @package    Zend_Db
 
17
 * @subpackage Table
 
18
 * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
 
19
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 
20
 * @version    $Id: Abstract.php 16971 2009-07-22 18:05:45Z mikaelkael $
 
21
 */
 
22
 
 
23
/**
 
24
 * @see Zend_Db_Adapter_Abstract
 
25
 */
 
26
require_once 'Zend/Db/Adapter/Abstract.php';
 
27
 
 
28
/**
 
29
 * @see Zend_Db_Adapter_Abstract
 
30
 */
 
31
require_once 'Zend/Db/Select.php';
 
32
 
 
33
/**
 
34
 * @see Zend_Db
 
35
 */
 
36
require_once 'Zend/Db.php';
 
37
 
 
38
/**
 
39
 * Class for SQL table interface.
 
40
 *
 
41
 * @category   Zend
 
42
 * @package    Zend_Db
 
43
 * @subpackage Table
 
44
 * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
 
45
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 
46
 */
 
47
abstract class Zend_Db_Table_Abstract
 
48
{
 
49
 
 
50
    const ADAPTER          = 'db';
 
51
    const DEFINITION        = 'definition';
 
52
    const DEFINITION_CONFIG_NAME = 'definitionConfigName';
 
53
    const SCHEMA           = 'schema';
 
54
    const NAME             = 'name';
 
55
    const PRIMARY          = 'primary';
 
56
    const COLS             = 'cols';
 
57
    const METADATA         = 'metadata';
 
58
    const METADATA_CACHE   = 'metadataCache';
 
59
    const METADATA_CACHE_IN_CLASS = 'metadataCacheInClass';
 
60
    const ROW_CLASS        = 'rowClass';
 
61
    const ROWSET_CLASS     = 'rowsetClass';
 
62
    const REFERENCE_MAP    = 'referenceMap';
 
63
    const DEPENDENT_TABLES = 'dependentTables';
 
64
    const SEQUENCE         = 'sequence';
 
65
 
 
66
    const COLUMNS          = 'columns';
 
67
    const REF_TABLE_CLASS  = 'refTableClass';
 
68
    const REF_COLUMNS      = 'refColumns';
 
69
    const ON_DELETE        = 'onDelete';
 
70
    const ON_UPDATE        = 'onUpdate';
 
71
 
 
72
    const CASCADE          = 'cascade';
 
73
    const RESTRICT         = 'restrict';
 
74
    const SET_NULL         = 'setNull';
 
75
 
 
76
    const DEFAULT_NONE     = 'defaultNone';
 
77
    const DEFAULT_CLASS    = 'defaultClass';
 
78
    const DEFAULT_DB       = 'defaultDb';
 
79
 
 
80
    const SELECT_WITH_FROM_PART    = true;
 
81
    const SELECT_WITHOUT_FROM_PART = false;
 
82
 
 
83
    /**
 
84
     * Default Zend_Db_Adapter_Abstract object.
 
85
     *
 
86
     * @var Zend_Db_Adapter_Abstract
 
87
     */
 
88
    protected static $_defaultDb;
 
89
 
 
90
    /**
 
91
     * Optional Zend_Db_Table_Definition object
 
92
     *
 
93
     * @var unknown_type
 
94
     */
 
95
    protected $_definition = null;
 
96
    
 
97
    /**
 
98
     * Optional definition config name used in concrete implementation
 
99
     *
 
100
     * @var string
 
101
     */
 
102
    protected $_definitionConfigName = null;
 
103
    
 
104
    /**
 
105
     * Default cache for information provided by the adapter's describeTable() method.
 
106
     *
 
107
     * @var Zend_Cache_Core
 
108
     */
 
109
    protected static $_defaultMetadataCache = null;
 
110
 
 
111
    /**
 
112
     * Zend_Db_Adapter_Abstract object.
 
113
     *
 
114
     * @var Zend_Db_Adapter_Abstract
 
115
     */
 
116
    protected $_db;
 
117
 
 
118
    /**
 
119
     * The schema name (default null means current schema)
 
120
     *
 
121
     * @var array
 
122
     */
 
123
    protected $_schema = null;
 
124
 
 
125
    /**
 
126
     * The table name.
 
127
     *
 
128
     * @var string
 
129
     */
 
130
    protected $_name = null;
 
131
 
 
132
    /**
 
133
     * The table column names derived from Zend_Db_Adapter_Abstract::describeTable().
 
134
     *
 
135
     * @var array
 
136
     */
 
137
    protected $_cols;
 
138
 
 
139
    /**
 
140
     * The primary key column or columns.
 
141
     * A compound key should be declared as an array.
 
142
     * You may declare a single-column primary key
 
143
     * as a string.
 
144
     *
 
145
     * @var mixed
 
146
     */
 
147
    protected $_primary = null;
 
148
 
 
149
    /**
 
150
     * If your primary key is a compound key, and one of the columns uses
 
151
     * an auto-increment or sequence-generated value, set _identity
 
152
     * to the ordinal index in the $_primary array for that column.
 
153
     * Note this index is the position of the column in the primary key,
 
154
     * not the position of the column in the table.  The primary key
 
155
     * array is 1-based.
 
156
     *
 
157
     * @var integer
 
158
     */
 
159
    protected $_identity = 1;
 
160
 
 
161
    /**
 
162
     * Define the logic for new values in the primary key.
 
163
     * May be a string, boolean true, or boolean false.
 
164
     *
 
165
     * @var mixed
 
166
     */
 
167
    protected $_sequence = true;
 
168
 
 
169
    /**
 
170
     * Information provided by the adapter's describeTable() method.
 
171
     *
 
172
     * @var array
 
173
     */
 
174
    protected $_metadata = array();
 
175
 
 
176
    /**
 
177
     * Cache for information provided by the adapter's describeTable() method.
 
178
     *
 
179
     * @var Zend_Cache_Core
 
180
     */
 
181
    protected $_metadataCache = null;
 
182
 
 
183
    /**
 
184
     * Flag: whether or not to cache metadata in the class
 
185
     * @var bool
 
186
     */
 
187
    protected $_metadataCacheInClass = true;
 
188
 
 
189
    /**
 
190
     * Classname for row
 
191
     *
 
192
     * @var string
 
193
     */
 
194
    protected $_rowClass = 'Zend_Db_Table_Row';
 
195
 
 
196
    /**
 
197
     * Classname for rowset
 
198
     *
 
199
     * @var string
 
200
     */
 
201
    protected $_rowsetClass = 'Zend_Db_Table_Rowset';
 
202
 
 
203
    /**
 
204
     * Associative array map of declarative referential integrity rules.
 
205
     * This array has one entry per foreign key in the current table.
 
206
     * Each key is a mnemonic name for one reference rule.
 
207
     *
 
208
     * Each value is also an associative array, with the following keys:
 
209
     * - columns       = array of names of column(s) in the child table.
 
210
     * - refTableClass = class name of the parent table.
 
211
     * - refColumns    = array of names of column(s) in the parent table,
 
212
     *                   in the same order as those in the 'columns' entry.
 
213
     * - onDelete      = "cascade" means that a delete in the parent table also
 
214
     *                   causes a delete of referencing rows in the child table.
 
215
     * - onUpdate      = "cascade" means that an update of primary key values in
 
216
     *                   the parent table also causes an update of referencing
 
217
     *                   rows in the child table.
 
218
     *
 
219
     * @var array
 
220
     */
 
221
    protected $_referenceMap = array();
 
222
 
 
223
    /**
 
224
     * Simple array of class names of tables that are "children" of the current
 
225
     * table, in other words tables that contain a foreign key to this one.
 
226
     * Array elements are not table names; they are class names of classes that
 
227
     * extend Zend_Db_Table_Abstract.
 
228
     *
 
229
     * @var array
 
230
     */
 
231
    protected $_dependentTables = array();
 
232
 
 
233
 
 
234
    protected $_defaultSource = self::DEFAULT_NONE;
 
235
    protected $_defaultValues = array();
 
236
 
 
237
    /**
 
238
     * Constructor.
 
239
     *
 
240
     * Supported params for $config are:
 
241
     * - db              = user-supplied instance of database connector,
 
242
     *                     or key name of registry instance.
 
243
     * - name            = table name.
 
244
     * - primary         = string or array of primary key(s).
 
245
     * - rowClass        = row class name.
 
246
     * - rowsetClass     = rowset class name.
 
247
     * - referenceMap    = array structure to declare relationship
 
248
     *                     to parent tables.
 
249
     * - dependentTables = array of child tables.
 
250
     * - metadataCache   = cache for information from adapter describeTable().
 
251
     *
 
252
     * @param  mixed $config Array of user-specified config options, or just the Db Adapter.
 
253
     * @return void
 
254
     */
 
255
    public function __construct($config = array())
 
256
    {
 
257
        /**
 
258
         * Allow a scalar argument to be the Adapter object or Registry key.
 
259
         */
 
260
        if (!is_array($config)) {
 
261
            $config = array(self::ADAPTER => $config);
 
262
        }
 
263
 
 
264
        if ($config) {
 
265
            $this->setOptions($config);
 
266
        }
 
267
 
 
268
        $this->_setup();
 
269
        $this->init();
 
270
    }
 
271
 
 
272
    /**
 
273
     * setOptions()
 
274
     *
 
275
     * @param array $options
 
276
     * @return Zend_Db_Table_Abstract
 
277
     */
 
278
    public function setOptions(Array $options)
 
279
    {
 
280
        foreach ($options as $key => $value) {
 
281
            switch ($key) {
 
282
                case self::ADAPTER:
 
283
                    $this->_setAdapter($value);
 
284
                    break;
 
285
                case self::DEFINITION:
 
286
                    $this->setDefinition($value);
 
287
                    break;
 
288
                case self::DEFINITION_CONFIG_NAME:
 
289
                    $this->setDefinitionConfigName($value);
 
290
                    break;
 
291
                case self::SCHEMA:
 
292
                    $this->_schema = (string) $value;
 
293
                    break;
 
294
                case self::NAME:
 
295
                    $this->_name = (string) $value;
 
296
                    break;
 
297
                case self::PRIMARY:
 
298
                    $this->_primary = (array) $value;
 
299
                    break;
 
300
                case self::ROW_CLASS:
 
301
                    $this->setRowClass($value);
 
302
                    break;
 
303
                case self::ROWSET_CLASS:
 
304
                    $this->setRowsetClass($value);
 
305
                    break;
 
306
                case self::REFERENCE_MAP:
 
307
                    $this->setReferences($value);
 
308
                    break;
 
309
                case self::DEPENDENT_TABLES:
 
310
                    $this->setDependentTables($value);
 
311
                    break;
 
312
                case self::METADATA_CACHE:
 
313
                    $this->_setMetadataCache($value);
 
314
                    break;
 
315
                case self::METADATA_CACHE_IN_CLASS:
 
316
                    $this->setMetadataCacheInClass($value);
 
317
                    break;
 
318
                case self::SEQUENCE:
 
319
                    $this->_setSequence($value);
 
320
                    break;
 
321
                default:
 
322
                    // ignore unrecognized configuration directive
 
323
                    break;
 
324
            }
 
325
        }
 
326
 
 
327
        return $this;
 
328
    }
 
329
    
 
330
    /**
 
331
     * setDefinition()
 
332
     *
 
333
     * @param Zend_Db_Table_Definition $definition
 
334
     * @return Zend_Db_Table_Abstract
 
335
     */
 
336
    public function setDefinition(Zend_Db_Table_Definition $definition)
 
337
    {
 
338
        $this->_definition = $definition;
 
339
        return $this;
 
340
    }
 
341
    
 
342
    /**
 
343
     * getDefinition()
 
344
     *
 
345
     * @return Zend_Db_Table_Definition|null
 
346
     */
 
347
    public function getDefinition()
 
348
    {
 
349
        return $this->_definition;
 
350
    }
 
351
    
 
352
    /**
 
353
     * setDefinitionConfigName()
 
354
     *
 
355
     * @param string $definition
 
356
     * @return Zend_Db_Table_Abstract
 
357
     */
 
358
    public function setDefinitionConfigName($definitionConfigName)
 
359
    {
 
360
        $this->_definitionConfigName = $definitionConfigName;
 
361
        return $this;
 
362
    }
 
363
    
 
364
    /**
 
365
     * getDefinitionConfigName()
 
366
     *
 
367
     * @return string
 
368
     */
 
369
    public function getDefinitionConfigName()
 
370
    {
 
371
        return $this->_definitionConfigName;
 
372
    }
 
373
 
 
374
    /**
 
375
     * @param  string $classname
 
376
     * @return Zend_Db_Table_Abstract Provides a fluent interface
 
377
     */
 
378
    public function setRowClass($classname)
 
379
    {
 
380
        $this->_rowClass = (string) $classname;
 
381
 
 
382
        return $this;
 
383
    }
 
384
 
 
385
    /**
 
386
     * @return string
 
387
     */
 
388
    public function getRowClass()
 
389
    {
 
390
        return $this->_rowClass;
 
391
    }
 
392
 
 
393
    /**
 
394
     * @param  string $classname
 
395
     * @return Zend_Db_Table_Abstract Provides a fluent interface
 
396
     */
 
397
    public function setRowsetClass($classname)
 
398
    {
 
399
        $this->_rowsetClass = (string) $classname;
 
400
 
 
401
        return $this;
 
402
    }
 
403
 
 
404
    /**
 
405
     * @return string
 
406
     */
 
407
    public function getRowsetClass()
 
408
    {
 
409
        return $this->_rowsetClass;
 
410
    }
 
411
 
 
412
    /**
 
413
     * Add a reference to the reference map
 
414
     *
 
415
     * @param string $ruleKey
 
416
     * @param string|array $columns
 
417
     * @param string $refTableClass
 
418
     * @param string|array $refColumns
 
419
     * @param string $onDelete
 
420
     * @param string $onUpdate
 
421
     * @return Zend_Db_Table_Abstract
 
422
     */
 
423
    public function addReference($ruleKey, $columns, $refTableClass, $refColumns,
 
424
                                 $onDelete = null, $onUpdate = null)
 
425
    {
 
426
        $reference = array(self::COLUMNS         => (array) $columns,
 
427
                           self::REF_TABLE_CLASS => $refTableClass,
 
428
                           self::REF_COLUMNS     => (array) $refColumns);
 
429
 
 
430
        if (!empty($onDelete)) {
 
431
            $reference[self::ON_DELETE] = $onDelete;
 
432
        }
 
433
 
 
434
        if (!empty($onUpdate)) {
 
435
            $reference[self::ON_UPDATE] = $onUpdate;
 
436
        }
 
437
 
 
438
        $this->_referenceMap[$ruleKey] = $reference;
 
439
 
 
440
        return $this;
 
441
    }
 
442
 
 
443
    /**
 
444
     * @param array $referenceMap
 
445
     * @return Zend_Db_Table_Abstract Provides a fluent interface
 
446
     */
 
447
    public function setReferences(array $referenceMap)
 
448
    {
 
449
        $this->_referenceMap = $referenceMap;
 
450
 
 
451
        return $this;
 
452
    }
 
453
 
 
454
    /**
 
455
     * @param string $tableClassname
 
456
     * @param string $ruleKey OPTIONAL
 
457
     * @return array
 
458
     * @throws Zend_Db_Table_Exception
 
459
     */
 
460
    public function getReference($tableClassname, $ruleKey = null)
 
461
    {
 
462
        $thisClass = get_class($this);
 
463
        if ($thisClass === 'Zend_Db_Table') {
 
464
            $thisClass = $this->_definitionConfigName;
 
465
        }
 
466
        $refMap = $this->_getReferenceMapNormalized();
 
467
        if ($ruleKey !== null) {
 
468
            if (!isset($refMap[$ruleKey])) {
 
469
                require_once "Zend/Db/Table/Exception.php";
 
470
                throw new Zend_Db_Table_Exception("No reference rule \"$ruleKey\" from table $thisClass to table $tableClassname");
 
471
            }
 
472
            if ($refMap[$ruleKey][self::REF_TABLE_CLASS] != $tableClassname) {
 
473
                require_once "Zend/Db/Table/Exception.php";
 
474
                throw new Zend_Db_Table_Exception("Reference rule \"$ruleKey\" does not reference table $tableClassname");
 
475
            }
 
476
            return $refMap[$ruleKey];
 
477
        }
 
478
        foreach ($refMap as $reference) {
 
479
            if ($reference[self::REF_TABLE_CLASS] == $tableClassname) {
 
480
                return $reference;
 
481
            }
 
482
        }
 
483
        require_once "Zend/Db/Table/Exception.php";
 
484
        throw new Zend_Db_Table_Exception("No reference from table $thisClass to table $tableClassname");
 
485
    }
 
486
 
 
487
    /**
 
488
     * @param  array $dependentTables
 
489
     * @return Zend_Db_Table_Abstract Provides a fluent interface
 
490
     */
 
491
    public function setDependentTables(array $dependentTables)
 
492
    {
 
493
        $this->_dependentTables = $dependentTables;
 
494
 
 
495
        return $this;
 
496
    }
 
497
 
 
498
    /**
 
499
     * @return array
 
500
     */
 
501
    public function getDependentTables()
 
502
    {
 
503
        return $this->_dependentTables;
 
504
    }
 
505
 
 
506
    /**
 
507
     * set the defaultSource property - this tells the table class where to find default values
 
508
     *
 
509
     * @param string $defaultSource
 
510
     * @return Zend_Db_Table_Abstract
 
511
     */
 
512
    public function setDefaultSource($defaultSource = self::DEFAULT_NONE)
 
513
    {
 
514
        if (!in_array($defaultSource, array(self::DEFAULT_CLASS, self::DEFAULT_DB, self::DEFAULT_NONE))) {
 
515
            $defaultSource = self::DEFAULT_NONE;
 
516
        }
 
517
 
 
518
        $this->_defaultSource = $defaultSource;
 
519
        return $this;
 
520
    }
 
521
 
 
522
    /**
 
523
     * returns the default source flag that determines where defaultSources come from
 
524
     *
 
525
     * @return unknown
 
526
     */
 
527
    public function getDefaultSource()
 
528
    {
 
529
        return $this->_defaultSource;
 
530
    }
 
531
 
 
532
    /**
 
533
     * set the default values for the table class
 
534
     *
 
535
     * @param array $defaultValues
 
536
     * @return Zend_Db_Table_Abstract
 
537
     */
 
538
    public function setDefaultValues(Array $defaultValues)
 
539
    {
 
540
        foreach ($defaultValues as $defaultName => $defaultValue) {
 
541
            if (array_key_exists($defaultName, $this->_metadata)) {
 
542
                $this->_defaultValues[$defaultName] = $defaultValue;
 
543
            }
 
544
        }
 
545
        return $this;
 
546
    }
 
547
 
 
548
    public function getDefaultValues()
 
549
    {
 
550
        return $this->_defaultValues;
 
551
    }
 
552
 
 
553
 
 
554
    /**
 
555
     * Sets the default Zend_Db_Adapter_Abstract for all Zend_Db_Table objects.
 
556
     *
 
557
     * @param  mixed $db Either an Adapter object, or a string naming a Registry key
 
558
     * @return void
 
559
     */
 
560
    public static function setDefaultAdapter($db = null)
 
561
    {
 
562
        self::$_defaultDb = self::_setupAdapter($db);
 
563
    }
 
564
 
 
565
    /**
 
566
     * Gets the default Zend_Db_Adapter_Abstract for all Zend_Db_Table objects.
 
567
     *
 
568
     * @return Zend_Db_Adapter_Abstract or null
 
569
     */
 
570
    public static function getDefaultAdapter()
 
571
    {
 
572
        return self::$_defaultDb;
 
573
    }
 
574
 
 
575
    /**
 
576
     * @param  mixed $db Either an Adapter object, or a string naming a Registry key
 
577
     * @return Zend_Db_Table_Abstract Provides a fluent interface
 
578
     */
 
579
    protected function _setAdapter($db)
 
580
    {
 
581
        $this->_db = self::_setupAdapter($db);
 
582
        return $this;
 
583
    }
 
584
 
 
585
    /**
 
586
     * Gets the Zend_Db_Adapter_Abstract for this particular Zend_Db_Table object.
 
587
     *
 
588
     * @return Zend_Db_Adapter_Abstract
 
589
     */
 
590
    public function getAdapter()
 
591
    {
 
592
        return $this->_db;
 
593
    }
 
594
 
 
595
    /**
 
596
     * @param  mixed $db Either an Adapter object, or a string naming a Registry key
 
597
     * @return Zend_Db_Adapter_Abstract
 
598
     * @throws Zend_Db_Table_Exception
 
599
     */
 
600
    protected static function _setupAdapter($db)
 
601
    {
 
602
        if ($db === null) {
 
603
            return null;
 
604
        }
 
605
        if (is_string($db)) {
 
606
            require_once 'Zend/Registry.php';
 
607
            $db = Zend_Registry::get($db);
 
608
        }
 
609
        if (!$db instanceof Zend_Db_Adapter_Abstract) {
 
610
            require_once 'Zend/Db/Table/Exception.php';
 
611
            throw new Zend_Db_Table_Exception('Argument must be of type Zend_Db_Adapter_Abstract, or a Registry key where a Zend_Db_Adapter_Abstract object is stored');
 
612
        }
 
613
        return $db;
 
614
    }
 
615
 
 
616
    /**
 
617
     * Sets the default metadata cache for information returned by Zend_Db_Adapter_Abstract::describeTable().
 
618
     *
 
619
     * If $defaultMetadataCache is null, then no metadata cache is used by default.
 
620
     *
 
621
     * @param  mixed $metadataCache Either a Cache object, or a string naming a Registry key
 
622
     * @return void
 
623
     */
 
624
    public static function setDefaultMetadataCache($metadataCache = null)
 
625
    {
 
626
        self::$_defaultMetadataCache = self::_setupMetadataCache($metadataCache);
 
627
    }
 
628
 
 
629
    /**
 
630
     * Gets the default metadata cache for information returned by Zend_Db_Adapter_Abstract::describeTable().
 
631
     *
 
632
     * @return Zend_Cache_Core or null
 
633
     */
 
634
    public static function getDefaultMetadataCache()
 
635
    {
 
636
        return self::$_defaultMetadataCache;
 
637
    }
 
638
 
 
639
    /**
 
640
     * Sets the metadata cache for information returned by Zend_Db_Adapter_Abstract::describeTable().
 
641
     *
 
642
     * If $metadataCache is null, then no metadata cache is used. Since there is no opportunity to reload metadata
 
643
     * after instantiation, this method need not be public, particularly because that it would have no effect
 
644
     * results in unnecessary API complexity. To configure the metadata cache, use the metadataCache configuration
 
645
     * option for the class constructor upon instantiation.
 
646
     *
 
647
     * @param  mixed $metadataCache Either a Cache object, or a string naming a Registry key
 
648
     * @return Zend_Db_Table_Abstract Provides a fluent interface
 
649
     */
 
650
    protected function _setMetadataCache($metadataCache)
 
651
    {
 
652
        $this->_metadataCache = self::_setupMetadataCache($metadataCache);
 
653
        return $this;
 
654
    }
 
655
 
 
656
    /**
 
657
     * Gets the metadata cache for information returned by Zend_Db_Adapter_Abstract::describeTable().
 
658
     *
 
659
     * @return Zend_Cache_Core or null
 
660
     */
 
661
    public function getMetadataCache()
 
662
    {
 
663
        return $this->_metadataCache;
 
664
    }
 
665
 
 
666
    /**
 
667
     * Indicate whether metadata should be cached in the class for the duration
 
668
     * of the instance
 
669
     *
 
670
     * @param  bool $flag
 
671
     * @return Zend_Db_Table_Abstract
 
672
     */
 
673
    public function setMetadataCacheInClass($flag)
 
674
    {
 
675
        $this->_metadataCacheInClass = (bool) $flag;
 
676
        return $this;
 
677
    }
 
678
 
 
679
    /**
 
680
     * Retrieve flag indicating if metadata should be cached for duration of
 
681
     * instance
 
682
     *
 
683
     * @return bool
 
684
     */
 
685
    public function metadataCacheInClass()
 
686
    {
 
687
        return $this->_metadataCacheInClass;
 
688
    }
 
689
 
 
690
    /**
 
691
     * @param mixed $metadataCache Either a Cache object, or a string naming a Registry key
 
692
     * @return Zend_Cache_Core
 
693
     * @throws Zend_Db_Table_Exception
 
694
     */
 
695
    protected static function _setupMetadataCache($metadataCache)
 
696
    {
 
697
        if ($metadataCache === null) {
 
698
            return null;
 
699
        }
 
700
        if (is_string($metadataCache)) {
 
701
            require_once 'Zend/Registry.php';
 
702
            $metadataCache = Zend_Registry::get($metadataCache);
 
703
        }
 
704
        if (!$metadataCache instanceof Zend_Cache_Core) {
 
705
            require_once 'Zend/Db/Table/Exception.php';
 
706
            throw new Zend_Db_Table_Exception('Argument must be of type Zend_Cache_Core, or a Registry key where a Zend_Cache_Core object is stored');
 
707
        }
 
708
        return $metadataCache;
 
709
    }
 
710
 
 
711
    /**
 
712
     * Sets the sequence member, which defines the behavior for generating
 
713
     * primary key values in new rows.
 
714
     * - If this is a string, then the string names the sequence object.
 
715
     * - If this is boolean true, then the key uses an auto-incrementing
 
716
     *   or identity mechanism.
 
717
     * - If this is boolean false, then the key is user-defined.
 
718
     *   Use this for natural keys, for example.
 
719
     *
 
720
     * @param mixed $sequence
 
721
     * @return Zend_Db_Table_Adapter_Abstract Provides a fluent interface
 
722
     */
 
723
    protected function _setSequence($sequence)
 
724
    {
 
725
        $this->_sequence = $sequence;
 
726
 
 
727
        return $this;
 
728
    }
 
729
 
 
730
    /**
 
731
     * Turnkey for initialization of a table object.
 
732
     * Calls other protected methods for individual tasks, to make it easier
 
733
     * for a subclass to override part of the setup logic.
 
734
     *
 
735
     * @return void
 
736
     */
 
737
    protected function _setup()
 
738
    {
 
739
        $this->_setupDatabaseAdapter();
 
740
        $this->_setupTableName();
 
741
    }
 
742
 
 
743
    /**
 
744
     * Initialize database adapter.
 
745
     *
 
746
     * @return void
 
747
     */
 
748
    protected function _setupDatabaseAdapter()
 
749
    {
 
750
        if (! $this->_db) {
 
751
            $this->_db = self::getDefaultAdapter();
 
752
            if (!$this->_db instanceof Zend_Db_Adapter_Abstract) {
 
753
                require_once 'Zend/Db/Table/Exception.php';
 
754
                throw new Zend_Db_Table_Exception('No adapter found for ' . get_class($this));
 
755
            }
 
756
        }
 
757
    }
 
758
 
 
759
    /**
 
760
     * Initialize table and schema names.
 
761
     *
 
762
     * If the table name is not set in the class definition,
 
763
     * use the class name itself as the table name.
 
764
     *
 
765
     * A schema name provided with the table name (e.g., "schema.table") overrides
 
766
     * any existing value for $this->_schema.
 
767
     *
 
768
     * @return void
 
769
     */
 
770
    protected function _setupTableName()
 
771
    {
 
772
        if (! $this->_name) {
 
773
            $this->_name = get_class($this);
 
774
        } else if (strpos($this->_name, '.')) {
 
775
            list($this->_schema, $this->_name) = explode('.', $this->_name);
 
776
        }
 
777
    }
 
778
 
 
779
    /**
 
780
     * Initializes metadata.
 
781
     *
 
782
     * If metadata cannot be loaded from cache, adapter's describeTable() method is called to discover metadata
 
783
     * information. Returns true if and only if the metadata are loaded from cache.
 
784
     *
 
785
     * @return boolean
 
786
     * @throws Zend_Db_Table_Exception
 
787
     */
 
788
    protected function _setupMetadata()
 
789
    {
 
790
        if ($this->metadataCacheInClass() && (count($this->_metadata) > 0)) {
 
791
            return true;
 
792
        }
 
793
 
 
794
        // Assume that metadata will be loaded from cache
 
795
        $isMetadataFromCache = true;
 
796
 
 
797
        // If $this has no metadata cache but the class has a default metadata cache
 
798
        if (null === $this->_metadataCache && null !== self::$_defaultMetadataCache) {
 
799
            // Make $this use the default metadata cache of the class
 
800
            $this->_setMetadataCache(self::$_defaultMetadataCache);
 
801
        }
 
802
 
 
803
        // If $this has a metadata cache
 
804
        if (null !== $this->_metadataCache) {
 
805
            // Define the cache identifier where the metadata are saved
 
806
            $cacheId = md5("$this->_schema.$this->_name");
 
807
        }
 
808
 
 
809
        // If $this has no metadata cache or metadata cache misses
 
810
        if (null === $this->_metadataCache || !($metadata = $this->_metadataCache->load($cacheId))) {
 
811
            // Metadata are not loaded from cache
 
812
            $isMetadataFromCache = false;
 
813
            // Fetch metadata from the adapter's describeTable() method
 
814
            $metadata = $this->_db->describeTable($this->_name, $this->_schema);
 
815
            // If $this has a metadata cache, then cache the metadata
 
816
            if (null !== $this->_metadataCache && !$this->_metadataCache->save($metadata, $cacheId)) {
 
817
                /**
 
818
                 * @see Zend_Db_Table_Exception
 
819
                 */
 
820
                require_once 'Zend/Db/Table/Exception.php';
 
821
                throw new Zend_Db_Table_Exception('Failed saving metadata to metadataCache');
 
822
            }
 
823
        }
 
824
 
 
825
        // Assign the metadata to $this
 
826
        $this->_metadata = $metadata;
 
827
 
 
828
        // Return whether the metadata were loaded from cache
 
829
        return $isMetadataFromCache;
 
830
    }
 
831
 
 
832
    /**
 
833
     * Retrieve table columns
 
834
     *
 
835
     * @return array
 
836
     */
 
837
    protected function _getCols()
 
838
    {
 
839
        if (null === $this->_cols) {
 
840
            $this->_setupMetadata();
 
841
            $this->_cols = array_keys($this->_metadata);
 
842
        }
 
843
        return $this->_cols;
 
844
    }
 
845
 
 
846
    /**
 
847
     * Initialize primary key from metadata.
 
848
     * If $_primary is not defined, discover primary keys
 
849
     * from the information returned by describeTable().
 
850
     *
 
851
     * @return void
 
852
     * @throws Zend_Db_Table_Exception
 
853
     */
 
854
    protected function _setupPrimaryKey()
 
855
    {
 
856
        if (!$this->_primary) {
 
857
            $this->_setupMetadata();
 
858
            $this->_primary = array();
 
859
            foreach ($this->_metadata as $col) {
 
860
                if ($col['PRIMARY']) {
 
861
                    $this->_primary[ $col['PRIMARY_POSITION'] ] = $col['COLUMN_NAME'];
 
862
                    if ($col['IDENTITY']) {
 
863
                        $this->_identity = $col['PRIMARY_POSITION'];
 
864
                    }
 
865
                }
 
866
            }
 
867
            // if no primary key was specified and none was found in the metadata
 
868
            // then throw an exception.
 
869
            if (empty($this->_primary)) {
 
870
                require_once 'Zend/Db/Table/Exception.php';
 
871
                throw new Zend_Db_Table_Exception('A table must have a primary key, but none was found');
 
872
            }
 
873
        } else if (!is_array($this->_primary)) {
 
874
            $this->_primary = array(1 => $this->_primary);
 
875
        } else if (isset($this->_primary[0])) {
 
876
            array_unshift($this->_primary, null);
 
877
            unset($this->_primary[0]);
 
878
        }
 
879
 
 
880
        $cols = $this->_getCols();
 
881
        if (! array_intersect((array) $this->_primary, $cols) == (array) $this->_primary) {
 
882
            require_once 'Zend/Db/Table/Exception.php';
 
883
            throw new Zend_Db_Table_Exception("Primary key column(s) ("
 
884
                . implode(',', (array) $this->_primary)
 
885
                . ") are not columns in this table ("
 
886
                . implode(',', $cols)
 
887
                . ")");
 
888
        }
 
889
 
 
890
        $primary    = (array) $this->_primary;
 
891
        $pkIdentity = $primary[(int) $this->_identity];
 
892
 
 
893
        /**
 
894
         * Special case for PostgreSQL: a SERIAL key implicitly uses a sequence
 
895
         * object whose name is "<table>_<column>_seq".
 
896
         */
 
897
        if ($this->_sequence === true && $this->_db instanceof Zend_Db_Adapter_Pdo_Pgsql) {
 
898
            $this->_sequence = $this->_db->quoteIdentifier("{$this->_name}_{$pkIdentity}_seq");
 
899
            if ($this->_schema) {
 
900
                $this->_sequence = $this->_db->quoteIdentifier($this->_schema) . '.' . $this->_sequence;
 
901
            }
 
902
        }
 
903
    }
 
904
 
 
905
    /**
 
906
     * Returns a normalized version of the reference map
 
907
     *
 
908
     * @return array
 
909
     */
 
910
    protected function _getReferenceMapNormalized()
 
911
    {
 
912
        $referenceMapNormalized = array();
 
913
 
 
914
        foreach ($this->_referenceMap as $rule => $map) {
 
915
 
 
916
            $referenceMapNormalized[$rule] = array();
 
917
 
 
918
            foreach ($map as $key => $value) {
 
919
                switch ($key) {
 
920
 
 
921
                    // normalize COLUMNS and REF_COLUMNS to arrays
 
922
                    case self::COLUMNS:
 
923
                    case self::REF_COLUMNS:
 
924
                        if (!is_array($value)) {
 
925
                            $referenceMapNormalized[$rule][$key] = array($value);
 
926
                        } else {
 
927
                            $referenceMapNormalized[$rule][$key] = $value;
 
928
                        }
 
929
                        break;
 
930
 
 
931
                    // other values are copied as-is
 
932
                    default:
 
933
                        $referenceMapNormalized[$rule][$key] = $value;
 
934
                        break;
 
935
                }
 
936
            }
 
937
        }
 
938
 
 
939
        return $referenceMapNormalized;
 
940
    }
 
941
 
 
942
    /**
 
943
     * Initialize object
 
944
     *
 
945
     * Called from {@link __construct()} as final step of object instantiation.
 
946
     *
 
947
     * @return void
 
948
     */
 
949
    public function init()
 
950
    {
 
951
    }
 
952
 
 
953
    /**
 
954
     * Returns table information.
 
955
     *
 
956
     * You can elect to return only a part of this information by supplying its key name,
 
957
     * otherwise all information is returned as an array.
 
958
     *
 
959
     * @param  $key The specific info part to return OPTIONAL
 
960
     * @return mixed
 
961
     */
 
962
    public function info($key = null)
 
963
    {
 
964
        $this->_setupPrimaryKey();
 
965
 
 
966
        $info = array(
 
967
            self::SCHEMA           => $this->_schema,
 
968
            self::NAME             => $this->_name,
 
969
            self::COLS             => $this->_getCols(),
 
970
            self::PRIMARY          => (array) $this->_primary,
 
971
            self::METADATA         => $this->_metadata,
 
972
            self::ROW_CLASS        => $this->_rowClass,
 
973
            self::ROWSET_CLASS     => $this->_rowsetClass,
 
974
            self::REFERENCE_MAP    => $this->_referenceMap,
 
975
            self::DEPENDENT_TABLES => $this->_dependentTables,
 
976
            self::SEQUENCE         => $this->_sequence
 
977
        );
 
978
 
 
979
        if ($key === null) {
 
980
            return $info;
 
981
        }
 
982
 
 
983
        if (!array_key_exists($key, $info)) {
 
984
            require_once 'Zend/Db/Table/Exception.php';
 
985
            throw new Zend_Db_Table_Exception('There is no table information for the key "' . $key . '"');
 
986
        }
 
987
 
 
988
        return $info[$key];
 
989
    }
 
990
 
 
991
    /**
 
992
     * Returns an instance of a Zend_Db_Table_Select object.
 
993
     *
 
994
     * @param bool $withFromPart Whether or not to include the from part of the select based on the table
 
995
     * @return Zend_Db_Table_Select
 
996
     */
 
997
    public function select($withFromPart = self::SELECT_WITHOUT_FROM_PART)
 
998
    {
 
999
        require_once 'Zend/Db/Table/Select.php';
 
1000
        $select = new Zend_Db_Table_Select($this);
 
1001
        if ($withFromPart == self::SELECT_WITH_FROM_PART) {
 
1002
            $select->from($this->info(self::NAME), Zend_Db_Table_Select::SQL_WILDCARD, $this->info(self::SCHEMA));
 
1003
        }
 
1004
        return $select;
 
1005
    }
 
1006
 
 
1007
    /**
 
1008
     * Inserts a new row.
 
1009
     *
 
1010
     * @param  array  $data  Column-value pairs.
 
1011
     * @return mixed         The primary key of the row inserted.
 
1012
     */
 
1013
    public function insert(array $data)
 
1014
    {
 
1015
        $this->_setupPrimaryKey();
 
1016
 
 
1017
        /**
 
1018
         * Zend_Db_Table assumes that if you have a compound primary key
 
1019
         * and one of the columns in the key uses a sequence,
 
1020
         * it's the _first_ column in the compound key.
 
1021
         */
 
1022
        $primary = (array) $this->_primary;
 
1023
        $pkIdentity = $primary[(int)$this->_identity];
 
1024
 
 
1025
        /**
 
1026
         * If this table uses a database sequence object and the data does not
 
1027
         * specify a value, then get the next ID from the sequence and add it
 
1028
         * to the row.  We assume that only the first column in a compound
 
1029
         * primary key takes a value from a sequence.
 
1030
         */
 
1031
        if (is_string($this->_sequence) && !isset($data[$pkIdentity])) {
 
1032
            $data[$pkIdentity] = $this->_db->nextSequenceId($this->_sequence);
 
1033
        }
 
1034
 
 
1035
        /**
 
1036
         * If the primary key can be generated automatically, and no value was
 
1037
         * specified in the user-supplied data, then omit it from the tuple.
 
1038
         */
 
1039
        if (array_key_exists($pkIdentity, $data) && $data[$pkIdentity] === null) {
 
1040
            unset($data[$pkIdentity]);
 
1041
        }
 
1042
 
 
1043
        /**
 
1044
         * INSERT the new row.
 
1045
         */
 
1046
        $tableSpec = ($this->_schema ? $this->_schema . '.' : '') . $this->_name;
 
1047
        $this->_db->insert($tableSpec, $data);
 
1048
 
 
1049
        /**
 
1050
         * Fetch the most recent ID generated by an auto-increment
 
1051
         * or IDENTITY column, unless the user has specified a value,
 
1052
         * overriding the auto-increment mechanism.
 
1053
         */
 
1054
        if ($this->_sequence === true && !isset($data[$pkIdentity])) {
 
1055
            $data[$pkIdentity] = $this->_db->lastInsertId();
 
1056
        }
 
1057
 
 
1058
        /**
 
1059
         * Return the primary key value if the PK is a single column,
 
1060
         * else return an associative array of the PK column/value pairs.
 
1061
         */
 
1062
        $pkData = array_intersect_key($data, array_flip($primary));
 
1063
        if (count($primary) == 1) {
 
1064
            reset($pkData);
 
1065
            return current($pkData);
 
1066
        }
 
1067
 
 
1068
        return $pkData;
 
1069
    }
 
1070
 
 
1071
    /**
 
1072
     * Check if the provided column is an identity of the table
 
1073
     *
 
1074
     * @param  string $column
 
1075
     * @throws Zend_Db_Table_Exception
 
1076
     * @return boolean
 
1077
     */
 
1078
    public function isIdentity($column)
 
1079
    {
 
1080
        $this->_setupPrimaryKey();
 
1081
 
 
1082
        if (!isset($this->_metadata[$column])) {
 
1083
            /**
 
1084
             * @see Zend_Db_Table_Exception
 
1085
             */
 
1086
            require_once 'Zend/Db/Table/Exception.php';
 
1087
 
 
1088
            throw new Zend_Db_Table_Exception('Column "' . $column . '" not found in table.');
 
1089
        }
 
1090
 
 
1091
        return (bool) $this->_metadata[$column]['IDENTITY'];
 
1092
    }
 
1093
 
 
1094
    /**
 
1095
     * Updates existing rows.
 
1096
     *
 
1097
     * @param  array        $data  Column-value pairs.
 
1098
     * @param  array|string $where An SQL WHERE clause, or an array of SQL WHERE clauses.
 
1099
     * @return int          The number of rows updated.
 
1100
     */
 
1101
    public function update(array $data, $where)
 
1102
    {
 
1103
        $tableSpec = ($this->_schema ? $this->_schema . '.' : '') . $this->_name;
 
1104
        return $this->_db->update($tableSpec, $data, $where);
 
1105
    }
 
1106
 
 
1107
    /**
 
1108
     * Called by a row object for the parent table's class during save() method.
 
1109
     *
 
1110
     * @param  string $parentTableClassname
 
1111
     * @param  array  $oldPrimaryKey
 
1112
     * @param  array  $newPrimaryKey
 
1113
     * @return int
 
1114
     */
 
1115
    public function _cascadeUpdate($parentTableClassname, array $oldPrimaryKey, array $newPrimaryKey)
 
1116
    {
 
1117
        $this->_setupMetadata();
 
1118
        $rowsAffected = 0;
 
1119
        foreach ($this->_getReferenceMapNormalized() as $map) {
 
1120
            if ($map[self::REF_TABLE_CLASS] == $parentTableClassname && isset($map[self::ON_UPDATE])) {
 
1121
                switch ($map[self::ON_UPDATE]) {
 
1122
                    case self::CASCADE:
 
1123
                        $newRefs = array();
 
1124
                        $where = array();
 
1125
                        for ($i = 0; $i < count($map[self::COLUMNS]); ++$i) {
 
1126
                            $col = $this->_db->foldCase($map[self::COLUMNS][$i]);
 
1127
                            $refCol = $this->_db->foldCase($map[self::REF_COLUMNS][$i]);
 
1128
                            if (array_key_exists($refCol, $newPrimaryKey)) {
 
1129
                                $newRefs[$col] = $newPrimaryKey[$refCol];
 
1130
                            }
 
1131
                            $type = $this->_metadata[$col]['DATA_TYPE'];
 
1132
                            $where[] = $this->_db->quoteInto(
 
1133
                                $this->_db->quoteIdentifier($col, true) . ' = ?',
 
1134
                                $oldPrimaryKey[$refCol], $type);
 
1135
                        }
 
1136
                        $rowsAffected += $this->update($newRefs, $where);
 
1137
                        break;
 
1138
                    default:
 
1139
                        // no action
 
1140
                        break;
 
1141
                }
 
1142
            }
 
1143
        }
 
1144
        return $rowsAffected;
 
1145
    }
 
1146
 
 
1147
    /**
 
1148
     * Deletes existing rows.
 
1149
     *
 
1150
     * @param  array|string $where SQL WHERE clause(s).
 
1151
     * @return int          The number of rows deleted.
 
1152
     */
 
1153
    public function delete($where)
 
1154
    {
 
1155
        $tableSpec = ($this->_schema ? $this->_schema . '.' : '') . $this->_name;
 
1156
        return $this->_db->delete($tableSpec, $where);
 
1157
    }
 
1158
 
 
1159
    /**
 
1160
     * Called by parent table's class during delete() method.
 
1161
     *
 
1162
     * @param  string $parentTableClassname
 
1163
     * @param  array  $primaryKey
 
1164
     * @return int    Number of affected rows
 
1165
     */
 
1166
    public function _cascadeDelete($parentTableClassname, array $primaryKey)
 
1167
    {
 
1168
        $this->_setupMetadata();
 
1169
        $rowsAffected = 0;
 
1170
        foreach ($this->_getReferenceMapNormalized() as $map) {
 
1171
            if ($map[self::REF_TABLE_CLASS] == $parentTableClassname && isset($map[self::ON_DELETE])) {
 
1172
                switch ($map[self::ON_DELETE]) {
 
1173
                    case self::CASCADE:
 
1174
                        $where = array();
 
1175
                        for ($i = 0; $i < count($map[self::COLUMNS]); ++$i) {
 
1176
                            $col = $this->_db->foldCase($map[self::COLUMNS][$i]);
 
1177
                            $refCol = $this->_db->foldCase($map[self::REF_COLUMNS][$i]);
 
1178
                            $type = $this->_metadata[$col]['DATA_TYPE'];
 
1179
                            $where[] = $this->_db->quoteInto(
 
1180
                                $this->_db->quoteIdentifier($col, true) . ' = ?',
 
1181
                                $primaryKey[$refCol], $type);
 
1182
                        }
 
1183
                        $rowsAffected += $this->delete($where);
 
1184
                        break;
 
1185
                    default:
 
1186
                        // no action
 
1187
                        break;
 
1188
                }
 
1189
            }
 
1190
        }
 
1191
        return $rowsAffected;
 
1192
    }
 
1193
 
 
1194
    /**
 
1195
     * Fetches rows by primary key.  The argument specifies one or more primary
 
1196
     * key value(s).  To find multiple rows by primary key, the argument must
 
1197
     * be an array.
 
1198
     *
 
1199
     * This method accepts a variable number of arguments.  If the table has a
 
1200
     * multi-column primary key, the number of arguments must be the same as
 
1201
     * the number of columns in the primary key.  To find multiple rows in a
 
1202
     * table with a multi-column primary key, each argument must be an array
 
1203
     * with the same number of elements.
 
1204
     *
 
1205
     * The find() method always returns a Rowset object, even if only one row
 
1206
     * was found.
 
1207
     *
 
1208
     * @param  mixed $key The value(s) of the primary keys.
 
1209
     * @return Zend_Db_Table_Rowset_Abstract Row(s) matching the criteria.
 
1210
     * @throws Zend_Db_Table_Exception
 
1211
     */
 
1212
    public function find()
 
1213
    {
 
1214
        $this->_setupPrimaryKey();
 
1215
        $args = func_get_args();
 
1216
        $keyNames = array_values((array) $this->_primary);
 
1217
 
 
1218
        if (count($args) < count($keyNames)) {
 
1219
            require_once 'Zend/Db/Table/Exception.php';
 
1220
            throw new Zend_Db_Table_Exception("Too few columns for the primary key");
 
1221
        }
 
1222
 
 
1223
        if (count($args) > count($keyNames)) {
 
1224
            require_once 'Zend/Db/Table/Exception.php';
 
1225
            throw new Zend_Db_Table_Exception("Too many columns for the primary key");
 
1226
        }
 
1227
 
 
1228
        $whereList = array();
 
1229
        $numberTerms = 0;
 
1230
        foreach ($args as $keyPosition => $keyValues) {
 
1231
            // Coerce the values to an array.
 
1232
            // Don't simply typecast to array, because the values
 
1233
            // might be Zend_Db_Expr objects.
 
1234
            if (!is_array($keyValues)) {
 
1235
                $keyValues = array($keyValues);
 
1236
            }
 
1237
            if ($numberTerms == 0) {
 
1238
                $numberTerms = count($keyValues);
 
1239
            } else if (count($keyValues) != $numberTerms) {
 
1240
                require_once 'Zend/Db/Table/Exception.php';
 
1241
                throw new Zend_Db_Table_Exception("Missing value(s) for the primary key");
 
1242
            }
 
1243
            for ($i = 0; $i < count($keyValues); ++$i) {
 
1244
                if (!isset($whereList[$i])) {
 
1245
                    $whereList[$i] = array();
 
1246
                }
 
1247
                $whereList[$i][$keyPosition] = $keyValues[$i];
 
1248
            }
 
1249
        }
 
1250
 
 
1251
        $whereClause = null;
 
1252
        if (count($whereList)) {
 
1253
            $whereOrTerms = array();
 
1254
            $tableName = $this->_db->quoteTableAs($this->_name, null, true);
 
1255
            foreach ($whereList as $keyValueSets) {
 
1256
                $whereAndTerms = array();
 
1257
                foreach ($keyValueSets as $keyPosition => $keyValue) {
 
1258
                    $type = $this->_metadata[$keyNames[$keyPosition]]['DATA_TYPE'];
 
1259
                    $columnName = $this->_db->quoteIdentifier($keyNames[$keyPosition], true);
 
1260
                    $whereAndTerms[] = $this->_db->quoteInto(
 
1261
                        $tableName . '.' . $columnName . ' = ?',
 
1262
                        $keyValue, $type);
 
1263
                }
 
1264
                $whereOrTerms[] = '(' . implode(' AND ', $whereAndTerms) . ')';
 
1265
            }
 
1266
            $whereClause = '(' . implode(' OR ', $whereOrTerms) . ')';
 
1267
        }
 
1268
 
 
1269
        return $this->fetchAll($whereClause);
 
1270
    }
 
1271
 
 
1272
    /**
 
1273
     * Fetches all rows.
 
1274
     *
 
1275
     * Honors the Zend_Db_Adapter fetch mode.
 
1276
     *
 
1277
     * @param string|array|Zend_Db_Table_Select $where  OPTIONAL An SQL WHERE clause or Zend_Db_Table_Select object.
 
1278
     * @param string|array                      $order  OPTIONAL An SQL ORDER clause.
 
1279
     * @param int                               $count  OPTIONAL An SQL LIMIT count.
 
1280
     * @param int                               $offset OPTIONAL An SQL LIMIT offset.
 
1281
     * @return Zend_Db_Table_Rowset_Abstract The row results per the Zend_Db_Adapter fetch mode.
 
1282
     */
 
1283
    public function fetchAll($where = null, $order = null, $count = null, $offset = null)
 
1284
    {
 
1285
        if (!($where instanceof Zend_Db_Table_Select)) {
 
1286
            $select = $this->select();
 
1287
 
 
1288
            if ($where !== null) {
 
1289
                $this->_where($select, $where);
 
1290
            }
 
1291
 
 
1292
            if ($order !== null) {
 
1293
                $this->_order($select, $order);
 
1294
            }
 
1295
 
 
1296
            if ($count !== null || $offset !== null) {
 
1297
                $select->limit($count, $offset);
 
1298
            }
 
1299
 
 
1300
        } else {
 
1301
            $select = $where;
 
1302
        }
 
1303
 
 
1304
        $rows = $this->_fetch($select);
 
1305
 
 
1306
        $data  = array(
 
1307
            'table'    => $this,
 
1308
            'data'     => $rows,
 
1309
            'readOnly' => $select->isReadOnly(),
 
1310
            'rowClass' => $this->_rowClass,
 
1311
            'stored'   => true
 
1312
        );
 
1313
 
 
1314
        if (!class_exists($this->_rowsetClass)) {
 
1315
            require_once 'Zend/Loader.php';
 
1316
            Zend_Loader::loadClass($this->_rowsetClass);
 
1317
        }
 
1318
        return new $this->_rowsetClass($data);
 
1319
    }
 
1320
 
 
1321
    /**
 
1322
     * Fetches one row in an object of type Zend_Db_Table_Row_Abstract,
 
1323
     * or returns null if no row matches the specified criteria.
 
1324
     *
 
1325
     * @param string|array|Zend_Db_Table_Select $where  OPTIONAL An SQL WHERE clause or Zend_Db_Table_Select object.
 
1326
     * @param string|array                      $order  OPTIONAL An SQL ORDER clause.
 
1327
     * @return Zend_Db_Table_Row_Abstract|null The row results per the
 
1328
     *     Zend_Db_Adapter fetch mode, or null if no row found.
 
1329
     */
 
1330
    public function fetchRow($where = null, $order = null)
 
1331
    {
 
1332
        if (!($where instanceof Zend_Db_Table_Select)) {
 
1333
            $select = $this->select();
 
1334
 
 
1335
            if ($where !== null) {
 
1336
                $this->_where($select, $where);
 
1337
            }
 
1338
 
 
1339
            if ($order !== null) {
 
1340
                $this->_order($select, $order);
 
1341
            }
 
1342
 
 
1343
            $select->limit(1);
 
1344
 
 
1345
        } else {
 
1346
            $select = $where->limit(1);
 
1347
        }
 
1348
 
 
1349
        $rows = $this->_fetch($select);
 
1350
 
 
1351
        if (count($rows) == 0) {
 
1352
            return null;
 
1353
        }
 
1354
 
 
1355
        $data = array(
 
1356
            'table'   => $this,
 
1357
            'data'     => $rows[0],
 
1358
            'readOnly' => $select->isReadOnly(),
 
1359
            'stored'  => true
 
1360
        );
 
1361
 
 
1362
        if (!class_exists($this->_rowClass)) {
 
1363
            require_once 'Zend/Loader.php';
 
1364
            Zend_Loader::loadClass($this->_rowClass);
 
1365
        }
 
1366
        return new $this->_rowClass($data);
 
1367
    }
 
1368
 
 
1369
    /**
 
1370
     * Fetches a new blank row (not from the database).
 
1371
     *
 
1372
     * @return Zend_Db_Table_Row_Abstract
 
1373
     * @deprecated since 0.9.3 - use createRow() instead.
 
1374
     */
 
1375
    public function fetchNew()
 
1376
    {
 
1377
        return $this->createRow();
 
1378
    }
 
1379
 
 
1380
    /**
 
1381
     * Fetches a new blank row (not from the database).
 
1382
     *
 
1383
     * @param  array $data OPTIONAL data to populate in the new row.
 
1384
     * @param  string $defaultSource OPTIONAL flag to force default values into new row
 
1385
     * @return Zend_Db_Table_Row_Abstract
 
1386
     */
 
1387
    public function createRow(array $data = array(), $defaultSource = null)
 
1388
    {
 
1389
        $cols     = $this->_getCols();
 
1390
        $defaults = array_combine($cols, array_fill(0, count($cols), null));
 
1391
 
 
1392
        // nothing provided at call-time, take the class value
 
1393
        if ($defaultSource == null) {
 
1394
            $defaultSource = $this->_defaultSource;
 
1395
        }
 
1396
 
 
1397
        if (!in_array($defaultSource, array(self::DEFAULT_CLASS, self::DEFAULT_DB, self::DEFAULT_NONE))) {
 
1398
            $defaultSource = self::DEFAULT_NONE;
 
1399
        }
 
1400
 
 
1401
        if ($defaultSource == self::DEFAULT_DB) {
 
1402
            foreach ($this->_metadata as $metadataName => $metadata) {
 
1403
                if (($metadata['DEFAULT'] != null) &&
 
1404
                    ($metadata['NULLABLE'] !== true || ($metadata['NULLABLE'] === true && isset($this->_defaultValues[$metadataName]) && $this->_defaultValues[$metadataName] === true)) &&
 
1405
                    (!(isset($this->_defaultValues[$metadataName]) && $this->_defaultValues[$metadataName] === false))) {
 
1406
                    $defaults[$metadataName] = $metadata['DEFAULT'];
 
1407
                }
 
1408
            }
 
1409
        } elseif ($defaultSource == self::DEFAULT_CLASS && $this->_defaultValues) {
 
1410
            foreach ($this->_defaultValues as $defaultName => $defaultValue) {
 
1411
                if (array_key_exists($defaultName, $defaults)) {
 
1412
                    $defaults[$defaultName] = $defaultValue;
 
1413
                }
 
1414
            }
 
1415
        }
 
1416
 
 
1417
        $config = array(
 
1418
            'table'    => $this,
 
1419
            'data'     => $defaults,
 
1420
            'readOnly' => false,
 
1421
            'stored'   => false
 
1422
        );
 
1423
 
 
1424
        if (!class_exists($this->_rowClass)) {
 
1425
            require_once 'Zend/Loader.php';
 
1426
            Zend_Loader::loadClass($this->_rowClass);
 
1427
        }
 
1428
        $row = new $this->_rowClass($config);
 
1429
        $row->setFromArray($data);
 
1430
        return $row;
 
1431
    }
 
1432
 
 
1433
    /**
 
1434
     * Generate WHERE clause from user-supplied string or array
 
1435
     *
 
1436
     * @param  string|array $where  OPTIONAL An SQL WHERE clause.
 
1437
     * @return Zend_Db_Table_Select
 
1438
     */
 
1439
    protected function _where(Zend_Db_Table_Select $select, $where)
 
1440
    {
 
1441
        $where = (array) $where;
 
1442
 
 
1443
        foreach ($where as $key => $val) {
 
1444
            // is $key an int?
 
1445
            if (is_int($key)) {
 
1446
                // $val is the full condition
 
1447
                $select->where($val);
 
1448
            } else {
 
1449
                // $key is the condition with placeholder,
 
1450
                // and $val is quoted into the condition
 
1451
                $select->where($key, $val);
 
1452
            }
 
1453
        }
 
1454
 
 
1455
        return $select;
 
1456
    }
 
1457
 
 
1458
    /**
 
1459
     * Generate ORDER clause from user-supplied string or array
 
1460
     *
 
1461
     * @param  string|array $order  OPTIONAL An SQL ORDER clause.
 
1462
     * @return Zend_Db_Table_Select
 
1463
     */
 
1464
    protected function _order(Zend_Db_Table_Select $select, $order)
 
1465
    {
 
1466
        if (!is_array($order)) {
 
1467
            $order = array($order);
 
1468
        }
 
1469
 
 
1470
        foreach ($order as $val) {
 
1471
            $select->order($val);
 
1472
        }
 
1473
 
 
1474
        return $select;
 
1475
    }
 
1476
 
 
1477
    /**
 
1478
     * Support method for fetching rows.
 
1479
     *
 
1480
     * @param  Zend_Db_Table_Select $select  query options.
 
1481
     * @return array An array containing the row results in FETCH_ASSOC mode.
 
1482
     */
 
1483
    protected function _fetch(Zend_Db_Table_Select $select)
 
1484
    {
 
1485
        $stmt = $this->_db->query($select);
 
1486
        $data = $stmt->fetchAll(Zend_Db::FETCH_ASSOC);
 
1487
        return $data;
 
1488
    }
 
1489
 
 
1490
}