Hydrate.php 20.8 KB
Newer Older
1
<?php
lsmith's avatar
lsmith committed
2
/*
doctrine's avatar
doctrine committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 *  $Id$
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information, see
 * <http://www.phpdoctrine.com>.
 */
21
Doctrine::autoload('Doctrine_Access');
doctrine's avatar
doctrine committed
22 23 24 25 26
/**
 * Doctrine_Hydrate is a base class for Doctrine_RawSql and Doctrine_Query.
 * Its purpose is to populate object graphs.
 *
 *
27 28 29 30 31 32 33 34
 * @package     Doctrine
 * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
 * @category    Object Relational Mapping
 * @link        www.phpdoctrine.com
 * @since       1.0
 * @version     $Revision$
 * @author      Konsta Vesterinen <kvesteri@cc.hut.fi>
 */
lsmith's avatar
lsmith committed
35 36
abstract class Doctrine_Hydrate extends Doctrine_Access
{
37
    /**
38
     * @var array $fetchmodes                   an array containing all fetchmodes
39 40 41
     */
    protected $fetchModes  = array();
    /**
42
     * @var array $tables                       an array containing all the tables used in the query
43 44 45
     */
    protected $tables      = array();
    /**
lsmith's avatar
lsmith committed
46
     * @var array $collections                  an array containing all collections
47
     *                                          this hydrater has created/will create
48 49 50
     */
    protected $collections = array();
    /**
51
     * @var array $joins                        an array containing all table joins
52 53
     */
    protected $joins       = array();
54 55 56
    /**
     * @var array $params                       query input parameters
     */
zYne's avatar
zYne committed
57
    protected $params      = array();
58
    /**
59
     * @var Doctrine_Connection $conn           Doctrine_Connection object
60
     */
61
    protected $conn;
62
    /**
63
     * @var Doctrine_View $view                 Doctrine_View object
64 65
     */
    protected $view;
66 67 68
    /**
     * @var boolean $inheritanceApplied
     */
69
    protected $inheritanceApplied = false;
70 71 72
    /**
     * @var boolean $aggregate
     */
73
    protected $aggregate  = false;
74 75 76 77
    /**
     * @var array $compAliases
     */
    protected $compAliases  = array();
78 79 80 81 82 83 84 85
    /**
     * @var array $tableAliases
     */
    protected $tableAliases = array();
    /**
     * @var array $tableIndexes
     */
    protected $tableIndexes = array();
86

zYne's avatar
zYne committed
87 88 89
    protected $pendingAggregates = array();

