Overview

Packages

  • CONTENIDO
  • Core
    • Authentication
    • Backend
    • Cache
    • CEC
    • Chain
    • ContentType
    • Database
    • Debug
    • Exception
    • Frontend
      • Search
      • URI
      • Util
    • GenericDB
      • Model
    • GUI
      • HTML
    • I18N
    • LayoutHandler
    • Log
    • Security
    • Session
    • Util
    • Validation
    • Versioning
    • XML
  • Module
    • ContentSitemapHtml
    • ContentSitemapXml
    • ContentUserForum
    • NavigationTop
    • ScriptCookieDirective
  • mpAutoloaderClassMap
  • None
  • PHP
  • Plugin
    • ContentAllocation
    • CronjobOverview
    • FormAssistant
    • FrontendLogic
    • FrontendUsers
    • Linkchecker
    • ModRewrite
    • Newsletter
    • Repository
      • FrontendNavigation
      • KeywordDensity
    • SmartyWrapper
    • UrlShortener
    • UserForum
    • Workflow
  • PluginManager
  • Setup
    • Form
    • GUI
    • Helper
      • Environment
      • Filesystem
      • MySQL
      • PHP
    • UpgradeJob

Classes

  • ArticleForum
  • cGenericDb
  • cGenericDbDriver
  • cGenericDbDriverMysql
  • cItemBaseAbstract
  • cItemCache
  • Item
  • ItemCollection
  • Overview
  • Package
  • Class
  • Tree
  • Deprecated
  • Todo
   1: <?php
   2: 
   3: /**
   4:  * This file contains the generic db item collection class.
   5:  *
   6:  * @package Core
   7:  * @subpackage GenericDB
   8:  *
   9:  * @author Timo Hummel
  10:  * @author Murat Purc <murat@purc.de>
  11:  * @copyright four for business AG <www.4fb.de>
  12:  * @license http://www.contenido.org/license/LIZENZ.txt
  13:  * @link http://www.4fb.de
  14:  * @link http://www.contenido.org
  15:  */
  16: 
  17: defined('CON_FRAMEWORK') || die('Illegal call: Missing framework initialization - request aborted.');
  18: 
  19: /**
  20:  * Class ItemCollection
  21:  * Abstract class for database based item collections.
  22:  *
  23:  * @package Core
  24:  * @subpackage GenericDB
  25:  */
  26: abstract class ItemCollection extends cItemBaseAbstract {
  27: 
  28:     /**
  29:      * Storage of all result items.
  30:      * Contains all result items.
  31:      *
  32:      * @var string
  33:      */
  34:     protected $objects;
  35: 
  36:     /**
  37:      * GenericDB driver object
  38:      *
  39:      * @var cGenericDbDriver
  40:      */
  41:     protected $_driver;
  42: 
  43:     /**
  44:      * List of instances of ItemCollection implementations
  45:      *
  46:      * @var array
  47:      */
  48:     protected $_collectionCache = array();
  49: 
  50:     /**
  51:      * Single item class
  52:      *
  53:      * @var string
  54:      */
  55:     protected $_itemClass;
  56: 
  57:     /**
  58:      * Iterator object for the next() method
  59:      *
  60:      * @var object
  61:      */
  62:     protected $_iteratorItem;
  63: 
  64:     /**
  65:      * Reverse join partners for this data object
  66:      *
  67:      * @var array
  68:      */
  69:     protected $_JoinPartners = array();
  70: 
  71:     /**
  72:      * Forward join partners for this data object
  73:      *
  74:      * @var array
  75:      */
  76:     protected $_forwardJoinPartners;
  77: 
  78:     /**
  79:      * Where restrictions for the query
  80:      *
  81:      * @var array
  82:      */
  83:     protected $_whereRestriction;
  84: 
  85:     /**
  86:      * Inner group conditions
  87:      *
  88:      * @var array
  89:      */
  90:     protected $_innerGroupConditions = array();
  91: 
  92:     /**
  93:      * Group conditions
  94:      *
  95:      * @var array
  96:      */
  97:     protected $_groupConditions;
  98: 
  99:     /**
 100:      * Result fields for the query
 101:      *
 102:      * @var array
 103:      */
 104:     protected $_resultFields = array();
 105: 
 106:     /**
 107:      * Encoding
 108:      *
 109:      * @var string
 110:      */
 111:     protected $_encoding;
 112: 
 113:     /**
 114:      * Item class instance
 115:      *
 116:      * @var object
 117:      */
 118:     protected $_itemClassInstance;
 119: 
 120:     /**
 121:      * Stores all operators which are supported by GenericDB
 122:      * Unsupported operators are passed trough as-is.
 123:      *
 124:      * @var array
 125:      */
 126:     protected $_aOperators;
 127: 
 128:     /**
 129:      * Flag to select all fields in a query.
 130:      * Reduces the number of queries send to the database.
 131:      *
 132:      * @var bool
 133:      */
 134:     protected $_bAllMode = false;
 135: 
 136:     /**
 137:      * Array with where conditions
 138:      *
 139:      * @var array
 140:      */
 141:     protected $_where;
 142: 
 143:     /**
 144:      * Order mode with direction
 145:      *
 146:      * @var string
 147:      */
 148:     protected $_order;
 149: 
 150:     /**
 151:      * Starting limit
 152:      *
 153:      * @var int
 154:      */
 155:     protected $_limitStart;
 156: 
 157:     /**
 158:      * Amount of items for limit
 159:      *
 160:      * @var int
 161:      */
 162:     protected $_limitCount;
 163: 
 164:     /**
 165:      * Last SQL statement
 166:      *
 167:      * @var string
 168:      */
 169:     protected $_lastSQL;
 170: 
 171:     /**
 172:      * Array with linked tables
 173:      *
 174:      * @var array
 175:      */
 176:     protected $_links;
 177: 
 178:     /**
 179:      * Constructor to create an instance of this class.
 180:      *
 181:      * @param string $sTable
 182:      *         The table to use as information source
 183:      * @param string $sPrimaryKey
 184:      *         The primary key to use
 185:      *
 186:      * @throws cInvalidArgumentException
 187:      */
 188:     public function __construct($sTable, $sPrimaryKey) {
 189:         parent::__construct($sTable, $sPrimaryKey, get_parent_class($this));
 190: 
 191:         $this->resetQuery();
 192: 
 193:         // Try to load driver
 194:         $this->_initializeDriver();
 195: 
 196:         // Try to find out the current encoding
 197:         if (isset($GLOBALS['lang']) && isset($GLOBALS['aLanguageEncodings'])) {
 198:             $this->setEncoding($GLOBALS['aLanguageEncodings'][$GLOBALS['lang']]);
 199:         }
 200: 
 201:         $this->_aOperators = array(
 202:             '=',
 203:             '!=',
 204:             '<>',
 205:             '<',
 206:             '>',
 207:             '<=',
 208:             '>=',
 209:             'LIKE',
 210:             'DIACRITICS'
 211:         );
 212:     }
 213: 
 214:     /**
 215:      * Defines the reverse links for this table.
 216:      *
 217:      * Important: The class specified by $sForeignCollectionClass needs to be a
 218:      * collection class and has to exist.
 219:      * Define all links in the constructor of your object.
 220:      *
 221:      * @param string $sForeignCollectionClass
 222:      *         Specifies the foreign class to use
 223:      * @throws cInvalidArgumentException
 224:      *         if the given foreign class can not be instantiated
 225:      */
 226:     protected function _setJoinPartner($sForeignCollectionClass) {
 227:         if (class_exists($sForeignCollectionClass)) {
 228:             // Add class
 229:             if (!in_array($sForeignCollectionClass, $this->_JoinPartners)) {
 230:                 $this->_JoinPartners[] = cString::toLowerCase($sForeignCollectionClass);
 231:             }
 232:         } else {
 233:             $msg = "Could not instantiate class [$sForeignCollectionClass] for use " . "with _setJoinPartner in class " . get_class($this);
 234:             throw new cInvalidArgumentException($msg);
 235:         }
 236:     }
 237: 
 238:     /**
 239:      * Method to set the accompanying item object.
 240:      *
 241:      * @param string $sClassName
 242:      *         Specifies the classname of item
 243:      * @throws cInvalidArgumentException
 244:      *         if the given class can not be instantiated
 245:      */
 246:     protected function _setItemClass($sClassName) {
 247:         if (class_exists($sClassName)) {
 248:             $this->_itemClass = $sClassName;
 249:             $this->_itemClassInstance = new $sClassName();
 250: 
 251:             // Initialize driver in case the developer does a setItemClass-Call
 252:             // before calling the parent constructor
 253:             $this->_initializeDriver();
 254:             $this->_driver->setItemClassInstance($this->_itemClassInstance);
 255:         } else {
 256:             $msg = "Could not instantiate class [$sClassName] for use with " . "_setItemClass in class " . get_class($this);
 257:             throw new cInvalidArgumentException($msg);
 258:         }
 259:     }
 260: 
 261:     /**
 262:      * Initializes the driver to use with GenericDB.
 263:      *
 264:      * @param bool $bForceInit [optional]
 265:      *         If true, forces the driver to initialize, even if it already exists.
 266:      */
 267:     protected function _initializeDriver($bForceInit = false) {
 268:         if (!is_object($this->_driver) || $bForceInit == true) {
 269:             $this->_driver = new cGenericDbDriverMysql();
 270:         }
 271:     }
 272: 
 273:     /**
 274:      * Sets the encoding.
 275:      *
 276:      * @param string $sEncoding
 277:      */
 278:     public function setEncoding($sEncoding) {
 279:         $this->_encoding = $sEncoding;
 280:         $this->_driver->setEncoding($sEncoding);
 281:     }
 282: 
 283:     /**
 284:      * Sets the query to use foreign tables in the resultset
 285:      *
 286:      * @param string $sForeignClass
 287:      *         The class of foreign table to use
 288:      * @throws cInvalidArgumentException
 289:      *         if the given foreign class does not exist
 290:      */
 291:     public function link($sForeignClass) {
 292:         if (class_exists($sForeignClass)) {
 293:             $this->_links[$sForeignClass] = new $sForeignClass();
 294:         } else {
 295:             $msg = "Could not find class [$sForeignClass] for use with link in class " . get_class($this);
 296:             throw new cInvalidArgumentException($msg);
 297:         }
 298:     }
 299: 
 300:     /**
 301:      * Sets the limit for results
 302:      *
 303:      * @param int $iRowStart
 304:      * @param int $iRowCount
 305:      */
 306:     public function setLimit($iRowStart, $iRowCount) {
 307:         $this->_limitStart = $iRowStart;
 308:         $this->_limitCount = $iRowCount;
 309:     }
 310: 
 311:     /**
 312:      * Restricts a query with a where clause
 313:      *
 314:      * @param string $sField
 315:      * @param mixed $mRestriction
 316:      * @param string $sOperator [optional]
 317:      */
 318:     public function setWhere($sField, $mRestriction, $sOperator = '=') {
 319:         $sField = cString::toLowerCase($sField);
 320:         $this->_where['global'][$sField]['operator'] = $sOperator;
 321:         $this->_where['global'][$sField]['restriction'] = $mRestriction;
 322:     }
 323: 
 324:     /**
 325:      * Removes a previous set where clause (@see ItemCollection::setWhere).
 326:      *
 327:      * @param string $sField
 328:      * @param mixed $mRestriction
 329:      * @param string $sOperator [optional]
 330:      */
 331:     public function deleteWhere($sField, $mRestriction, $sOperator = '=') {
 332:         $sField = cString::toLowerCase($sField);
 333:         if (isset($this->_where['global'][$sField]) && is_array($this->_where['global'][$sField])) {
 334:             if ($this->_where['global'][$sField]['operator'] == $sOperator && $this->_where['global'][$sField]['restriction'] == $mRestriction) {
 335:                 unset($this->_where['global'][$sField]);
 336:             }
 337:         }
 338:     }
 339: 
 340:     /**
 341:      * Restricts a query with a where clause, groupable
 342:      *
 343:      * @param string $sGroup
 344:      * @param string $sField
 345:      * @param mixed $mRestriction
 346:      * @param string $sOperator [optional]
 347:      */
 348:     public function setWhereGroup($sGroup, $sField, $mRestriction, $sOperator = '=') {
 349:         $sField = cString::toLowerCase($sField);
 350:         $this->_where['groups'][$sGroup][$sField]['operator'] = $sOperator;
 351:         $this->_where['groups'][$sGroup][$sField]['restriction'] = $mRestriction;
 352:     }
 353: 
 354:     /**
 355:      * Removes a previous set groupable where clause (@see
 356:      * ItemCollection::setWhereGroup).
 357:      *
 358:      * @param string $sGroup
 359:      * @param string $sField
 360:      * @param mixed $mRestriction
 361:      * @param string $sOperator [optional]
 362:      */
 363:     public function deleteWhereGroup($sGroup, $sField, $mRestriction, $sOperator = '=') {
 364:         $sField = cString::toLowerCase($sField);
 365:         if (is_array($this->_where['groups'][$sGroup]) && isset($this->_where['groups'][$sGroup][$sField]) && is_array($this->_where['groups'][$sGroup][$sField])) {
 366:             if ($this->_where['groups'][$sGroup][$sField]['operator'] == $sOperator && $this->_where['groups'][$sGroup][$sField]['restriction'] == $mRestriction) {
 367:                 unset($this->_where['groups'][$sGroup][$sField]);
 368:             }
 369:         }
 370:     }
 371: 
 372:     /**
 373:      * Defines how relations in one group are linked each together
 374:      *
 375:      * @param string $sGroup
 376:      * @param string $sCondition [optional]
 377:      */
 378:     public function setInnerGroupCondition($sGroup, $sCondition = 'AND') {
 379:         $this->_innerGroupConditions[$sGroup] = $sCondition;
 380:     }
 381: 
 382:     /**
 383:      * Defines how groups are linked to each other
 384:      *
 385:      * @param string $sGroup1
 386:      * @param string $sGroup2
 387:      * @param string $sCondition [optional]
 388:      */
 389:     public function setGroupCondition($sGroup1, $sGroup2, $sCondition = 'AND') {
 390:         $this->_groupConditions[$sGroup1][$sGroup2] = $sCondition;
 391:     }
 392: 
 393:     /**
 394:      * Builds a where statement out of the setGroupWhere calls
 395:      *
 396:      * @return array
 397:      *         With all where statements
 398:      */
 399:     protected function _buildGroupWhereStatements() {
 400:         $aWheres = array();
 401:         $aGroupWhere = array();
 402: 
 403:         $mLastGroup = false;
 404:         $sGroupWhereStatement = '';
 405: 
 406:         // Find out if there are any defined groups
 407:         if (count($this->_where['groups']) > 0) {
 408:             // Step trough all groups
 409:             foreach ($this->_where['groups'] as $groupname => $group) {
 410:                 $aWheres = array();
 411: 
 412:                 // Fetch restriction, fields and operators and build single
 413:                 // group
 414:                 // where statements
 415:                 foreach ($group as $field => $item) {
 416:                     $aWheres[] = $this->_driver->buildOperator($field, $item['operator'], $item['restriction']);
 417:                 }
 418: 
 419:                 // Add completed substatements
 420:                 $sOperator = 'AND';
 421:                 if (isset($this->_innerGroupConditions[$groupname])) {
 422:                     $sOperator = $this->_innerGroupConditions[$groupname];
 423:                 }
 424: 
 425:                 $aGroupWhere[$groupname] = implode(' ' . $sOperator . ' ', $aWheres);
 426:             }
 427:         }
 428: 
 429:         // Combine groups
 430:         foreach ($aGroupWhere as $groupname => $group) {
 431:             if ($mLastGroup != false) {
 432:                 $sOperator = 'AND';
 433:                 // Check if there's a group condition
 434:                 if (isset($this->_groupConditions[$groupname])) {
 435:                     if (isset($this->_groupConditions[$groupname][$mLastGroup])) {
 436:                         $sOperator = $this->_groupConditions[$groupname][$mLastGroup];
 437:                     }
 438:                 }
 439: 
 440:                 // Reverse check
 441:                 if (isset($this->_groupConditions[$mLastGroup])) {
 442:                     if (isset($this->_groupConditions[$mLastGroup][$groupname])) {
 443:                         $sOperator = $this->_groupConditions[$mLastGroup][$groupname];
 444:                     }
 445:                 }
 446: 
 447:                 $sGroupWhereStatement .= ' ' . $sOperator . ' (' . $group . ')';
 448:             } else {
 449:                 $sGroupWhereStatement .= '(' . $group . ')';
 450:             }
 451: 
 452:             $mLastGroup = $groupname;
 453:         }
 454: 
 455:         return $sGroupWhereStatement;
 456:     }
 457: 
 458:     /**
 459:      * Builds a where statement out of the setWhere calls
 460:      *
 461:      * @return array
 462:      *         With all where statements
 463:      */
 464:     protected function _buildWhereStatements() {
 465:         $aWheres = array();
 466: 
 467:         // Build global where condition
 468:         foreach ($this->_where['global'] as $field => $item) {
 469:             $aWheres[] = $this->_driver->buildOperator($field, $item['operator'], $item['restriction']);
 470:         }
 471: 
 472:         return implode(' AND ', $aWheres);
 473:     }
 474: 
 475:     /**
 476:      * Fetches all tables which will be joined later on.
 477:      *
 478:      * The returned array has the following format:
 479:      * <pre>
 480:      * array(
 481:      * array(fields),
 482:      * array(tables),
 483:      * array(joins),
 484:      * array(wheres)
 485:      * );
 486:      * </pre>
 487:      *
 488:      * Notes:
 489:      * The table is the table name which needs to be added to the FROM clause
 490:      * The join statement which is inserted after the master table
 491:      * The where statement is combined with all other where statements
 492:      * The fields to select from
 493:      *
 494:      * @throws cException
 495:      *         if no join partner could be found
 496:      * @return array
 497:      *         Array structure, see above
 498:      */
 499:     protected function _fetchJoinTables() {
 500:         $aParameters = array();
 501:         $aFields = array();
 502:         $aTables = array();
 503:         $aJoins = array();
 504:         $aWheres = array();
 505: 
 506:         // Fetch linked tables
 507:         foreach ($this->_links as $link => $object) {
 508:             $matches = $this->_findReverseJoinPartner(cString::toLowerCase(get_class($this)), $link);
 509:             if ($matches !== false) {
 510:                 if (isset($matches['desttable'])) {
 511:                     // Driver function: Build query parts
 512:                     $aParameters[] = $this->_driver->buildJoinQuery($matches['desttable'], cString::toLowerCase($matches['destclass']), $matches['key'], cString::toLowerCase($matches['sourceclass']), $matches['key']);
 513:                 } else {
 514:                     foreach ($matches as $match) {
 515:                         $aParameters[] = $this->_driver->buildJoinQuery($match['desttable'], cString::toLowerCase($match['destclass']), $match['key'], cString::toLowerCase($match['sourceclass']), $match['key']);
 516:                     }
 517:                 }
 518:             } else {
 519:                 throw new cUnexpectedValueException("The join partner '" . get_class($this) . "' is not registered and can not be used with link().");
 520:             }
 521:         }
 522: 
 523:         // Add this class
 524:         $aFields[] = cString::toLowerCase(cString::toLowerCase(get_class($this))) . '.' . $this->getPrimaryKeyName();
 525: 
 526:         // Make the parameters unique
 527:         foreach ($aParameters as $parameter) {
 528:             array_unshift($aFields, $parameter['field']);
 529:             array_unshift($aTables, $parameter['table']);
 530:             array_unshift($aJoins, $parameter['join']);
 531:             array_unshift($aWheres, $parameter['where']);
 532:         }
 533: 
 534:         $aFields = array_filter(array_unique($aFields));
 535:         $aTables = array_filter(array_unique($aTables));
 536:         $aJoins = array_filter(array_unique($aJoins));
 537:         $aWheres = array_filter(array_unique($aWheres));
 538: 
 539:         return array(
 540:             'fields' => $aFields,
 541:             'tables' => $aTables,
 542:             'joins' => $aJoins,
 543:             'wheres' => $aWheres
 544:         );
 545:     }
 546: 
 547:     /**
 548:      * Resolves links (class names of joined partners)
 549:      *
 550:      * @return array
 551:      */
 552:     protected function _resolveLinks() {
 553:         $aResolvedLinks = array();
 554:         $aResolvedLinks[] = cString::toLowerCase(get_class($this));
 555: 
 556:         foreach ($this->_JoinPartners as $link) {
 557:             $class = new $link();
 558:             $aResolvedLinks = array_merge($class->_resolveLinks(), $aResolvedLinks);
 559:         }
 560:         return $aResolvedLinks;
 561:     }
 562: 
 563:     /**
 564:      * Resets the properties
 565:      */
 566:     public function resetQuery() {
 567:         $this->setLimit(0, 0);
 568:         $this->_forwardJoinPartners = array();
 569:         $this->_links = array();
 570:         $this->_where['global'] = array();
 571:         $this->_where['groups'] = array();
 572:         $this->_groupConditions = array();
 573:         $this->_resultFields = array();
 574:     }
 575: 
 576:     /**
 577:      * Builds and runs the query
 578:      *
 579:      * @throws cException
 580:      *         if no item class has been set
 581:      * @return bool
 582:      */
 583:     public function query() {
 584:         if (!isset($this->_itemClassInstance)) {
 585:             throw new cException('GenericDB can\'t use query() if no item class is set via setItemClass');
 586:         }
 587: 
 588:         $aGroupWhereStatements = $this->_buildGroupWhereStatements();
 589:         $sWhereStatements = $this->_buildWhereStatements();
 590:         $aParameters = $this->_fetchJoinTables();
 591: 
 592:         $aStatement = array(
 593:             'SELECT',
 594:             implode(', ', (array_merge($aParameters['fields'], $this->_resultFields))),
 595:             'FROM',
 596:             '`' . $this->table . '` AS ' . cString::toLowerCase(get_class($this))
 597:         );
 598: 
 599:         if (count($aParameters['tables']) > 0) {
 600:             $aStatement[] = implode(', ', $aParameters['tables']);
 601:         }
 602: 
 603:         if (count($aParameters['joins']) > 0) {
 604:             $aStatement[] = implode(' ', $aParameters['joins']);
 605:         }
 606: 
 607:         $aWheres = array();
 608: 
 609:         if (count($aParameters['wheres']) > 0) {
 610:             $aWheres[] = implode(', ', $aParameters['wheres']);
 611:         }
 612: 
 613:         if ($aGroupWhereStatements != '') {
 614:             $aWheres[] = $aGroupWhereStatements;
 615:         }
 616: 
 617:         if ($sWhereStatements != '') {
 618:             $aWheres[] = $sWhereStatements;
 619:         }
 620: 
 621:         if (count($aWheres) > 0) {
 622:             $aStatement[] = 'WHERE ' . implode(' AND ', $aWheres);
 623:         }
 624: 
 625:         if ($this->_order != '') {
 626:             $aStatement[] = 'ORDER BY ' . $this->_order;
 627:         }
 628: 
 629:         if ($this->_limitStart > 0 || $this->_limitCount > 0) {
 630:             $iRowStart = intval($this->_limitStart);
 631:             $iRowCount = intval($this->_limitCount);
 632:             $aStatement[] = "LIMIT $iRowStart, $iRowCount";
 633:         }
 634: 
 635:         $sql = implode(' ', $aStatement);
 636: 
 637:         $result = $this->db->query($sql);
 638:         $this->_lastSQL = $sql;
 639:         // @todo disable all mode in this method for the moment. It has to be
 640:         // verified,
 641:         // if enabling will result in negative side effects.
 642:         $this->_bAllMode = false;
 643:         return ($result) ? true : false;
 644:     }
 645: 
 646:     /**
 647:      * Sets the result order part of the query
 648:      * (e.
 649:      * g. "fieldname", "fieldname DESC", "fieldname DESC, field2name ASC")
 650:      *
 651:      * @param string $order
 652:      */
 653:     public function setOrder($order) {
 654:         $this->_order = cString::toLowerCase($order);
 655:     }
 656: 
 657:     /**
 658:      * Adds a result field
 659:      *
 660:      * @param string $sField
 661:      */
 662:     public function addResultField($sField) {
 663:         $sField = cString::toLowerCase($sField);
 664:         if (!in_array($sField, $this->_resultFields)) {
 665:             $this->_resultFields[] = $sField;
 666:         }
 667:     }
 668: 
 669:     /**
 670:      * Removes existing result field
 671:      *
 672:      * @param string $sField
 673:      */
 674:     public function removeResultField($sField) {
 675:         $sField = cString::toLowerCase($sField);
 676:         $key = array_search($sField, $this->_resultFields);
 677:         if ($key !== false) {
 678:             unset($this->_resultFields[$key]);
 679:         }
 680:     }
 681: 
 682:     /**
 683:      * Returns reverse join partner.
 684:      *
 685:      * @param string $sParentClass
 686:      * @param string $sClassName
 687:      * @return array|bool
 688:      */
 689:     protected function _findReverseJoinPartner($sParentClass, $sClassName) {
 690:         // Make the parameters lowercase, as get_class is buggy
 691:         $sClassName = cString::toLowerCase($sClassName);
 692:         $sParentClass = cString::toLowerCase($sParentClass);
 693: 
 694:         // Check if we found a direct link
 695:         if (in_array($sClassName, $this->_JoinPartners)) {
 696:             $obj = new $sClassName();
 697:             return array(
 698:                 'desttable' => $obj->table,
 699:                 'destclass' => $sClassName,
 700:                 'sourceclass' => $sParentClass,
 701:                 'key' => $obj->getPrimaryKeyName()
 702:             );
 703:         } else {
 704:             // Recurse all items
 705:             foreach ($this->_JoinPartners as $join => $tmpClassname) {
 706:                 $obj = new $tmpClassname();
 707:                 $status = $obj->_findReverseJoinPartner($tmpClassname, $sClassName);
 708: 
 709:                 if (is_array($status)) {
 710:                     $returns = array();
 711: 
 712:                     if (!isset($status['desttable'])) {
 713:                         foreach ($status as $subitem) {
 714:                             $returns[] = $subitem;
 715:                         }
 716:                     } else {
 717:                         $returns[] = $status;
 718:                     }
 719: 
 720:                     $returns[] = array(
 721:                         'desttable' => $obj->table,
 722:                         'destclass' => $tmpClassname,
 723:                         'sourceclass' => $sParentClass,
 724:                         'key' => $obj->getPrimaryKeyName()
 725:                     );
 726:                     return $returns;
 727:                 }
 728:             }
 729:         }
 730:         return false;
 731:     }
 732: 
 733:     /**
 734:      * Selects all entries from the database.
 735:      * Objects are loaded using their primary key.
 736:      *
 737:      * @param string $sWhere   [optional]
 738:      *                         Specifies the where clause.
 739:      * @param string $sGroupBy [optional]
 740:      *                         Specifies the group by clause.
 741:      * @param string $sOrderBy [optional]
 742:      *                         Specifies the order by clause.
 743:      * @param string $sLimit   [optional]
 744:      *                         Specifies the limit by clause.
 745:      *
 746:      * @return bool
 747:      *         True on success, otherwise false
 748:      * @throws cDbException
 749:      */
 750:     public function select($sWhere = '', $sGroupBy = '', $sOrderBy = '', $sLimit = '') {
 751:         unset($this->objects);
 752: 
 753:         if ($sWhere == '') {
 754:             $sWhere = '';
 755:         } else {
 756:             $sWhere = ' WHERE ' . $sWhere;
 757:         }
 758: 
 759:         if ($sGroupBy != '') {
 760:             $sGroupBy = ' GROUP BY ' . $sGroupBy;
 761:         }
 762: 
 763:         if ($sOrderBy != '') {
 764:             $sOrderBy = ' ORDER BY ' . $sOrderBy;
 765:         }
 766: 
 767:         if ($sLimit != '') {
 768:             $sLimit = ' LIMIT ' . $sLimit;
 769:         }
 770: 
 771:         $sFields = ($this->_settings['select_all_mode']) ? '*' : $this->getPrimaryKeyName();
 772:         $sql = 'SELECT ' . $sFields . ' FROM `' . $this->table . '`' . $sWhere . $sGroupBy . $sOrderBy . $sLimit;
 773:         $this->db->query($sql);
 774:         $this->_lastSQL = $sql;
 775:         $this->_bAllMode = $this->_settings['select_all_mode'];
 776: 
 777:         if ($this->db->numRows() == 0) {
 778:             return false;
 779:         } else {
 780:             return true;
 781:         }
 782:     }
 783: 
 784:     /**
 785:      * Selects all entries from the database.
 786:      * Objects are loaded using their primary key.
 787:      *
 788:      * @param string $sDistinct [optional]
 789:      *                          Specifies if distinct will be added to the SQL statement
 790:      *                          ($sDistinct !== '' -> DISTINCT)
 791:      * @param string $sFrom     [optional]
 792:      *                          Specifies the additional from clause (e.g.
 793:      *                          'con_news_groups AS groups, con_news_groupmembers AS groupmembers').
 794:      * @param string $sWhere    [optional]
 795:      *                          Specifies the where clause.
 796:      * @param string $sGroupBy  [optional]
 797:      *                          Specifies the group by clause.
 798:      * @param string $sOrderBy  [optional]
 799:      *                          Specifies the order by clause.
 800:      * @param string $sLimit    [optional]
 801:      *                          Specifies the limit by clause.
 802:      * @return bool
 803:      *                          True on success, otherwise false
 804:      * @throws cDbException
 805:      */
 806:     public function flexSelect($sDistinct = '', $sFrom = '', $sWhere = '', $sGroupBy = '', $sOrderBy = '', $sLimit = '') {
 807:         unset($this->objects);
 808: 
 809:         if ($sDistinct != '') {
 810:             $sDistinct = 'DISTINCT ';
 811:         }
 812: 
 813:         if ($sFrom != '') {
 814:             $sFrom = ', ' . $sFrom;
 815:         }
 816: 
 817:         if ($sWhere != '') {
 818:             $sWhere = ' WHERE ' . $sWhere;
 819:         }
 820: 
 821:         if ($sGroupBy != '') {
 822:             $sGroupBy = ' GROUP BY ' . $sGroupBy;
 823:         }
 824: 
 825:         if ($sOrderBy != '') {
 826:             $sOrderBy = ' ORDER BY ' . $sOrderBy;
 827:         }
 828: 
 829:         if ($sLimit != '') {
 830:             $sLimit = ' LIMIT ' . $sLimit;
 831:         }
 832: 
 833:         $sql = 'SELECT ' . $sDistinct . cString::toLowerCase(get_class($this)) . '.' . $this->getPrimaryKeyName() . ' AS ' . $this->getPrimaryKeyName() . ' FROM `' . $this->table . '` AS ' . cString::toLowerCase(get_class($this)) . $sFrom . $sWhere . $sGroupBy . $sOrderBy . $sLimit;
 834: 
 835:         $this->db->query($sql);
 836:         $this->_lastSQL = $sql;
 837:         // @todo disable all mode in this method
 838:         $this->_bAllMode = false;
 839: 
 840:         if ($this->db->numRows() == 0) {
 841:             return false;
 842:         } else {
 843:             return true;
 844:         }
 845:     }
 846: 
 847:     /**
 848:      * Checks if a specific entry exists.
 849:      *
 850:      * @param mixed $mId
 851:      *         The id to check for (could be numeric or string)
 852:      * @return bool
 853:      *         True if object exists, false if not
 854:      * @throws cDbException
 855:      */
 856:     public function exists($mId) {
 857:         $oDb = $this->_getSecondDBInstance();
 858:         $sql = "SELECT `%s` FROM %s WHERE %s='%s'";
 859:         $oDb->query($sql, $this->getPrimaryKeyName(), $this->table, $this->getPrimaryKeyName(), $mId);
 860:         return ($oDb->nextRecord()) ? true : false;
 861:     }
 862: 
 863:     /**
 864:      * Advances to the next item in the database.
 865:      *
 866:      * @return Item|bool
 867:      *         next object, or false if no more objects
 868:      * @throws cDbException
 869:      * @throws cException
 870:      */
 871:     public function next() {
 872:         $ret = false;
 873:         while ($this->db->nextRecord()) {
 874:             if ($this->_bAllMode) {
 875:                 $aRs = $this->db->toArray(cDb::FETCH_BOTH);
 876:                 $ret = $this->loadItem($aRs);
 877:             } else {
 878:                 $ret = $this->loadItem($this->db->f($this->getPrimaryKeyName()));
 879:             }
 880: 
 881:             if ($ret->get($this->getPrimaryKeyName()) == "") {
 882:                 continue;
 883:             } else {
 884:                 break;
 885:             }
 886:         }
 887:         return $ret;
 888:     }
 889: 
 890:     /**
 891:      * Fetches the resultset related to current loaded primary key as an object.
 892:      *
 893:      * @param string $sClassName
 894:      * @return Item
 895:      * @throws cException
 896:      */
 897:     public function fetchObject($sClassName) {
 898:         $sKey = cString::toLowerCase($sClassName);
 899: 
 900:         if (!is_object($this->_collectionCache[$sKey])) {
 901:             $this->_collectionCache[$sKey] = new $sClassName();
 902:         }
 903:         /* @var $obj ItemCollection */
 904:         $obj = $this->_collectionCache[$sKey];
 905:         return $obj->loadItem($this->db->f($obj->getPrimaryKeyName()));
 906:     }
 907: 
 908:     /**
 909:      * Notes:
 910:      * If the array contains keys, the key will be used as alias for the field.
 911:      * Example: array('id' => 'idcat') will put 'idcat' into field 'id'
 912:      * $aObjects = array with the objects to fetch. Notes: If the array contains
 913:      * keys, the key will be used as alias for the object. If you specify more
 914:      * than one object with the same key, the array will be multi-dimensional.
 915:      *
 916:      * @param array $aFields  [optional]
 917:      *                        array with the fields to fetch
 918:      * @param array $aObjects [optional]
 919:      * @return array
 920:      * @throws cDbException
 921:      * @throws cException
 922:      */
 923:     public function fetchTable(array $aFields = array(), array $aObjects = array()) {
 924:         $row = 1;
 925:         $aTable = array();
 926: 
 927:         if ($this->count() > 0) {
 928: 
 929:             $this->db->seek(0);
 930: 
 931:             while ($this->db->nextRecord()) {
 932:                 foreach ($aFields as $alias => $field) {
 933:                     if ($alias != '') {
 934:                         $aTable[$row][$alias] = $this->db->f($field);
 935:                     } else {
 936:                         $aTable[$row][$field] = $this->db->f($field);
 937:                     }
 938:                 }
 939: 
 940:                 // Fetch objects
 941:                 foreach ($aObjects as $alias => $object) {
 942:                     if ($alias != '') {
 943:                         if (isset($aTable[$row][$alias])) {
 944:                             // Is set, check for array. If no array, create one
 945:                             if (is_array($aTable[$row][$alias])) {
 946:                                 $aTable[$row][$alias][] = $this->fetchObject($object);
 947:                             } else {
 948:                                 // $tmpObj = $aTable[$row][$alias];
 949:                                 $aTable[$row][$alias] = array();
 950:                                 $aTable[$row][$alias][] = $this->fetchObject($object);
 951:                             }
 952:                         } else {
 953:                             $aTable[$row][$alias] = $this->fetchObject($object);
 954:                         }
 955:                     } else {
 956:                         $aTable[$row][$object] = $this->fetchObject($object);
 957:                     }
 958:                 }
 959:                 $row++;
 960:             }
 961: 
 962:             $this->db->seek(0);
 963: 
 964:         }
 965: 
 966:         return $aTable;
 967:     }
 968: 
 969:     /**
 970:      * Returns an array of arrays
 971:      *
 972:      * @param array $aObjects
 973:      *         With the correct order of the objects
 974:      * @return array
 975:      *         Result
 976:      * @throws cDbException
 977:      * @throws cException
 978:      */
 979:     public function queryAndFetchStructured(array $aObjects) {
 980:         $aOrder = array();
 981:         $aFetchObjects = array();
 982:         $aResult = array();
 983: 
 984:         foreach ($aObjects as $object) {
 985:             $x = new $object();
 986:             $object = cString::toLowerCase($object);
 987:             $aOrder[] = $object . '.' . $x->getPrimaryKeyName() . ' ASC';
 988:             $aFetchObjects[] = $x;
 989:         }
 990: 
 991:         $this->setOrder(implode(', ', $aOrder));
 992:         $this->query();
 993: 
 994:         $this->db->seek(0);
 995: 
 996:         while ($this->db->nextRecord()) {
 997:             $aResult = $this->_recursiveStructuredFetch($aFetchObjects, $aResult);
 998:         }
 999: 
1000:         return $aResult;
1001:     }
1002: 
1003:     /**
1004:      *
1005:      * @param array $aObjects
1006:      * @param array $aResult
1007:      * @return array
1008:      */
1009:     protected function _recursiveStructuredFetch(array $aObjects, array $aResult) {
1010:         $i = array_shift($aObjects);
1011: 
1012:         $value = $this->db->f($i->getPrimaryKeyName());
1013: 
1014:         if (!is_null($value)) {
1015:             $aResult[$value]['class'] = cString::toLowerCase(get_class($i));
1016:             $aResult[$value]['object'] = $i->loadItem($value);
1017: 
1018:             if (count($aObjects) > 0) {
1019:                 $aResult[$value]['items'] = $this->_recursiveStructuredFetch($aObjects, $aResult[$value]['items']);
1020:             }
1021:         }
1022: 
1023:         return $aResult;
1024:     }
1025: 
1026:     /**
1027:      * Returns the amount of returned items
1028:      *
1029:      * @return int
1030:      *         Number of rows
1031:      */
1032:     public function count() {
1033:         return $this->db->numRows();
1034:     }
1035: 
1036:     /**
1037:      * Loads a single entry by it's id.
1038:      *
1039:      * @param string|int $id
1040:      *         The primary key of the item to load.
1041:      * @return Item
1042:      *         The loaded item
1043:      * @throws cException
1044:      */
1045:     public function fetchById($id) {
1046:         if (is_numeric($id)) {
1047:             $id = (int) $id;
1048:         } elseif (is_string($id)) {
1049:             $id = $this->escape($id);
1050:         }
1051:         return $this->loadItem($id);
1052:     }
1053: 
1054:     /**
1055:      * Loads a single object from the database.
1056:      *
1057:      * @param mixed $mItem
1058:      *         The primary key of the item to load or a recordset with itemdata
1059:      *         (array) to inject to the item object.
1060:      * @throws cException
1061:      *         If item class is not set
1062:      * @return Item
1063:      *         The newly created object
1064:      */
1065:     public function loadItem($mItem) {
1066:         if (empty($this->_itemClass)) {
1067:             $sMsg = "ItemClass has to be set in the constructor of class " . get_class($this) . ")";
1068:             throw new cException($sMsg);
1069:         }
1070: 
1071:         if (!is_object($this->_iteratorItem)) {
1072:             $this->_iteratorItem = new $this->_itemClass();
1073:         }
1074:         $obj = clone $this->_iteratorItem;
1075: 
1076:         if (is_array($mItem)) {
1077:             $obj->loadByRecordSet($mItem);
1078:         } else {
1079:             $obj->loadByPrimaryKey($mItem);
1080:         }
1081: 
1082:         return $obj;
1083:     }
1084: 
1085:     /**
1086:      * Creates a new item in the table and loads it afterwards.
1087:      *
1088:      * @param string|array $data [optional]
1089:      *                           optional parameter for direct input of primary key value
1090:      *                           (string) or multiple column name - value pairs
1091:      *
1092:      * @return Item
1093:      *                           The newly created object
1094:      * @throws cInvalidArgumentException
1095:      * @throws cDbException
1096:      * @throws cException
1097:      */
1098:     public function createNewItem($data = NULL) {
1099:         $this->_executeCallbacks(self::CREATE_BEFORE, get_class($this), array());
1100: 
1101:         $db = $this->_getSecondDBInstance();
1102: 
1103:         $primaryKeyValue = NULL;
1104:         // prepare the primary key value and the data depending on the type of
1105:         // $data
1106:         if (is_array($data)) {
1107:             if (array_key_exists($this->getPrimaryKeyName(), $data)) {
1108:                 $primaryKeyValue = $data[$this->getPrimaryKeyName()];
1109:             }
1110:         } else {
1111:             // data is the primary key
1112:             $primaryKeyValue = $data;
1113:             $data = array(
1114:                 $this->getPrimaryKeyName() => $data
1115:             );
1116:         }
1117: 
1118:         // build the insert statement and execute it
1119:         $sql = $db->buildInsert($this->table, $data);
1120: 
1121:         $db->query($sql);
1122: 
1123:         if ($primaryKeyValue === NULL) {
1124:             $primaryKeyValue = $db->getLastInsertedId();
1125:         }
1126: 
1127:         if ($db->affectedRows() == 0) {
1128:             $this->_executeCallbacks(self::CREATE_FAILURE, $this->_itemClass, array());
1129:         } else {
1130:             $this->_executeCallbacks(self::CREATE_SUCCESS, $this->_itemClass, array(
1131:                 $primaryKeyValue
1132:             ));
1133:         }
1134: 
1135:         return $this->loadItem($primaryKeyValue);
1136:     }
1137: 
1138:     /**
1139:      * Inserts a new item entry by using a existing item entry.
1140:      *
1141:      * @param object $srcItem
1142:      *                                  Source Item instance to copy
1143:      * @param array  $fieldsToOverwrite [optional]
1144:      *                                  Assoziative list of fields to overwrite.
1145:      * @return Item|NULL
1146:      * @throws cDbException
1147:      * @throws cException
1148:      * @throws cInvalidArgumentException If Item class doesn't match the defined _itemClass property
1149:      *                                  or passed Item instance has no loaded recordset
1150:      */
1151:     public function copyItem($srcItem, array $fieldsToOverwrite = array()) {
1152:         if (get_class($srcItem) !== $this->_itemClass) {
1153:             throw new cInvalidArgumentException("Item class doesn't match");
1154:         } elseif (!$srcItem->isLoaded()) {
1155:             throw new cInvalidArgumentException("Item instance has no loaded recordset");
1156:         }
1157: 
1158:         $destItem = self::createNewItem();
1159:         if (!is_object($destItem)) {
1160:             return NULL;
1161:         }
1162: 
1163:         $rs = $srcItem->toArray();
1164: 
1165:         foreach ($rs as $field => $value) {
1166:             if (is_numeric($field)) {
1167:                 // Skip index based field
1168:                 continue;
1169:             } elseif ($field == $this->getPrimaryKeyName()) {
1170:                 // Skip primary key
1171:                 continue;
1172:             }
1173: 
1174:             if (isset($fieldsToOverwrite[$field])) {
1175:                 $value = $fieldsToOverwrite[$field];
1176:             }
1177: 
1178:             $destItem->set($field, $value);
1179:         }
1180: 
1181:         $destItem->store();
1182:         return $destItem;
1183:     }
1184: 
1185:     /**
1186:      * Returns all ids of recordsets in the table matching the rules in the
1187:      * passed where clause.
1188:      *
1189:      * @param string $sWhere
1190:      *         The where clause of the SQL statement
1191:      * @return array
1192:      *         List of ids
1193:      * @throws cDbException
1194:      */
1195:     public function getIdsByWhereClause($sWhere) {
1196:         $oDb = $this->_getSecondDBInstance();
1197: 
1198:         $aIds = array();
1199: 
1200:         // Get all ids
1201:         $sql = 'SELECT ' . $this->getPrimaryKeyName() . ' AS pk FROM `' . $this->table . '` WHERE ' . $sWhere;
1202:         $oDb->query($sql);
1203:         while ($oDb->nextRecord()) {
1204:             $aIds[] = $oDb->f('pk');
1205:         }
1206: 
1207:         return $aIds;
1208:     }
1209: 
1210:     /**
1211:      * Returns all specified fields of recordsets in the table matching the
1212:      * rules in the passed where clause.
1213:      *
1214:      * @param array  $aFields
1215:      *         List of fields to get
1216:      * @param string $sWhere
1217:      *         The where clause of the SQL statement
1218:      * @return array
1219:      *         List of entries with specified fields
1220:      * @throws cDbException
1221:      */
1222:     public function getFieldsByWhereClause(array $aFields, $sWhere) {
1223:         $oDb = $this->_getSecondDBInstance();
1224: 
1225:         $aEntries = array();
1226: 
1227:         if (count($aFields) == 0) {
1228:             return $aEntries;
1229:         }
1230: 
1231:         // Delete multiple db entries at once
1232:         $aEscapedFields = array_map(array(
1233:             $oDb,
1234:             'escape'
1235:         ), $aFields);
1236: 
1237:         $fields = implode(', ', $aEscapedFields);
1238: 
1239:         // Get all fields
1240:         $sql = 'SELECT ' . $fields . ' FROM `' . $this->table . '` WHERE ' . $sWhere;
1241:         $oDb->query($sql);
1242:         while ($oDb->nextRecord()) {
1243:             $data = array();
1244:             foreach ($aFields as $field) {
1245:                 $data[$field] = $oDb->f($field);
1246:             }
1247:             $aEntries[] = $data;
1248:         }
1249: 
1250:         return $aEntries;
1251:     }
1252: 
1253:     /**
1254:      * Returns all ids of recordsets in the table.
1255:      *
1256:      * @return array
1257:      *         List of ids
1258:      * @throws cDbException
1259:      */
1260:     public function getAllIds() {
1261:         $oDb = $this->_getSecondDBInstance();
1262: 
1263:         $aIds = array();
1264: 
1265:         // Get all ids
1266:         $sql = 'SELECT ' . $this->getPrimaryKeyName() . ' AS pk FROM `' . $this->table . '`';
1267:         $oDb->query($sql);
1268:         while ($oDb->nextRecord()) {
1269:             $aIds[] = $oDb->f('pk');
1270:         }
1271: 
1272:         return $aIds;
1273:     }
1274: 
1275:     /**
1276:      * Deletes an item in the table.
1277:      * Deletes also cached e entry and any existing properties.
1278:      *
1279:      * @param mixed $mId
1280:      *         Id of entry to delete
1281:      * 
1282:      * @return bool
1283:      * 
1284:      * @throws cDbException
1285:      * @throws cInvalidArgumentException
1286:      */
1287:     public function delete($mId) {
1288:         $result = $this->_delete($mId);
1289:         return $result;
1290:     }
1291: 
1292:     /**
1293:      * Deletes all found items in the table matching the rules in the passed
1294:      * where clause.
1295:      * Deletes also cached e entries and any existing properties.
1296:      *
1297:      * @param string $sWhere
1298:      *         The where clause of the SQL statement
1299:      * @return int
1300:      *         Number of deleted entries
1301:      * @throws cDbException
1302:      * @throws cInvalidArgumentException
1303:      */
1304:     public function deleteByWhereClause($sWhere) {
1305:         // Get all ids and delete related entries
1306:         $aIds = $this->getIdsByWhereClause($sWhere);
1307: 
1308:         if (!is_array($aIds) || 0 >= count($aIds)) {
1309:             return 0;
1310:         }
1311: 
1312:         $numDeleted = $this->_deleteMultiple($aIds);
1313:         return $numDeleted;
1314:     }
1315: 
1316:     /**
1317:      * Deletes all found items in the table matching the passed field and it's
1318:      * value.
1319:      * Deletes also cached e entries and any existing properties.
1320:      *
1321:      * @param string $sField
1322:      *         The field name
1323:      * @param mixed  $mValue
1324:      *         The value of the field
1325:      *
1326:      * @return int
1327:      *         Number of deleted entries
1328:      *
1329:      * @throws cDbException
1330:      * @throws cInvalidArgumentException
1331:      */
1332:     public function deleteBy($sField, $mValue) {
1333:         $where = (is_string($mValue)) ? "`%s` = '%s'" : "`%s` = %d";
1334:         $where = $this->db->prepare($where, $sField, $mValue);
1335: 
1336:         return $this->deleteByWhereClause($where);
1337:     }
1338: 
1339:     /**
1340:      * Deletes an item in the table, deletes also existing cache entries and
1341:      * properties of the item.
1342:      *
1343:      * @param mixed $mId
1344:      *         Id of entry to delete
1345:      * @return bool
1346:      * 
1347:      * @throws cDbException
1348:      * @throws cInvalidArgumentException
1349:      */
1350:     protected function _delete($mId) {
1351:         $this->_executeCallbacks(self::DELETE_BEFORE, $this->_itemClass, array(
1352:             $mId
1353:         ));
1354: 
1355:         $oDb = $this->_getSecondDBInstance();
1356: 
1357:         // delete db entry
1358:         $sql = "DELETE FROM `%s` WHERE %s = '%s'";
1359:         $oDb->query($sql, $this->table, $this->getPrimaryKeyName(), $mId);
1360:         $success = $oDb->affectedRows();
1361: 
1362:         // delete cache entry
1363:         $this->_oCache->removeItem($mId);
1364: 
1365:         // delete the property values
1366:         $oProperties = $this->_getPropertiesCollectionInstance();
1367:         $oProperties->deleteProperties($this->getPrimaryKeyName(), $mId);
1368: 
1369:         if ($success == 0) {
1370:             $this->_executeCallbacks(self::DELETE_FAILURE, $this->_itemClass, array(
1371:                 $mId
1372:             ));
1373:             return false;
1374:         } else {
1375:             $this->_executeCallbacks(self::DELETE_SUCCESS, $this->_itemClass, array(
1376:                 $mId
1377:             ));
1378:             return true;
1379:         }
1380:     }
1381: 
1382:     /**
1383:      * Deletes all items in the table, deletes also existing cache entries and
1384:      * properties of the item.
1385:      *
1386:      * @param array $aIds
1387:      *         Id of entries to delete (has to be called w/ an array!)
1388:      * @return int
1389:      *         Number of affected records
1390:      * @throws cDbException
1391:      * @throws cInvalidArgumentException
1392:      */
1393:     protected function _deleteMultiple(array $aIds) {
1394:         foreach ($aIds as $mId) {
1395:             $this->_executeCallbacks(self::DELETE_BEFORE, $this->_itemClass, array(
1396:                 $mId
1397:             ));
1398:         }
1399: 
1400:         $oDb = $this->_getSecondDBInstance();
1401: 
1402:         // Delete multiple db entries at once
1403:         $aEscapedIds = array_map(array(
1404:             $oDb,
1405:             'escape'
1406:         ), $aIds);
1407:         $in = "'" . implode("', '", $aEscapedIds) . "'";
1408:         $sql = "DELETE FROM `%s` WHERE %s IN (" . $in . ")";
1409:         $oDb->query($sql, $this->table, $this->getPrimaryKeyName());
1410:         $numAffected = $oDb->affectedRows();
1411: 
1412:         // Delete cache entries
1413:         $this->_oCache->removeItems($aIds);
1414: 
1415:         // Delete the property values
1416:         $oProperties = $this->_getPropertiesCollectionInstance();
1417:         $oProperties->deletePropertiesMultiple($this->getPrimaryKeyName(), $aIds);
1418: 
1419:         // NOTE: Deleteing multiple entries at once has a drawback. There is no
1420:         // way to detect faulty ids, if one or more entries couldn't deleted.
1421:         if ($numAffected == 0) {
1422:             foreach ($aIds as $mId) {
1423:                 $this->_executeCallbacks(self::DELETE_FAILURE, $this->_itemClass, array(
1424:                     $mId
1425:                 ));
1426:             }
1427:         } else {
1428:             foreach ($aIds as $mId) {
1429:                 $this->_executeCallbacks(self::DELETE_SUCCESS, $this->_itemClass, array(
1430:                     $mId
1431:                 ));
1432:             }
1433:         }
1434:         return $numAffected;
1435:     }
1436: 
1437:     /**
1438:      * Fetches an array of fields from the database.
1439:      *
1440:      * Example:
1441:      * $i = $object->fetchArray('idartlang', array('idlang', 'name'));
1442:      *
1443:      * could result in:
1444:      * $i[5] = array('idlang' => 5, 'name' => 'My Article');
1445:      *
1446:      * Important: If you don't pass an array for fields, the function
1447:      * doesn't create an array.
1448:      *
1449:      * @param string $sKey
1450:      *         Name of the field to use for the key
1451:      * @param mixed  $mFields
1452:      *         String or array
1453:      * @return array
1454:      *         Resulting array
1455:      * @throws cDbException
1456:      * @throws cException
1457:      */
1458:     public function fetchArray($sKey, $mFields) {
1459:         $aResult = array();
1460: 
1461:         while (($item = $this->next()) !== false) {
1462:             if (is_array($mFields)) {
1463:                 foreach ($mFields as $value) {
1464:                     $aResult[$item->get($sKey)][$value] = $item->get($value);
1465:                 }
1466:             } else {
1467:                 $aResult[$item->get($sKey)] = $item->get($mFields);
1468:             }
1469:         }
1470: 
1471:         return $aResult;
1472:     }
1473: }
1474: 
CMS CONTENIDO 4.10.0 API documentation generated by ApiGen 2.8.0