    protected $aggregateMap      = array();
90
    /**
lsmith's avatar
lsmith committed
91
     * @var Doctrine_Hydrate_Alias $aliasHandler
92
     */
93
    protected $aliasHandler;
94 95 96 97
    /**
     * @var array $parts            SQL query string parts
     */
    protected $parts = array(
zYne's avatar
zYne committed
98 99 100 101 102 103 104 105 106 107
        'select'    => array(),
        'from'      => array(),
        'set'       => array(),
        'join'      => array(),
        'where'     => array(),
        'groupby'   => array(),
        'having'    => array(),
        'orderby'   => array(),
        'limit'     => false,
        'offset'    => false,
108 109 110 111
        );
    /**
     * constructor
     *
112
     * @param Doctrine_Connection|null $connection
113
     */
lsmith's avatar
lsmith committed
114 115
    public function __construct($connection = null)
    {
lsmith's avatar
lsmith committed
116
        if ( ! ($connection instanceof Doctrine_Connection)) {
117
            $connection = Doctrine_Manager::getInstance()->getCurrentConnection();
lsmith's avatar
lsmith committed
118
        }
119
        $this->conn = $connection;
120
        $this->aliasHandler = new Doctrine_Hydrate_Alias();
121
    }
122 123
    /**
     * getComponentAliases
124 125
     *
     * @return array
126
     */
lsmith's avatar
lsmith committed
127 128
    public function getComponentAliases()
    {
129 130 131 132 133 134 135
        return $this->compAliases;
    }
    /**
     * getTableAliases
     *
     * @return array
     */
lsmith's avatar
lsmith committed
136 137
    public function getTableAliases()
    {
138 139 140 141 142 143 144
        return $this->tableAliases;
    }
    /**
     * getTableIndexes
     *
     * @return array
     */
lsmith's avatar
lsmith committed
145 146
    public function getTableIndexes()
    {
147 148
        return $this->tableIndexes;
    }
149 150 151 152 153
    /**
     * getTables
     *
     * @return array
     */
lsmith's avatar
lsmith committed
154 155
    public function getTables()
    {
156 157
        return $this->tables;
    }
158 159 160 161 162
    /**
     * copyAliases
     *
     * @return void
     */
lsmith's avatar
lsmith committed
163 164
    public function copyAliases(Doctrine_Hydrate $query)
    {
165 166 167
        $this->compAliases  = $query->getComponentAliases();
        $this->tableAliases = $query->getTableAliases();
        $this->tableIndexes = $query->getTableIndexes();
lsmith's avatar
lsmith committed
168

169
        return $this;
170
    }
lsmith's avatar
lsmith committed
171

lsmith's avatar
lsmith committed
172 173
    public function getPathAlias($path)
    {
zYne's avatar
zYne committed
174
        $s = array_search($path, $this->compAliases);
lsmith's avatar
lsmith committed
175
        if ($s === false)
zYne's avatar
zYne committed
176
            return $path;
177

zYne's avatar
zYne committed
178 179
        return $s;
    }
180 181
    /**
     * createSubquery
lsmith's avatar
lsmith committed
182
     *
183 184
     * @return Doctrine_Hydrate
     */
lsmith's avatar
lsmith committed
185 186
    public function createSubquery()
    {
187 188
        $class = get_class($this);
        $obj   = new $class();
lsmith's avatar
lsmith committed
189

190 191
        // copy the aliases to the subquery
        $obj->copyAliases($this);
192

193 194
        return $obj;
    }
195
    /**
doctrine's avatar
doctrine committed
196 197 198 199 200
     * getQuery
     *
     * @return string
     */
    abstract public function getQuery();
201 202
    /**
     * limitSubqueryUsed
zYne's avatar
zYne committed
203 204
     *
     * @return boolean
205
     */
lsmith's avatar
lsmith committed
206 207
    public function isLimitSubqueryUsed()
    {
208 209 210
        return false;
    }

211 212 213 214 215
    /**
     * remove
     *
     * @param $name
     */
lsmith's avatar
lsmith committed
216 217
    public function remove($name)
    {
lsmith's avatar
lsmith committed
218 219 220 221 222 223 224 225 226
        if (isset($this->parts[$name])) {
            if ($name == "limit" || $name == "offset") {
                $this->parts[$name] = false;
            } else {
                $this->parts[$name] = array();
            }
        }
        return $this;
    }
227 228 229
    /**
     * clear
     * resets all the variables
lsmith's avatar
lsmith committed
230
     *
231 232
     * @return void
     */
lsmith's avatar
lsmith committed
233 234
    protected function clear()
    {
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
        $this->fetchModes   = array();
        $this->tables       = array();
        $this->parts = array(
                  "select"    => array(),
                  "from"      => array(),
                  "join"      => array(),
                  "where"     => array(),
                  "groupby"   => array(),
                  "having"    => array(),
                  "orderby"   => array(),
                  "limit"     => false,
                  "offset"    => false,
                );
        $this->inheritanceApplied = false;
        $this->aggregate        = false;
250

251 252 253 254
        $this->collections      = array();
        $this->joins            = array();
        $this->tableIndexes     = array();
        $this->tableAliases     = array();
255
        $this->aliasHandler->clear();
256 257
    }
    /**
zYne's avatar
zYne committed
258 259
     * getConnection
     *
zYne's avatar
zYne committed
260
     * @return Doctrine_Connection
261
     */
lsmith's avatar
lsmith committed
262 263
    public function getConnection()
    {
264
        return $this->conn;
265 266 267 268 269 270 271 272 273
    }
    /**
     * setView
     * sets a database view this query object uses
     * this method should only be called internally by doctrine
     *
     * @param Doctrine_View $view       database view
     * @return void
     */
lsmith's avatar
lsmith committed
274 275
    public function setView(Doctrine_View $view)
    {
276 277 278 279
        $this->view = $view;
    }
    /**
     * getView
280
     * returns the view associated with this query object (if any)
281
     *
282
     * @return Doctrine_View        the view associated with this query object
283
     */
lsmith's avatar
lsmith committed
284 285
    public function getView()
    {
286 287
        return $this->view;
    }
zYne's avatar
zYne committed
288 289
    /**
     * getParams
lsmith's avatar
lsmith committed
290
     *
zYne's avatar
zYne committed
291 292
     * @return array
     */
lsmith's avatar
lsmith committed
293 294
    public function getParams()
    {
zYne's avatar
zYne committed
295 296
        return $this->params;                           	
    }
297 298 299 300 301 302
    /**
     * getTableAlias
     *
     * @param string $path
     * @return string
     */
lsmith's avatar
lsmith committed
303 304
    final public function getTableAlias($path)
    {
lsmith's avatar
lsmith committed
305
        if (isset($this->compAliases[$path])) {
zYne's avatar
zYne committed
306
            $path = $this->compAliases[$path];
lsmith's avatar
lsmith committed
307 308
        }
        if ( ! isset($this->tableAliases[$path])) {
309
            return false;
lsmith's avatar
lsmith committed
310
        }
311 312 313 314 315 316 317 318
        return $this->tableAliases[$path];
    }
    /**
     * getCollection
     *
     * @parma string $name              component name
     * @param integer $index
     */
lsmith's avatar
lsmith committed
319 320
    private function getCollection($name)
    {
321
        $table = $this->tables[$name];
lsmith's avatar
lsmith committed
322
        if ( ! isset($this->fetchModes[$name])) {
zYne's avatar
zYne committed
323
            return new Doctrine_Collection($table);
lsmith's avatar
lsmith committed
324 325
        }
        switch ($this->fetchModes[$name]) {
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
            case Doctrine::FETCH_BATCH:
                $coll = new Doctrine_Collection_Batch($table);
                break;
            case Doctrine::FETCH_LAZY:
                $coll = new Doctrine_Collection_Lazy($table);
                break;
            case Doctrine::FETCH_OFFSET:
                $coll = new Doctrine_Collection_Offset($table);
                break;
            case Doctrine::FETCH_IMMEDIATE:
                $coll = new Doctrine_Collection_Immediate($table);
                break;
            case Doctrine::FETCH_LAZY_OFFSET:
                $coll = new Doctrine_Collection_LazyOffset($table);
                break;
            default:
                throw new Doctrine_Exception("Unknown fetchmode");
lsmith's avatar
lsmith committed
343
        };
344 345 346

        return $coll;
    }
347 348 349 350 351 352 353
    /**
     * convertBoolean
     * converts boolean to integers
     *
     * @param mixed $item
     * @return void
     */
lsmith's avatar
lsmith committed
354 355
    public static function convertBoolean(&$item)
    {
lsmith's avatar
lsmith committed
356
        if (is_bool($item)) {
357
            $item = (int) $item;
lsmith's avatar
lsmith committed
358
        }
359
    }
zYne's avatar
zYne committed
360 361 362 363 364 365 366 367
    /**
     * setParams
     *
     * @param array $params
     */
    public function setParams(array $params = array()) {
        $this->params = $params;
    }
368 369 370 371 372 373 374
    /**
     * execute
     * executes the dql query and populates all collections
     *
     * @param string $params
     * @return Doctrine_Collection            the root collection
     */
375
    public function execute($params = array(), $return = Doctrine::FETCH_RECORD) {
376
        $this->collections = array();
lsmith's avatar
lsmith committed
377

zYne's avatar
zYne committed
378
        $params = array_merge($this->params, $params);
lsmith's avatar
lsmith committed
379

380
        array_walk($params, array(__CLASS__, 'convertBoolean'));
lsmith's avatar
lsmith committed
381 382

        if ( ! $this->view) {
383
            $query = $this->getQuery($params);
lsmith's avatar
lsmith committed
384
        } else {
385
            $query = $this->view->getSelectSql();
lsmith's avatar
lsmith committed
386
        }
387

lsmith's avatar
lsmith committed
388 389 390
        if ($this->isLimitSubqueryUsed()
           && $this->conn->getDBH()->getAttribute(PDO::ATTR_DRIVER_NAME) !== 'mysql'
        ) {
391
            $params = array_merge($params, $params);
lsmith's avatar
lsmith committed
392
        }
393
        $stmt  = $this->conn->execute($query, $params);
zYne's avatar
zYne committed
394

lsmith's avatar
lsmith committed
395
        if ($this->aggregate)
396
            return $stmt->fetchAll(PDO::FETCH_ASSOC);
397

lsmith's avatar
lsmith committed
398
        if (count($this->tables) == 0) {
zYne's avatar
zYne committed
399
            throw new Doctrine_Query_Exception("No components selected");
lsmith's avatar
lsmith committed
400
        }
401 402
        $keys  = array_keys($this->tables);
        $root  = $keys[0];
zYne's avatar
zYne committed
403

404
        $previd = array();
405

406 407
        $coll        = $this->getCollection($root);
        $prev[$root] = $coll;
408

lsmith's avatar
lsmith committed
409
        if ($this->aggregate)
410
            $return = Doctrine::FETCH_ARRAY;
zYne's avatar
zYne committed
411

412
        $array = $this->parseData($stmt);
413

lsmith's avatar
lsmith committed
414
        if ($return == Doctrine::FETCH_ARRAY)
415
            return $array;
416

lsmith's avatar
lsmith committed
417
        foreach ($array as $data) {
418 419 420
            /**
             * remove duplicated data rows and map data into objects
             */
lsmith's avatar
lsmith committed
421 422
            foreach ($data as $key => $row) {
                if (empty($row)) {
423
                    continue;
lsmith's avatar
lsmith committed
424
                }
425
                //$key = array_search($key, $this->shortAliases);
426

lsmith's avatar
lsmith committed
427 428
                foreach ($this->tables as $k => $t) {
                    if ( ! strcasecmp($key, $k)) {
zYne's avatar
zYne committed
429
                        $key = $k;
lsmith's avatar
lsmith committed
430
                    }
zYne's avatar
zYne committed
431
                }
zYne's avatar
zYne committed
432

lsmith's avatar
lsmith committed
433 434 435
                if ( !isset($this->tables[$key]) ) {
                    throw new Doctrine_Exception('No table named ' . $key . ' found.');
                }
436 437
                $ids     = $this->tables[$key]->getIdentifier();
                $name    = $key;
438

lsmith's avatar
lsmith committed
439 440
                if ($this->isIdentifiable($row, $ids)) {
                    if ($name !== $root) {
zYne's avatar
zYne committed
441 442 443
                        $prev = $this->initRelated($prev, $name);
                    }
                    // aggregate values have numeric keys
lsmith's avatar
lsmith committed
444
                    if (isset($row[0])) {
zYne's avatar
zYne committed
445
                        $component = $this->tables[$name]->getComponentName();
lsmith's avatar
lsmith committed
446

zYne's avatar
zYne committed
447 448 449
                        // if the collection already has objects, get the last object
                        // otherwise create a new one where the aggregate values are being mapped

lsmith's avatar
lsmith committed
450
                        if ($prev[$name]->count() > 0) {
zYne's avatar
zYne committed
451
                            $record = $prev[$name]->getLast();
zYne's avatar
zYne committed
452
                        } else {
zYne's avatar
zYne committed
453 454 455
                            $record = new $component();
                            $prev[$name]->add($record);
                        }
456

zYne's avatar
zYne committed
457 458
                        $path    = array_search($name, $this->tableAliases);
                        $alias   = $this->getPathAlias($path);
zYne's avatar
zYne committed
459

zYne's avatar
zYne committed
460
                        // map each aggregate value
lsmith's avatar
lsmith committed
461
                        foreach ($row as $index => $value) {
zYne's avatar
zYne committed
462
                            $agg = false;
zYne's avatar
zYne committed
463

lsmith's avatar
lsmith committed
464
                            if (isset($this->pendingAggregates[$alias][$index])) {
zYne's avatar
zYne committed
465
                                $agg = $this->pendingAggregates[$alias][$index][3];
zYne's avatar
zYne committed
466 467
                            }

zYne's avatar
zYne committed
468
                            $record->mapValue($agg, $value);
469
                        }
zYne's avatar
zYne committed
470 471
                    }

472
                    continue;
zYne's avatar
zYne committed
473

474
                }
475

lsmith's avatar
lsmith committed
476 477 478 479
                if ( ! isset($previd[$name])) {
                    $previd[$name] = array();
                }
                if ($previd[$name] !== $row) {
zYne's avatar
zYne committed
480 481 482 483 484 485
                    // set internal data

                    $this->tables[$name]->setData($row);

                    // initialize a new record
                    $record = $this->tables[$name]->getRecord();
486

zYne's avatar
zYne committed
487
                    // aggregate values have numeric keys
lsmith's avatar
lsmith committed
488
                    if (isset($row[0])) {
zYne's avatar
zYne committed
489 490
                        $path    = array_search($name, $this->tableAliases);
                        $alias   = $this->getPathAlias($path);
zYne's avatar
zYne committed
491

zYne's avatar
zYne committed
492
                        // map each aggregate value
lsmith's avatar
lsmith committed
493
                        foreach ($row as $index => $value) {
zYne's avatar
zYne committed
494
                            $agg = false;
495

lsmith's avatar
lsmith committed
496
                            if (isset($this->pendingAggregates[$alias][$index])) {
zYne's avatar
zYne committed
497
                                $agg = $this->pendingAggregates[$alias][$index][3];
lsmith's avatar
lsmith committed
498
                            }
zYne's avatar
zYne committed
499 500 501
                            $record->mapValue($agg, $value);
                        }
                    }
zYne's avatar
zYne committed
502

lsmith's avatar
lsmith committed
503
                    if ($name == $root) {
504 505 506
                        // add record into root collection
                        $coll->add($record);
                        unset($previd);
507

508
                    } else {
zYne's avatar
zYne committed
509 510

                            $prev = $this->addRelated($prev, $name, $record);
511
                    }
lsmith's avatar
lsmith committed
512

513 514 515
                    // following statement is needed to ensure that mappings
                    // are being done properly when the result set doesn't
                    // contain the rows in 'right order'
lsmith's avatar
lsmith committed
516 517

                    if ($prev[$name] !== $record)
518
                        $prev[$name] = $record;
519 520
                }

521 522 523 524 525
                $previd[$name] = $row;
            }
        }

        return $coll;
526
    }
lsmith's avatar
lsmith committed
527
    /**
zYne's avatar
zYne committed
528 529 530 531 532 533
     * initRelation
     *
     * @param array $prev
     * @param string $name
     * @return array
     */
lsmith's avatar
lsmith committed
534 535
    public function initRelated(array $prev, $name)
    {
zYne's avatar
zYne committed
536 537
        $pointer = $this->joins[$name];
        $path    = array_search($name, $this->tableAliases);
zYne's avatar
zYne committed
538
        $tmp     = explode('.', $path);
zYne's avatar
zYne committed
539 540
        $alias   = end($tmp);

lsmith's avatar
lsmith committed
541
        if ( ! isset($prev[$pointer]) ) {
zYne's avatar
zYne committed
542
            return $prev;
lsmith's avatar
lsmith committed
543
        }
zYne's avatar
zYne committed
544 545
        $fk      = $this->tables[$pointer]->getRelation($alias);

lsmith's avatar
lsmith committed
546 547 548
        if ( ! $fk->isOneToOne()) {
            if ($prev[$pointer]->getLast() instanceof Doctrine_Record) {
                if ( ! $prev[$pointer]->getLast()->hasReference($alias)) {
zYne's avatar
zYne committed
549 550
                    $prev[$name] = $this->getCollection($name);
                    $prev[$pointer]->getLast()->initReference($prev[$name],$fk);
lsmith's avatar
lsmith committed
551
                } else {
zYne's avatar
zYne committed
552
                    $prev[$name] = $prev[$pointer]->getLast()->get($alias);
lsmith's avatar
lsmith committed
553
                }
zYne's avatar
zYne committed
554 555 556 557 558 559 560 561 562 563 564 565
            }
        }

        return $prev;
    }
    /**
     * addRelated
     *
     * @param array $prev
     * @param string $name
     * @return array
     */
lsmith's avatar
lsmith committed
566 567
    public function addRelated(array $prev, $name, Doctrine_Record $record)
    {
zYne's avatar
zYne committed
568
        $pointer = $this->joins[$name];
569

zYne's avatar
zYne committed
570
        $path    = array_search($name, $this->tableAliases);
571
        $tmp     = explode('.', $path);
zYne's avatar
zYne committed
572 573 574 575
        $alias   = end($tmp);

        $fk      = $this->tables[$pointer]->getRelation($alias);

lsmith's avatar
lsmith committed
576
        if ($fk->isOneToOne()) {
zYne's avatar
zYne committed
577 578 579 580 581 582
            $prev[$pointer]->getLast()->set($fk->getAlias(), $record);

            $prev[$name] = $record;
        } else {
            // one-to-many relation or many-to-many relation

lsmith's avatar
lsmith committed
583
            if ( ! $prev[$pointer]->getLast()->hasReference($alias)) {
zYne's avatar
zYne committed
584 585 586 587 588 589 590 591 592 593 594 595
                $prev[$name] = $this->getCollection($name);
                $prev[$pointer]->getLast()->initReference($prev[$name], $fk);

            } else {
                // previous entry found from memory
                $prev[$name] = $prev[$pointer]->getLast()->get($alias);
            }

            $prev[$pointer]->getLast()->addReference($record, $fk);
        }
        return $prev;
    }
596
    /**
597
     * isIdentifiable
lsmith's avatar
lsmith committed
598
     * returns whether or not a given data row is identifiable (it contains
599
     * all id fields specified in the second argument)
600 601 602 603 604
     *
     * @param array $row
     * @param mixed $ids
     * @return boolean
     */
lsmith's avatar
lsmith committed
605 606
    public function isIdentifiable(array $row, $ids)
    {
lsmith's avatar
lsmith committed
607 608 609
        if (is_array($ids)) {
            foreach ($ids as $id) {
                if ($row[$id] == null)
610 611 612
                    return true;
            }
        } else {
lsmith's avatar
lsmith committed
613
            if ( ! isset($row[$ids])) {
614
                return true;
lsmith's avatar
lsmith committed
615
            }
616 617 618
        }
        return false;
    }
619 620
    /**
     * applyInheritance
621
     * applies column aggregation inheritance to DQL / SQL query
622 623 624
     *
     * @return string
     */
lsmith's avatar
lsmith committed
625 626
    public function applyInheritance()
    {
627 628 629
        // get the inheritance maps
        $array = array();

lsmith's avatar
lsmith committed
630
        foreach ($this->tables as $alias => $table) {
631
            $array[$alias][] = $table->getInheritanceMap();
lsmith's avatar
lsmith committed
632
        };
633 634 635 636 637 638

        // apply inheritance maps
        $str = "";
        $c = array();

        $index = 0;
lsmith's avatar
lsmith committed
639
        foreach ($array as $tableAlias => $maps) {
640
            $a = array();
lsmith's avatar
lsmith committed
641
            foreach ($maps as $map) {
642
                $b = array();
lsmith's avatar
lsmith committed
643 644
                foreach ($map as $field => $value) {
                    if ($index > 0) {
zYne's avatar
zYne committed
645 646
                        $b[] = '(' . $tableAlias . '.' . $field . ' = ' . $value 
                             . ' OR ' . $tableAlias . '.' . $field . ' IS NULL)';
lsmith's avatar
lsmith committed
647
                    } else {
648
                        $b[] = $tableAlias . '.' . $field . ' = ' . $value;
lsmith's avatar
lsmith committed
649
                    }
650
                }
lsmith's avatar
lsmith committed
651 652

                if ( ! empty($b)) {
653
                    $a[] = implode(' AND ', $b);
lsmith's avatar
lsmith committed
654
                }
655
            }
656

lsmith's avatar
lsmith committed
657
            if ( ! empty($a)) {
658
                $c[] = implode(' AND ', $a);
lsmith's avatar
lsmith committed
659
            }
660 661 662
            $index++;
        }

663
        $str .= implode(' AND ', $c);
664 665 666 667 668 669 670

        return $str;
    }
    /**
     * parseData
     * parses the data returned by PDOStatement
     *
671
     * @param PDOStatement $stmt
672 673
     * @return array
     */
lsmith's avatar
lsmith committed
674 675
    public function parseData(PDOStatement $stmt)
    {
676
        $array = array();
lsmith's avatar
lsmith committed
677 678

        while ($data = $stmt->fetch(PDO::FETCH_ASSOC)) {
679 680 681
            /**
             * parse the data into two-dimensional array
             */
lsmith's avatar
lsmith committed
682
            foreach ($data as $key => $value) {
683
                $e = explode('__', $key);
684

685 686
                $field     = strtolower(array_pop($e));
                $component = strtolower(implode('__', $e));
zYne's avatar
zYne committed
687

688 689
                $data[$component][$field] = $value;

690
                unset($data[$key]);
lsmith's avatar
lsmith committed
691
            };
692
            $array[] = $data;
lsmith's avatar
lsmith committed
693
        };
694

695 696 697 698 699 700 701
        $stmt->closeCursor();
        return $array;
    }
    /**
     * returns a Doctrine_Table for given name
     *
     * @param string $name              component name
702
     * @return Doctrine_Table|boolean
703
     */
lsmith's avatar
lsmith committed
704 705
    public function getTable($name)
    {
lsmith's avatar
lsmith committed
706
        if (isset($this->tables[$name])) {
707
            return $this->tables[$name];
lsmith's avatar
lsmith committed
708
        }
709
        return false;
710
    }
711 712 713
    /**
     * @return string                   returns a string representation of this object
     */
lsmith's avatar
lsmith committed
714 715
    public function __toString()
    {
716 717
        return Doctrine_Lib::formatSql($this->getQuery());
    }
718
}